├── android ├── settings_aar.gradle ├── .gitignore ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── drawable │ │ │ │ │ ├── ic_splash.webp │ │ │ │ │ ├── launch_background.xml │ │ │ │ │ └── ic_launcher_foreground.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── values │ │ │ │ │ ├── colors.xml │ │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── drawable-v23 │ │ │ │ │ └── launch_background.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── xyz │ │ │ │ │ └── arjunsinh │ │ │ │ │ └── elderlauncher │ │ │ │ │ ├── Constants.kt │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── build.gradle ├── fastlane └── metadata │ └── android │ ├── en-US │ ├── title.txt │ ├── short_description.txt │ ├── images │ │ ├── icon.png │ │ ├── featureGraphic.jpg │ │ └── phoneScreenshots │ │ │ ├── 1.png │ │ │ ├── 2.png │ │ │ ├── 3.png │ │ │ ├── 4.png │ │ │ ├── 5.png │ │ │ ├── 6.png │ │ │ ├── 7.png │ │ │ ├── 8.png │ │ │ └── 9.png │ └── full_description.txt │ └── de │ └── short_description.txt ├── lib ├── constants │ ├── edit_mode.dart │ ├── channels.dart │ ├── custom_functions.dart │ ├── route_names.dart │ └── keys.dart ├── models │ ├── interfaces │ │ └── data_repository.dart │ └── item.dart ├── l10n │ ├── intl_en_US.arb │ ├── intl_en.arb │ ├── intl_it.arb │ ├── intl_hi.arb │ ├── intl_cs.arb │ ├── intl_nl.arb │ ├── intl_pl.arb │ ├── intl_de.arb │ ├── intl_pt.arb │ ├── intl_ru.arb │ ├── intl_es.arb │ └── intl_fr.arb ├── ui │ ├── common │ │ ├── loading_widget.dart │ │ ├── action_panel.dart │ │ ├── elder_page_scaffold.dart │ │ ├── buttons.dart │ │ └── info_action_widget.dart │ ├── pages │ │ ├── settings_page │ │ │ └── settings_page.dart │ │ ├── edit_page │ │ │ ├── multi_select_widget.dart │ │ │ └── edit_page.dart │ │ ├── reorder_page │ │ │ ├── reorder_widget.dart │ │ │ └── reorder_page.dart │ │ ├── home_page │ │ │ ├── call_dialog.dart │ │ │ ├── fav_grid_view.dart │ │ │ ├── edit_dialog.dart │ │ │ ├── apps_tab.dart │ │ │ ├── contacts_tab.dart │ │ │ └── home_page.dart │ │ └── app_drawer │ │ │ └── app_drawer.dart │ ├── colors.dart │ ├── router.dart │ └── theme.dart ├── providers │ ├── date_time_provider.dart │ ├── app_provider.dart │ └── contact_provider.dart ├── utils │ ├── shared_prefs.dart │ └── native_methods.dart ├── main.dart ├── generated │ ├── intl │ │ ├── messages_en_US.dart │ │ ├── messages_en.dart │ │ ├── messages_it.dart │ │ ├── messages_hi.dart │ │ ├── messages_cs.dart │ │ ├── messages_nl.dart │ │ ├── messages_de.dart │ │ ├── messages_pl.dart │ │ ├── messages_pt.dart │ │ ├── messages_ru.dart │ │ ├── messages_fr.dart │ │ ├── messages_es.dart │ │ └── messages_all.dart │ └── l10n.dart ├── services │ └── edit_service.dart └── data_sources │ ├── app_repository.dart │ └── contact_repository.dart ├── analysis_options.yaml ├── .metadata ├── .github └── workflows │ └── ci.yml ├── pubspec.yaml ├── .gitignore ├── test ├── test_utils │ ├── set_mock_apps.dart │ └── set_mock_contacts.dart ├── test_data │ ├── fake_contacts.dart │ └── fake_apps.dart └── data_sources │ ├── app_repository_test.dart │ └── contact_repository_test.dart ├── LICENSE ├── docs └── CONTRIBUTING.md ├── README.md └── pubspec.lock /android/settings_aar.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/title.txt: -------------------------------------------------------------------------------- 1 | Elder Launcher -------------------------------------------------------------------------------- /lib/constants/edit_mode.dart: -------------------------------------------------------------------------------- 1 | enum EditMode { apps, contacts } 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | a minimalistic launcher for seniors with large fonts & icons -------------------------------------------------------------------------------- /fastlane/metadata/android/de/short_description.txt: -------------------------------------------------------------------------------- 1 | ein minimalistischer Launcher für Senioren, mit großer Schrift und großen Icons -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /lib/constants/channels.dart: -------------------------------------------------------------------------------- 1 | const channelContacts = 'xyz.arjunsinh.elderlauncher/contacts'; 2 | const channelCore = 'xyz.arjunsinh.elderlauncher/core'; 3 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_splash.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/android/app/src/main/res/drawable/ic_splash.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #407577 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/featureGraphic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/fastlane/metadata/android/en-US/images/featureGraphic.jpg -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/8.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsarjunsinh/elder_launcher/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/9.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #407577 4 | -------------------------------------------------------------------------------- /lib/constants/custom_functions.dart: -------------------------------------------------------------------------------- 1 | import '../models/item.dart'; 2 | 3 | typedef VoidFunction = void Function(); 4 | typedef VoidStringFunction = void Function(String); 5 | typedef VoidItemFunction = void Function(Item); 6 | -------------------------------------------------------------------------------- /lib/constants/route_names.dart: -------------------------------------------------------------------------------- 1 | const homePageRoute = 'HomePage'; 2 | const appDrawerRoute = 'AppDrawer'; 3 | const editPageRoute = 'EditPage'; 4 | const reorderPageRoute = 'ReorderPage'; 5 | const settingsPageRoute = 'SettingsPage'; 6 | -------------------------------------------------------------------------------- /lib/models/interfaces/data_repository.dart: -------------------------------------------------------------------------------- 1 | import '../item.dart'; 2 | 3 | abstract class DataRepository { 4 | Future> getAllItems(); 5 | Future> getFavItems(); 6 | void setFavItems(List itemIds); 7 | } 8 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | # Fix for certain devices. https://issuetracker.google.com/issues/147096055 5 | android.bundle.enableUncompressedNativeLibs=false 6 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | analyzer: 4 | exclude: [lib/generated/**] 5 | 6 | linter: 7 | rules: 8 | avoid_void_async: true 9 | public_member_api_docs: false 10 | sort_child_properties_last: true 11 | -------------------------------------------------------------------------------- /lib/constants/keys.dart: -------------------------------------------------------------------------------- 1 | const keyFavApps = 'key_favourites_apps'; 2 | const keyFavContacts = 'key_favourite_contacts'; 3 | const keyDeprecatedFavApps = 'key_favorites'; 4 | const keyIsFirstRun = 'key_is_first_run'; 5 | const keySelectedTheme = 'key_selected_theme'; 6 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/xyz/arjunsinh/elderlauncher/Constants.kt: -------------------------------------------------------------------------------- 1 | package xyz.arjunsinh.elderlauncher 2 | 3 | const val CHANNEL_CONTACTS = "xyz.arjunsinh.elderlauncher/contacts"; 4 | const val CHANNEL_CORE = "xyz.arjunsinh.elderlauncher/core"; 5 | const val DEFAULT_LAUNCHER_INTENT_REQUEST_CODE = 1103 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /lib/l10n/intl_en_US.arb: -------------------------------------------------------------------------------- 1 | { 2 | "btnAddFavApps": "Add Favorite Apps", 3 | "btnAddFavContacts": "Add Favorite Contacts", 4 | "msgNoFavourites": "You haven't added any favorites", 5 | "msgNoContactsPermission": "To add favorite contacts to this screen, allow this app access to your contacts.", 6 | "dlgEditTitle": "Edit Favorites" 7 | } -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 0b8abb4724aa590dd0f429683339b1e045a1594d 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v23/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /lib/ui/common/loading_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class LoadingWidget extends StatelessWidget { 4 | const LoadingWidget({Key? key}) : super(key: key); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return const Expanded( 9 | child: Center( 10 | child: SizedBox( 11 | height: 150, 12 | width: 150, 13 | child: CircularProgressIndicator(), 14 | ), 15 | ), 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | flutter_test: 6 | name: Run flutter test and analyze 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: actions/setup-java@v1 11 | with: 12 | java-version: "12.x" 13 | - uses: subosito/flutter-action@v1 14 | with: 15 | channel: "stable" 16 | - run: flutter pub get 17 | - run: flutter analyze --no-fatal-infos --no-fatal-warnings 18 | - run: flutter test 19 | -------------------------------------------------------------------------------- /lib/models/item.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | import 'package:flutter/foundation.dart'; 3 | 4 | /// Data class used for storing app or contact info. 5 | @immutable 6 | class Item { 7 | /// App package name or Phone number 8 | final String id; 9 | 10 | final String name; 11 | final Uint8List? icon; 12 | 13 | const Item(this.id, this.name, this.icon); 14 | 15 | @override 16 | // ignore: type_annotate_public_apis, avoid_renaming_method_parameters 17 | bool operator ==(Object o) => o is Item && o.id.compareTo(id) == 0; 18 | 19 | @override 20 | int get hashCode => id.hashCode; 21 | } 22 | -------------------------------------------------------------------------------- /lib/ui/pages/settings_page/settings_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../../common/elder_page_scaffold.dart'; 3 | 4 | class SettingsPage extends StatelessWidget { 5 | const SettingsPage({Key? key}) : super(key: key); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return ElderPageScaffold( 10 | title: 'Settings', 11 | body: ListView( 12 | children: const [ 13 | Text('Appearance Settings'), 14 | CheckboxListTile( 15 | title: Text('Allow Homescreen Rotation'), 16 | value: true, 17 | onChanged: null, 18 | ), 19 | ], 20 | ), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: elder_launcher 2 | description: A launcher focused on simplicity and legibility. 3 | publish_to: 'none' 4 | version: 1.6+12 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | 9 | dependencies: 10 | auto_size_text: ^3.0.0 11 | contacts_service: ^0.6.3 12 | device_apps: ^2.1.1 13 | flutter: 14 | sdk: flutter 15 | flutter_localizations: 16 | sdk: flutter 17 | intl: ^0.17.0 18 | permission_handler: ^8.3.0 19 | provider: ^6.0.2 20 | shared_preferences: ^2.0.12 21 | 22 | dev_dependencies: 23 | flutter_lints: ^1.0.4 24 | flutter_test: 25 | sdk: flutter 26 | mockito: ^5.0.8 27 | 28 | flutter: 29 | uses-material-design: true 30 | 31 | flutter_intl: 32 | enabled: true 33 | -------------------------------------------------------------------------------- /lib/ui/colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /* Light - (Default) Teal */ 4 | const elderTeal = Color(0xFF407577); 5 | const elderBlueGrey = Color(0xFFCFD8DC); 6 | const elderDarkBlueGrey = Color(0xFFB1C4CB); 7 | const elderPink = Color(0xFFF9316A); 8 | const elderWhite = Color(0xFFF9F9F9); 9 | const youBlue = Color(0xFF6eb6d8); 10 | const youCard = Color(0xFFf1f5fb); 11 | const youPeach = Color(0xFFf2b8b5); 12 | 13 | /* Dark */ 14 | const elderDarkGrey = Color(0xFF444444); 15 | const elderDarkGreySecondary = Color(0xFF666666); 16 | const elderBlack = Color(0xFF222222); 17 | const youBlack = Color(0xFF303238); 18 | const youBlackDim = Color(0xFF1f1f1f); 19 | 20 | /* Common */ 21 | const elderGrey = Color(0xFFAAAAAA); 22 | -------------------------------------------------------------------------------- /lib/providers/date_time_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:intl/intl.dart'; 4 | 5 | class DateTimeProvider extends ChangeNotifier { 6 | late final Timer _timer; 7 | String _date = ''; 8 | String _time = ''; 9 | 10 | DateTimeProvider() { 11 | _timer = Timer.periodic( 12 | const Duration(seconds: 1), (timer) => _updateDateTime()); 13 | } 14 | 15 | String get date => _date; 16 | String get time => _time; 17 | 18 | @override 19 | void dispose() { 20 | _timer.cancel(); 21 | super.dispose(); 22 | } 23 | 24 | void _updateDateTime() { 25 | _time = DateFormat.jm().format(DateTime.now()); 26 | _date = DateFormat.MMMMEEEEd().format(DateTime.now()); 27 | notifyListeners(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Exceptions to above rules. 37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 38 | 39 | # Android Keystore 40 | android/key.* 41 | 42 | #IDE 43 | .vscode/ 44 | .idea/ -------------------------------------------------------------------------------- /test/test_utils/set_mock_apps.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:elder_launcher/constants/keys.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_test/flutter_test.dart'; 5 | import 'package:shared_preferences/shared_preferences.dart'; 6 | import '../test_data/fake_apps.dart'; 7 | 8 | void setMockApps() { 9 | SharedPreferences.setMockInitialValues( 10 | {'flutter.$keyFavApps': fakeFavPackageNames}); 11 | 12 | const deviceAppsChannel = MethodChannel('g123k/device_apps'); 13 | final allApps = fakeInstalledApps; 14 | 15 | TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger 16 | .setMockMethodCallHandler(deviceAppsChannel, (call) async { 17 | if (call.method == 'getInstalledApps') { 18 | return allApps; 19 | } 20 | 21 | if (call.method == 'getApp') { 22 | return allApps.firstWhereOrNull( 23 | (app) => app['package_name'] == call.arguments['package_name']); 24 | } 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 |

Elder Launcher is an app which makes using an Android device easy for the elderly or first-time smartphone users. It allows quickly accessing favourite apps and contacts.

2 |

Elder Launcher is a launcher designed for seniors focused on simplicity and legibility. It supports pinning favorite apps and contacts to the homescreen for quick access. You can easily phone call your favorite contacts from the homescreen.

3 |

The edit menu is useful for managing the home screen. It can be opened by clicking the pencil icon on the top right.

    4 |
  • You can add/remove favourite apps or contacts.
  • 5 |
  • You can also rearrange selected favourites.
  • 6 |
  • Lastly, in case a newly installed app isn‘t immediately visible then use the Reload option.
7 |

Elder Launcher’s clear layout with big icons and text, makes using phones very easy for everyone. If your phone has Android 10, then you can turn on Dark Mode in your settings app to make Elder Launcher dark with a black background.

8 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 8 | 13 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Arjunsinh Shridevsinh Jadeja 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/test_utils/set_mock_contacts.dart: -------------------------------------------------------------------------------- 1 | //import 'package:collection/collection.dart'; 2 | import 'package:elder_launcher/constants/keys.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_test/flutter_test.dart'; 5 | import 'package:shared_preferences/shared_preferences.dart'; 6 | import '../test_data/fake_contacts.dart'; 7 | 8 | void setMockContacts() { 9 | SharedPreferences.setMockInitialValues( 10 | {'flutter.$keyFavContacts': fakeFavContactNumbers}); 11 | 12 | const contactsChannel = 13 | MethodChannel('github.com/clovisnicolas/flutter_contacts'); 14 | final allContacts = fakeDeviceContacts; 15 | 16 | TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger 17 | .setMockMethodCallHandler(contactsChannel, (call) async { 18 | if (call.method == 'getContacts') { 19 | //print(call.arguments); 20 | //print("Returning"); 21 | return allContacts; 22 | } 23 | 24 | /*if (call.method == 'getApp') { 25 | return allContacts.firstWhereOrNull( 26 | (contact) => app['package_name'] == call.arguments['package_name']); 27 | }*/ 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 20 | 21 | -------------------------------------------------------------------------------- /lib/l10n/intl_en.arb: -------------------------------------------------------------------------------- 1 | { 2 | "Apps": "Apps", 3 | "Contacts": "Contacts", 4 | "btnAllApps": "All Apps", 5 | "btnAllContacts": "All Contacts", 6 | "btnAddFavApps": "Add Favourite Apps", 7 | "btnAddFavContacts": "Add Favourite Contacts", 8 | "btnBackToHome": "Back to Home", 9 | "btnGrantPermission": "Grant Permission", 10 | "btnSetDefaultLauncher": "Set as Default", 11 | "msgNoData": "No data available. Try again in a few seconds.", 12 | "msgNoFavourites": "You haven't added any favourites", 13 | "msgNoContactsPermission": "To add favourite contacts to this screen, allow this app access to your contacts.", 14 | "msgNoPhonePermission": "To make calling easier, allow this app to start phone calls for you.", 15 | "msgNotDefaultLauncher": "Open apps and contacts faster by making Elder Launcher the home app.", 16 | "dlgCall" : "Call", 17 | "dlgEditTitle": "Edit Favourites", 18 | "dlgCancel": "Cancel", 19 | "dlgOpenSettings": "Open Settings", 20 | "dlgAppsAddRemove": "Add/Remove Apps", 21 | "dlgAppsReorder": "Reorder Apps", 22 | "dlgAppsReload": "Reload App List", 23 | "dlgContactsAddRemove": "Add/Remove Contacts", 24 | "dlgContactsReorder": "Reorder Contacts", 25 | "dlgContactsReload": "Reload Contacts" 26 | } -------------------------------------------------------------------------------- /lib/l10n/intl_it.arb: -------------------------------------------------------------------------------- 1 | { 2 | "Apps": "Apps", 3 | "Contacts": "Contatti", 4 | "btnAllApps": "Tutte le App", 5 | "btnAllContacts": "Tutti contatti", 6 | "btnAddFavApps": "Aggiungi app preferite", 7 | "btnAddFavContacts": "Aggiungi contatti preferiti", 8 | "btnBackToHome": "Indietro", 9 | "btnGrantPermission": "Dai il permesso", 10 | "btnSetDefaultLauncher": "Imposta come predefinito", 11 | "msgNoData": "Non ci sono dei dati. Prego riprovare tra pochi secondi.", 12 | "msgNoFavourites": "Lei non ha aggiunto dei preferiti", 13 | "msgNoContactsPermission": "Per aggiungere contatti preferiti a questa schermata, pergo dare i permessi per accedere ai contatti.", 14 | "msgNoPhonePermission": "Abilita l'app a fare chiamate.", 15 | "msgNotDefaultLauncher": "Apri app e contatti più velocemente, impostando Elder Launcher come Home-App.", 16 | "dlgCall" : "Chiamare", 17 | "dlgEditTitle": "Modificare preferiti", 18 | "dlgCancel": "Annulla", 19 | "dlgAppsAddRemove": "Aggiungi/rimuovi app", 20 | "dlgAppsReorder": "Riorganizza le app", 21 | "dlgAppsReload": "Ricarica la lista delle app", 22 | "dlgContactsAddRemove": "Aggiungi/rimuovi contatti", 23 | "dlgContactsReorder": "Riorganizza i contatti", 24 | "dlgContactsReload": "Ricarica la lista dei contatti" 25 | } 26 | -------------------------------------------------------------------------------- /lib/utils/shared_prefs.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | class SharedPrefs { 4 | Future> getList(String key) async { 5 | var prefs = await SharedPreferences.getInstance(); 6 | var stringList = prefs.getStringList(key) ?? []; 7 | return stringList; 8 | } 9 | 10 | Future setList(String key, List stringList) async { 11 | var prefs = await SharedPreferences.getInstance(); 12 | await prefs.setStringList(key, stringList); 13 | } 14 | 15 | // ignore: avoid_positional_boolean_parameters 16 | Future getBool(String key, bool defaultValue) async { 17 | var prefs = await SharedPreferences.getInstance(); 18 | return prefs.getBool(key) ?? defaultValue; 19 | } 20 | 21 | // ignore: avoid_positional_boolean_parameters 22 | Future setBool(String key, bool value) async { 23 | var prefs = await SharedPreferences.getInstance(); 24 | await prefs.setBool(key, value); 25 | } 26 | 27 | Future getInt(String key, int defaultValue) async { 28 | var prefs = await SharedPreferences.getInstance(); 29 | return prefs.getInt(key) ?? defaultValue; 30 | } 31 | 32 | Future setInt(String key, int value) async { 33 | var prefs = await SharedPreferences.getInstance(); 34 | await prefs.setInt(key, value); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/l10n/intl_hi.arb: -------------------------------------------------------------------------------- 1 | { 2 | "Apps": "ऐप्स", 3 | "Contacts": "संपर्क", 4 | "btnAllApps": "सभी ऐप्स", 5 | "btnAllContacts": "सभी संपर्क", 6 | "btnAddFavApps": "पसंदीदा एप्लिकेशन जोड़ें", 7 | "btnAddFavContacts": "पसंदीदा संपर्क जोड़ें", 8 | "btnBackToHome": "वापस जाओ", 9 | "btnGrantPermission": "अनुमति प्रदान करें", 10 | "btnSetDefaultLauncher": "डिफ़ॉल्ट लॉन्चर बनाएं", 11 | "msgNoData": "कोई डेटा उपलब्ध नहीं है। कुछ क्षण में फिर से कोशिश करें।", 12 | "msgNoFavourites": "आपने कोई पसंदीदा नहीं जोड़ा है", 13 | "msgNoContactsPermission": "इस स्क्रीन पर पसंदीदा संपर्क जोड़ने के लिए, इस एप्लिकेशन को आपके संपर्क देखने दें।", 14 | "msgNoPhonePermission": "कॉलिंग को आसान बनाने के लिए, इस ऐप को आपके लिए फ़ोन कॉल शुरू करने की अनुमति दें।", 15 | "msgNotDefaultLauncher": "एल्डर लॉन्चर को होम एप बनाकर एप्स और कॉन्टैक्ट्स को तेजी से खोलें।", 16 | "dlgCall" : "कॉल", 17 | "dlgEditTitle": "पसंदीदा संपादित करें", 18 | "dlgCancel": "रद्द करें", 19 | "dlgOpenSettings": "सेटिंग्स खोलें", 20 | "dlgAppsAddRemove": "एप्लिकेशन जोड़ें/निकालें", 21 | "dlgAppsReorder": "्यवस्थित करें ऐप्स", 22 | "dlgAppsReload": "एप्लिकेशन सूची ताज़ा करें", 23 | "dlgContactsAddRemove": "संपर्क जोड़ें/निकालें", 24 | "dlgContactsReorder": "व्यवस्थित करें संपर्क", 25 | "dlgContactsReload": "संपर्क सूची ताज़ा करें" 26 | } -------------------------------------------------------------------------------- /lib/l10n/intl_cs.arb: -------------------------------------------------------------------------------- 1 | { 2 | "Apps": "Aplikace", 3 | "Contacts": "Kontakty", 4 | "btnAllApps": "Všechny Aplikace", 5 | "btnAllContacts": "Všechny Kontakty", 6 | "btnAddFavApps": "Přidat Oblíbené Aplikace", 7 | "btnAddFavContacts": "Přidat Oblíbené Kontakty", 8 | "btnBackToHome": "Zpět Domů", 9 | "btnGrantPermission": "Udělit Oprávnění", 10 | "btnSetDefaultLauncher": "Změnit na výchozi", 11 | "msgNoData": "Žádná data k dispozici. Zkuste znova za pár vteřin.", 12 | "msgNoFavourites": "Nepřidali jste žádné oblíbené položky", 13 | "msgNoContactsPermission": "Pro přidání oblíbených kontaktů na tuto stránku povolte této aplikaci přístup ke Kontaktům.", 14 | "msgNoPhonePermission": "Pro usnadnění volání, povolte této aplikaci zahájení hovorů.", 15 | "msgNotDefaultLauncher": "Otevírejte aplikace a kontakty rychleji tím, že si Elder Launcher uděláte domácí aplikaci.", 16 | "dlgCall" : "Volat", 17 | "dlgEditTitle": "Změnit Oblíbené", 18 | "dlgCancel": "Zrušit", 19 | "dlgOpenSettings": "Změnit nastavení", 20 | "dlgAppsAddRemove": "Přidat/Odebrat Aplikace", 21 | "dlgAppsReorder": "Přeuspořádat Aplikace", 22 | "dlgAppsReload": "Znovu načíst Seznam Aplikací", 23 | "dlgContactsAddRemove": "Přidat/Odebrat Kontakty", 24 | "dlgContactsReorder": "Přeuspořádat Kontakty", 25 | "dlgContactsReload": "Znovu načíst Kontakty" 26 | } 27 | -------------------------------------------------------------------------------- /lib/l10n/intl_nl.arb: -------------------------------------------------------------------------------- 1 | { 2 | "Apps": "Apps", 3 | "Contacts": "Contacten", 4 | "btnAllApps": "Alle apps", 5 | "btnAllContacts": "Alle contacten", 6 | "btnAddFavApps": "Voeg favoriete apps toe", 7 | "btnAddFavContacts": "Voeg favoriete contacten toe", 8 | "btnBackToHome": "Terug", 9 | "btnGrantPermission": "Toestemming verlenen", 10 | "btnSetDefaultLauncher": "Standaard instellen", 11 | "msgNoData": "Geen gegevens beschikbaar, probeer over een paar seconden opnieuw", 12 | "msgNoFavourites": "Je hebt nog geen favorieten toegevoegd", 13 | "msgNoContactsPermission": "Om favoriete contacten aan dit scherm toe te voegen, moet u deze app toegang geven tot uw contacten.", 14 | "msgNoPhonePermission": "Om het bellen te vergemakkelijken, kunt u deze app telefoongesprekken voor u laten starten.", 15 | "msgNotDefaultLauncher": "Open apps en contacten sneller door van Elder Launcher de thuis-app te maken.", 16 | "dlgCall" : "Bel", 17 | "dlgEditTitle": "Wijzig favorieten", 18 | "dlgCancel": "Annuleer", 19 | "dlgOpenSettings": "Instellingen veranderen", 20 | "dlgAppsAddRemove": "Toevoegen/verwijderen apps", 21 | "dlgAppsReorder": "Apps sorteren", 22 | "dlgAppsReload": "App lijst opnieuw laden", 23 | "dlgContactsAddRemove": "Toevoegen/verwijderen contacten", 24 | "dlgContactsReorder": "Contacten sorteren", 25 | "dlgContactsReload": "Contacten laden" 26 | } 27 | -------------------------------------------------------------------------------- /lib/l10n/intl_pl.arb: -------------------------------------------------------------------------------- 1 | { 2 | "Apps": "Aplikacje", 3 | "Contacts": "Kontakty", 4 | "btnAllApps": "Wszystkie Aplikacje", 5 | "btnAllContacts": "Wszystkie Kontakty", 6 | "btnAddFavApps": "Dodaj ulubione aplikacje", 7 | "btnAddFavContacts": "Dodaj ulubione kontakty", 8 | "btnBackToHome": "Wrócić do Domu", 9 | "btnGrantPermission": "Dać Pozwolenie", 10 | "btnSetDefaultLauncher": "Ustaw jako domyslną", 11 | "msgNoData": "Brak dostępnych danych. Spróbuj ponownie za kilka sekund.", 12 | "msgNoFavourites": "Nie dodałeś żadnych ulubionych.", 13 | "msgNoContactsPermission": "Aby dodać ulubione kontakty do tego ekranu, zezwól tej aplikacji na dostęp do swoich kontaktów.", 14 | "msgNoPhonePermission": "Aby ułatwić dzwonienie, zezwól tej aplikacji na nawiązywanie połączeń telefonicznych za Ciebie.", 15 | "msgNotDefaultLauncher": "Otwieraj aplikacje i kontakty szybciej, ustawiając Elder Launcher jako aplikację domową.", 16 | "dlgCall" : "Połączenie", 17 | "dlgEditTitle": "Zmień ulubione", 18 | "dlgCancel": "Anulować", 19 | "dlgOpenSettings": "Zmień ustawienia", 20 | "dlgAppsAddRemove": "Dodaj/Usuń Aplikacje", 21 | "dlgAppsReorder": "Zmień kolejność aplikacji", 22 | "dlgAppsReload": "Załaduj ponownie listę aplikacji", 23 | "dlgContactsAddRemove": "Dodaj/Usuń Kontakty", 24 | "dlgContactsReorder": "Zmień kolejność Kontakty", 25 | "dlgContactsReload": "Załaduj ponownie listę kontaktów" 26 | } -------------------------------------------------------------------------------- /lib/l10n/intl_de.arb: -------------------------------------------------------------------------------- 1 | { 2 | "Apps": "Apps", 3 | "Contacts": "Kontakte", 4 | "btnAllApps": "Alle Apps", 5 | "btnAllContacts": "Alle Kontakte", 6 | "btnAddFavApps": "Favorisierte Apps hinzufügen", 7 | "btnAddFavContacts": "Favorisierte Kontakte hinzufügen", 8 | "btnBackToHome": "Zurück", 9 | "btnGrantPermission": "Erlaubnis erteilen", 10 | "btnSetDefaultLauncher": "Als Standard festlegen", 11 | "msgNoData": "Keine Daten verfügbar. Versuchen Sie es in wenigen Sekunden erneut.", 12 | "msgNoFavourites": "Sie haben keine Favoriten hinzugefügt", 13 | "msgNoContactsPermission": "Um diesem Bildschirm favorisierte Kontakte hinzuzufügen, erlauben Sie dieser App den Zugriff auf Ihre Kontakte.", 14 | "msgNoPhonePermission": "Ermöglichen Sie dieser App, Anrufe für Sie zu starten, um das Telefonieren zu vereinfachen.", 15 | "msgNotDefaultLauncher": "Öffnen Sie Apps und Kontakte schneller, indem Sie Elder Launcher zur Heim-App machen.", 16 | "dlgCall" : "Anrufen", 17 | "dlgEditTitle": "Favoriten bearbeiten", 18 | "dlgCancel": "Abbrechen", 19 | "dlgOpenSettings": "Einstellungen ändern", 20 | "dlgAppsAddRemove": "Apps hinzufügen/entfernen", 21 | "dlgAppsReorder": "Apps neu anordnen", 22 | "dlgAppsReload": "App Liste neu laden", 23 | "dlgContactsAddRemove": "Kontakte hinzufügen/entfernen", 24 | "dlgContactsReorder": "Kontakte neu anordnen", 25 | "dlgContactsReload": "Kontaktliste neu laden" 26 | } 27 | -------------------------------------------------------------------------------- /lib/l10n/intl_pt.arb: -------------------------------------------------------------------------------- 1 | { 2 | "Apps": "Apps", 3 | "Contacts": "Contatos", 4 | "btnAllApps": "Todos os apps", 5 | "btnAllContacts": "Todos os contatos", 6 | "btnAddFavApps": "Adicionar Aplicativos favoritos", 7 | "btnAddFavContacts": "Adicionar Contatos favoritos", 8 | "btnBackToHome": "Volte", 9 | "btnGrantPermission": "Conceder permissão", 10 | "btnSetDefaultLauncher": "Definir como predef.", 11 | "msgNoData": "Não há dados disponíveis. Tente novamente em alguns segundos.", 12 | "msgNoFavourites": "Você não adicionou nenhum favorito", 13 | "msgNoContactsPermission": "Para adicionar contatos favoritos a essa tela, permita que este aplicativo acesse seus contatos.", 14 | "msgNoPhonePermission": "Para facilitar as chamadas, permita que este aplicativo inicie chamadas telefônicas para você.", 15 | "msgNotDefaultLauncher": "Abra aplicativos e contatos mais rapidamente, tornando o Elder Launcher o aplicativo doméstico.", 16 | "dlgCall" : "Ligar", 17 | "dlgEditTitle": "Editar Favoritos", 18 | "dlgCancel": "Cancelar", 19 | "dlgOpenSettings": "Mudar configurações", 20 | "dlgAppsAddRemove": "Adicionar/Remover Aplicativos", 21 | "dlgAppsReorder": "Reordenar Aplicativos", 22 | "dlgAppsReload": "Recarregar lista de aplicativos", 23 | "dlgContactsAddRemove": "Adicionar/Remover Contatos", 24 | "dlgContactsReorder": "Reordenar Contatos", 25 | "dlgContactsReload": "Recarregar lista de contatos" 26 | } -------------------------------------------------------------------------------- /lib/l10n/intl_ru.arb: -------------------------------------------------------------------------------- 1 | { 2 | "Apps": "программы", 3 | "Contacts": "контакты", 4 | "btnAllApps": "Все программы", 5 | "btnAllContacts": "Все контакты", 6 | "btnAddFavApps": "Добавить любимые программы", 7 | "btnAddFavContacts": "Добавить избранные контакты", 8 | "btnBackToHome": "Вернуться домой", 9 | "btnGrantPermission": "Предоставить разрешение", 10 | "btnSetDefaultLauncher": "По умолчанию", 11 | "msgNoData": "Данные недоступны. Попробуйте еще раз через несколько секунд.", 12 | "msgNoFavourites": "Вы не добавили ни одного избранного", 13 | "msgNoContactsPermission": "Чтобы добавить избранные контакты на этот экран, разрешите этому приложению доступ к вашим контактам.", 14 | "msgNoPhonePermission": "Чтобы сделать звонки проще, позвольте этому приложению начинать телефонные звонки для вас.", 15 | "msgNotDefaultLauncher": "Открывайте приложения и контакты быстрее, сделав Elder Launcher домашним приложением.", 16 | "dlgCall" : "звонок", 17 | "dlgEditTitle": "Редактировать избранное", 18 | "dlgCancel": "Отмена", 19 | "dlgOpenSettings": "Изменить настройки", 20 | "dlgAppsAddRemove": "Добавить/удалять программы", 21 | "dlgAppsReorder": "Изменение порядка программы", 22 | "dlgAppsReload": "Перезагрузить список программы", 23 | "dlgContactsAddRemove": "Добавить/удалять контакты", 24 | "dlgContactsReorder": "Изменение порядка контакты", 25 | "dlgContactsReload": "Перезагрузить список контактов" 26 | } -------------------------------------------------------------------------------- /lib/l10n/intl_es.arb: -------------------------------------------------------------------------------- 1 | { 2 | "Apps": "Aplicaciones", 3 | "Contacts": "Contactos", 4 | "btnAllApps": "Todas las aplicaciones", 5 | "btnAllContacts": "Todos los contactos", 6 | "btnAddFavApps": "Agregar aplicaciones favoritas", 7 | "btnAddFavContacts": "Agregar contactos favoritos", 8 | "btnBackToHome": "Regresa", 9 | "btnGrantPermission": "Conceder permiso", 10 | "btnSetDefaultLauncher": "Establecer como predeterminado", 11 | "msgNoData": "Datos no disponibles. Inténtalo de nuevo en unos segundos.", 12 | "msgNoFavourites": "No ha agregado ningún favorito", 13 | "msgNoContactsPermission": "Para agregar contactos favoritos a esta pantalla, permita que esta aplicación acceda a sus contactos.", 14 | "msgNoPhonePermission": "Para facilitar las llamadas, permita que esta aplicación inicie llamadas telefónicas por usted.", 15 | "msgNotDefaultLauncher": "Abra aplicaciones y contactos más rápido haciendo que Elder Launcher sea la aplicación de inicio.", 16 | "dlgCall" : "Llamada", 17 | "dlgEditTitle": "Editar Favoritos", 18 | "dlgCancel": "Cancelar", 19 | "dlgOpenSettings": "Cambiar ajustes", 20 | "dlgAppsAddRemove": "Agregar/quitar Aplicaciones", 21 | "dlgAppsReorder": "Reordenar Aplicaciones", 22 | "dlgAppsReload": "Recargar lista de aplicaciones", 23 | "dlgContactsAddRemove": "Agregar/quitar Contactos", 24 | "dlgContactsReorder": "Reordenar Contactos", 25 | "dlgContactsReload": "Recargar lista de contactos" 26 | } -------------------------------------------------------------------------------- /lib/l10n/intl_fr.arb: -------------------------------------------------------------------------------- 1 | { 2 | "Apps": "Apps", 3 | "Contacts": "Contacts", 4 | "btnAllApps": "All Apps", 5 | "btnAllContacts": "All Contacts", 6 | "btnAddFavApps": "Ajouter des apps favorites", 7 | "btnAddFavContacts": "Ajouter des contacts favoris", 8 | "btnBackToHome": "retourner", 9 | "btnGrantPermission": "Donner la permission", 10 | "btnSetDefaultLauncher": "Définir par défaut", 11 | "msgNoData": "Pas de données disponibles. Réessayez dans quelques secondes.", 12 | "msgNoFavourites": "Vous n'avez ajouté aucun favori", 13 | "msgNoContactsPermission": "Pour ajouter des contacts favoris à cet écran, autorisez cette application à accéder à vos contacts.", 14 | "msgNoPhonePermission": "Pour faciliter les appels, autorisez cette application à démarrer des appels téléphoniques à votre place.", 15 | "msgNotDefaultLauncher": "Ouvrez les applications et les contacts plus rapidement en faisant d'Elder Launcher l'application domestique.", 16 | "dlgCall" : "Téléphoner", 17 | "dlgEditTitle": "Modifier les favoris", 18 | "dlgCancel": "Annuler", 19 | "dlgOpenSettings": "Modifier les paramètres", 20 | "dlgAppsAddRemove": "Ajouter/supprimer des Apps", 21 | "dlgAppsReorder": "Réorganiser les Apps", 22 | "dlgAppsReload": "Recharger la liste des Apps", 23 | "dlgContactsAddRemove": "Ajouter/supprimer des Contacts", 24 | "dlgContactsReorder": "Réorganiser les Contacts", 25 | "dlgContactsReload": "Recharger la liste de contacts" 26 | } -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Important Rules 2 | 1. Please make your commits in a new **feature** or **patch** branch. 3 | 2. Do not push Pubspec files unless a dependency change is necessary. 4 | 5 | Aside from this, the guidelines stated below are very lax and you can start contributing in no time. 6 | 7 | ## Features and UI/UX Changes 8 | Elder Launcher focuses on **Legibility** and **Simplicity**. All new features and visual changes must adhere to this. 9 | 10 | Before working on any new major features or changing the appearance of the app, please post about in the relevant section of the project's Github Discussions [page](https://github.com/itsarjunsinh/elder_launcher/discussions). This is to ensure your work aligns with the vision of the project. 11 | 12 | ## Translations 13 | Improving or providing translations is hugely beneficial to many users and is highly appreciated. However, please send a separate PR for translation changes. 14 | ie. Do not include them with a bugfix/feature/UI-changes PR. 15 | 16 | ❌ Commit: Added Welsh; Fixed (some bug) 17 | 18 | ❌ Commit: Added Welsh; Added (some feature) 19 | 20 | ✔️ Commit: Added Welsh 21 | 22 | This rule doesn't apply, if your code requires strings which don't exist in the project. 23 | 24 | ✔️ Commit: Added (some feature) and (some strings for the feature) 25 | 26 | ## Code Quality 27 | Please follow existing indentation pattern of the project. PRs that don't reflect this, will be rejected for the sake of consistency. 28 | 29 | > *Code styling guidelines will be added in the future.* 30 | -------------------------------------------------------------------------------- /lib/utils/native_methods.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import '../constants/channels.dart'; 3 | 4 | class NativeMethods { 5 | final platformCore = const MethodChannel(channelCore); 6 | final platformContacts = const MethodChannel(channelContacts); 7 | 8 | /* 9 | ** Contacts & Call 10 | */ 11 | 12 | Future hasTelephoneFeature() async { 13 | var hasTelephoneFeatureResult = 14 | await platformContacts.invokeMethod('hasTelephoneFeature'); 15 | return hasTelephoneFeatureResult ?? false; 16 | } 17 | 18 | void launchContactsApp() { 19 | platformContacts.invokeMethod('launchContactsApp'); 20 | } 21 | 22 | void launchDialerApp(String number) { 23 | platformContacts.invokeMethod('launchDialerWithNumber', {'number': number}); 24 | } 25 | 26 | void startPhoneCall(String number) { 27 | platformContacts.invokeMethod('startPhoneCall', {'number': number}); 28 | } 29 | 30 | /* 31 | ** Core 32 | */ 33 | 34 | Future canSetDefaultLauncher() async { 35 | var canSetDefaultLauncherResult = 36 | await platformCore.invokeMethod('canSetDefaultLauncher'); 37 | return canSetDefaultLauncherResult ?? false; 38 | } 39 | 40 | void setDefaultLauncher() { 41 | platformCore.invokeMethod('setDefaultLauncher'); 42 | } 43 | 44 | Future> getDeprecatedPrefsList() async { 45 | var favAppsInDeprecatedList = 46 | await platformCore.invokeListMethod('getDeprecatedFavAppIds'); 47 | return favAppsInDeprecatedList ?? []; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/ui/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../constants/edit_mode.dart'; 3 | import '../constants/route_names.dart'; 4 | import '../ui/pages/app_drawer/app_drawer.dart'; 5 | import '../ui/pages/edit_page/edit_page.dart'; 6 | import '../ui/pages/home_page/home_page.dart'; 7 | import '../ui/pages/reorder_page/reorder_page.dart'; 8 | import '../ui/pages/settings_page/settings_page.dart'; 9 | 10 | Route generateRoute(RouteSettings settings) { 11 | if (settings.name == null) throw 'Route Name is null!'; 12 | 13 | switch (settings.name) { 14 | case homePageRoute: 15 | return _getPageRoute(settings.name!, const HomePage()); 16 | case appDrawerRoute: 17 | return _getPageRoute(settings.name!, const AppDrawerScreen()); 18 | case editPageRoute: 19 | var editMode = settings.arguments as EditMode; 20 | return _getPageRoute(settings.name!, EditPage(editMode)); 21 | case reorderPageRoute: 22 | var editMode = settings.arguments as EditMode; 23 | return _getPageRoute(settings.name!, ReorderPage(editMode)); 24 | case settingsPageRoute: 25 | return _getPageRoute(settings.name!, const SettingsPage()); 26 | default: 27 | return MaterialPageRoute( 28 | builder: (_) => const Scaffold( 29 | body: Center( 30 | child: Text('Undefined Route'), 31 | ), 32 | ), 33 | ); 34 | } 35 | } 36 | 37 | PageRoute _getPageRoute(String routeName, Widget viewToShow) { 38 | return MaterialPageRoute( 39 | settings: RouteSettings(name: routeName), builder: (_) => viewToShow); 40 | } 41 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:elder_launcher/data_sources/app_repository.dart'; 2 | import 'package:elder_launcher/data_sources/contact_repository.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_localizations/flutter_localizations.dart'; 5 | import 'package:provider/provider.dart'; 6 | import 'generated/l10n.dart'; 7 | import 'providers/app_provider.dart'; 8 | import 'providers/contact_provider.dart'; 9 | import 'ui/pages/home_page/home_page.dart'; 10 | import 'ui/router.dart'; 11 | import 'ui/theme.dart'; 12 | 13 | void main() => {runApp(const MyApp())}; 14 | 15 | class MyApp extends StatelessWidget { 16 | const MyApp({Key? key}) : super(key: key); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return MultiProvider( 21 | providers: [ 22 | ChangeNotifierProvider( 23 | create: (_) => AppProvider(AppRepository())), 24 | ChangeNotifierProvider( 25 | create: (_) => ContactProvider(ContactRepository())), 26 | ], 27 | child: MaterialApp( 28 | title: 'Elder Launcher', 29 | home: const DefaultTabController(length: 2, child: HomePage()), 30 | onGenerateRoute: generateRoute, 31 | localizationsDelegates: const [ 32 | S.delegate, 33 | GlobalCupertinoLocalizations.delegate, 34 | GlobalMaterialLocalizations.delegate, 35 | GlobalWidgetsLocalizations.delegate, 36 | ], 37 | supportedLocales: S.delegate.supportedLocales, 38 | theme: tealTheme, 39 | darkTheme: darkTheme, 40 | ), 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/ui/common/action_panel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../colors.dart'; 3 | import '../theme.dart'; 4 | 5 | class ActionPanel extends StatefulWidget { 6 | const ActionPanel({Key? key, required this.heading, required this.body}) 7 | : super(key: key); 8 | 9 | final String heading; 10 | final Widget body; 11 | 12 | @override 13 | State createState() => _ActionPanelState(); 14 | } 15 | 16 | class _ActionPanelState extends State { 17 | bool active = true; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return ExpansionPanelList( 22 | expansionCallback: (_, __) { 23 | setState(() { 24 | active = (active == true) ? false : true; 25 | }); 26 | }, 27 | children: [ 28 | ExpansionPanel( 29 | backgroundColor: Theme.of(context).brightness == Brightness.light 30 | ? elderDarkBlueGrey 31 | : elderTeal, 32 | headerBuilder: (_, __) { 33 | return Align( 34 | alignment: AlignmentDirectional.centerStart, 35 | child: Padding( 36 | padding: const EdgeInsets.only(left: 8.0), 37 | child: Text( 38 | widget.heading, 39 | style: TextStyles.listTitle, 40 | ), 41 | ), 42 | ); 43 | }, 44 | body: Padding( 45 | padding: const EdgeInsets.only(bottom: 8.0), 46 | child: widget.body, 47 | ), 48 | canTapOnHeader: true, 49 | isExpanded: active, 50 | ), 51 | ], 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/ui/common/elder_page_scaffold.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../../../generated/l10n.dart'; 3 | import '../theme.dart'; 4 | import 'buttons.dart'; 5 | 6 | class ElderPageScaffold extends StatelessWidget { 7 | /// Creates a Scaffold with a styled title and large back button. 8 | const ElderPageScaffold( 9 | {required this.title, 10 | required this.body, 11 | this.floatingActionButton, 12 | Key? key}) 13 | : super(key: key); 14 | 15 | final Widget body; 16 | final String title; 17 | final Widget? floatingActionButton; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | void backToHome() { 22 | Navigator.pop(context); 23 | } 24 | 25 | return Scaffold( 26 | appBar: AppBar( 27 | title: Text( 28 | title, 29 | style: TextStyles.headerDate, 30 | ), 31 | automaticallyImplyLeading: false, 32 | centerTitle: true, 33 | elevation: 0, 34 | ), 35 | backgroundColor: Theme.of(context).primaryColor, 36 | body: Column( 37 | children: [ 38 | Flexible( 39 | child: ClipRRect( 40 | borderRadius: const BorderRadius.only( 41 | topRight: Radius.circular(20), 42 | ), 43 | child: Container( 44 | color: Theme.of(context).backgroundColor, 45 | child: body, 46 | ), 47 | ), 48 | ), 49 | Align( 50 | alignment: Alignment.bottomCenter, 51 | child: PrimaryButton(S.of(context).btnBackToHome, backToHome), 52 | ), 53 | ], 54 | ), 55 | floatingActionButton: floatingActionButton, 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/ui/pages/edit_page/multi_select_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../../../services/edit_service.dart'; 3 | import '../../../ui/theme.dart'; 4 | 5 | class MultiSelectWidget extends StatelessWidget { 6 | final EditService editService; 7 | final bool showId; 8 | 9 | const MultiSelectWidget(this.editService, {Key? key, required this.showId}) 10 | : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | final _allItems = editService.sortedItems; 15 | 16 | void toggleFav(int position) { 17 | editService.toggleFav(position); 18 | } 19 | 20 | return ListView.separated( 21 | itemCount: _allItems.length, 22 | padding: const EdgeInsets.symmetric(vertical: 6), 23 | itemBuilder: (context, position) { 24 | var item = _allItems[position]; 25 | var isFav = editService.isFav(position); 26 | 27 | return CheckboxListTile( 28 | key: Key(item.id), 29 | title: Text( 30 | item.name, 31 | style: TextStyles.listTitle, 32 | ), 33 | subtitle: showId 34 | ? Text( 35 | item.id, 36 | style: TextStyles.listTitle, 37 | ) 38 | : null, 39 | secondary: item.icon?.isNotEmpty ?? false 40 | ? Image( 41 | image: MemoryImage(item.icon!), 42 | ) 43 | : const CircleAvatar(), 44 | value: isFav, 45 | onChanged: (isChecked) { 46 | toggleFav(position); 47 | }, 48 | ); 49 | }, 50 | separatorBuilder: (_, __) { 51 | return const Divider( 52 | thickness: Values.dividerThickness, 53 | ); 54 | }, 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_en_US.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a en_US locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'en_US'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "btnAddFavApps": 26 | MessageLookupByLibrary.simpleMessage("Add Favorite Apps"), 27 | "btnAddFavContacts": 28 | MessageLookupByLibrary.simpleMessage("Add Favorite Contacts"), 29 | "dlgEditTitle": MessageLookupByLibrary.simpleMessage("Edit Favorites"), 30 | "msgNoContactsPermission": MessageLookupByLibrary.simpleMessage( 31 | "To add favorite contacts to this screen, allow this app access to your contacts."), 32 | "msgNoFavourites": MessageLookupByLibrary.simpleMessage( 33 | "You haven\'t added any favorites") 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /lib/services/edit_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../models/item.dart'; 3 | 4 | class EditService extends ChangeNotifier { 5 | final sortedItems = []; 6 | final _favPositions = []; 7 | 8 | bool isListLoaded = false; 9 | bool isListModified = false; 10 | 11 | var _allItems = []; 12 | var _favItems = []; 13 | 14 | EditService({required List favItems, List? allItems}) { 15 | _favItems = favItems; 16 | if (allItems != null) _allItems = allItems; 17 | _generateSortedList(); 18 | } 19 | 20 | /// Sort favourite items to top of the list 21 | Future _generateSortedList() async { 22 | for (var i = 0; i < _favItems.length; i++) { 23 | sortedItems.add(_favItems[i]); 24 | _favPositions.add(i); 25 | } 26 | for (var item in _allItems) { 27 | if (!_favItems.contains(item)) sortedItems.add(item); 28 | } 29 | isListLoaded = true; 30 | } 31 | 32 | bool isFav(int position) { 33 | if (_favPositions.contains(position)) { 34 | return true; 35 | } else { 36 | return false; 37 | } 38 | } 39 | 40 | void toggleFav(int position) { 41 | isFav(position) 42 | ? _favPositions.remove(position) 43 | : _favPositions.add(position); 44 | isListModified = true; 45 | notifyListeners(); 46 | } 47 | 48 | void reorderFavItems(int oldIndex, int newIndex) { 49 | sortedItems.insert(newIndex, sortedItems[oldIndex]); 50 | sortedItems.removeAt(oldIndex > newIndex ? oldIndex + 1 : oldIndex); 51 | isListModified = true; 52 | notifyListeners(); 53 | } 54 | 55 | List getFavIds() { 56 | _favPositions.sort(); 57 | var favIds = []; 58 | for (var i in _favPositions) { 59 | favIds.add(sortedItems[i].id); 60 | } 61 | return favIds; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/ui/pages/reorder_page/reorder_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../../../models/item.dart'; 3 | import '../../../services/edit_service.dart'; 4 | import '../../../ui/theme.dart'; 5 | 6 | class ReorderWidget extends StatelessWidget { 7 | final EditService editService; 8 | final bool showId; 9 | 10 | const ReorderWidget(this.editService, {Key? key, required this.showId}) 11 | : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | final _favItems = editService.sortedItems; 16 | 17 | void reorderItems(int oldIndex, int newIndex) { 18 | editService.reorderFavItems(oldIndex, newIndex); 19 | } 20 | 21 | return ReorderableListView( 22 | onReorder: reorderItems, 23 | padding: const EdgeInsets.all(6), 24 | children: _favItems 25 | .map((item) => ReorderableCard( 26 | item: item, 27 | key: Key(item.id), 28 | )) 29 | .toList(), 30 | ); 31 | } 32 | } 33 | 34 | class ReorderableCard extends StatelessWidget { 35 | const ReorderableCard({ 36 | Key? key, 37 | required this.item, 38 | }) : super(key: key); 39 | 40 | final Item item; 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | return Card( 45 | margin: const EdgeInsets.symmetric(vertical: 2), 46 | child: Padding( 47 | padding: const EdgeInsets.all(8.0), 48 | child: ListTile( 49 | title: Text( 50 | item.name, 51 | style: TextStyles.listTitle, 52 | ), 53 | leading: item.icon?.isNotEmpty ?? false 54 | ? Image( 55 | image: MemoryImage(item.icon!), 56 | ) 57 | : const CircleAvatar(), 58 | trailing: const Icon(Icons.drag_handle), 59 | ), 60 | ), 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/test_data/fake_contacts.dart: -------------------------------------------------------------------------------- 1 | import 'package:elder_launcher/models/item.dart'; 2 | 3 | const fakeAllContacts = [ 4 | Item('1234', 'Alligator', null), 5 | Item('2345', 'Bat', null), 6 | Item('3456', 'Cat', null), 7 | Item('4567', 'Dog', null), 8 | Item('5678', 'Elephant', null), 9 | Item('6789', 'Fox', null), 10 | Item('7890', 'Gorilla', null), 11 | Item('8901', 'Horse', null), 12 | Item('9012', 'Iguana', null), 13 | Item('9876', 'Jellyfish', null), 14 | Item('8765', 'Kangaroo', null), 15 | Item('7654', 'Lion', null), 16 | Item('6543', 'Mongoose', null) 17 | ]; 18 | 19 | const fakeFavContactNumbers = ['3456', '4567', '8765', '7654']; 20 | 21 | List get fakeFavApps { 22 | var favApps = []; 23 | for (var number in fakeFavContactNumbers) { 24 | final fakeApp = fakeAllContacts.firstWhere((item) => item.id == number); 25 | favApps.add(fakeApp); 26 | } 27 | return favApps; 28 | } 29 | 30 | /// Mock list of installed apps for the ContactsService package. 31 | Iterable get fakeDeviceContacts { 32 | var contactList = >[]; 33 | for (var fakeContact in fakeAllContacts) { 34 | final contact = Map.from(_baseContact).cast(); 35 | contact['displayName'] = fakeContact.name; 36 | contact['phones'] = [ 37 | {'label': '', 'value': fakeContact.id, 'type': 0} 38 | ]; 39 | contactList.add(contact); 40 | } 41 | return contactList; 42 | } 43 | 44 | /// Mock application hashmap used in DeviceApps package. 45 | const _baseContact = { 46 | 'identifier': 'a', 47 | 'displayName': 'a', 48 | 'givenName': 'a', 49 | 'middleName': 'a', 50 | 'familyName': 'a', 51 | 'prefix': 'a', 52 | 'suffix': 'a', 53 | 'company': 'a', 54 | 'jobTitle': 'a', 55 | 'androidAccountType': 'a', 56 | 'androidAccountName': 'a', 57 | 'emails': {}, 58 | 'phones': {}, 59 | 'postalAddresses': {}, 60 | 'avatar': '', 61 | 'birthday': [], 62 | }; 63 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 12 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /lib/data_sources/app_repository.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | import 'package:device_apps/device_apps.dart'; 3 | import '../constants/keys.dart'; 4 | import '../models/interfaces/data_repository.dart'; 5 | import '../models/item.dart'; 6 | import '../utils/shared_prefs.dart'; 7 | 8 | class AppRepository implements DataRepository { 9 | @override 10 | Future> getAllItems() async { 11 | var allApps = await DeviceApps.getInstalledApplications( 12 | includeAppIcons: true, 13 | includeSystemApps: true, 14 | onlyAppsWithLaunchIntent: true); 15 | return _sortItems(await _toItems(allApps)); 16 | } 17 | 18 | @override 19 | Future> getFavItems() async { 20 | var favPackages = await SharedPrefs().getList(keyFavApps); 21 | var favApps = []; 22 | 23 | for (var packageName in favPackages) { 24 | var app = await DeviceApps.getApp(packageName, true); 25 | if (app != null) favApps.add(app as ApplicationWithIcon); 26 | } 27 | 28 | return _toItems(favApps); 29 | } 30 | 31 | @override 32 | void setFavItems(List favPackages) { 33 | SharedPrefs().setList(keyFavApps, favPackages); 34 | } 35 | 36 | Future> _toItems(List apps) async { 37 | var _apps = []; 38 | 39 | for (final app in apps) { 40 | _apps.add( 41 | Item( 42 | app.packageName, 43 | app.appName, 44 | app is ApplicationWithIcon 45 | ? app.icon as Uint8List // ignore: unnecessary_cast 46 | : null), 47 | ); 48 | } 49 | 50 | return _apps; 51 | } 52 | 53 | // TODO: Sort Apps as per locale and pronounciation 54 | Future> _sortItems(List apps) async { 55 | apps.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase())); 56 | 57 | /* 58 | unorm sort causing slowdown 59 | _apps.sort((a, b) => unorm.nfd(a.name).compareTo(unorm.nfd(b.name))); 60 | */ 61 | return apps; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/ui/common/buttons.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_size_text/auto_size_text.dart'; 2 | import 'package:flutter/material.dart'; 3 | import '../../constants/custom_functions.dart'; 4 | import '../theme.dart'; 5 | 6 | class FatButton extends StatelessWidget { 7 | final String label; 8 | final IconData icon; 9 | final VoidFunction onClickAction; 10 | 11 | const FatButton(this.label, this.icon, this.onClickAction, {Key? key}) 12 | : super(key: key); 13 | const FatButton.next(this.label, this.onClickAction, {Key? key}) 14 | : icon = Icons.navigate_next, 15 | super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return ElevatedButton( 20 | onPressed: onClickAction, 21 | child: Padding( 22 | padding: const EdgeInsets.all(16.0), 23 | child: Row( 24 | mainAxisSize: MainAxisSize.min, 25 | children: [ 26 | Text( 27 | label, 28 | style: TextStyles.actionButtonLabel, 29 | ), 30 | Icon( 31 | icon, 32 | size: 35, 33 | ) 34 | ], 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | 41 | class PrimaryButton extends StatelessWidget { 42 | final String label; 43 | final VoidFunction onClickAction; 44 | 45 | const PrimaryButton(this.label, this.onClickAction, {Key? key}) 46 | : super(key: key); 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | return Container( 51 | color: Theme.of(context).primaryColor, 52 | height: 70, 53 | width: double.infinity, 54 | child: TextButton( 55 | onPressed: onClickAction, 56 | style: TextButton.styleFrom( 57 | backgroundColor: Theme.of(context).primaryColor, 58 | padding: const EdgeInsets.only(top: 15, bottom: 15)), 59 | child: AutoSizeText( 60 | label, 61 | maxLines: 1, 62 | style: TextStyles.primaryButtonLabel, 63 | ), 64 | ), 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /test/test_data/fake_apps.dart: -------------------------------------------------------------------------------- 1 | import 'package:elder_launcher/models/item.dart'; 2 | 3 | const fakeAllApps = [ 4 | Item('dev.arjunsinh.calc', 'Calculator', null), 5 | Item('dev.arjunsinh.calendar', 'Calendar', null), 6 | Item('dev.arjunsinh.cam', 'Camera', null), 7 | Item('dev.arjunsinh.clock', 'Clock', null), 8 | Item('dev.arjunsinh.contacts', 'Contacts', null), 9 | Item('dev.arjunsinh.email', 'E-mail', null), 10 | Item('dev.arjunsinh.files', 'Files', null), 11 | Item('dev.arjunsinh.gallery', 'Gallery', null), 12 | Item('dev.arjunsinh.messages', 'Messages', null), 13 | Item('dev.arjunsinh.music', 'Music Player', null), 14 | Item('dev.arjunsinh.phone', 'Phone', null), 15 | Item('dev.arjunsinh.settings', 'Settings', null), 16 | Item('dev.arjunsinh.video', 'Video Player', null) 17 | ]; 18 | 19 | const fakeFavPackageNames = [ 20 | 'dev.arjunsinh.cam', 21 | 'dev.arjunsinh.files', 22 | 'dev.arjunsinh.gallery', 23 | 'dev.arjunsinh.phone' 24 | ]; 25 | 26 | List get fakeFavApps { 27 | var favApps = []; 28 | for (var packageName in fakeFavPackageNames) { 29 | final fakeApp = fakeAllApps.firstWhere((item) => item.id == packageName); 30 | favApps.add(fakeApp); 31 | } 32 | return favApps; 33 | } 34 | 35 | /// Mock list of installed apps for the DeviceApps package. 36 | List> get fakeInstalledApps { 37 | var applicationList = >[]; 38 | for (var fakeApp in fakeAllApps) { 39 | final application = Map.from(_baseApplication).cast(); 40 | application['app_name'] = fakeApp.name; 41 | application['package_name'] = fakeApp.id; 42 | applicationList.add(application); 43 | } 44 | return applicationList; 45 | } 46 | 47 | /// Mock application hashmap used in DeviceApps package. 48 | const _baseApplication = { 49 | 'apk_file_path': '', 50 | 'app_icon': '', 51 | 'app_name': '', 52 | 'category': 8, 53 | 'data_dir': '', 54 | 'install_time': 1, 55 | 'is_enabled': true, 56 | 'package_name': '', 57 | 'system_app': true, 58 | 'update_time': 1, 59 | 'version_code': 12, 60 | 'version_name': '12.0', 61 | }; 62 | -------------------------------------------------------------------------------- /test/data_sources/app_repository_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:elder_launcher/constants/keys.dart'; 3 | import 'package:elder_launcher/data_sources/app_repository.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_test/flutter_test.dart'; 6 | import 'package:shared_preferences/shared_preferences.dart'; 7 | import '../test_data/fake_apps.dart'; 8 | import '../test_utils/set_mock_apps.dart'; 9 | 10 | void main() { 11 | setUpAll(() { 12 | TestWidgetsFlutterBinding.ensureInitialized(); 13 | setMockApps(); 14 | }); 15 | 16 | test('Loads all apps', () async { 17 | await AppRepository().getAllItems().then( 18 | (allApps) { 19 | expect(allApps, fakeAllApps); 20 | }, 21 | ); 22 | }); 23 | 24 | test('Loads fav apps', () async { 25 | await AppRepository().getFavItems().then( 26 | (favApps) { 27 | expect(favApps, fakeFavApps); 28 | }, 29 | ); 30 | }); 31 | 32 | group('Scenario: Missing Fav App', () { 33 | setUp(() { 34 | TestWidgetsFlutterBinding.ensureInitialized(); 35 | 36 | // Add package name of app that isn't installed 37 | final packageNames = fakeFavPackageNames.toList(); 38 | packageNames.add('com.doesntexist'); 39 | 40 | SharedPreferences.setMockInitialValues( 41 | {'flutter.$keyFavApps': packageNames}); 42 | 43 | const deviceAppsChannel = MethodChannel('g123k/device_apps'); 44 | final allApps = fakeInstalledApps; 45 | 46 | deviceAppsChannel.setMockMethodCallHandler((call) async { 47 | if (call.method == 'getInstalledApps') { 48 | return allApps; 49 | } 50 | 51 | if (call.method == 'getApp') { 52 | return allApps.firstWhereOrNull( 53 | (app) => app['package_name'] == call.arguments['package_name']); 54 | } 55 | }); 56 | }); 57 | 58 | test('Excludes missing fav app', () async { 59 | await AppRepository().getFavItems().then( 60 | (favApps) { 61 | // Loads favourite apps excluding the one that doesn't exist 62 | expect(favApps, fakeFavApps); 63 | }, 64 | ); 65 | }); 66 | }); 67 | } 68 | -------------------------------------------------------------------------------- /lib/ui/pages/home_page/call_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:provider/provider.dart'; 4 | import '../../../generated/l10n.dart'; 5 | import '../../../providers/contact_provider.dart'; 6 | import '../../../models/item.dart'; 7 | import '../../../ui/theme.dart'; 8 | 9 | // ignore: non_constant_identifier_names 10 | Future CallDialog(BuildContext context, Item contact) { 11 | return showCupertinoModalPopup( 12 | context: context, 13 | builder: (context) => CupertinoActionSheet( 14 | title: Text( 15 | contact.name, 16 | style: TextStyles.dialogTitle.copyWith( 17 | color: Theme.of(context).colorScheme.onBackground, 18 | ), 19 | ), 20 | message: Text( 21 | contact.id, 22 | style: TextStyles.dialogSubtitle.copyWith( 23 | color: Theme.of(context).colorScheme.onBackground, 24 | ), 25 | ), 26 | actions: [ 27 | CupertinoActionSheetAction( 28 | onPressed: () => { 29 | Navigator.pop(context), 30 | context.read().callPhoneNumber(contact.id) 31 | }, 32 | isDefaultAction: true, 33 | child: Padding( 34 | padding: const EdgeInsets.all(4.0), 35 | child: Row( 36 | mainAxisSize: MainAxisSize.min, 37 | children: [ 38 | const Padding( 39 | padding: EdgeInsets.fromLTRB(0, 0, 4, 0), 40 | child: Icon( 41 | Icons.call, 42 | color: Colors.green, 43 | size: 30, 44 | ), 45 | ), 46 | Text( 47 | S.of(context).dlgCall, 48 | style: TextStyles.dialogActionMain, 49 | ), 50 | ], 51 | ), 52 | ), 53 | ), 54 | ], 55 | cancelButton: CupertinoActionSheetAction( 56 | onPressed: () => {Navigator.pop(context)}, 57 | child: Padding( 58 | padding: const EdgeInsets.all(4.0), 59 | child: Text( 60 | S.of(context).dlgCancel, 61 | ), 62 | ), 63 | ), 64 | ), 65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /test/data_sources/contact_repository_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | import 'package:collection/collection.dart'; 3 | import 'package:elder_launcher/constants/keys.dart'; 4 | import 'package:elder_launcher/data_sources/contact_repository.dart'; 5 | import 'package:flutter_test/flutter_test.dart'; 6 | import 'package:shared_preferences/shared_preferences.dart'; 7 | import '../test_data/fake_contacts.dart'; 8 | import '../test_utils/set_mock_contacts.dart'; 9 | */ 10 | 11 | void main() { 12 | /* 13 | setUpAll(() { 14 | TestWidgetsFlutterBinding.ensureInitialized(); 15 | setMockContacts(); 16 | }); 17 | 18 | test('Loads all contacts', () async { 19 | await ContactRepository().getAllItems().then( 20 | (allContacts) { 21 | //for (var contact in allContacts) print(contact.name); 22 | //expect(allContacts, fakeAllContacts); 23 | }, 24 | ); 25 | }); 26 | 27 | test('Loads fav apps', () async { 28 | await AppRepository().getFavItems().then( 29 | (favApps) { 30 | expect(favApps, fakeFavApps); 31 | }, 32 | ); 33 | }); 34 | 35 | group('Scenario: Missing Fav App', () { 36 | setUp(() { 37 | TestWidgetsFlutterBinding.ensureInitialized(); 38 | 39 | // Add package name of app that isn't installed 40 | final packageNames = fakeFavPackageNames.toList(); 41 | packageNames.add('com.doesntexist'); 42 | 43 | SharedPreferences.setMockInitialValues( 44 | {'flutter.$keyFavApps': packageNames}); 45 | 46 | const deviceAppsChannel = MethodChannel('g123k/device_apps'); 47 | final allApps = fakeInstalledApps; 48 | 49 | deviceAppsChannel.setMockMethodCallHandler((call) async { 50 | if (call.method == 'getInstalledApps') { 51 | return allApps; 52 | } 53 | 54 | if (call.method == 'getApp') { 55 | return allApps.firstWhereOrNull( 56 | (app) => app['package_name'] == call.arguments['package_name']); 57 | } 58 | }); 59 | }); 60 | 61 | test('Excludes missing fav app', () async { 62 | await AppRepository().getFavItems().then( 63 | (favApps) { 64 | // Loads favourite apps excluding the one that doesn't exist 65 | expect(favApps, fakeFavApps); 66 | }, 67 | ); 68 | }); 69 | });*/ 70 | } 71 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | def keystoreProperties = new Properties() 29 | def keystorePropertiesFile = rootProject.file('key.properties') 30 | if (keystorePropertiesFile.exists()) { 31 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 32 | } 33 | 34 | android { 35 | compileSdkVersion 31 36 | 37 | sourceSets { 38 | main.java.srcDirs += 'src/main/kotlin' 39 | } 40 | 41 | defaultConfig { 42 | applicationId "xyz.arjunsinh.elderlauncher" 43 | minSdkVersion 16 44 | targetSdkVersion 31 45 | versionCode flutterVersionCode.toInteger() 46 | versionName flutterVersionName 47 | } 48 | 49 | signingConfigs { 50 | release { 51 | keyAlias keystoreProperties['keyAlias'] 52 | keyPassword keystoreProperties['keyPassword'] 53 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 54 | storePassword keystoreProperties['storePassword'] 55 | } 56 | } 57 | 58 | buildTypes { 59 | release { 60 | signingConfig signingConfigs.release 61 | minifyEnabled true 62 | shrinkResources true 63 | } 64 | } 65 | } 66 | 67 | flutter { 68 | source '../..' 69 | } 70 | 71 | dependencies { 72 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 73 | } 74 | -------------------------------------------------------------------------------- /lib/data_sources/contact_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:contacts_service/contacts_service.dart'; 2 | import '../constants/keys.dart'; 3 | import '../models/interfaces/data_repository.dart'; 4 | import '../models/item.dart' as item; 5 | import '../utils/shared_prefs.dart'; 6 | 7 | class ContactRepository implements DataRepository { 8 | @override 9 | Future> getAllItems() async { 10 | var allContacts = await ContactsService.getContacts( 11 | orderByGivenName: true, photoHighResolution: false); 12 | return _toDistinctItems(allContacts); 13 | } 14 | 15 | @override 16 | Future> getFavItems() async { 17 | var favContactNumbers = await SharedPrefs().getList(keyFavContacts); 18 | var favContacts = []; 19 | for (var number in favContactNumbers) { 20 | favContacts.add( 21 | _toItem(number, await ContactsService.getContactsForPhone(number))); 22 | } 23 | return favContacts; 24 | } 25 | 26 | @override 27 | void setFavItems(List favNumbers) { 28 | SharedPrefs().setList(keyFavContacts, favNumbers); 29 | } 30 | 31 | // TODO: Performance optimization 32 | List _toDistinctItems(Iterable contacts) { 33 | var _contacts = {}; 34 | 35 | for (var contact in contacts) { 36 | var numbers = {}; 37 | 38 | if (contact.phones != null) { 39 | for (var number in contact.phones!) { 40 | if (number.value != null) { 41 | numbers.add( 42 | number.value!.replaceAll( 43 | RegExp('[^a-zA-Z0-9+]+'), 44 | '', 45 | ), 46 | ); 47 | } 48 | } 49 | } 50 | 51 | for (var number in numbers) { 52 | if (contact.displayName != null) { 53 | _contacts.add( 54 | item.Item(number, contact.displayName!, contact.avatar), 55 | ); 56 | } else { 57 | _contacts.add( 58 | item.Item(number, number, contact.avatar), 59 | ); 60 | } 61 | } 62 | } 63 | return _contacts.toList(); 64 | } 65 | 66 | item.Item _toItem(String number, Iterable contacts) { 67 | if (contacts.first.displayName != null) { 68 | return item.Item( 69 | number, contacts.first.displayName!, contacts.first.avatar); 70 | } else { 71 | return item.Item(number, number, contacts.first.avatar); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/ui/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'colors.dart'; 4 | 5 | /* Light Themes */ 6 | ThemeData baseLightTheme = ThemeData.from( 7 | colorScheme: const ColorScheme.light( 8 | background: youCard, 9 | secondary: youBlue, 10 | surface: elderBlueGrey, 11 | ), 12 | textTheme: Typography.blackMountainView); 13 | 14 | ThemeData tealTheme = baseLightTheme.copyWith( 15 | colorScheme: baseLightTheme.colorScheme.copyWith( 16 | primary: elderTeal, 17 | ), 18 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 19 | backgroundColor: youPeach, 20 | ), 21 | primaryColor: elderTeal, 22 | splashColor: elderTeal, 23 | ); 24 | 25 | /* Dark Themes */ 26 | ThemeData baseDarkTheme = ThemeData.from( 27 | colorScheme: const ColorScheme.dark( 28 | background: youBlackDim, 29 | primary: youBlack, 30 | secondary: youBlue, 31 | surface: elderDarkGrey, 32 | ), 33 | textTheme: Typography.whiteMountainView); 34 | 35 | ThemeData darkTheme = baseDarkTheme.copyWith( 36 | appBarTheme: const AppBarTheme(color: youBlack), 37 | cupertinoOverrideTheme: const CupertinoThemeData( 38 | primaryColor: elderWhite, 39 | ), 40 | dividerTheme: const DividerThemeData(color: elderDarkGreySecondary), 41 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 42 | backgroundColor: youPeach, 43 | ), 44 | primaryColor: youBlack, 45 | splashColor: elderBlack, 46 | ); 47 | 48 | class TextStyles { 49 | static const cardTitle = TextStyle(fontSize: 30); 50 | static const listTitle = TextStyle(fontSize: 30); 51 | static const headerTime = TextStyle(fontSize: 40); 52 | static const headerDate = TextStyle(color: Colors.white, fontSize: 30); 53 | static const infoMessage = TextStyle(fontSize: 25); 54 | static const actionButtonLabel = TextStyle(color: Colors.white, fontSize: 30); 55 | static const primaryButtonLabel = 56 | TextStyle(color: Colors.white, fontSize: 50, height: 1); 57 | static const dialogTitle = TextStyle(fontSize: 25); 58 | static const dialogSubtitle = TextStyle(fontSize: 24); 59 | static const dialogAction = TextStyle( 60 | fontSize: 22, 61 | fontWeight: FontWeight.bold, 62 | ); 63 | static const dialogActionMain = TextStyle(fontSize: 25); 64 | } 65 | 66 | class Values { 67 | static const double dividerThickness = 2; 68 | static const double primaryButtonHeight = 55; 69 | static const double fabSafeBottomPadding = 45; 70 | } 71 | -------------------------------------------------------------------------------- /lib/ui/pages/app_drawer/app_drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | import '../../../constants/custom_functions.dart'; 4 | import '../../../generated/l10n.dart'; 5 | import '../../../providers/app_provider.dart'; 6 | import '../../../models/item.dart'; 7 | import '../../../ui/common/loading_widget.dart'; 8 | import '../../../ui/theme.dart'; 9 | import '../../common/elder_page_scaffold.dart'; 10 | 11 | class AppDrawerScreen extends StatelessWidget { 12 | const AppDrawerScreen({Key? key}) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | void launchApp(String packageName) { 17 | context.read().launchApp(packageName); 18 | Navigator.pop(context); 19 | } 20 | 21 | return ElderPageScaffold( 22 | title: S.of(context).btnAllApps, 23 | body: Consumer(builder: (context, appService, _) { 24 | if (appService.isAppListLoaded && appService.allApps.isNotEmpty) { 25 | return AppDrawer(appService.allApps, launchApp); 26 | } else { 27 | return Flex( 28 | direction: Axis.vertical, 29 | children: const [ 30 | LoadingWidget(), 31 | ], 32 | ); 33 | } 34 | }), 35 | ); 36 | } 37 | } 38 | 39 | class AppDrawer extends StatelessWidget { 40 | // Creates a vertical list of given apps and handles click events. 41 | const AppDrawer( 42 | this.allItems, 43 | this.itemOnClickAction, { 44 | Key? key, 45 | }) : super(key: key); 46 | 47 | final List allItems; 48 | final VoidStringFunction itemOnClickAction; 49 | 50 | @override 51 | Widget build(BuildContext context) { 52 | return ListView.separated( 53 | itemCount: allItems.length, 54 | padding: const EdgeInsets.symmetric(vertical: 6), 55 | itemBuilder: (context, position) { 56 | var app = allItems[position]; 57 | return ListTile( 58 | leading: app.icon?.isNotEmpty ?? false 59 | ? Image( 60 | image: MemoryImage(app.icon!), 61 | ) 62 | : const CircleAvatar(), 63 | title: Text( 64 | app.name, 65 | style: TextStyles.listTitle, 66 | ), 67 | onTap: () => itemOnClickAction(app.id), 68 | ); 69 | }, 70 | separatorBuilder: (_, __) { 71 | return const Divider( 72 | thickness: Values.dividerThickness, 73 | ); 74 | }, 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/ui/pages/home_page/fav_grid_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_size_text/auto_size_text.dart'; 2 | import 'package:flutter/material.dart'; 3 | import '../../../constants/custom_functions.dart'; 4 | import '../../../models/item.dart'; 5 | import '../../../ui/theme.dart'; 6 | 7 | class FavGridView extends StatelessWidget { 8 | final List favItems; 9 | final VoidItemFunction itemOnClickAction; 10 | 11 | const FavGridView( 12 | this.favItems, 13 | this.itemOnClickAction, { 14 | Key? key, 15 | }) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | var itemName = AutoSizeGroup(); 20 | 21 | return Expanded( 22 | child: GridView.count( 23 | childAspectRatio: 1.25, 24 | crossAxisCount: 2, 25 | padding: const EdgeInsets.all(6), 26 | children: favItems.map((item) { 27 | return FavItemCard( 28 | item: item, 29 | itemOnClickAction: itemOnClickAction, 30 | itemName: itemName); 31 | }).toList(), 32 | ), 33 | ); 34 | } 35 | } 36 | 37 | class FavItemCard extends StatelessWidget { 38 | const FavItemCard({ 39 | Key? key, 40 | required this.item, 41 | required this.itemOnClickAction, 42 | required this.itemName, 43 | }) : super(key: key); 44 | 45 | final Item item; 46 | final VoidItemFunction itemOnClickAction; 47 | final AutoSizeGroup itemName; 48 | 49 | @override 50 | Widget build(BuildContext context) { 51 | return Card( 52 | color: Theme.of(context).cardColor, 53 | elevation: 4, 54 | child: InkWell( 55 | splashColor: Colors.blue.withAlpha(50), 56 | onTap: () => itemOnClickAction(item), 57 | child: Column( 58 | mainAxisSize: MainAxisSize.min, 59 | mainAxisAlignment: MainAxisAlignment.center, 60 | children: [ 61 | if (item.icon?.isNotEmpty ?? false) ...[ 62 | Image( 63 | height: 70, 64 | image: MemoryImage(item.icon!), 65 | ), 66 | ], 67 | Flexible( 68 | child: Padding( 69 | padding: const EdgeInsets.fromLTRB(0, 2, 0, 0), 70 | child: AutoSizeText( 71 | item.name, 72 | group: itemName, 73 | maxLines: 2, 74 | style: TextStyles.cardTitle, 75 | textAlign: TextAlign.center, 76 | ), 77 | ), 78 | ), 79 | ], 80 | ), 81 | ), 82 | ); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Elder Launcher 2 | 3 | Elder Launcher is a launcher designed for seniors focused on simplicity and legibility. 4 | 5 | Elder Launcher supports pinning favorite apps and contacts to the homescreen for quick access. 6 | 7 | 8 | Homescreen with favourite apps 9 | Homescreen with favourite contacts 10 | App Drawer 11 | 12 | 13 | ## Install 14 | 15 | 16 | 17 | Also available from [GitHub Releases](https://github.com/itsarjunsinh/elder_launcher/releases) 18 | 19 | ## Tech 20 | 21 | Elder Launcher is developed using [Flutter](flutter.dev). 22 | 23 | The app relies heavily on the following open source Flutter packages: 24 | 25 | * [contact_service](https://pub.dev/packages/contacts_service) - For accessing contacts 26 | * [device_apps](https://pub.dev/packages/device_apps) - For accessing apps 27 | 28 | > Install __Flutter Intl plugin__ ([VS Code](https://marketplace.visualstudio.com/items?itemName=localizely.flutter-intl), [InteliJ](https://plugins.jetbrains.com/plugin/13666-flutter-intl)) for generating localisation files. Your IDE might show errors and warnings if this is not installed. 29 | 30 | ## More Screenshots 31 | 32 | Homescreen call shortcut dialog 33 | Favourite App selection screen 34 | Favourite Contacts reorder screen 35 | 36 |
37 | 38 | ### Dark mode (Android 10+) 39 | Can be enabled from system settings 40 | 41 | 42 | Dark Mode: Homescreen with favourite apps 43 | Dark Mode: Homescreen with favourite contacts 44 | Dark Mode: App Drawer 45 | 46 | 47 | ## License 48 | 49 | This project is licensed under the [MIT License](LICENSE.md). 50 | -------------------------------------------------------------------------------- /lib/ui/common/info_action_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_size_text/auto_size_text.dart'; 2 | import 'package:flutter/material.dart'; 3 | import '../../constants/custom_functions.dart'; 4 | import '../theme.dart'; 5 | 6 | class InfoActionWidget extends StatelessWidget { 7 | final String message; 8 | final String buttonLabel; 9 | final IconData buttonIcon; 10 | final VoidFunction buttonOnClickAction; 11 | 12 | const InfoActionWidget( 13 | this.message, this.buttonLabel, this.buttonIcon, this.buttonOnClickAction, 14 | {Key? key}) 15 | : super(key: key); 16 | 17 | const InfoActionWidget.add( 18 | {required this.message, 19 | required this.buttonLabel, 20 | required this.buttonOnClickAction, 21 | Key? key}) 22 | : buttonIcon = Icons.add, 23 | super(key: key); 24 | 25 | const InfoActionWidget.close( 26 | {required this.message, 27 | required this.buttonLabel, 28 | required this.buttonOnClickAction, 29 | Key? key}) 30 | : buttonIcon = Icons.close, 31 | super(key: key); 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | return Center( 36 | child: Padding( 37 | padding: const EdgeInsets.symmetric(horizontal: 8.0), 38 | child: Column( 39 | mainAxisSize: MainAxisSize.min, 40 | children: [ 41 | Padding( 42 | padding: const EdgeInsets.symmetric(vertical: 8.0), 43 | child: Text( 44 | message, 45 | style: TextStyles.infoMessage, 46 | textAlign: TextAlign.center, 47 | ), 48 | ), 49 | ElevatedButton( 50 | onPressed: buttonOnClickAction, 51 | style: ElevatedButton.styleFrom( 52 | primary: Theme.of(context).primaryColor, 53 | ), 54 | child: Padding( 55 | padding: const EdgeInsets.all(8.0), 56 | child: Row( 57 | mainAxisAlignment: MainAxisAlignment.center, 58 | children: [ 59 | Padding( 60 | padding: const EdgeInsets.only(right: 8.0), 61 | child: Icon( 62 | buttonIcon, 63 | color: Colors.white, 64 | size: 35, 65 | ), 66 | ), 67 | Flexible( 68 | child: AutoSizeText( 69 | buttonLabel, 70 | style: TextStyles.actionButtonLabel, 71 | textAlign: TextAlign.start, 72 | ), 73 | ), 74 | ], 75 | ), 76 | )) 77 | ], 78 | ), 79 | ), 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/ui/pages/home_page/edit_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:provider/provider.dart'; 4 | import '../../../constants/edit_mode.dart'; 5 | import '../../../constants/route_names.dart'; 6 | import '../../../generated/l10n.dart'; 7 | import '../../../providers/app_provider.dart'; 8 | import '../../../providers/contact_provider.dart'; 9 | import '../../../ui/theme.dart'; 10 | 11 | // ignore: non_constant_identifier_names 12 | Future EditDialog(BuildContext context, EditMode editMode) { 13 | return showCupertinoModalPopup( 14 | context: context, 15 | builder: (context) => CupertinoActionSheet( 16 | title: Text( 17 | S.of(context).dlgEditTitle, 18 | style: TextStyles.dialogTitle.copyWith( 19 | color: Theme.of(context).colorScheme.onBackground, 20 | ), 21 | ), 22 | actions: [ 23 | CupertinoActionSheetAction( 24 | onPressed: () => { 25 | Navigator.pop(context), 26 | Navigator.pushNamed(context, editPageRoute, arguments: editMode) 27 | }, 28 | child: Padding( 29 | padding: const EdgeInsets.all(4.0), 30 | child: Text( 31 | editMode == EditMode.apps 32 | ? S.of(context).dlgAppsAddRemove 33 | : S.of(context).dlgContactsAddRemove, 34 | style: TextStyles.dialogAction, 35 | ), 36 | ), 37 | ), 38 | CupertinoActionSheetAction( 39 | onPressed: () => { 40 | Navigator.pop(context), 41 | Navigator.pushNamed(context, reorderPageRoute, arguments: editMode) 42 | }, 43 | child: Padding( 44 | padding: const EdgeInsets.all(4.0), 45 | child: Text( 46 | editMode == EditMode.apps 47 | ? S.of(context).dlgAppsReorder 48 | : S.of(context).dlgContactsReorder, 49 | style: TextStyles.dialogAction, 50 | ), 51 | ), 52 | ), 53 | CupertinoActionSheetAction( 54 | onPressed: () => { 55 | Navigator.pop(context), 56 | if (editMode == EditMode.apps) 57 | {context.read().reloadLists()} 58 | else 59 | {context.read().reloadLists()} 60 | }, 61 | child: Padding( 62 | padding: const EdgeInsets.all(4.0), 63 | child: Text( 64 | editMode == EditMode.apps 65 | ? S.of(context).dlgAppsReload 66 | : S.of(context).dlgContactsReload, 67 | style: TextStyles.dialogAction, 68 | ), 69 | ), 70 | ), 71 | ], 72 | cancelButton: Padding( 73 | padding: const EdgeInsets.all(4.0), 74 | child: CupertinoActionSheetAction( 75 | onPressed: () => {Navigator.pop(context)}, 76 | child: Text(S.of(context).dlgCancel), 77 | ), 78 | ), 79 | ), 80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /lib/ui/pages/reorder_page/reorder_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | import '../../../constants/edit_mode.dart'; 4 | import '../../../generated/l10n.dart'; 5 | import '../../../providers/app_provider.dart'; 6 | import '../../../providers/contact_provider.dart'; 7 | import '../../../models/item.dart'; 8 | import '../../../services/edit_service.dart'; 9 | import '../../../ui/common/info_action_widget.dart'; 10 | import '../../../ui/pages/reorder_page/reorder_widget.dart'; 11 | import '../../../ui/theme.dart'; 12 | import '../../common/elder_page_scaffold.dart'; 13 | 14 | class ReorderPage extends StatelessWidget { 15 | const ReorderPage(this.editMode, {Key? key}) : super(key: key); 16 | 17 | final EditMode editMode; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | var _favItems = []; 22 | 23 | if (editMode == EditMode.apps) { 24 | _favItems = context.read().favApps; 25 | } else { 26 | _favItems = context.read().favContacts; 27 | } 28 | 29 | void backToHome() { 30 | Navigator.pop(context); 31 | } 32 | 33 | void saveFavItems(List newFavItems) { 34 | if (editMode == EditMode.apps) { 35 | context.read().saveFavApps(newFavItems); 36 | } else { 37 | context.read().saveFavContacts(newFavItems); 38 | } 39 | } 40 | 41 | return ChangeNotifierProvider( 42 | create: (_) => EditService(favItems: _favItems), 43 | child: ElderPageScaffold( 44 | title: editMode == EditMode.apps 45 | ? S.of(context).dlgAppsReorder 46 | : S.of(context).dlgContactsReorder, 47 | body: Consumer(builder: (_, editService, __) { 48 | if (editService.sortedItems.isNotEmpty) { 49 | return ReorderWidget(editService, 50 | showId: editMode == EditMode.contacts ? true : false); 51 | } else { 52 | return InfoActionWidget.close( 53 | message: S.of(context).msgNoData, 54 | buttonLabel: S.of(context).btnBackToHome, 55 | buttonOnClickAction: backToHome); 56 | } 57 | }), 58 | floatingActionButton: Consumer( 59 | builder: (context, editService, _) { 60 | if (editService.isListModified) { 61 | return Padding( 62 | padding: 63 | const EdgeInsets.only(bottom: Values.fabSafeBottomPadding), 64 | child: FloatingActionButton.extended( 65 | icon: const Icon(Icons.save), 66 | label: const Text('Save'), 67 | onPressed: () { 68 | saveFavItems(editService.getFavIds()); 69 | Navigator.pop(context); 70 | }, 71 | ), 72 | ); 73 | } else { 74 | return const SizedBox(height: 0, width: 0); 75 | } 76 | }, 77 | ), 78 | ), 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/ui/pages/home_page/apps_tab.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | import '../../../constants/edit_mode.dart'; 4 | import '../../../constants/route_names.dart'; 5 | import '../../../generated/l10n.dart'; 6 | import '../../../providers/app_provider.dart'; 7 | import '../../../models/item.dart'; 8 | import '../../../ui/common/buttons.dart'; 9 | import '../../../ui/common/info_action_widget.dart'; 10 | import '../../../ui/common/loading_widget.dart'; 11 | import '../../../ui/pages/home_page/fav_grid_view.dart'; 12 | import '../../common/action_panel.dart'; 13 | 14 | class AppsTab extends StatelessWidget { 15 | const AppsTab({ 16 | Key? key, 17 | }) : super(key: key); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | void openAllApps() { 22 | Navigator.pushNamed(context, appDrawerRoute); 23 | } 24 | 25 | void openEditScreen() { 26 | Navigator.pushNamed(context, editPageRoute, arguments: EditMode.apps); 27 | } 28 | 29 | void launchApp(Item app) { 30 | context.read().launchApp(app.id); 31 | } 32 | 33 | void setDefaultLauncher() { 34 | context.read().setDefaultLauncher(); 35 | } 36 | 37 | return Column(children: [ 38 | Flexible( 39 | child: Consumer( 40 | builder: (_, appProvider, __) => Column( 41 | children: [ 42 | if (appProvider.isFavListLoaded && 43 | appProvider.favApps.isNotEmpty && 44 | !appProvider.canSetDefaultLauncher) ...[ 45 | // Show Favourite Apps 46 | FavGridView(appProvider.favApps, launchApp), 47 | ] else if (appProvider.isFavListLoaded && 48 | appProvider.favApps.isNotEmpty && 49 | appProvider.canSetDefaultLauncher) ...[ 50 | // Show Favourite Apps with Set Default Launcher Prompt 51 | ActionPanel( 52 | heading: S.of(context).btnSetDefaultLauncher, 53 | body: InfoActionWidget( 54 | S.of(context).msgNotDefaultLauncher, 55 | S.of(context).btnSetDefaultLauncher, 56 | Icons.home, 57 | setDefaultLauncher), 58 | ), 59 | FavGridView(appProvider.favApps, launchApp) 60 | ] else if (appProvider.isFavListLoaded && 61 | appProvider.favApps.isEmpty) ...[ 62 | // Show Add Favourites Prompt 63 | Expanded( 64 | child: InfoActionWidget.add( 65 | message: S.of(context).msgNoFavourites, 66 | buttonLabel: S.of(context).btnAddFavApps, 67 | buttonOnClickAction: openEditScreen, 68 | ), 69 | ), 70 | ] else ...[ 71 | const LoadingWidget() 72 | ], 73 | ], 74 | ), 75 | ), 76 | ), 77 | Align( 78 | alignment: Alignment.bottomCenter, 79 | child: PrimaryButton(S.of(context).btnAllApps, openAllApps), 80 | ), 81 | ]); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/ui/pages/edit_page/edit_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | import '../../../constants/edit_mode.dart'; 4 | import '../../../generated/l10n.dart'; 5 | import '../../../providers/app_provider.dart'; 6 | import '../../../providers/contact_provider.dart'; 7 | import '../../../models/item.dart'; 8 | import '../../../services/edit_service.dart'; 9 | import '../../../ui/common/info_action_widget.dart'; 10 | import '../../../ui/pages/edit_page/multi_select_widget.dart'; 11 | import '../../../ui/theme.dart'; 12 | import '../../common/elder_page_scaffold.dart'; 13 | 14 | class EditPage extends StatelessWidget { 15 | const EditPage(this.editMode, {Key? key}) : super(key: key); 16 | 17 | final EditMode editMode; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | List allItems; 22 | List favItems; 23 | 24 | if (editMode == EditMode.apps) { 25 | allItems = context.read().allApps; 26 | favItems = context.read().favApps; 27 | } else { 28 | allItems = context.read().allContacts; 29 | favItems = context.read().favContacts; 30 | } 31 | 32 | void backToHome() { 33 | Navigator.pop(context); 34 | } 35 | 36 | void saveFavItems(List newFavItems) { 37 | if (editMode == EditMode.apps) { 38 | context.read().saveFavApps(newFavItems); 39 | } else { 40 | context.read().saveFavContacts(newFavItems); 41 | } 42 | } 43 | 44 | return ChangeNotifierProvider( 45 | create: (_) => EditService(favItems: favItems, allItems: allItems), 46 | child: ElderPageScaffold( 47 | title: editMode == EditMode.apps 48 | ? S.of(context).dlgAppsAddRemove 49 | : S.of(context).dlgContactsAddRemove, 50 | body: Consumer(builder: (_, editService, __) { 51 | if (editService.sortedItems.isNotEmpty) { 52 | return MultiSelectWidget(editService, 53 | showId: editMode == EditMode.contacts); 54 | } else { 55 | return InfoActionWidget.close( 56 | message: S.of(context).msgNoData, 57 | buttonLabel: S.of(context).btnBackToHome, 58 | buttonOnClickAction: backToHome, 59 | ); 60 | } 61 | }), 62 | floatingActionButton: Consumer( 63 | builder: (context, editService, _) { 64 | if (editService.isListModified) { 65 | return Padding( 66 | padding: 67 | const EdgeInsets.only(bottom: Values.fabSafeBottomPadding), 68 | child: FloatingActionButton.extended( 69 | icon: const Icon(Icons.save), 70 | label: const Text('Save'), 71 | onPressed: () { 72 | saveFavItems(editService.getFavIds()); 73 | Navigator.pop(context); 74 | }, 75 | ), 76 | ); 77 | } else { 78 | return const SizedBox(height: 0, width: 0); 79 | } 80 | }, 81 | ), 82 | ), 83 | ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_en.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a en locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'en'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "Apps": MessageLookupByLibrary.simpleMessage("Apps"), 26 | "Contacts": MessageLookupByLibrary.simpleMessage("Contacts"), 27 | "btnAddFavApps": 28 | MessageLookupByLibrary.simpleMessage("Add Favourite Apps"), 29 | "btnAddFavContacts": 30 | MessageLookupByLibrary.simpleMessage("Add Favourite Contacts"), 31 | "btnAllApps": MessageLookupByLibrary.simpleMessage("All Apps"), 32 | "btnAllContacts": MessageLookupByLibrary.simpleMessage("All Contacts"), 33 | "btnBackToHome": MessageLookupByLibrary.simpleMessage("Back to Home"), 34 | "btnGrantPermission": 35 | MessageLookupByLibrary.simpleMessage("Grant Permission"), 36 | "btnSetDefaultLauncher": 37 | MessageLookupByLibrary.simpleMessage("Set as Default"), 38 | "dlgAppsAddRemove": 39 | MessageLookupByLibrary.simpleMessage("Add/Remove Apps"), 40 | "dlgAppsReload": 41 | MessageLookupByLibrary.simpleMessage("Reload App List"), 42 | "dlgAppsReorder": MessageLookupByLibrary.simpleMessage("Reorder Apps"), 43 | "dlgCall": MessageLookupByLibrary.simpleMessage("Call"), 44 | "dlgCancel": MessageLookupByLibrary.simpleMessage("Cancel"), 45 | "dlgContactsAddRemove": 46 | MessageLookupByLibrary.simpleMessage("Add/Remove Contacts"), 47 | "dlgContactsReload": 48 | MessageLookupByLibrary.simpleMessage("Reload Contacts"), 49 | "dlgContactsReorder": 50 | MessageLookupByLibrary.simpleMessage("Reorder Contacts"), 51 | "dlgEditTitle": MessageLookupByLibrary.simpleMessage("Edit Favourites"), 52 | "dlgOpenSettings": 53 | MessageLookupByLibrary.simpleMessage("Open Settings"), 54 | "msgNoContactsPermission": MessageLookupByLibrary.simpleMessage( 55 | "To add favourite contacts to this screen, allow this app access to your contacts."), 56 | "msgNoData": MessageLookupByLibrary.simpleMessage( 57 | "No data available. Try again in a few seconds."), 58 | "msgNoFavourites": MessageLookupByLibrary.simpleMessage( 59 | "You haven\'t added any favourites"), 60 | "msgNoPhonePermission": MessageLookupByLibrary.simpleMessage( 61 | "To make calling easier, allow this app to start phone calls for you."), 62 | "msgNotDefaultLauncher": MessageLookupByLibrary.simpleMessage( 63 | "Open apps and contacts faster by making Elder Launcher the home app.") 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_it.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a it locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'it'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "Apps": MessageLookupByLibrary.simpleMessage("Apps"), 26 | "Contacts": MessageLookupByLibrary.simpleMessage("Contatti"), 27 | "btnAddFavApps": 28 | MessageLookupByLibrary.simpleMessage("Aggiungi app preferite"), 29 | "btnAddFavContacts": 30 | MessageLookupByLibrary.simpleMessage("Aggiungi contatti preferiti"), 31 | "btnAllApps": MessageLookupByLibrary.simpleMessage("Tutte le App"), 32 | "btnAllContacts": 33 | MessageLookupByLibrary.simpleMessage("Tutti contatti"), 34 | "btnBackToHome": MessageLookupByLibrary.simpleMessage("Indietro"), 35 | "btnGrantPermission": 36 | MessageLookupByLibrary.simpleMessage("Dai il permesso"), 37 | "btnSetDefaultLauncher": 38 | MessageLookupByLibrary.simpleMessage("Imposta come predefinito"), 39 | "dlgAppsAddRemove": 40 | MessageLookupByLibrary.simpleMessage("Aggiungi/rimuovi app"), 41 | "dlgAppsReload": 42 | MessageLookupByLibrary.simpleMessage("Ricarica la lista delle app"), 43 | "dlgAppsReorder": 44 | MessageLookupByLibrary.simpleMessage("Riorganizza le app"), 45 | "dlgCall": MessageLookupByLibrary.simpleMessage("Chiamare"), 46 | "dlgCancel": MessageLookupByLibrary.simpleMessage("Annulla"), 47 | "dlgContactsAddRemove": 48 | MessageLookupByLibrary.simpleMessage("Aggiungi/rimuovi contatti"), 49 | "dlgContactsReload": MessageLookupByLibrary.simpleMessage( 50 | "Ricarica la lista dei contatti"), 51 | "dlgContactsReorder": 52 | MessageLookupByLibrary.simpleMessage("Riorganizza i contatti"), 53 | "dlgEditTitle": 54 | MessageLookupByLibrary.simpleMessage("Modificare preferiti"), 55 | "msgNoContactsPermission": MessageLookupByLibrary.simpleMessage( 56 | "Per aggiungere contatti preferiti a questa schermata, pergo dare i permessi per accedere ai contatti."), 57 | "msgNoData": MessageLookupByLibrary.simpleMessage( 58 | "Non ci sono dei dati. Prego riprovare tra pochi secondi."), 59 | "msgNoFavourites": MessageLookupByLibrary.simpleMessage( 60 | "Lei non ha aggiunto dei preferiti"), 61 | "msgNoPhonePermission": MessageLookupByLibrary.simpleMessage( 62 | "Abilita l\'app a fare chiamate."), 63 | "msgNotDefaultLauncher": MessageLookupByLibrary.simpleMessage( 64 | "Apri app e contatti più velocemente, impostando Elder Launcher come Home-App.") 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_hi.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a hi locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'hi'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "Apps": MessageLookupByLibrary.simpleMessage("ऐप्स"), 26 | "Contacts": MessageLookupByLibrary.simpleMessage("संपर्क"), 27 | "btnAddFavApps": 28 | MessageLookupByLibrary.simpleMessage("पसंदीदा एप्लिकेशन जोड़ें"), 29 | "btnAddFavContacts": 30 | MessageLookupByLibrary.simpleMessage("पसंदीदा संपर्क जोड़ें"), 31 | "btnAllApps": MessageLookupByLibrary.simpleMessage("सभी ऐप्स"), 32 | "btnAllContacts": MessageLookupByLibrary.simpleMessage("सभी संपर्क"), 33 | "btnBackToHome": MessageLookupByLibrary.simpleMessage("वापस जाओ"), 34 | "btnGrantPermission": 35 | MessageLookupByLibrary.simpleMessage("अनुमति प्रदान करें"), 36 | "btnSetDefaultLauncher": 37 | MessageLookupByLibrary.simpleMessage("डिफ़ॉल्ट लॉन्चर बनाएं"), 38 | "dlgAppsAddRemove": 39 | MessageLookupByLibrary.simpleMessage("एप्लिकेशन जोड़ें/निकालें"), 40 | "dlgAppsReload": 41 | MessageLookupByLibrary.simpleMessage("एप्लिकेशन सूची ताज़ा करें"), 42 | "dlgAppsReorder": 43 | MessageLookupByLibrary.simpleMessage("्यवस्थित करें ऐप्स"), 44 | "dlgCall": MessageLookupByLibrary.simpleMessage("कॉल"), 45 | "dlgCancel": MessageLookupByLibrary.simpleMessage("रद्द करें"), 46 | "dlgContactsAddRemove": 47 | MessageLookupByLibrary.simpleMessage("संपर्क जोड़ें/निकालें"), 48 | "dlgContactsReload": 49 | MessageLookupByLibrary.simpleMessage("संपर्क सूची ताज़ा करें"), 50 | "dlgContactsReorder": 51 | MessageLookupByLibrary.simpleMessage("व्यवस्थित करें संपर्क"), 52 | "dlgEditTitle": 53 | MessageLookupByLibrary.simpleMessage("पसंदीदा संपादित करें"), 54 | "dlgOpenSettings": 55 | MessageLookupByLibrary.simpleMessage("सेटिंग्स खोलें"), 56 | "msgNoContactsPermission": MessageLookupByLibrary.simpleMessage( 57 | "इस स्क्रीन पर पसंदीदा संपर्क जोड़ने के लिए, इस एप्लिकेशन को आपके संपर्क देखने दें।"), 58 | "msgNoData": MessageLookupByLibrary.simpleMessage( 59 | "कोई डेटा उपलब्ध नहीं है। कुछ क्षण में फिर से कोशिश करें।"), 60 | "msgNoFavourites": MessageLookupByLibrary.simpleMessage( 61 | "आपने कोई पसंदीदा नहीं जोड़ा है"), 62 | "msgNoPhonePermission": MessageLookupByLibrary.simpleMessage( 63 | "कॉलिंग को आसान बनाने के लिए, इस ऐप को आपके लिए फ़ोन कॉल शुरू करने की अनुमति दें।"), 64 | "msgNotDefaultLauncher": MessageLookupByLibrary.simpleMessage( 65 | "एल्डर लॉन्चर को होम एप बनाकर एप्स और कॉन्टैक्ट्स को तेजी से खोलें।") 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /lib/providers/app_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:collection'; 3 | import 'package:device_apps/device_apps.dart'; 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter/services.dart'; 6 | import '../constants/channels.dart'; 7 | import '../constants/keys.dart'; 8 | import '../data_sources/app_repository.dart'; 9 | import '../models/item.dart'; 10 | import '../utils/native_methods.dart'; 11 | import '../utils/shared_prefs.dart'; 12 | 13 | class AppProvider extends ChangeNotifier { 14 | bool _isAppListLoaded = false; 15 | bool _isFavListLoaded = false; 16 | bool _canSetDefaultLauncher = false; 17 | 18 | List _allApps = []; 19 | List _favApps = []; 20 | 21 | final _appEvent = DeviceApps.listenToAppsChanges(); 22 | final _methodChannel = const MethodChannel(channelCore); 23 | final AppRepository _repository; 24 | late final Timer _refreshTimer; 25 | 26 | AppProvider(this._repository) { 27 | _loadApps(); 28 | _appEvent.listen((event) { 29 | _loadApps(); 30 | }); 31 | _refreshTimer = 32 | Timer.periodic(const Duration(minutes: 15), (timer) => _loadApps()); 33 | _checkCanSetDefaultLauncher(); 34 | _methodChannel.setMethodCallHandler(_failedSettingDefaultLauncher); 35 | } 36 | 37 | bool get canSetDefaultLauncher => _canSetDefaultLauncher; 38 | bool get isAppListLoaded => _isAppListLoaded; 39 | bool get isFavListLoaded => _isFavListLoaded; 40 | UnmodifiableListView get allApps => UnmodifiableListView(_allApps); 41 | UnmodifiableListView get favApps => UnmodifiableListView(_favApps); 42 | 43 | @override 44 | void dispose() { 45 | _appEvent.listen((event) {}).cancel(); 46 | _refreshTimer.cancel(); 47 | super.dispose(); 48 | } 49 | 50 | Future _loadApps() async { 51 | _loadAllItems(); 52 | _loadFavItems(); 53 | var isFirstRun = await SharedPrefs().getBool(keyIsFirstRun, true); 54 | if (isFirstRun) { 55 | _restoreFavAppsFromDeprecatedList(); 56 | } 57 | } 58 | 59 | Future _loadAllItems() async { 60 | _allApps = await _repository.getAllItems(); 61 | _isAppListLoaded = true; 62 | notifyListeners(); 63 | } 64 | 65 | Future _loadFavItems() async { 66 | _favApps = await _repository.getFavItems(); 67 | _isFavListLoaded = true; 68 | notifyListeners(); 69 | } 70 | 71 | Future _checkCanSetDefaultLauncher() async { 72 | var result = await NativeMethods().canSetDefaultLauncher(); 73 | _canSetDefaultLauncher = result; 74 | notifyListeners(); 75 | } 76 | 77 | /// Restore favourite apps saved with different key in builds prior to v1.0 78 | Future _restoreFavAppsFromDeprecatedList() async { 79 | var deprecatedFavAppIds = await NativeMethods().getDeprecatedPrefsList(); 80 | if (deprecatedFavAppIds.isNotEmpty) { 81 | saveFavApps(deprecatedFavAppIds); 82 | } 83 | SharedPrefs().setBool(keyIsFirstRun, false); 84 | } 85 | 86 | void launchApp(String packageName) { 87 | DeviceApps.openApp(packageName); 88 | } 89 | 90 | void reloadLists() { 91 | _loadApps(); 92 | } 93 | 94 | Future saveFavApps(List newFavPackages) async { 95 | _repository.setFavItems(newFavPackages); 96 | _loadFavItems(); 97 | } 98 | 99 | void setDefaultLauncher() { 100 | _canSetDefaultLauncher = false; // Hide the dialog 101 | NativeMethods().setDefaultLauncher(); 102 | notifyListeners(); 103 | } 104 | 105 | Future _failedSettingDefaultLauncher(MethodCall call) async { 106 | switch (call.method) { 107 | case 'setDefaultLauncherFailure': 108 | // Confirm and show set default dialog 109 | _checkCanSetDefaultLauncher(); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_cs.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a cs locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'cs'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "Apps": MessageLookupByLibrary.simpleMessage("Aplikace"), 26 | "Contacts": MessageLookupByLibrary.simpleMessage("Kontakty"), 27 | "btnAddFavApps": 28 | MessageLookupByLibrary.simpleMessage("Přidat Oblíbené Aplikace"), 29 | "btnAddFavContacts": 30 | MessageLookupByLibrary.simpleMessage("Přidat Oblíbené Kontakty"), 31 | "btnAllApps": MessageLookupByLibrary.simpleMessage("Všechny Aplikace"), 32 | "btnAllContacts": 33 | MessageLookupByLibrary.simpleMessage("Všechny Kontakty"), 34 | "btnBackToHome": MessageLookupByLibrary.simpleMessage("Zpět Domů"), 35 | "btnGrantPermission": 36 | MessageLookupByLibrary.simpleMessage("Udělit Oprávnění"), 37 | "btnSetDefaultLauncher": 38 | MessageLookupByLibrary.simpleMessage("Změnit na výchozi"), 39 | "dlgAppsAddRemove": 40 | MessageLookupByLibrary.simpleMessage("Přidat/Odebrat Aplikace"), 41 | "dlgAppsReload": MessageLookupByLibrary.simpleMessage( 42 | "Znovu načíst Seznam Aplikací"), 43 | "dlgAppsReorder": 44 | MessageLookupByLibrary.simpleMessage("Přeuspořádat Aplikace"), 45 | "dlgCall": MessageLookupByLibrary.simpleMessage("Volat"), 46 | "dlgCancel": MessageLookupByLibrary.simpleMessage("Zrušit"), 47 | "dlgContactsAddRemove": 48 | MessageLookupByLibrary.simpleMessage("Přidat/Odebrat Kontakty"), 49 | "dlgContactsReload": 50 | MessageLookupByLibrary.simpleMessage("Znovu načíst Kontakty"), 51 | "dlgContactsReorder": 52 | MessageLookupByLibrary.simpleMessage("Přeuspořádat Kontakty"), 53 | "dlgEditTitle": MessageLookupByLibrary.simpleMessage("Změnit Oblíbené"), 54 | "dlgOpenSettings": 55 | MessageLookupByLibrary.simpleMessage("Změnit nastavení"), 56 | "msgNoContactsPermission": MessageLookupByLibrary.simpleMessage( 57 | "Pro přidání oblíbených kontaktů na tuto stránku povolte této aplikaci přístup ke Kontaktům."), 58 | "msgNoData": MessageLookupByLibrary.simpleMessage( 59 | "Žádná data k dispozici. Zkuste znova za pár vteřin."), 60 | "msgNoFavourites": MessageLookupByLibrary.simpleMessage( 61 | "Nepřidali jste žádné oblíbené položky"), 62 | "msgNoPhonePermission": MessageLookupByLibrary.simpleMessage( 63 | "Pro usnadnění volání, povolte této aplikaci zahájení hovorů."), 64 | "msgNotDefaultLauncher": MessageLookupByLibrary.simpleMessage( 65 | "Otevírejte aplikace a kontakty rychleji tím, že si Elder Launcher uděláte domácí aplikaci.") 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_nl.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a nl locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'nl'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "Apps": MessageLookupByLibrary.simpleMessage("Apps"), 26 | "Contacts": MessageLookupByLibrary.simpleMessage("Contacten"), 27 | "btnAddFavApps": 28 | MessageLookupByLibrary.simpleMessage("Voeg favoriete apps toe"), 29 | "btnAddFavContacts": MessageLookupByLibrary.simpleMessage( 30 | "Voeg favoriete contacten toe"), 31 | "btnAllApps": MessageLookupByLibrary.simpleMessage("Alle apps"), 32 | "btnAllContacts": 33 | MessageLookupByLibrary.simpleMessage("Alle contacten"), 34 | "btnBackToHome": MessageLookupByLibrary.simpleMessage("Terug"), 35 | "btnGrantPermission": 36 | MessageLookupByLibrary.simpleMessage("Toestemming verlenen"), 37 | "btnSetDefaultLauncher": 38 | MessageLookupByLibrary.simpleMessage("Standaard instellen"), 39 | "dlgAppsAddRemove": 40 | MessageLookupByLibrary.simpleMessage("Toevoegen/verwijderen apps"), 41 | "dlgAppsReload": 42 | MessageLookupByLibrary.simpleMessage("App lijst opnieuw laden"), 43 | "dlgAppsReorder": MessageLookupByLibrary.simpleMessage("Apps sorteren"), 44 | "dlgCall": MessageLookupByLibrary.simpleMessage("Bel"), 45 | "dlgCancel": MessageLookupByLibrary.simpleMessage("Annuleer"), 46 | "dlgContactsAddRemove": MessageLookupByLibrary.simpleMessage( 47 | "Toevoegen/verwijderen contacten"), 48 | "dlgContactsReload": 49 | MessageLookupByLibrary.simpleMessage("Contacten laden"), 50 | "dlgContactsReorder": 51 | MessageLookupByLibrary.simpleMessage("Contacten sorteren"), 52 | "dlgEditTitle": 53 | MessageLookupByLibrary.simpleMessage("Wijzig favorieten"), 54 | "dlgOpenSettings": 55 | MessageLookupByLibrary.simpleMessage("Instellingen veranderen"), 56 | "msgNoContactsPermission": MessageLookupByLibrary.simpleMessage( 57 | "Om favoriete contacten aan dit scherm toe te voegen, moet u deze app toegang geven tot uw contacten."), 58 | "msgNoData": MessageLookupByLibrary.simpleMessage( 59 | "Geen gegevens beschikbaar, probeer over een paar seconden opnieuw"), 60 | "msgNoFavourites": MessageLookupByLibrary.simpleMessage( 61 | "Je hebt nog geen favorieten toegevoegd"), 62 | "msgNoPhonePermission": MessageLookupByLibrary.simpleMessage( 63 | "Om het bellen te vergemakkelijken, kunt u deze app telefoongesprekken voor u laten starten."), 64 | "msgNotDefaultLauncher": MessageLookupByLibrary.simpleMessage( 65 | "Open apps en contacten sneller door van Elder Launcher de thuis-app te maken.") 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_de.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a de locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'de'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "Apps": MessageLookupByLibrary.simpleMessage("Apps"), 26 | "Contacts": MessageLookupByLibrary.simpleMessage("Kontakte"), 27 | "btnAddFavApps": MessageLookupByLibrary.simpleMessage( 28 | "Favorisierte Apps hinzufügen"), 29 | "btnAddFavContacts": MessageLookupByLibrary.simpleMessage( 30 | "Favorisierte Kontakte hinzufügen"), 31 | "btnAllApps": MessageLookupByLibrary.simpleMessage("Alle Apps"), 32 | "btnAllContacts": MessageLookupByLibrary.simpleMessage("Alle Kontakte"), 33 | "btnBackToHome": MessageLookupByLibrary.simpleMessage("Zurück"), 34 | "btnGrantPermission": 35 | MessageLookupByLibrary.simpleMessage("Erlaubnis erteilen"), 36 | "btnSetDefaultLauncher": 37 | MessageLookupByLibrary.simpleMessage("Als Standard festlegen"), 38 | "dlgAppsAddRemove": 39 | MessageLookupByLibrary.simpleMessage("Apps hinzufügen/entfernen"), 40 | "dlgAppsReload": 41 | MessageLookupByLibrary.simpleMessage("App Liste neu laden"), 42 | "dlgAppsReorder": 43 | MessageLookupByLibrary.simpleMessage("Apps neu anordnen"), 44 | "dlgCall": MessageLookupByLibrary.simpleMessage("Anrufen"), 45 | "dlgCancel": MessageLookupByLibrary.simpleMessage("Abbrechen"), 46 | "dlgContactsAddRemove": MessageLookupByLibrary.simpleMessage( 47 | "Kontakte hinzufügen/entfernen"), 48 | "dlgContactsReload": 49 | MessageLookupByLibrary.simpleMessage("Kontaktliste neu laden"), 50 | "dlgContactsReorder": 51 | MessageLookupByLibrary.simpleMessage("Kontakte neu anordnen"), 52 | "dlgEditTitle": 53 | MessageLookupByLibrary.simpleMessage("Favoriten bearbeiten"), 54 | "dlgOpenSettings": 55 | MessageLookupByLibrary.simpleMessage("Einstellungen ändern"), 56 | "msgNoContactsPermission": MessageLookupByLibrary.simpleMessage( 57 | "Um diesem Bildschirm favorisierte Kontakte hinzuzufügen, erlauben Sie dieser App den Zugriff auf Ihre Kontakte."), 58 | "msgNoData": MessageLookupByLibrary.simpleMessage( 59 | "Keine Daten verfügbar. Versuchen Sie es in wenigen Sekunden erneut."), 60 | "msgNoFavourites": MessageLookupByLibrary.simpleMessage( 61 | "Sie haben keine Favoriten hinzugefügt"), 62 | "msgNoPhonePermission": MessageLookupByLibrary.simpleMessage( 63 | "Ermöglichen Sie dieser App, Anrufe für Sie zu starten, um das Telefonieren zu vereinfachen."), 64 | "msgNotDefaultLauncher": MessageLookupByLibrary.simpleMessage( 65 | "Öffnen Sie Apps und Kontakte schneller, indem Sie Elder Launcher zur Heim-App machen.") 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_pl.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a pl locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'pl'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "Apps": MessageLookupByLibrary.simpleMessage("Aplikacje"), 26 | "Contacts": MessageLookupByLibrary.simpleMessage("Kontakty"), 27 | "btnAddFavApps": 28 | MessageLookupByLibrary.simpleMessage("Dodaj ulubione aplikacje"), 29 | "btnAddFavContacts": 30 | MessageLookupByLibrary.simpleMessage("Dodaj ulubione kontakty"), 31 | "btnAllApps": 32 | MessageLookupByLibrary.simpleMessage("Wszystkie Aplikacje"), 33 | "btnAllContacts": 34 | MessageLookupByLibrary.simpleMessage("Wszystkie Kontakty"), 35 | "btnBackToHome": MessageLookupByLibrary.simpleMessage("Wrócić do Domu"), 36 | "btnGrantPermission": 37 | MessageLookupByLibrary.simpleMessage("Dać Pozwolenie"), 38 | "btnSetDefaultLauncher": 39 | MessageLookupByLibrary.simpleMessage("Ustaw jako domyslną"), 40 | "dlgAppsAddRemove": 41 | MessageLookupByLibrary.simpleMessage("Dodaj/Usuń Aplikacje"), 42 | "dlgAppsReload": MessageLookupByLibrary.simpleMessage( 43 | "Załaduj ponownie listę aplikacji"), 44 | "dlgAppsReorder": 45 | MessageLookupByLibrary.simpleMessage("Zmień kolejność aplikacji"), 46 | "dlgCall": MessageLookupByLibrary.simpleMessage("Połączenie"), 47 | "dlgCancel": MessageLookupByLibrary.simpleMessage("Anulować"), 48 | "dlgContactsAddRemove": 49 | MessageLookupByLibrary.simpleMessage("Dodaj/Usuń Kontakty"), 50 | "dlgContactsReload": MessageLookupByLibrary.simpleMessage( 51 | "Załaduj ponownie listę kontaktów"), 52 | "dlgContactsReorder": 53 | MessageLookupByLibrary.simpleMessage("Zmień kolejność Kontakty"), 54 | "dlgEditTitle": MessageLookupByLibrary.simpleMessage("Zmień ulubione"), 55 | "dlgOpenSettings": 56 | MessageLookupByLibrary.simpleMessage("Zmień ustawienia"), 57 | "msgNoContactsPermission": MessageLookupByLibrary.simpleMessage( 58 | "Aby dodać ulubione kontakty do tego ekranu, zezwól tej aplikacji na dostęp do swoich kontaktów."), 59 | "msgNoData": MessageLookupByLibrary.simpleMessage( 60 | "Brak dostępnych danych. Spróbuj ponownie za kilka sekund."), 61 | "msgNoFavourites": MessageLookupByLibrary.simpleMessage( 62 | "Nie dodałeś żadnych ulubionych."), 63 | "msgNoPhonePermission": MessageLookupByLibrary.simpleMessage( 64 | "Aby ułatwić dzwonienie, zezwól tej aplikacji na nawiązywanie połączeń telefonicznych za Ciebie."), 65 | "msgNotDefaultLauncher": MessageLookupByLibrary.simpleMessage( 66 | "Otwieraj aplikacje i kontakty szybciej, ustawiając Elder Launcher jako aplikację domową.") 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_pt.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a pt locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'pt'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "Apps": MessageLookupByLibrary.simpleMessage("Apps"), 26 | "Contacts": MessageLookupByLibrary.simpleMessage("Contatos"), 27 | "btnAddFavApps": MessageLookupByLibrary.simpleMessage( 28 | "Adicionar Aplicativos favoritos"), 29 | "btnAddFavContacts": MessageLookupByLibrary.simpleMessage( 30 | "Adicionar Contatos favoritos"), 31 | "btnAllApps": MessageLookupByLibrary.simpleMessage("Todos os apps"), 32 | "btnAllContacts": 33 | MessageLookupByLibrary.simpleMessage("Todos os contatos"), 34 | "btnBackToHome": MessageLookupByLibrary.simpleMessage("Volte"), 35 | "btnGrantPermission": 36 | MessageLookupByLibrary.simpleMessage("Conceder permissão"), 37 | "btnSetDefaultLauncher": 38 | MessageLookupByLibrary.simpleMessage("Definir como predef."), 39 | "dlgAppsAddRemove": MessageLookupByLibrary.simpleMessage( 40 | "Adicionar/Remover Aplicativos"), 41 | "dlgAppsReload": MessageLookupByLibrary.simpleMessage( 42 | "Recarregar lista de aplicativos"), 43 | "dlgAppsReorder": 44 | MessageLookupByLibrary.simpleMessage("Reordenar Aplicativos"), 45 | "dlgCall": MessageLookupByLibrary.simpleMessage("Ligar"), 46 | "dlgCancel": MessageLookupByLibrary.simpleMessage("Cancelar"), 47 | "dlgContactsAddRemove": 48 | MessageLookupByLibrary.simpleMessage("Adicionar/Remover Contatos"), 49 | "dlgContactsReload": MessageLookupByLibrary.simpleMessage( 50 | "Recarregar lista de contatos"), 51 | "dlgContactsReorder": 52 | MessageLookupByLibrary.simpleMessage("Reordenar Contatos"), 53 | "dlgEditTitle": 54 | MessageLookupByLibrary.simpleMessage("Editar Favoritos"), 55 | "dlgOpenSettings": 56 | MessageLookupByLibrary.simpleMessage("Mudar configurações"), 57 | "msgNoContactsPermission": MessageLookupByLibrary.simpleMessage( 58 | "Para adicionar contatos favoritos a essa tela, permita que este aplicativo acesse seus contatos."), 59 | "msgNoData": MessageLookupByLibrary.simpleMessage( 60 | "Não há dados disponíveis. Tente novamente em alguns segundos."), 61 | "msgNoFavourites": MessageLookupByLibrary.simpleMessage( 62 | "Você não adicionou nenhum favorito"), 63 | "msgNoPhonePermission": MessageLookupByLibrary.simpleMessage( 64 | "Para facilitar as chamadas, permita que este aplicativo inicie chamadas telefônicas para você."), 65 | "msgNotDefaultLauncher": MessageLookupByLibrary.simpleMessage( 66 | "Abra aplicativos e contatos mais rapidamente, tornando o Elder Launcher o aplicativo doméstico.") 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_ru.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a ru locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'ru'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "Apps": MessageLookupByLibrary.simpleMessage("программы"), 26 | "Contacts": MessageLookupByLibrary.simpleMessage("контакты"), 27 | "btnAddFavApps": 28 | MessageLookupByLibrary.simpleMessage("Добавить любимые программы"), 29 | "btnAddFavContacts": 30 | MessageLookupByLibrary.simpleMessage("Добавить избранные контакты"), 31 | "btnAllApps": MessageLookupByLibrary.simpleMessage("Все программы"), 32 | "btnAllContacts": MessageLookupByLibrary.simpleMessage("Все контакты"), 33 | "btnBackToHome": 34 | MessageLookupByLibrary.simpleMessage("Вернуться домой"), 35 | "btnGrantPermission": 36 | MessageLookupByLibrary.simpleMessage("Предоставить разрешение"), 37 | "btnSetDefaultLauncher": 38 | MessageLookupByLibrary.simpleMessage("По умолчанию"), 39 | "dlgAppsAddRemove": 40 | MessageLookupByLibrary.simpleMessage("Добавить/удалять программы"), 41 | "dlgAppsReload": MessageLookupByLibrary.simpleMessage( 42 | "Перезагрузить список программы"), 43 | "dlgAppsReorder": 44 | MessageLookupByLibrary.simpleMessage("Изменение порядка программы"), 45 | "dlgCall": MessageLookupByLibrary.simpleMessage("звонок"), 46 | "dlgCancel": MessageLookupByLibrary.simpleMessage("Отмена"), 47 | "dlgContactsAddRemove": 48 | MessageLookupByLibrary.simpleMessage("Добавить/удалять контакты"), 49 | "dlgContactsReload": MessageLookupByLibrary.simpleMessage( 50 | "Перезагрузить список контактов"), 51 | "dlgContactsReorder": 52 | MessageLookupByLibrary.simpleMessage("Изменение порядка контакты"), 53 | "dlgEditTitle": 54 | MessageLookupByLibrary.simpleMessage("Редактировать избранное"), 55 | "dlgOpenSettings": 56 | MessageLookupByLibrary.simpleMessage("Изменить настройки"), 57 | "msgNoContactsPermission": MessageLookupByLibrary.simpleMessage( 58 | "Чтобы добавить избранные контакты на этот экран, разрешите этому приложению доступ к вашим контактам."), 59 | "msgNoData": MessageLookupByLibrary.simpleMessage( 60 | "Данные недоступны. Попробуйте еще раз через несколько секунд."), 61 | "msgNoFavourites": MessageLookupByLibrary.simpleMessage( 62 | "Вы не добавили ни одного избранного"), 63 | "msgNoPhonePermission": MessageLookupByLibrary.simpleMessage( 64 | "Чтобы сделать звонки проще, позвольте этому приложению начинать телефонные звонки для вас."), 65 | "msgNotDefaultLauncher": MessageLookupByLibrary.simpleMessage( 66 | "Открывайте приложения и контакты быстрее, сделав Elder Launcher домашним приложением.") 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_fr.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a fr locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'fr'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "Apps": MessageLookupByLibrary.simpleMessage("Apps"), 26 | "Contacts": MessageLookupByLibrary.simpleMessage("Contacts"), 27 | "btnAddFavApps": 28 | MessageLookupByLibrary.simpleMessage("Ajouter des apps favorites"), 29 | "btnAddFavContacts": MessageLookupByLibrary.simpleMessage( 30 | "Ajouter des contacts favoris"), 31 | "btnAllApps": MessageLookupByLibrary.simpleMessage("All Apps"), 32 | "btnAllContacts": MessageLookupByLibrary.simpleMessage("All Contacts"), 33 | "btnBackToHome": MessageLookupByLibrary.simpleMessage("retourner"), 34 | "btnGrantPermission": 35 | MessageLookupByLibrary.simpleMessage("Donner la permission"), 36 | "btnSetDefaultLauncher": 37 | MessageLookupByLibrary.simpleMessage("Définir par défaut"), 38 | "dlgAppsAddRemove": 39 | MessageLookupByLibrary.simpleMessage("Ajouter/supprimer des Apps"), 40 | "dlgAppsReload": 41 | MessageLookupByLibrary.simpleMessage("Recharger la liste des Apps"), 42 | "dlgAppsReorder": 43 | MessageLookupByLibrary.simpleMessage("Réorganiser les Apps"), 44 | "dlgCall": MessageLookupByLibrary.simpleMessage("Téléphoner"), 45 | "dlgCancel": MessageLookupByLibrary.simpleMessage("Annuler"), 46 | "dlgContactsAddRemove": MessageLookupByLibrary.simpleMessage( 47 | "Ajouter/supprimer des Contacts"), 48 | "dlgContactsReload": MessageLookupByLibrary.simpleMessage( 49 | "Recharger la liste de contacts"), 50 | "dlgContactsReorder": 51 | MessageLookupByLibrary.simpleMessage("Réorganiser les Contacts"), 52 | "dlgEditTitle": 53 | MessageLookupByLibrary.simpleMessage("Modifier les favoris"), 54 | "dlgOpenSettings": 55 | MessageLookupByLibrary.simpleMessage("Modifier les paramètres"), 56 | "msgNoContactsPermission": MessageLookupByLibrary.simpleMessage( 57 | "Pour ajouter des contacts favoris à cet écran, autorisez cette application à accéder à vos contacts."), 58 | "msgNoData": MessageLookupByLibrary.simpleMessage( 59 | "Pas de données disponibles. Réessayez dans quelques secondes."), 60 | "msgNoFavourites": MessageLookupByLibrary.simpleMessage( 61 | "Vous n\'avez ajouté aucun favori"), 62 | "msgNoPhonePermission": MessageLookupByLibrary.simpleMessage( 63 | "Pour faciliter les appels, autorisez cette application à démarrer des appels téléphoniques à votre place."), 64 | "msgNotDefaultLauncher": MessageLookupByLibrary.simpleMessage( 65 | "Ouvrez les applications et les contacts plus rapidement en faisant d\'Elder Launcher l\'application domestique.") 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_es.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a es locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'es'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "Apps": MessageLookupByLibrary.simpleMessage("Aplicaciones"), 26 | "Contacts": MessageLookupByLibrary.simpleMessage("Contactos"), 27 | "btnAddFavApps": MessageLookupByLibrary.simpleMessage( 28 | "Agregar aplicaciones favoritas"), 29 | "btnAddFavContacts": 30 | MessageLookupByLibrary.simpleMessage("Agregar contactos favoritos"), 31 | "btnAllApps": 32 | MessageLookupByLibrary.simpleMessage("Todas las aplicaciones"), 33 | "btnAllContacts": 34 | MessageLookupByLibrary.simpleMessage("Todos los contactos"), 35 | "btnBackToHome": MessageLookupByLibrary.simpleMessage("Regresa"), 36 | "btnGrantPermission": 37 | MessageLookupByLibrary.simpleMessage("Conceder permiso"), 38 | "btnSetDefaultLauncher": MessageLookupByLibrary.simpleMessage( 39 | "Establecer como predeterminado"), 40 | "dlgAppsAddRemove": 41 | MessageLookupByLibrary.simpleMessage("Agregar/quitar Aplicaciones"), 42 | "dlgAppsReload": MessageLookupByLibrary.simpleMessage( 43 | "Recargar lista de aplicaciones"), 44 | "dlgAppsReorder": 45 | MessageLookupByLibrary.simpleMessage("Reordenar Aplicaciones"), 46 | "dlgCall": MessageLookupByLibrary.simpleMessage("Llamada"), 47 | "dlgCancel": MessageLookupByLibrary.simpleMessage("Cancelar"), 48 | "dlgContactsAddRemove": 49 | MessageLookupByLibrary.simpleMessage("Agregar/quitar Contactos"), 50 | "dlgContactsReload": 51 | MessageLookupByLibrary.simpleMessage("Recargar lista de contactos"), 52 | "dlgContactsReorder": 53 | MessageLookupByLibrary.simpleMessage("Reordenar Contactos"), 54 | "dlgEditTitle": 55 | MessageLookupByLibrary.simpleMessage("Editar Favoritos"), 56 | "dlgOpenSettings": 57 | MessageLookupByLibrary.simpleMessage("Cambiar ajustes"), 58 | "msgNoContactsPermission": MessageLookupByLibrary.simpleMessage( 59 | "Para agregar contactos favoritos a esta pantalla, permita que esta aplicación acceda a sus contactos."), 60 | "msgNoData": MessageLookupByLibrary.simpleMessage( 61 | "Datos no disponibles. Inténtalo de nuevo en unos segundos."), 62 | "msgNoFavourites": MessageLookupByLibrary.simpleMessage( 63 | "No ha agregado ningún favorito"), 64 | "msgNoPhonePermission": MessageLookupByLibrary.simpleMessage( 65 | "Para facilitar las llamadas, permita que esta aplicación inicie llamadas telefónicas por usted."), 66 | "msgNotDefaultLauncher": MessageLookupByLibrary.simpleMessage( 67 | "Abra aplicaciones y contactos más rápido haciendo que Elder Launcher sea la aplicación de inicio.") 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_all.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that looks up messages for specific locales by 3 | // delegating to the appropriate library. 4 | 5 | // Ignore issues from commonly used lints in this file. 6 | // ignore_for_file:implementation_imports, file_names, unnecessary_new 7 | // ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering 8 | // ignore_for_file:argument_type_not_assignable, invalid_assignment 9 | // ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases 10 | // ignore_for_file:comment_references 11 | 12 | import 'dart:async'; 13 | 14 | import 'package:intl/intl.dart'; 15 | import 'package:intl/message_lookup_by_library.dart'; 16 | import 'package:intl/src/intl_helpers.dart'; 17 | 18 | import 'messages_cs.dart' as messages_cs; 19 | import 'messages_de.dart' as messages_de; 20 | import 'messages_en.dart' as messages_en; 21 | import 'messages_en_US.dart' as messages_en_us; 22 | import 'messages_es.dart' as messages_es; 23 | import 'messages_fr.dart' as messages_fr; 24 | import 'messages_hi.dart' as messages_hi; 25 | import 'messages_it.dart' as messages_it; 26 | import 'messages_nl.dart' as messages_nl; 27 | import 'messages_pl.dart' as messages_pl; 28 | import 'messages_pt.dart' as messages_pt; 29 | import 'messages_ru.dart' as messages_ru; 30 | 31 | typedef Future LibraryLoader(); 32 | Map _deferredLibraries = { 33 | 'cs': () => new Future.value(null), 34 | 'de': () => new Future.value(null), 35 | 'en': () => new Future.value(null), 36 | 'en_US': () => new Future.value(null), 37 | 'es': () => new Future.value(null), 38 | 'fr': () => new Future.value(null), 39 | 'hi': () => new Future.value(null), 40 | 'it': () => new Future.value(null), 41 | 'nl': () => new Future.value(null), 42 | 'pl': () => new Future.value(null), 43 | 'pt': () => new Future.value(null), 44 | 'ru': () => new Future.value(null), 45 | }; 46 | 47 | MessageLookupByLibrary? _findExact(String localeName) { 48 | switch (localeName) { 49 | case 'cs': 50 | return messages_cs.messages; 51 | case 'de': 52 | return messages_de.messages; 53 | case 'en': 54 | return messages_en.messages; 55 | case 'en_US': 56 | return messages_en_us.messages; 57 | case 'es': 58 | return messages_es.messages; 59 | case 'fr': 60 | return messages_fr.messages; 61 | case 'hi': 62 | return messages_hi.messages; 63 | case 'it': 64 | return messages_it.messages; 65 | case 'nl': 66 | return messages_nl.messages; 67 | case 'pl': 68 | return messages_pl.messages; 69 | case 'pt': 70 | return messages_pt.messages; 71 | case 'ru': 72 | return messages_ru.messages; 73 | default: 74 | return null; 75 | } 76 | } 77 | 78 | /// User programs should call this before using [localeName] for messages. 79 | Future initializeMessages(String localeName) async { 80 | var availableLocale = Intl.verifiedLocale( 81 | localeName, (locale) => _deferredLibraries[locale] != null, 82 | onFailure: (_) => null); 83 | if (availableLocale == null) { 84 | return new Future.value(false); 85 | } 86 | var lib = _deferredLibraries[availableLocale]; 87 | await (lib == null ? new Future.value(false) : lib()); 88 | initializeInternalMessageLookup(() => new CompositeMessageLookup()); 89 | messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); 90 | return new Future.value(true); 91 | } 92 | 93 | bool _messagesExistFor(String locale) { 94 | try { 95 | return _findExact(locale) != null; 96 | } catch (e) { 97 | return false; 98 | } 99 | } 100 | 101 | MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { 102 | var actualLocale = 103 | Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null); 104 | if (actualLocale == null) return null; 105 | return _findExact(actualLocale); 106 | } 107 | -------------------------------------------------------------------------------- /lib/ui/pages/home_page/contacts_tab.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | import '../../../constants/edit_mode.dart'; 4 | import '../../../constants/route_names.dart'; 5 | import '../../../generated/l10n.dart'; 6 | import '../../../providers/contact_provider.dart'; 7 | import '../../../models/item.dart'; 8 | import '../../../ui/common/buttons.dart'; 9 | import '../../../ui/common/info_action_widget.dart'; 10 | import '../../../ui/common/loading_widget.dart'; 11 | import '../../../ui/pages/home_page/call_dialog.dart'; 12 | import '../../../ui/pages/home_page/fav_grid_view.dart'; 13 | import '../../common/action_panel.dart'; 14 | 15 | class ContactsTab extends StatelessWidget { 16 | const ContactsTab({ 17 | Key? key, 18 | }) : super(key: key); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | void openAllContacts() { 23 | context.read().launchContactsApp(); 24 | } 25 | 26 | void openContactDialog(Item contact) { 27 | CallDialog(context, contact); 28 | } 29 | 30 | void openEditScreen() { 31 | Navigator.pushNamed(context, editPageRoute, arguments: EditMode.contacts); 32 | } 33 | 34 | void requestContactsPermission() { 35 | context.read().requestContactsPermission(); 36 | } 37 | 38 | void requestPhonePermission() { 39 | context.read().requestPhonePermission(); 40 | } 41 | 42 | return Column( 43 | children: [ 44 | Flexible( 45 | child: Consumer( 46 | builder: (_, contactProvider, __) => Column( 47 | children: [ 48 | if (contactProvider.isFavListLoaded && 49 | contactProvider.favContacts.isNotEmpty && 50 | contactProvider.isPhonePermissionChecked && 51 | contactProvider.isPhonePermissionGranted) ...[ 52 | // Show Favourite Contacts 53 | FavGridView(contactProvider.favContacts, openContactDialog) 54 | ] else if (contactProvider.isFavListLoaded && 55 | contactProvider.favContacts.isNotEmpty && 56 | contactProvider.isPhonePermissionChecked && 57 | !contactProvider.isPhonePermissionGranted && 58 | contactProvider.isTelephoneFeatureChecked && 59 | contactProvider.hasTelephoneFeature) ...[ 60 | // Show Favourite Contacts with Phone Permission Prompt 61 | ActionPanel( 62 | heading: S.of(context).btnGrantPermission, 63 | body: InfoActionWidget( 64 | S.of(context).msgNoPhonePermission, 65 | S.of(context).btnGrantPermission, 66 | Icons.phone, 67 | requestPhonePermission), 68 | ), 69 | FavGridView(contactProvider.favContacts, openContactDialog) 70 | ] else if (contactProvider.isFavListLoaded && 71 | contactProvider.favContacts.isEmpty) ...[ 72 | // Show Add Favourites Prompt 73 | Expanded( 74 | child: InfoActionWidget.add( 75 | message: S.of(context).msgNoFavourites, 76 | buttonLabel: S.of(context).btnAddFavContacts, 77 | buttonOnClickAction: openEditScreen, 78 | ), 79 | ), 80 | ] else if (contactProvider.isContactsPermissionChecked && 81 | !contactProvider.isContactsPermissionGranted) ...[ 82 | // Show Contacts Permission Prompt 83 | Expanded( 84 | child: InfoActionWidget( 85 | S.of(context).msgNoContactsPermission, 86 | S.of(context).btnGrantPermission, 87 | Icons.perm_contact_calendar, 88 | requestContactsPermission), 89 | ) 90 | ] else ...[ 91 | const LoadingWidget(), 92 | ], 93 | ], 94 | ), 95 | ), 96 | ), 97 | Align( 98 | alignment: Alignment.bottomCenter, 99 | child: PrimaryButton( 100 | S.of(context).btnAllContacts, 101 | openAllContacts, 102 | ), 103 | ), 104 | ], 105 | ); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/providers/contact_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:collection'; 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:permission_handler/permission_handler.dart'; 5 | import '../data_sources/contact_repository.dart'; 6 | import '../models/item.dart'; 7 | import '../utils/native_methods.dart'; 8 | 9 | class ContactProvider extends ChangeNotifier { 10 | bool _isContactListLoaded = false; 11 | bool _isFavListLoaded = false; 12 | bool _isContactsPermissionChecked = false; 13 | bool _isPhonePermissionChecked = false; 14 | bool _isTelephoneFeatureChecked = false; 15 | 16 | late bool _hasTelephoneFeature; 17 | late bool _isContactsPermissionGranted; 18 | late bool _isPhonePermissionGranted; 19 | late Timer _refreshTimer; 20 | 21 | List _allContacts = []; 22 | List _favContacts = []; 23 | 24 | final ContactRepository _repository; 25 | 26 | ContactProvider(this._repository) { 27 | _loadContactsWithPermissionCheck(); 28 | _checkPhonePermission(); 29 | _refreshTimer = Timer.periodic(const Duration(minutes: 10), 30 | (timer) => _loadContactsWithPermissionCheck()); 31 | } 32 | 33 | bool get isContactListLoaded => _isContactListLoaded; 34 | bool get isFavListLoaded => _isFavListLoaded; 35 | bool get isContactsPermissionChecked => _isContactsPermissionChecked; 36 | bool get isContactsPermissionGranted => _isContactsPermissionGranted; 37 | bool get isPhonePermissionChecked => _isPhonePermissionChecked; 38 | bool get isPhonePermissionGranted => _isPhonePermissionGranted; 39 | bool get isTelephoneFeatureChecked => _isTelephoneFeatureChecked; 40 | bool get hasTelephoneFeature => _hasTelephoneFeature; 41 | UnmodifiableListView get allContacts => 42 | UnmodifiableListView(_allContacts); 43 | UnmodifiableListView get favContacts => 44 | UnmodifiableListView(_favContacts); 45 | 46 | @override 47 | void dispose() { 48 | _refreshTimer.cancel(); 49 | super.dispose(); 50 | } 51 | 52 | Future _loadContactsWithPermissionCheck() async { 53 | if (await Permission.contacts.isGranted) { 54 | _isContactsPermissionGranted = true; 55 | _loadContacts(); 56 | } else { 57 | _isContactsPermissionGranted = false; 58 | } 59 | _isContactsPermissionChecked = true; 60 | notifyListeners(); 61 | } 62 | 63 | Future _loadContacts() async { 64 | _loadAllItems(); 65 | _loadFavItems(); 66 | } 67 | 68 | Future _loadAllItems() async { 69 | _allContacts = await _repository.getAllItems(); 70 | _isContactListLoaded = true; 71 | notifyListeners(); 72 | } 73 | 74 | Future _loadFavItems() async { 75 | _favContacts = await _repository.getFavItems(); 76 | _isFavListLoaded = true; 77 | notifyListeners(); 78 | } 79 | 80 | Future _checkPhonePermission() async { 81 | _checkTelephoneFeature(); 82 | if (await Permission.phone.isGranted) { 83 | _isPhonePermissionGranted = true; 84 | } else { 85 | _isPhonePermissionGranted = false; 86 | } 87 | _isPhonePermissionChecked = true; 88 | notifyListeners(); 89 | } 90 | 91 | Future _checkTelephoneFeature() async { 92 | if (await NativeMethods().hasTelephoneFeature()) { 93 | _hasTelephoneFeature = true; 94 | } else { 95 | _hasTelephoneFeature = false; 96 | } 97 | _isTelephoneFeatureChecked = true; 98 | } 99 | 100 | Future _requestPermission(Permission permission) async { 101 | var permissionStatus = await permission.request(); 102 | if (permissionStatus == PermissionStatus.permanentlyDenied) { 103 | await openAppSettings(); 104 | } 105 | } 106 | 107 | Future launchContactsApp() async { 108 | NativeMethods().launchContactsApp(); 109 | } 110 | 111 | Future callPhoneNumber(String number) async { 112 | if (_isTelephoneFeatureChecked) { 113 | if (_isPhonePermissionGranted && _hasTelephoneFeature) { 114 | NativeMethods().startPhoneCall(number); 115 | } else { 116 | NativeMethods().launchDialerApp(number); 117 | } 118 | } 119 | } 120 | 121 | void reloadLists() { 122 | _loadContactsWithPermissionCheck(); 123 | } 124 | 125 | Future requestContactsPermission() async { 126 | await _requestPermission(Permission.contacts); 127 | _loadContactsWithPermissionCheck(); 128 | } 129 | 130 | Future requestPhonePermission() async { 131 | await _requestPermission(Permission.phone); 132 | _checkPhonePermission(); 133 | } 134 | 135 | Future saveFavContacts(List newFavContacts) async { 136 | _repository.setFavItems(newFavContacts); 137 | _loadFavItems(); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /lib/ui/pages/home_page/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_size_text/auto_size_text.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:provider/provider.dart'; 4 | import '../../../constants/edit_mode.dart'; 5 | import '../../../generated/l10n.dart'; 6 | import '../../../providers/date_time_provider.dart'; 7 | import '../../../ui/pages/home_page/apps_tab.dart'; 8 | import '../../../ui/pages/home_page/contacts_tab.dart'; 9 | import '../../../ui/pages/home_page/edit_dialog.dart'; 10 | import '../../../ui/theme.dart'; 11 | 12 | class HomePage extends StatelessWidget { 13 | const HomePage({Key? key}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | var _appBarTextSizeGroup = AutoSizeGroup(); 18 | 19 | void openEditDialog(int tabIndex) { 20 | EditMode editMode; 21 | 22 | if (tabIndex == 0) { 23 | editMode = EditMode.apps; 24 | } else { 25 | editMode = EditMode.contacts; 26 | } 27 | 28 | EditDialog(context, editMode); 29 | } 30 | 31 | return WillPopScope( 32 | onWillPop: () => Future.value(false), 33 | child: ChangeNotifierProvider( 34 | create: (_) => DateTimeProvider(), 35 | child: Scaffold( 36 | backgroundColor: Theme.of(context).backgroundColor, 37 | appBar: AppBar( 38 | actions: [ 39 | Padding( 40 | padding: const EdgeInsets.symmetric(horizontal: 8.0), 41 | child: IconButton( 42 | icon: const Icon(Icons.edit), 43 | onPressed: () { 44 | if (DefaultTabController.of(context) != null) { 45 | openEditDialog(DefaultTabController.of(context)!.index); 46 | } 47 | }, 48 | ), 49 | ), 50 | ], 51 | centerTitle: true, 52 | title: Consumer( 53 | builder: (_, dateTimeProvider, __) => AutoSizeText( 54 | dateTimeProvider.time, 55 | maxLines: 1, 56 | style: TextStyles.headerTime, 57 | ), 58 | ), 59 | bottom: PreferredSize( 60 | preferredSize: const Size(0, 80), 61 | child: Column( 62 | children: [ 63 | Consumer( 64 | builder: (_, dateTimeProvider, __) => AutoSizeText( 65 | dateTimeProvider.date, 66 | group: _appBarTextSizeGroup, 67 | maxLines: 1, 68 | style: TextStyles.headerDate, 69 | ), 70 | ), 71 | TabBar(tabs: [ 72 | Tab( 73 | child: Center( 74 | child: Row( 75 | mainAxisSize: MainAxisSize.min, 76 | children: [ 77 | const Padding( 78 | padding: EdgeInsets.fromLTRB(0, 0, 4.0, 0), 79 | child: Icon(Icons.apps), 80 | ), 81 | Flexible( 82 | child: AutoSizeText( 83 | S.of(context).Apps, 84 | group: _appBarTextSizeGroup, 85 | maxLines: 1, 86 | style: const TextStyle(fontSize: 50), 87 | ), 88 | ), 89 | ], 90 | ), 91 | ), 92 | ), 93 | Tab( 94 | child: Center( 95 | child: Row( 96 | mainAxisSize: MainAxisSize.min, 97 | children: [ 98 | const Padding( 99 | padding: EdgeInsets.fromLTRB(0, 0, 4.0, 0), 100 | child: Icon(Icons.contacts), 101 | ), 102 | Flexible( 103 | child: AutoSizeText( 104 | S.of(context).Contacts, 105 | group: _appBarTextSizeGroup, 106 | maxLines: 1, 107 | style: const TextStyle(fontSize: 50), 108 | ), 109 | ) 110 | ], 111 | ), 112 | ), 113 | ), 114 | ]), 115 | ], 116 | ), 117 | ), 118 | ), 119 | body: const TabBarView(children: [AppsTab(), ContactsTab()]), 120 | ), 121 | ), 122 | ); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/xyz/arjunsinh/elderlauncher/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package xyz.arjunsinh.elderlauncher 2 | 3 | import android.app.role.RoleManager 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.pm.PackageManager 7 | import android.net.Uri 8 | import android.os.Build 9 | import android.provider.ContactsContract 10 | import androidx.annotation.NonNull 11 | import io.flutter.embedding.android.FlutterActivity 12 | import io.flutter.embedding.engine.FlutterEngine 13 | import io.flutter.plugin.common.MethodChannel 14 | import io.flutter.plugins.GeneratedPluginRegistrant 15 | 16 | class MainActivity : FlutterActivity() { 17 | 18 | override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { 19 | GeneratedPluginRegistrant.registerWith(flutterEngine) 20 | 21 | MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_CONTACTS).setMethodCallHandler { call, result -> 22 | when (call.method) { 23 | "hasTelephoneFeature" -> result.success(hasTelephoneFeature()) 24 | "launchContactsApp" -> openContactsApp() 25 | "launchDialerWithNumber" -> { 26 | if (call.hasArgument("number")) { 27 | val number = call.argument("number")!! 28 | openDialerWithNumber(number) 29 | } 30 | } 31 | "startPhoneCall" -> { 32 | if (call.hasArgument("number")) { 33 | val number = call.argument("number")!! 34 | startPhoneCall(number) 35 | } 36 | } 37 | else -> result.notImplemented() 38 | } 39 | } 40 | 41 | MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_CORE).setMethodCallHandler { call, result -> 42 | when (call.method) { 43 | "canSetDefaultLauncher" -> result.success(canSetDefaultLauncher()) 44 | "getDeprecatedFavAppIds" -> result.success(getDeprecatedFavAppIds()) 45 | "setDefaultLauncher" -> setDefaultLauncher() 46 | else -> result.notImplemented() 47 | } 48 | } 49 | } 50 | 51 | /* 52 | ** Contacts & Phone 53 | */ 54 | 55 | private fun hasTelephoneFeature(): Boolean { 56 | val pm = applicationContext.packageManager; 57 | return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY); 58 | } 59 | 60 | // TODO: Fix Contacts App opening twice. 61 | private fun openContactsApp() { 62 | val intent = Intent(Intent.ACTION_VIEW, ContactsContract.Contacts.CONTENT_URI) 63 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 64 | if (intent != null) { 65 | startActivity(intent) 66 | } 67 | } 68 | 69 | private fun openDialerWithNumber(number: String) { 70 | val intent = Intent(Intent.ACTION_DIAL) 71 | intent.data = Uri.parse("tel:$number") 72 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 73 | if (intent != null) { 74 | startActivity(intent) 75 | } 76 | } 77 | 78 | private fun startPhoneCall(number: String) { 79 | val intent = Intent(Intent.ACTION_CALL) 80 | intent.data = Uri.parse("tel:$number") 81 | if (intent != null) { 82 | startActivity(intent) 83 | } 84 | } 85 | 86 | /* 87 | ** Core 88 | */ 89 | 90 | private fun canSetDefaultLauncher(): Boolean { 91 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 92 | val roleManager = getSystemService(RoleManager::class.java) 93 | val roleAvailable = roleManager.isRoleAvailable(RoleManager.ROLE_HOME) 94 | val roleHeld = roleManager.isRoleHeld(RoleManager.ROLE_HOME) 95 | roleAvailable && !roleHeld 96 | } else { 97 | false 98 | } 99 | } 100 | 101 | private fun getDeprecatedFavAppIds(): List { 102 | val sharedPrefs = applicationContext.getSharedPreferences("key_apps", Context.MODE_PRIVATE) 103 | var deprecatedFavAppIds = sharedPrefs.getStringSet("key_favorites", emptySet()) 104 | return deprecatedFavAppIds!!.toList() 105 | } 106 | 107 | private fun setDefaultLauncher() { 108 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 109 | val roleManager = getSystemService(RoleManager::class.java) 110 | val roleHeld = roleManager.isRoleHeld(RoleManager.ROLE_HOME) 111 | if (!roleHeld) { 112 | val roleRequestIntent = roleManager.createRequestRoleIntent(RoleManager.ROLE_HOME) 113 | startActivityForResult(roleRequestIntent, DEFAULT_LAUNCHER_INTENT_REQUEST_CODE) // Creates new instance of main activity if successful 114 | } 115 | } 116 | } 117 | 118 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 119 | super.onActivityResult(requestCode, resultCode, data) 120 | if (requestCode == DEFAULT_LAUNCHER_INTENT_REQUEST_CODE) { 121 | if (resultCode != RESULT_OK) { 122 | MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CHANNEL_CORE)?.invokeMethod("setDefaultLauncherFailure", null) 123 | } 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /lib/generated/l10n.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | import 'package:flutter/material.dart'; 3 | import 'package:intl/intl.dart'; 4 | import 'intl/messages_all.dart'; 5 | 6 | // ************************************************************************** 7 | // Generator: Flutter Intl IDE plugin 8 | // Made by Localizely 9 | // ************************************************************************** 10 | 11 | // ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars 12 | // ignore_for_file: join_return_with_assignment, prefer_final_in_for_each 13 | // ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes 14 | 15 | class S { 16 | S(); 17 | 18 | static S? _current; 19 | 20 | static S get current { 21 | assert(_current != null, 22 | 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.'); 23 | return _current!; 24 | } 25 | 26 | static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); 27 | 28 | static Future load(Locale locale) { 29 | final name = (locale.countryCode?.isEmpty ?? false) 30 | ? locale.languageCode 31 | : locale.toString(); 32 | final localeName = Intl.canonicalizedLocale(name); 33 | return initializeMessages(localeName).then((_) { 34 | Intl.defaultLocale = localeName; 35 | final instance = S(); 36 | S._current = instance; 37 | 38 | return instance; 39 | }); 40 | } 41 | 42 | static S of(BuildContext context) { 43 | final instance = S.maybeOf(context); 44 | assert(instance != null, 45 | 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?'); 46 | return instance!; 47 | } 48 | 49 | static S? maybeOf(BuildContext context) { 50 | return Localizations.of(context, S); 51 | } 52 | 53 | /// `Apps` 54 | String get Apps { 55 | return Intl.message( 56 | 'Apps', 57 | name: 'Apps', 58 | desc: '', 59 | args: [], 60 | ); 61 | } 62 | 63 | /// `Contacts` 64 | String get Contacts { 65 | return Intl.message( 66 | 'Contacts', 67 | name: 'Contacts', 68 | desc: '', 69 | args: [], 70 | ); 71 | } 72 | 73 | /// `All Apps` 74 | String get btnAllApps { 75 | return Intl.message( 76 | 'All Apps', 77 | name: 'btnAllApps', 78 | desc: '', 79 | args: [], 80 | ); 81 | } 82 | 83 | /// `All Contacts` 84 | String get btnAllContacts { 85 | return Intl.message( 86 | 'All Contacts', 87 | name: 'btnAllContacts', 88 | desc: '', 89 | args: [], 90 | ); 91 | } 92 | 93 | /// `Add Favourite Apps` 94 | String get btnAddFavApps { 95 | return Intl.message( 96 | 'Add Favourite Apps', 97 | name: 'btnAddFavApps', 98 | desc: '', 99 | args: [], 100 | ); 101 | } 102 | 103 | /// `Add Favourite Contacts` 104 | String get btnAddFavContacts { 105 | return Intl.message( 106 | 'Add Favourite Contacts', 107 | name: 'btnAddFavContacts', 108 | desc: '', 109 | args: [], 110 | ); 111 | } 112 | 113 | /// `Back to Home` 114 | String get btnBackToHome { 115 | return Intl.message( 116 | 'Back to Home', 117 | name: 'btnBackToHome', 118 | desc: '', 119 | args: [], 120 | ); 121 | } 122 | 123 | /// `Grant Permission` 124 | String get btnGrantPermission { 125 | return Intl.message( 126 | 'Grant Permission', 127 | name: 'btnGrantPermission', 128 | desc: '', 129 | args: [], 130 | ); 131 | } 132 | 133 | /// `Set as Default` 134 | String get btnSetDefaultLauncher { 135 | return Intl.message( 136 | 'Set as Default', 137 | name: 'btnSetDefaultLauncher', 138 | desc: '', 139 | args: [], 140 | ); 141 | } 142 | 143 | /// `No data available. Try again in a few seconds.` 144 | String get msgNoData { 145 | return Intl.message( 146 | 'No data available. Try again in a few seconds.', 147 | name: 'msgNoData', 148 | desc: '', 149 | args: [], 150 | ); 151 | } 152 | 153 | /// `You haven't added any favourites` 154 | String get msgNoFavourites { 155 | return Intl.message( 156 | 'You haven\'t added any favourites', 157 | name: 'msgNoFavourites', 158 | desc: '', 159 | args: [], 160 | ); 161 | } 162 | 163 | /// `To add favourite contacts to this screen, allow this app access to your contacts.` 164 | String get msgNoContactsPermission { 165 | return Intl.message( 166 | 'To add favourite contacts to this screen, allow this app access to your contacts.', 167 | name: 'msgNoContactsPermission', 168 | desc: '', 169 | args: [], 170 | ); 171 | } 172 | 173 | /// `To make calling easier, allow this app to start phone calls for you.` 174 | String get msgNoPhonePermission { 175 | return Intl.message( 176 | 'To make calling easier, allow this app to start phone calls for you.', 177 | name: 'msgNoPhonePermission', 178 | desc: '', 179 | args: [], 180 | ); 181 | } 182 | 183 | /// `Open apps and contacts faster by making Elder Launcher the home app.` 184 | String get msgNotDefaultLauncher { 185 | return Intl.message( 186 | 'Open apps and contacts faster by making Elder Launcher the home app.', 187 | name: 'msgNotDefaultLauncher', 188 | desc: '', 189 | args: [], 190 | ); 191 | } 192 | 193 | /// `Call` 194 | String get dlgCall { 195 | return Intl.message( 196 | 'Call', 197 | name: 'dlgCall', 198 | desc: '', 199 | args: [], 200 | ); 201 | } 202 | 203 | /// `Edit Favourites` 204 | String get dlgEditTitle { 205 | return Intl.message( 206 | 'Edit Favourites', 207 | name: 'dlgEditTitle', 208 | desc: '', 209 | args: [], 210 | ); 211 | } 212 | 213 | /// `Cancel` 214 | String get dlgCancel { 215 | return Intl.message( 216 | 'Cancel', 217 | name: 'dlgCancel', 218 | desc: '', 219 | args: [], 220 | ); 221 | } 222 | 223 | /// `Open Settings` 224 | String get dlgOpenSettings { 225 | return Intl.message( 226 | 'Open Settings', 227 | name: 'dlgOpenSettings', 228 | desc: '', 229 | args: [], 230 | ); 231 | } 232 | 233 | /// `Add/Remove Apps` 234 | String get dlgAppsAddRemove { 235 | return Intl.message( 236 | 'Add/Remove Apps', 237 | name: 'dlgAppsAddRemove', 238 | desc: '', 239 | args: [], 240 | ); 241 | } 242 | 243 | /// `Reorder Apps` 244 | String get dlgAppsReorder { 245 | return Intl.message( 246 | 'Reorder Apps', 247 | name: 'dlgAppsReorder', 248 | desc: '', 249 | args: [], 250 | ); 251 | } 252 | 253 | /// `Reload App List` 254 | String get dlgAppsReload { 255 | return Intl.message( 256 | 'Reload App List', 257 | name: 'dlgAppsReload', 258 | desc: '', 259 | args: [], 260 | ); 261 | } 262 | 263 | /// `Add/Remove Contacts` 264 | String get dlgContactsAddRemove { 265 | return Intl.message( 266 | 'Add/Remove Contacts', 267 | name: 'dlgContactsAddRemove', 268 | desc: '', 269 | args: [], 270 | ); 271 | } 272 | 273 | /// `Reorder Contacts` 274 | String get dlgContactsReorder { 275 | return Intl.message( 276 | 'Reorder Contacts', 277 | name: 'dlgContactsReorder', 278 | desc: '', 279 | args: [], 280 | ); 281 | } 282 | 283 | /// `Reload Contacts` 284 | String get dlgContactsReload { 285 | return Intl.message( 286 | 'Reload Contacts', 287 | name: 'dlgContactsReload', 288 | desc: '', 289 | args: [], 290 | ); 291 | } 292 | } 293 | 294 | class AppLocalizationDelegate extends LocalizationsDelegate { 295 | const AppLocalizationDelegate(); 296 | 297 | List get supportedLocales { 298 | return const [ 299 | Locale.fromSubtags(languageCode: 'en'), 300 | Locale.fromSubtags(languageCode: 'cs'), 301 | Locale.fromSubtags(languageCode: 'de'), 302 | Locale.fromSubtags(languageCode: 'en', countryCode: 'US'), 303 | Locale.fromSubtags(languageCode: 'es'), 304 | Locale.fromSubtags(languageCode: 'fr'), 305 | Locale.fromSubtags(languageCode: 'hi'), 306 | Locale.fromSubtags(languageCode: 'it'), 307 | Locale.fromSubtags(languageCode: 'nl'), 308 | Locale.fromSubtags(languageCode: 'pl'), 309 | Locale.fromSubtags(languageCode: 'pt'), 310 | Locale.fromSubtags(languageCode: 'ru'), 311 | ]; 312 | } 313 | 314 | @override 315 | bool isSupported(Locale locale) => _isSupported(locale); 316 | @override 317 | Future load(Locale locale) => S.load(locale); 318 | @override 319 | bool shouldReload(AppLocalizationDelegate old) => false; 320 | 321 | bool _isSupported(Locale locale) { 322 | for (var supportedLocale in supportedLocales) { 323 | if (supportedLocale.languageCode == locale.languageCode) { 324 | return true; 325 | } 326 | } 327 | return false; 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _fe_analyzer_shared: 5 | dependency: transitive 6 | description: 7 | name: _fe_analyzer_shared 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "34.0.0" 11 | analyzer: 12 | dependency: transitive 13 | description: 14 | name: analyzer 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "3.2.0" 18 | args: 19 | dependency: transitive 20 | description: 21 | name: args 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.3.0" 25 | async: 26 | dependency: transitive 27 | description: 28 | name: async 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "2.8.2" 32 | auto_size_text: 33 | dependency: "direct main" 34 | description: 35 | name: auto_size_text 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "3.0.0" 39 | boolean_selector: 40 | dependency: transitive 41 | description: 42 | name: boolean_selector 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "2.1.0" 46 | build: 47 | dependency: transitive 48 | description: 49 | name: build 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "2.2.1" 53 | built_collection: 54 | dependency: transitive 55 | description: 56 | name: built_collection 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "5.1.1" 60 | built_value: 61 | dependency: transitive 62 | description: 63 | name: built_value 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "8.1.4" 67 | characters: 68 | dependency: transitive 69 | description: 70 | name: characters 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "1.2.0" 74 | charcode: 75 | dependency: transitive 76 | description: 77 | name: charcode 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "1.3.1" 81 | cli_util: 82 | dependency: transitive 83 | description: 84 | name: cli_util 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "0.3.5" 88 | clock: 89 | dependency: transitive 90 | description: 91 | name: clock 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "1.1.0" 95 | code_builder: 96 | dependency: transitive 97 | description: 98 | name: code_builder 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "4.1.0" 102 | collection: 103 | dependency: transitive 104 | description: 105 | name: collection 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "1.15.0" 109 | contacts_service: 110 | dependency: "direct main" 111 | description: 112 | name: contacts_service 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "0.6.3" 116 | convert: 117 | dependency: transitive 118 | description: 119 | name: convert 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "3.0.1" 123 | crypto: 124 | dependency: transitive 125 | description: 126 | name: crypto 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "3.0.1" 130 | dart_style: 131 | dependency: transitive 132 | description: 133 | name: dart_style 134 | url: "https://pub.dartlang.org" 135 | source: hosted 136 | version: "2.2.1" 137 | device_apps: 138 | dependency: "direct main" 139 | description: 140 | name: device_apps 141 | url: "https://pub.dartlang.org" 142 | source: hosted 143 | version: "2.1.1" 144 | fake_async: 145 | dependency: transitive 146 | description: 147 | name: fake_async 148 | url: "https://pub.dartlang.org" 149 | source: hosted 150 | version: "1.2.0" 151 | ffi: 152 | dependency: transitive 153 | description: 154 | name: ffi 155 | url: "https://pub.dartlang.org" 156 | source: hosted 157 | version: "1.1.2" 158 | file: 159 | dependency: transitive 160 | description: 161 | name: file 162 | url: "https://pub.dartlang.org" 163 | source: hosted 164 | version: "6.1.2" 165 | fixnum: 166 | dependency: transitive 167 | description: 168 | name: fixnum 169 | url: "https://pub.dartlang.org" 170 | source: hosted 171 | version: "1.0.0" 172 | flutter: 173 | dependency: "direct main" 174 | description: flutter 175 | source: sdk 176 | version: "0.0.0" 177 | flutter_lints: 178 | dependency: "direct dev" 179 | description: 180 | name: flutter_lints 181 | url: "https://pub.dartlang.org" 182 | source: hosted 183 | version: "1.0.4" 184 | flutter_localizations: 185 | dependency: "direct main" 186 | description: flutter 187 | source: sdk 188 | version: "0.0.0" 189 | flutter_test: 190 | dependency: "direct dev" 191 | description: flutter 192 | source: sdk 193 | version: "0.0.0" 194 | flutter_web_plugins: 195 | dependency: transitive 196 | description: flutter 197 | source: sdk 198 | version: "0.0.0" 199 | glob: 200 | dependency: transitive 201 | description: 202 | name: glob 203 | url: "https://pub.dartlang.org" 204 | source: hosted 205 | version: "2.0.2" 206 | intl: 207 | dependency: "direct main" 208 | description: 209 | name: intl 210 | url: "https://pub.dartlang.org" 211 | source: hosted 212 | version: "0.17.0" 213 | js: 214 | dependency: transitive 215 | description: 216 | name: js 217 | url: "https://pub.dartlang.org" 218 | source: hosted 219 | version: "0.6.3" 220 | lints: 221 | dependency: transitive 222 | description: 223 | name: lints 224 | url: "https://pub.dartlang.org" 225 | source: hosted 226 | version: "1.0.1" 227 | logging: 228 | dependency: transitive 229 | description: 230 | name: logging 231 | url: "https://pub.dartlang.org" 232 | source: hosted 233 | version: "1.0.2" 234 | matcher: 235 | dependency: transitive 236 | description: 237 | name: matcher 238 | url: "https://pub.dartlang.org" 239 | source: hosted 240 | version: "0.12.11" 241 | material_color_utilities: 242 | dependency: transitive 243 | description: 244 | name: material_color_utilities 245 | url: "https://pub.dartlang.org" 246 | source: hosted 247 | version: "0.1.3" 248 | meta: 249 | dependency: transitive 250 | description: 251 | name: meta 252 | url: "https://pub.dartlang.org" 253 | source: hosted 254 | version: "1.7.0" 255 | mockito: 256 | dependency: "direct dev" 257 | description: 258 | name: mockito 259 | url: "https://pub.dartlang.org" 260 | source: hosted 261 | version: "5.0.17" 262 | nested: 263 | dependency: transitive 264 | description: 265 | name: nested 266 | url: "https://pub.dartlang.org" 267 | source: hosted 268 | version: "1.0.0" 269 | package_config: 270 | dependency: transitive 271 | description: 272 | name: package_config 273 | url: "https://pub.dartlang.org" 274 | source: hosted 275 | version: "2.0.2" 276 | path: 277 | dependency: transitive 278 | description: 279 | name: path 280 | url: "https://pub.dartlang.org" 281 | source: hosted 282 | version: "1.8.0" 283 | path_provider_linux: 284 | dependency: transitive 285 | description: 286 | name: path_provider_linux 287 | url: "https://pub.dartlang.org" 288 | source: hosted 289 | version: "2.1.5" 290 | path_provider_platform_interface: 291 | dependency: transitive 292 | description: 293 | name: path_provider_platform_interface 294 | url: "https://pub.dartlang.org" 295 | source: hosted 296 | version: "2.0.3" 297 | path_provider_windows: 298 | dependency: transitive 299 | description: 300 | name: path_provider_windows 301 | url: "https://pub.dartlang.org" 302 | source: hosted 303 | version: "2.0.5" 304 | permission_handler: 305 | dependency: "direct main" 306 | description: 307 | name: permission_handler 308 | url: "https://pub.dartlang.org" 309 | source: hosted 310 | version: "8.3.0" 311 | permission_handler_platform_interface: 312 | dependency: transitive 313 | description: 314 | name: permission_handler_platform_interface 315 | url: "https://pub.dartlang.org" 316 | source: hosted 317 | version: "3.7.0" 318 | platform: 319 | dependency: transitive 320 | description: 321 | name: platform 322 | url: "https://pub.dartlang.org" 323 | source: hosted 324 | version: "3.1.0" 325 | plugin_platform_interface: 326 | dependency: transitive 327 | description: 328 | name: plugin_platform_interface 329 | url: "https://pub.dartlang.org" 330 | source: hosted 331 | version: "2.1.2" 332 | process: 333 | dependency: transitive 334 | description: 335 | name: process 336 | url: "https://pub.dartlang.org" 337 | source: hosted 338 | version: "4.2.4" 339 | provider: 340 | dependency: "direct main" 341 | description: 342 | name: provider 343 | url: "https://pub.dartlang.org" 344 | source: hosted 345 | version: "6.0.2" 346 | pub_semver: 347 | dependency: transitive 348 | description: 349 | name: pub_semver 350 | url: "https://pub.dartlang.org" 351 | source: hosted 352 | version: "2.1.0" 353 | quiver: 354 | dependency: transitive 355 | description: 356 | name: quiver 357 | url: "https://pub.dartlang.org" 358 | source: hosted 359 | version: "3.0.1+1" 360 | shared_preferences: 361 | dependency: "direct main" 362 | description: 363 | name: shared_preferences 364 | url: "https://pub.dartlang.org" 365 | source: hosted 366 | version: "2.0.12" 367 | shared_preferences_android: 368 | dependency: transitive 369 | description: 370 | name: shared_preferences_android 371 | url: "https://pub.dartlang.org" 372 | source: hosted 373 | version: "2.0.10" 374 | shared_preferences_ios: 375 | dependency: transitive 376 | description: 377 | name: shared_preferences_ios 378 | url: "https://pub.dartlang.org" 379 | source: hosted 380 | version: "2.0.9" 381 | shared_preferences_linux: 382 | dependency: transitive 383 | description: 384 | name: shared_preferences_linux 385 | url: "https://pub.dartlang.org" 386 | source: hosted 387 | version: "2.0.4" 388 | shared_preferences_macos: 389 | dependency: transitive 390 | description: 391 | name: shared_preferences_macos 392 | url: "https://pub.dartlang.org" 393 | source: hosted 394 | version: "2.0.2" 395 | shared_preferences_platform_interface: 396 | dependency: transitive 397 | description: 398 | name: shared_preferences_platform_interface 399 | url: "https://pub.dartlang.org" 400 | source: hosted 401 | version: "2.0.0" 402 | shared_preferences_web: 403 | dependency: transitive 404 | description: 405 | name: shared_preferences_web 406 | url: "https://pub.dartlang.org" 407 | source: hosted 408 | version: "2.0.3" 409 | shared_preferences_windows: 410 | dependency: transitive 411 | description: 412 | name: shared_preferences_windows 413 | url: "https://pub.dartlang.org" 414 | source: hosted 415 | version: "2.0.4" 416 | sky_engine: 417 | dependency: transitive 418 | description: flutter 419 | source: sdk 420 | version: "0.0.99" 421 | source_gen: 422 | dependency: transitive 423 | description: 424 | name: source_gen 425 | url: "https://pub.dartlang.org" 426 | source: hosted 427 | version: "1.2.1" 428 | source_span: 429 | dependency: transitive 430 | description: 431 | name: source_span 432 | url: "https://pub.dartlang.org" 433 | source: hosted 434 | version: "1.8.1" 435 | stack_trace: 436 | dependency: transitive 437 | description: 438 | name: stack_trace 439 | url: "https://pub.dartlang.org" 440 | source: hosted 441 | version: "1.10.0" 442 | stream_channel: 443 | dependency: transitive 444 | description: 445 | name: stream_channel 446 | url: "https://pub.dartlang.org" 447 | source: hosted 448 | version: "2.1.0" 449 | string_scanner: 450 | dependency: transitive 451 | description: 452 | name: string_scanner 453 | url: "https://pub.dartlang.org" 454 | source: hosted 455 | version: "1.1.0" 456 | term_glyph: 457 | dependency: transitive 458 | description: 459 | name: term_glyph 460 | url: "https://pub.dartlang.org" 461 | source: hosted 462 | version: "1.2.0" 463 | test_api: 464 | dependency: transitive 465 | description: 466 | name: test_api 467 | url: "https://pub.dartlang.org" 468 | source: hosted 469 | version: "0.4.8" 470 | typed_data: 471 | dependency: transitive 472 | description: 473 | name: typed_data 474 | url: "https://pub.dartlang.org" 475 | source: hosted 476 | version: "1.3.0" 477 | vector_math: 478 | dependency: transitive 479 | description: 480 | name: vector_math 481 | url: "https://pub.dartlang.org" 482 | source: hosted 483 | version: "2.1.1" 484 | watcher: 485 | dependency: transitive 486 | description: 487 | name: watcher 488 | url: "https://pub.dartlang.org" 489 | source: hosted 490 | version: "1.0.1" 491 | win32: 492 | dependency: transitive 493 | description: 494 | name: win32 495 | url: "https://pub.dartlang.org" 496 | source: hosted 497 | version: "2.3.10" 498 | xdg_directories: 499 | dependency: transitive 500 | description: 501 | name: xdg_directories 502 | url: "https://pub.dartlang.org" 503 | source: hosted 504 | version: "0.2.0" 505 | yaml: 506 | dependency: transitive 507 | description: 508 | name: yaml 509 | url: "https://pub.dartlang.org" 510 | source: hosted 511 | version: "3.1.0" 512 | sdks: 513 | dart: ">=2.15.0 <3.0.0" 514 | flutter: ">=2.5.0" 515 | --------------------------------------------------------------------------------