From 749e4e3073f544b9f4b13ec8c6ff7c154765c80d Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Sat, 18 Sep 2021 23:37:52 +0200 Subject: [PATCH] Initial commit --- .gitignore | 304 +++++++ .idea/codeStyles/Project.xml | 178 ++++ .idea/codeStyles/codeStyleConfig.xml | 6 + .idea/deploymentTargetDropDown.xml | 17 + .idea/gradle.properties | 0 .idea/inspectionProfiles/Project_Default.xml | 6 + .idea/kotlinScripting.xml | 6 + LICENSE.txt | 674 +++++++++++++++ app/.gitignore | 1 + app/build.gradle.kts | 153 ++++ app/proguard-rules.pro | 21 + app/src/debug/google-services.json | 59 ++ app/src/debug/ic_launcher-playstore.png | Bin 0 -> 46331 bytes app/src/debug/ic_launcher-web.png | Bin 0 -> 45985 bytes .../java/de/mm20/launcher2/debug/Debug.kt | 22 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + app/src/debug/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 2880 bytes .../mipmap-hdpi/ic_launcher_background.png | Bin 0 -> 1648 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 4823 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 4716 bytes app/src/debug/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2009 bytes .../mipmap-mdpi/ic_launcher_background.png | Bin 0 -> 923 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 2632 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2946 bytes .../debug/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 3934 bytes .../mipmap-xhdpi/ic_launcher_background.png | Bin 0 -> 2456 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 7330 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 6681 bytes .../debug/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6485 bytes .../mipmap-xxhdpi/ic_launcher_background.png | Bin 0 -> 4442 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 15471 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 11173 bytes .../debug/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9365 bytes .../mipmap-xxxhdpi/ic_launcher_background.png | Bin 0 -> 6619 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 23735 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 16190 bytes app/src/debug/res/values-de/strings.xml | 3 + app/src/debug/res/values/bools.xml | 4 + app/src/debug/res/values/strings.xml | 3 + app/src/main/AndroidManifest.xml | 84 ++ app/src/main/ic_launcher-playstore.png | Bin 0 -> 42258 bytes .../de/mm20/launcher2/LauncherApplication.kt | 84 ++ .../launcher2/activity/AddItemActivity.kt | 28 + .../launcher2/activity/SettingsActivity.kt | 86 ++ .../launcher2/content/GenericFileProvider.kt | 5 + .../fragment/PreferencesAboutFragment.kt | 176 ++++ .../fragment/PreferencesAppearanceFragment.kt | 182 ++++ .../fragment/PreferencesBadgesFragment.kt | 47 + .../fragment/PreferencesCalendarFragment.kt | 121 +++ .../fragment/PreferencesCardFragment.kt | 199 +++++ .../fragment/PreferencesEasterEggFragment.kt | 29 + .../fragment/PreferencesLicenseFragment.kt | 60 ++ .../fragment/PreferencesMainFragment.kt | 60 ++ .../fragment/PreferencesSearchFragment.kt | 176 ++++ .../fragment/PreferencesServicesFragment.kt | 174 ++++ .../fragment/PreferencesWeatherFragment.kt | 166 ++++ .../PreferencesWebSearchesFragment.kt | 229 +++++ .../helper/DebugInformationDumper.kt | 45 + .../ui/preferences/AppStartAnimPreference.kt | 99 +++ .../mm20/launcher2/ui/view/PreferencesView.kt | 73 ++ app/src/main/res/anim/app_to_launcher_in.xml | 11 + app/src/main/res/anim/app_to_launcher_out.xml | 11 + .../main/res/anim/fast_out_extra_slow_in.xml | 3 + .../main/res/anim/ic_pause_to_play_path.xml | 9 + .../main/res/anim/ic_pause_to_play_rotate.xml | 8 + .../main/res/anim/ic_play_to_pause_path.xml | 9 + .../main/res/anim/ic_play_to_pause_rotate.xml | 8 + app/src/main/res/anim/ic_skip_next_arrow1.xml | 9 + app/src/main/res/anim/ic_skip_next_arrow2.xml | 9 + app/src/main/res/anim/ic_skip_prev_arrow1.xml | 9 + app/src/main/res/anim/ic_skip_prev_arrow2.xml | 9 + .../anim/preference_fragment_child_enter.xml | 9 + .../anim/preference_fragment_child_exit.xml | 9 + .../anim/preference_fragment_parent_enter.xml | 9 + .../anim/preference_fragment_parent_exit.xml | 9 + .../main/res/animator/card_raise_animator.xml | 23 + .../main/res/drawable-hdpi/ic_wikipedia.webp | Bin 0 -> 1114 bytes .../main/res/drawable-mdpi/ic_wikipedia.webp | Bin 0 -> 692 bytes .../drawable-night/ic_account_owncloud.xml | 4 + .../drawable-night/ic_permission_calendar.xml | 19 + .../main/res/drawable-xhdpi/ic_wikipedia.webp | Bin 0 -> 1592 bytes .../res/drawable-xxhdpi/ic_wikipedia.webp | Bin 0 -> 2498 bytes .../res/drawable-xxxhdpi/ic_about_mm20.webp | Bin 0 -> 6964 bytes .../res/drawable-xxxhdpi/ic_wikipedia.webp | Bin 0 -> 3380 bytes app/src/main/res/drawable/ic_about_fdroid.xml | 18 + app/src/main/res/drawable/ic_about_github.xml | 16 + .../main/res/drawable/ic_about_telegram.xml | 15 + app/src/main/res/drawable/ic_about_xda.xml | 8 + .../main/res/drawable/ic_account_google.xml | 30 + .../res/drawable/ic_account_microsoft.xml | 7 + .../res/drawable/ic_account_nextcloud.xml | 5 + .../main/res/drawable/ic_account_owncloud.xml | 4 + .../main/res/drawable/ic_arrow_drop_down.xml | 10 + app/src/main/res/drawable/ic_cancel.xml | 9 + app/src/main/res/drawable/ic_clear.xml | 9 + app/src/main/res/drawable/ic_description.xml | 9 + app/src/main/res/drawable/ic_drag_handle.xml | 9 + app/src/main/res/drawable/ic_expand_more.xml | 9 + app/src/main/res/drawable/ic_file_android.xml | 5 + app/src/main/res/drawable/ic_file_archive.xml | 5 + app/src/main/res/drawable/ic_file_code.xml | 5 + .../main/res/drawable/ic_file_document.xml | 5 + app/src/main/res/drawable/ic_file_folder.xml | 5 + app/src/main/res/drawable/ic_file_form.xml | 7 + app/src/main/res/drawable/ic_file_generic.xml | 5 + app/src/main/res/drawable/ic_file_markup.xml | 9 + app/src/main/res/drawable/ic_file_music.xml | 5 + app/src/main/res/drawable/ic_file_pdf.xml | 5 + app/src/main/res/drawable/ic_file_picture.xml | 5 + .../res/drawable/ic_file_presentation.xml | 5 + .../main/res/drawable/ic_file_spreadsheet.xml | 5 + app/src/main/res/drawable/ic_file_video.xml | 5 + app/src/main/res/drawable/ic_location.xml | 9 + app/src/main/res/drawable/ic_more_horiz.xml | 9 + .../main/res/drawable/ic_open_external.xml | 9 + .../main/res/drawable/ic_open_in_browser.xml | 9 + .../res/drawable/ic_permission_calendar.xml | 19 + .../res/drawable/ic_precipitation_none.xml | 7 + .../res/drawable/ic_precipitation_rain.xml | 7 + .../drawable/ic_precipitation_rain_snow.xml | 8 + .../res/drawable/ic_precipitation_snow.xml | 7 + app/src/main/res/drawable/ic_pref_about.xml | 15 + app/src/main/res/drawable/ic_pref_account.xml | 15 + .../main/res/drawable/ic_pref_appearance.xml | 15 + app/src/main/res/drawable/ic_pref_badge.xml | 20 + .../main/res/drawable/ic_pref_calendar.xml | 15 + app/src/main/res/drawable/ic_pref_plugins.xml | 15 + app/src/main/res/drawable/ic_pref_search.xml | 15 + app/src/main/res/drawable/ic_pref_weather.xml | 15 + .../drawable/ic_preference_websearch_new.xml | 10 + .../res/drawable/ic_resize_drag_handle.xml | 9 + app/src/main/res/drawable/ic_settings.xml | 9 + app/src/main/res/drawable/ic_skip_next.xml | 18 + .../main/res/drawable/ic_skip_next_anim.xml | 11 + app/src/main/res/drawable/ic_skip_prev.xml | 18 + .../main/res/drawable/ic_skip_prev_anim.xml | 11 + app/src/main/res/drawable/ic_today.xml | 9 + app/src/main/res/drawable/ic_website.xml | 5 + .../main/res/drawable/ic_widget_resize.xml | 9 + app/src/main/res/drawable/ic_wind.xml | 7 + app/src/main/res/layout/dialog_websearch.xml | 85 ++ .../res/layout/fragment_card_settings.xml | 43 + .../main/res/layout/fragment_easteregg.xml | 26 + app/src/main/res/layout/fragment_license.xml | 60 ++ app/src/main/res/layout/permission_view.xml | 29 + .../res/layout/preference_icon_shape_row.xml | 30 + .../res/layout/preference_start_anim_item.xml | 30 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../mipmap-hdpi/ic_launcher_background.png | Bin 0 -> 1648 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 4348 bytes .../mipmap-mdpi/ic_launcher_background.png | Bin 0 -> 923 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 2346 bytes .../mipmap-xhdpi/ic_launcher_background.png | Bin 0 -> 2456 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 6670 bytes .../mipmap-xxhdpi/ic_launcher_background.png | Bin 0 -> 4442 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 14272 bytes .../mipmap-xxxhdpi/ic_launcher_background.png | Bin 0 -> 6619 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 22128 bytes .../main/res/raw/app_start_anim_default.json | 1 + app/src/main/res/raw/app_start_anim_fade.json | 1 + app/src/main/res/raw/app_start_anim_m.json | 1 + .../res/raw/app_start_anim_slide_bottom.json | 1 + .../main/res/raw/app_start_anim_splash1.json | 1 + .../main/res/raw/app_start_anim_splash2.json | 1 + app/src/main/res/raw/license_apache_2.txt | 202 +++++ app/src/main/res/raw/license_bsd_2clause.txt | 7 + app/src/main/res/raw/license_bsd_3clause.txt | 9 + app/src/main/res/raw/license_glide.txt | 94 ++ app/src/main/res/raw/license_gpl_3.txt | 674 +++++++++++++++ app/src/main/res/raw/license_mit.txt | 17 + app/src/main/res/values/arrays.xml | 107 +++ app/src/main/res/values/attrs.xml | 7 + app/src/main/res/values/bools.xml | 5 + app/src/main/res/values/donottranslate.xml | 3 + app/src/main/res/values/integers.xml | 5 + app/src/main/res/values/licenses.xml | 313 +++++++ app/src/main/res/values/styles.xml | 11 + .../res/xml/motion_calendar_event_view.xml | 19 + app/src/main/res/xml/preferences_about.xml | 55 ++ .../main/res/xml/preferences_appearance.xml | 67 ++ app/src/main/res/xml/preferences_badges.xml | 23 + app/src/main/res/xml/preferences_calendar.xml | 21 + app/src/main/res/xml/preferences_cards.xml | 42 + app/src/main/res/xml/preferences_main.xml | 38 + app/src/main/res/xml/preferences_search.xml | 145 ++++ app/src/main/res/xml/preferences_services.xml | 29 + app/src/main/res/xml/preferences_weather.xml | 28 + app/src/main/res/xml/provider_paths.xml | 9 + app/src/release/ic_launcher-playstore.png | Bin 0 -> 43278 bytes .../java/de/mm20/launcher2/debug/Debug.kt | 14 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../release/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 2728 bytes .../mipmap-hdpi/ic_launcher_background.png | Bin 0 -> 1648 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 4488 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 4528 bytes .../release/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1936 bytes .../mipmap-mdpi/ic_launcher_background.png | Bin 0 -> 923 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 2443 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2854 bytes .../release/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 3742 bytes .../mipmap-xhdpi/ic_launcher_background.png | Bin 0 -> 2456 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 6851 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 6400 bytes .../release/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6089 bytes .../mipmap-xxhdpi/ic_launcher_background.png | Bin 0 -> 4442 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 14547 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 10662 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 8760 bytes .../mipmap-xxxhdpi/ic_launcher_background.png | Bin 0 -> 6619 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 22562 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 15424 bytes applications/.gitignore | 1 + applications/build.gradle.kts | 54 ++ applications/consumer-rules.pro | 0 applications/proguard-rules.pro | 21 + applications/src/main/AndroidManifest.xml | 5 + .../launcher2/applications/AppRepository.kt | 227 +++++ .../launcher2/applications/AppViewModel.kt | 13 + .../launcher2/search/data/AppInstallation.kt | 67 ++ .../mm20/launcher2/search/data/AppShortcut.kt | 161 ++++ .../mm20/launcher2/search/data/Application.kt | 88 ++ .../mm20/launcher2/search/data/LauncherApp.kt | 123 +++ .../main/res/drawable/ic_app_placeholder.xml | 5 + appsearch/.gitignore | 1 + appsearch/build.gradle.kts | 58 ++ appsearch/consumer-rules.pro | 0 appsearch/proguard-rules.pro | 21 + appsearch/src/main/AndroidManifest.xml | 5 + .../appsearch/AppSearchRepository.kt | 69 ++ .../launcher2/appsearch/AppSearchViewModel.kt | 11 + .../launcher2/search/data/AppSearchResult.kt | 15 + assets/icons/MM20Launcher2-alpha.svg | 437 ++++++++++ assets/icons/MM20Launcher2-beta.svg | 437 ++++++++++ assets/icons/MM20Launcher2-debug.svg | 437 ++++++++++ assets/icons/MM20Launcher2-release.svg | 433 ++++++++++ assets/icons/MM20Launcher2.svg | 414 +++++++++ badges/.gitignore | 1 + badges/build.gradle.kts | 48 ++ badges/consumer-rules.pro | 0 badges/proguard-rules.pro | 21 + badges/src/main/AndroidManifest.xml | 5 + .../java/de/mm20/launcher2/badges/Badge.kt | 10 + .../de/mm20/launcher2/badges/BadgeProvider.kt | 150 ++++ .../res/drawable-hdpi/ic_badge_gdrive.webp | Bin 0 -> 10488 bytes .../res/drawable-mdpi/ic_badge_gdrive.webp | Bin 0 -> 9182 bytes .../res/drawable-xhdpi/ic_badge_gdrive.webp | Bin 0 -> 9596 bytes .../res/drawable-xxhdpi/ic_badge_gdrive.webp | Bin 0 -> 10894 bytes .../res/drawable-xxxhdpi/ic_badge_gdrive.webp | Bin 0 -> 10404 bytes .../main/res/drawable/ic_badge_nextcloud.xml | 10 + .../main/res/drawable/ic_badge_onedrive.xml | 7 + .../main/res/drawable/ic_badge_owncloud.xml | 10 + .../main/res/drawable/ic_badge_suspended.xml | 15 + .../res/drawable/ic_badge_workprofile.xml | 15 + base/.gitignore | 1 + base/build.gradle.kts | 51 ++ base/consumer-rules.pro | 0 base/proguard-rules.pro | 21 + base/src/main/AndroidManifest.xml | 4 + .../mm20/launcher2/graphics/BadgeDrawable.kt | 58 ++ .../de/mm20/launcher2/helper/NetworkUtils.kt | 17 + .../de/mm20/launcher2/icons/LauncherIcon.kt | 84 ++ .../mm20/launcher2/view/ElevationImageView.kt | 135 +++ base/src/main/res/color/chip_background.xml | 5 + base/src/main/res/color/chip_stroke.xml | 9 + base/src/main/res/color/chip_textcolor.xml | 5 + .../src/main/res/color/text_color_primary.xml | 5 + .../main/res/color/text_color_secondary.xml | 5 + base/src/main/res/drawable/ic_permission.xml | 26 + .../res/drawable/ic_weather_broken_clouds.xml | 54 ++ .../ic_weather_broken_clouds_night.xml | 75 ++ .../main/res/drawable/ic_weather_clear.xml | 27 + .../res/drawable/ic_weather_clear_night.xml | 55 ++ .../main/res/drawable/ic_weather_cloudy.xml | 26 + .../src/main/res/drawable/ic_weather_cold.xml | 37 + .../main/res/drawable/ic_weather_drizzle.xml | 56 ++ base/src/main/res/drawable/ic_weather_fog.xml | 65 ++ .../src/main/res/drawable/ic_weather_hail.xml | 47 + .../src/main/res/drawable/ic_weather_haze.xml | 69 ++ .../res/drawable/ic_weather_haze_night.xml | 104 +++ base/src/main/res/drawable/ic_weather_hot.xml | 37 + .../res/drawable/ic_weather_mostly_cloudy.xml | 37 + .../ic_weather_mostly_cloudy_night.xml | 44 + .../res/drawable/ic_weather_partly_cloudy.xml | 44 + .../ic_weather_partly_cloudy_night.xml | 65 ++ .../main/res/drawable/ic_weather_showers.xml | 47 + .../main/res/drawable/ic_weather_sleet.xml | 75 ++ .../src/main/res/drawable/ic_weather_snow.xml | 47 + .../res/drawable/ic_weather_thunderstorm.xml | 33 + .../ic_weather_thunderstorm_with_rain.xml | 47 + .../src/main/res/drawable/ic_weather_wind.xml | 13 + base/src/main/res/values-night/bools.xml | 4 + base/src/main/res/values-night/colors.xml | 49 ++ base/src/main/res/values-night/styles.xml | 20 + .../main/res/values-notnight-v23/colors.xml | 4 + base/src/main/res/values/attrs.xml | 7 + base/src/main/res/values/colors.xml | 53 ++ base/src/main/res/values/styles.xml | 121 +++ base/src/main/res/values/typography.xml | 28 + build.gradle.kts | 36 + calculator/.gitignore | 1 + calculator/build.gradle.kts | 50 ++ calculator/consumer-rules.pro | 0 calculator/proguard-rules.pro | 21 + calculator/src/main/AndroidManifest.xml | 5 + .../calculator/CalculatorRepository.kt | 64 ++ .../calculator/CalculatorViewModel.kt | 7 + .../mm20/launcher2/search/data/Calculator.kt | 89 ++ calendar/.gitignore | 1 + calendar/build.gradle.kts | 52 ++ calendar/consumer-rules.pro | 0 calendar/proguard-rules.pro | 21 + calendar/src/main/AndroidManifest.xml | 5 + .../launcher2/calendar/CalendarRepository.kt | 81 ++ .../launcher2/calendar/CalendarViewModel.kt | 11 + .../launcher2/search/data/CalendarEvent.kt | 321 +++++++ .../main/res/drawable-hdpi/ic_calendar.webp | Bin 0 -> 730 bytes .../main/res/drawable-mdpi/ic_calendar.webp | Bin 0 -> 474 bytes .../main/res/drawable-xhdpi/ic_calendar.webp | Bin 0 -> 1048 bytes .../main/res/drawable-xxhdpi/ic_calendar.webp | Bin 0 -> 1748 bytes .../res/drawable-xxxhdpi/ic_calendar.webp | Bin 0 -> 2338 bytes compat/.gitignore | 1 + compat/build.gradle.kts | 42 + compat/consumer-rules.pro | 0 compat/proguard-rules.pro | 21 + compat/src/main/AndroidManifest.xml | 5 + .../launcher2/compat/PackageManagerCompat.kt | 33 + contacts/.gitignore | 1 + contacts/build.gradle.kts | 52 ++ contacts/consumer-rules.pro | 0 contacts/proguard-rules.pro | 21 + contacts/src/main/AndroidManifest.xml | 5 + .../launcher2/contacts/ContactRepository.kt | 47 + .../launcher2/contacts/ContactViewModel.kt | 10 + .../de/mm20/launcher2/search/data/Contact.kt | 210 +++++ crashreporter/.gitignore | 1 + crashreporter/build.gradle.kts | 46 + crashreporter/consumer-rules.pro | 0 crashreporter/proguard-rules.pro | 21 + crashreporter/readme.md | 26 + crashreporter/src/main/AndroidManifest.xml | 24 + .../crashreporter/CrashReporter.java | 72 ++ .../CrashReporterInitProvider.java | 68 ++ .../adapter/CrashLogAdapter.java | 81 ++ .../adapter/MainPagerAdapter.java | 50 ++ .../crashreporter/ui/CrashLogFragment.java | 95 ++ .../ui/CrashReporterActivity.java | 114 +++ .../ui/ExceptionLogFragment.java | 96 +++ .../crashreporter/ui/LogMessageActivity.java | 90 ++ .../crashreporter/utils/AppUtils.java | 106 +++ .../crashreporter/utils/Constants.java | 15 + .../utils/CrashReporterException.java | 64 ++ .../utils/CrashReporterExceptionHandler.java | 18 + .../CrashReporterNotInitializedException.java | 47 + .../crashreporter/utils/CrashUtil.java | 154 ++++ .../crashreporter/utils/FileUtils.java | 119 +++ .../utils/SimplePageChangeListener.java | 17 + .../launcher2/crashreporter/CrashReporter.kt | 14 + .../drawable/ic_menu_delete_white_24dp.xml | 9 + .../res/drawable/ic_menu_share_white_24dp.xml | 9 + .../res/drawable/ic_search_white_24dp.xml | 9 + .../res/drawable/ic_warning_black_24dp.xml | 9 + .../main/res/layout/activity_log_message.xml | 49 ++ .../src/main/res/layout/crash_log.xml | 4 + .../res/layout/crash_reporter_activity.xml | 38 + .../src/main/res/layout/custom_item.xml | 41 + .../src/main/res/layout/exception_log.xml | 4 + .../src/main/res/menu/crash_detail_menu.xml | 15 + .../src/main/res/menu/log_main_menu.xml | 16 + crashreporter/src/main/res/values/colors.xml | 7 + crashreporter/src/main/res/values/strings.xml | 11 + crashreporter/src/main/res/values/styles.xml | 9 + currencies/.gitignore | 1 + currencies/build.gradle.kts | 52 ++ currencies/consumer-rules.pro | 0 currencies/proguard-rules.pro | 21 + currencies/src/main/AndroidManifest.xml | 3 + .../de/mm20/launcher2/currencies/Currency.kt | 19 + .../currencies/CurrencyRepository.kt | 109 +++ .../currencies/ExchangeRateWorker.kt | 56 ++ database/.gitignore | 1 + database/build.gradle.kts | 57 ++ database/consumer-rules.pro | 0 database/proguard-rules.pro | 21 + database/readme.md | 4 + .../0.json | 218 +++++ .../1.json | 224 +++++ .../10.json | 380 ++++++++ .../11.json | 374 ++++++++ .../12.json | 406 +++++++++ .../13.json | 439 ++++++++++ .../14.json | 439 ++++++++++ .../2.json | 224 +++++ .../3.json | 224 +++++ .../4.json | 312 +++++++ .../5.json | 312 +++++++ .../6.json | 362 ++++++++ .../7.json | 362 ++++++++ .../8.json | 374 ++++++++ .../9.json | 380 ++++++++ database/src/main/AndroidManifest.xml | 5 + .../de/mm20/launcher2/database/AppDatabase.kt | 147 ++++ .../de/mm20/launcher2/database/Converters.kt | 34 + .../de/mm20/launcher2/database/CurrencyDao.kt | 34 + .../de/mm20/launcher2/database/IconDao.kt | 73 ++ .../de/mm20/launcher2/database/SearchDao.kt | 120 +++ .../de/mm20/launcher2/database/WeatherDao.kt | 27 + .../de/mm20/launcher2/database/WidgetDao.kt | 23 + .../database/entities/CurrencyEntity.kt | 11 + .../database/entities/FavoritesItemEntity.kt | 14 + .../database/entities/ForecastEntity.kt | 33 + .../launcher2/database/entities/IconEntity.kt | 15 + .../database/entities/IconPackEntity.kt | 12 + .../database/entities/PluginEntity.kt | 10 + .../database/entities/WebsearchEntity.kt | 13 + .../database/entities/WidgetEntity.kt | 15 + favorites/.gitignore | 1 + favorites/build.gradle.kts | 54 ++ favorites/consumer-rules.pro | 0 favorites/proguard-rules.pro | 21 + favorites/src/main/AndroidManifest.xml | 1 + .../mm20/launcher2/favorites/FavoritesItem.kt | 35 + .../favorites/FavoritesRepository.kt | 218 +++++ .../launcher2/favorites/FavoritesViewModel.kt | 58 ++ .../favorites/SearchableDeserializer.kt | 47 + files/.gitignore | 1 + files/build.gradle.kts | 57 ++ files/consumer-rules.pro | 0 files/proguard-rules.pro | 21 + files/src/main/AndroidManifest.xml | 5 + .../mm20/launcher2/files/FilesRepository.kt | 70 ++ .../de/mm20/launcher2/files/FilesViewModel.kt | 16 + .../launcher2/media/ThumbnailUtilsCompat.kt | 26 + .../de/mm20/launcher2/search/data/File.kt | 403 +++++++++ .../mm20/launcher2/search/data/GDriveFile.kt | 126 +++ .../launcher2/search/data/NextcloudFile.kt | 101 +++ .../launcher2/search/data/OneDriveFile.kt | 123 +++ .../launcher2/search/data/OwncloudFile.kt | 101 +++ .../src/main/res/drawable/ic_file_android.xml | 5 + .../src/main/res/drawable/ic_file_archive.xml | 5 + files/src/main/res/drawable/ic_file_code.xml | 5 + .../main/res/drawable/ic_file_document.xml | 5 + .../src/main/res/drawable/ic_file_folder.xml | 5 + files/src/main/res/drawable/ic_file_form.xml | 7 + .../src/main/res/drawable/ic_file_generic.xml | 5 + .../src/main/res/drawable/ic_file_markup.xml | 9 + files/src/main/res/drawable/ic_file_music.xml | 5 + files/src/main/res/drawable/ic_file_pdf.xml | 5 + .../src/main/res/drawable/ic_file_picture.xml | 5 + .../res/drawable/ic_file_presentation.xml | 5 + .../main/res/drawable/ic_file_spreadsheet.xml | 5 + files/src/main/res/drawable/ic_file_video.xml | 5 + g-services/.gitignore | 2 + g-services/build.gradle.kts | 55 ++ g-services/consumer-rules.pro | 0 g-services/proguard-rules.pro | 21 + g-services/readme.md | 30 + .../src/debug/res/raw/g_services_example.json | 1 + g-services/src/main/AndroidManifest.xml | 21 + .../de/mm20/launcher2/gservices/DriveFile.kt | 40 + .../mm20/launcher2/gservices/GoogleAccount.kt | 5 + .../launcher2/gservices/GoogleApiHelper.kt | 222 +++++ .../gservices/GoogleAuthRedirectActivity.kt | 24 + g-services/src/main/res/values/styles.xml | 10 + .../release/res/raw/g_services_example.json | 1 + gradle.properties | 23 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 ++++ gradlew.bat | 84 ++ hiddenitems/.gitignore | 1 + hiddenitems/build.gradle.kts | 45 + hiddenitems/consumer-rules.pro | 0 hiddenitems/proguard-rules.pro | 21 + hiddenitems/src/main/AndroidManifest.xml | 5 + .../hiddenitems/HiddenItemsRepository.kt | 29 + .../hiddenitems/HiddenItemsViewModel.kt | 9 + i18n/.gitignore | 1 + i18n/build.gradle.kts | 43 + i18n/consumer-rules.pro | 0 i18n/proguard-rules.pro | 21 + i18n/readme.md | 12 + i18n/src/main/AndroidManifest.xml | 4 + i18n/src/main/res/values-de/defaults.xml | 5 + i18n/src/main/res/values-de/strings.xml | 409 +++++++++ i18n/src/main/res/values-de/units.xml | 263 ++++++ i18n/src/main/res/values-en-rUS/defaults.xml | 5 + i18n/src/main/res/values/defaults.xml | 5 + i18n/src/main/res/values/strings.xml | 443 ++++++++++ i18n/src/main/res/values/units.xml | 267 ++++++ icons/.gitignore | 1 + icons/build.gradle.kts | 53 ++ icons/consumer-rules.pro | 0 icons/proguard-rules.pro | 21 + icons/src/main/AndroidManifest.xml | 12 + .../icons/CalendarDynamicLauncherIcon.kt | 65 ++ .../icons/ClockDynamicLauncherIcon.kt | 57 ++ .../launcher2/icons/DynamicIconController.kt | 66 ++ .../launcher2/icons/DynamicLauncherIcon.kt | 22 + .../main/java/de/mm20/launcher2/icons/Icon.kt | 30 + .../java/de/mm20/launcher2/icons/IconPack.kt | 26 + .../mm20/launcher2/icons/IconPackManager.kt | 517 +++++++++++ .../de/mm20/launcher2/icons/IconRepository.kt | 67 ++ ktx/.gitignore | 1 + ktx/build.gradle.kts | 45 + ktx/consumer-rules.pro | 0 ktx/proguard-rules.pro | 21 + ktx/readme.md | 3 + ktx/src/main/AndroidManifest.xml | 5 + .../java/de/mm20/launcher2/ktx/Address.kt | 11 + .../java/de/mm20/launcher2/ktx/Context.kt | 29 + .../main/java/de/mm20/launcher2/ktx/Double.kt | 7 + .../java/de/mm20/launcher2/ktx/Drawable.kt | 16 + .../java/de/mm20/launcher2/ktx/Extensions.kt | 36 + .../main/java/de/mm20/launcher2/ktx/Float.kt | 7 + .../java/de/mm20/launcher2/ktx/Fragment.kt | 11 + .../java/de/mm20/launcher2/ktx/InputStream.kt | 9 + .../main/java/de/mm20/launcher2/ktx/Int.kt | 19 + .../main/java/de/mm20/launcher2/ktx/List.kt | 13 + .../de/mm20/launcher2/ktx/Notification.kt | 9 + .../main/java/de/mm20/launcher2/ktx/Rect.kt | 19 + .../main/java/de/mm20/launcher2/ktx/RectF.kt | 25 + .../java/de/mm20/launcher2/ktx/Resources.kt | 29 + .../mm20/launcher2/ktx/SharedPreferences.kt | 17 + .../main/java/de/mm20/launcher2/ktx/String.kt | 7 + .../java/de/mm20/launcher2/ktx/TextView.kt | 13 + .../java/de/mm20/launcher2/ktx/UserHandle.kt | 12 + .../main/java/de/mm20/launcher2/ktx/View.kt | 24 + ms-services/.gitignore | 2 + ms-services/build.gradle.kts | 49 ++ ms-services/consumer-rules.pro | 0 ms-services/proguard-rules.pro | 21 + ms-services/readme.md | 41 + .../res/raw/msal_auth_config_example.json | 14 + ms-services/src/main/AndroidManifest.xml | 20 + .../de/mm20/launcher2/msservices/DriveItem.kt | 39 + .../msservices/MicrosoftGraphApiHelper.kt | 195 +++++ .../de/mm20/launcher2/msservices/MsUser.kt | 5 + .../res/raw/msal_auth_config_example.json | 14 + music/.gitignore | 1 + music/build.gradle.kts | 46 + music/consumer-rules.pro | 0 music/proguard-rules.pro | 21 + music/src/main/AndroidManifest.xml | 5 + .../mm20/launcher2/music/MusicRepository.kt | 268 ++++++ .../de/mm20/launcher2/music/MusicViewModel.kt | 50 ++ music/src/main/res/values/dimens.xml | 3 + nextcloud/.gitignore | 1 + nextcloud/build.gradle.kts | 55 ++ nextcloud/consumer-rules.pro | 0 nextcloud/proguard-rules.pro | 21 + nextcloud/src/main/AndroidManifest.xml | 12 + .../mm20/launcher2/nextcloud/LoginActivity.kt | 81 ++ .../de/mm20/launcher2/nextcloud/NcUser.kt | 6 + .../launcher2/nextcloud/NextcloudApiHelper.kt | 217 +++++ .../main/res/drawable/ic_nextcloud_logo.xml | 13 + .../res/layout/activity_nextcloud_login.xml | 46 + .../src/main/res/values-night/styles.xml | 9 + nextcloud/src/main/res/values/styles.xml | 11 + notifications/.gitignore | 1 + notifications/build.gradle.kts | 50 ++ notifications/consumer-rules.pro | 0 notifications/proguard-rules.pro | 21 + notifications/src/main/AndroidManifest.xml | 15 + .../notifications/NotificationService.kt | 116 +++ owncloud/.gitignore | 1 + owncloud/build.gradle.kts | 57 ++ owncloud/consumer-rules.pro | 0 owncloud/proguard-rules.pro | 21 + owncloud/src/main/AndroidManifest.xml | 17 + .../mm20/launcher2/owncloud/LoginActivity.kt | 62 ++ .../java/de/mm20/launcher2/owncloud/OcUser.kt | 6 + .../mm20/launcher2/owncloud/OwncloudClient.kt | 197 +++++ .../res/drawable-night/ic_owncloud_logo.xml | 6 + .../main/res/drawable/ic_owncloud_logo.xml | 6 + .../res/layout/activity_owncloud_login.xml | 46 + ...ivity_owncloud_login_username_password.xml | 66 ++ .../layout/owncloud_login_drop_down_item.xml | 8 + owncloud/src/main/res/values-night/styles.xml | 9 + owncloud/src/main/res/values/styles.xml | 11 + permissions/.gitignore | 1 + permissions/build.gradle.kts | 46 + permissions/consumer-rules.pro | 0 permissions/proguard-rules.pro | 21 + permissions/src/main/AndroidManifest.xml | 5 + .../data/search/MissingPermission.kt | 19 + .../permissions/PermissionsManager.kt | 74 ++ preferences/.gitignore | 1 + preferences/build.gradle.kts | 71 ++ preferences/consumer-rules.pro | 0 preferences/proguard-rules.pro | 21 + preferences/src/main/AndroidManifest.xml | 4 + .../preferences/LauncherPreferences.kt | 186 ++++ .../preferences/PreferenceDelegates.kt | 89 ++ .../preferences/SettingsSerializer.kt | 24 + preferences/src/main/proto/settings.proto | 57 ++ readme.md | 102 +++ rssparser/.gitignore | 1 + rssparser/build.gradle.kts | 41 + rssparser/proguard-rules.pro | 21 + rssparser/src/main/AndroidManifest.xml | 2 + .../main/java/de/mm20/rssparser/Article.kt | 52 ++ .../main/java/de/mm20/rssparser/DayOfWeek.kt | 5 + .../main/java/de/mm20/rssparser/Exceptions.kt | 3 + .../main/java/de/mm20/rssparser/FeedInfo.kt | 84 ++ .../main/java/de/mm20/rssparser/RssParser.kt | 133 +++ rssparser/src/main/res/values/strings.xml | 3 + search/.gitignore | 1 + search/build.gradle.kts | 47 + search/consumer-rules.pro | 0 search/proguard-rules.pro | 21 + search/src/main/AndroidManifest.xml | 1 + .../search/BaseSearchableRepository.kt | 50 ++ .../mm20/launcher2/search/SearchRepository.kt | 38 + .../mm20/launcher2/search/SearchViewModel.kt | 16 + .../launcher2/search/WebsearchRepository.kt | 64 ++ .../launcher2/search/WebsearchViewModel.kt | 22 + .../mm20/launcher2/search/data/Searchable.kt | 56 ++ .../mm20/launcher2/search/data/Websearch.kt | 45 + settings.gradle.kts | 390 +++++++++ transition/.gitignore | 1 + transition/build.gradle.kts | 45 + transition/consumer-rules.pro | 0 transition/proguard-rules.pro | 21 + transition/src/main/AndroidManifest.xml | 5 + .../launcher2/transition/BackgroundColor.kt | 36 + .../mm20/launcher2/transition/CardCorners.kt | 33 + .../transition/ChangingLayoutTransition.kt | 9 + .../de/mm20/launcher2/transition/Elevation.kt | 28 + .../transition/OneShotLayoutTransition.kt | 26 + .../mm20/launcher2/transition/TextResize.kt | 399 +++++++++ ui/.gitignore | 1 + ui/build.gradle.kts | 118 +++ ui/consumer-rules.pro | 0 ui/proguard-rules.pro | 21 + ui/src/main/AndroidManifest.xml | 40 + .../java/de/mm20/launcher2/ui/AppTheme.kt | 235 +++++ .../de/mm20/launcher2/ui/ColorSchemeTest.kt | 72 ++ .../java/de/mm20/launcher2/ui/Extensions.kt | 9 + .../de/mm20/launcher2/ui/InformationText.kt | 35 + .../java/de/mm20/launcher2/ui/LauncherCard.kt | 8 + .../launcher2/ui/LauncherIconShapeAmbient.kt | 55 ++ .../mm20/launcher2/ui/LauncherMainScreen.kt | 140 +++ .../de/mm20/launcher2/ui/LegacyAppTheme.kt | 43 + .../java/de/mm20/launcher2/ui/Resources.kt | 23 + .../java/de/mm20/launcher2/ui/SearchBar.kt | 120 +++ .../java/de/mm20/launcher2/ui/SearchColumn.kt | 72 ++ .../mm20/launcher2/ui/ShapedLauncherIcon.kt | 202 +++++ .../java/de/mm20/launcher2/ui/WidgetColumn.kt | 103 +++ .../launcher2/ui/activity/ComposeActivity.kt | 97 +++ .../compat/AnimatedVectorResourcePolyfill.kt | 23 + .../de/mm20/launcher2/ui/component/Chip.kt | 23 + .../mm20/launcher2/ui/component/ChipGroup.kt | 19 + .../de/mm20/launcher2/ui/component/Clocks.kt | 184 ++++ .../ui/component/DefaultSwipeActions.kt | 157 ++++ .../mm20/launcher2/ui/component/LottieIcon.kt | 28 + .../de/mm20/launcher2/ui/component/Spacer.kt | 13 + .../mm20/launcher2/ui/component/TextClock.kt | 160 ++++ .../de/mm20/launcher2/ui/component/Toolbar.kt | 194 +++++ .../java/de/mm20/launcher2/ui/icons/Icons.kt | 620 +++++++++++++ .../de/mm20/launcher2/ui/icons/Searchable.kt | 145 ++++ .../java/de/mm20/launcher2/ui/ktx/Float.kt | 14 + .../main/java/de/mm20/launcher2/ui/ktx/Int.kt | 14 + .../java/de/mm20/launcher2/ui/ktx/Modifier.kt | 11 + .../java/de/mm20/launcher2/ui/ktx/Offset.kt | 9 + .../ui/legacy/activity/LauncherActivity.kt | 811 ++++++++++++++++++ .../ui/legacy/component/ApplicationView.kt | 34 + .../ui/legacy/component/CalculatorView.kt | 73 ++ .../ui/legacy/component/CalendarView.kt | 55 ++ .../ui/legacy/component/ContactView.kt | 54 ++ .../ui/legacy/component/EditFavoritesRow.kt | 32 + .../ui/legacy/component/EditFavoritesView.kt | 141 +++ .../ui/legacy/component/FavoritesView.kt | 36 + .../launcher2/ui/legacy/component/FileView.kt | 53 ++ .../ui/legacy/component/SearchBar.kt | 262 ++++++ .../ui/legacy/component/UnitConverterView.kt | 191 +++++ .../ui/legacy/component/WebSearchView.kt | 73 ++ .../ui/legacy/component/WebsiteView.kt | 42 + .../ui/legacy/component/WidgetView.kt | 156 ++++ .../ui/legacy/component/WikipediaView.kt | 42 + .../ui/legacy/data/InformationText.kt | 30 + .../legacy/fragment/SearchableBottomSheet.kt | 35 + .../ui/legacy/helper/ActivityStarter.kt | 209 +++++ .../ui/legacy/helper/BitmapHolder.kt | 22 + .../ui/legacy/helper/WallpaperBlur.kt | 93 ++ .../search/ApplicationDetailRepresentation.kt | 393 +++++++++ .../legacy/search/BasicGridRepresentation.kt | 58 ++ .../search/CalendarDetailRepresentation.kt | 127 +++ .../search/CalendarListRepresentation.kt | 110 +++ .../search/ContactDetailRepresentation.kt | 352 ++++++++ .../search/ContactListRepresentation.kt | 61 ++ .../legacy/search/FileDetailRepresentation.kt | 150 ++++ .../legacy/search/FileListRepresentation.kt | 93 ++ .../search/InformationListRepresentation.kt | 25 + .../search/PermissionListRepresentation.kt | 28 + .../ui/legacy/search/Representation.kt | 9 + .../ui/legacy/search/SearchGridView.kt | 304 +++++++ .../ui/legacy/search/SearchListView.kt | 101 +++ .../search/ShortcutDetailRepresentation.kt | 67 ++ .../search/WebsiteDetailRepresentation.kt | 115 +++ .../search/WebsiteListRepresentation.kt | 108 +++ .../search/WikipediaDetailRepresentation.kt | 76 ++ .../search/WikipediaListRepresentation.kt | 71 ++ .../ui/legacy/searchable/SearchableView.kt | 144 ++++ .../ui/legacy/transition/LauncherCards.kt | 72 ++ .../transition/LauncherIconViewTransition.kt | 47 + .../ui/legacy/view/AspectRationImageView.kt | 32 + .../ui/legacy/view/BatteryChargingView.kt | 139 +++ .../mm20/launcher2/ui/legacy/view/BlurView.kt | 96 +++ .../launcher2/ui/legacy/view/InnerCardView.kt | 22 + .../ui/legacy/view/LauncherCardView.kt | 68 ++ .../ui/legacy/view/LauncherIconView.kt | 460 ++++++++++ .../launcher2/ui/legacy/view/SwipeCardView.kt | 446 ++++++++++ .../launcher2/ui/legacy/view/ToolbarView.kt | 273 ++++++ .../ui/legacy/view/WidgetResizeDragView.kt | 43 + .../ui/legacy/widget/CalendarWidget.kt | 203 +++++ .../launcher2/ui/legacy/widget/CompactView.kt | 9 + .../ui/legacy/widget/ExternalWidget.kt | 66 ++ .../ui/legacy/widget/LauncherWidget.kt | 43 + .../launcher2/ui/legacy/widget/MusicWidget.kt | 176 ++++ .../launcher2/ui/legacy/widget/SmartWidget.kt | 279 ++++++ .../ui/legacy/widget/WeatherWidget.kt | 74 ++ .../launcher2/ui/locals/CompositionLocals.kt | 16 + .../launcher2/ui/search/ApplicationItem.kt | 163 ++++ .../launcher2/ui/search/ApplicationResults.kt | 18 + .../mm20/launcher2/ui/search/BasicGridItem.kt | 48 ++ .../launcher2/ui/search/CalculatorItem.kt | 67 ++ .../launcher2/ui/search/FavoriteResults.kt | 17 + .../de/mm20/launcher2/ui/search/FileItem.kt | 177 ++++ .../mm20/launcher2/ui/search/FileResults.kt | 16 + .../mm20/launcher2/ui/search/GridItemLabel.kt | 34 + .../launcher2/ui/search/SearchableGrid.kt | 228 +++++ .../launcher2/ui/search/SearchableItem.kt | 113 +++ .../launcher2/ui/search/SearchableList.kt | 23 + .../mm20/launcher2/ui/search/WikipediaItem.kt | 76 ++ .../launcher2/ui/search/WikipediaResult.kt | 28 + .../ui/searchable/CalendarEventItem.kt | 240 ++++++ .../ui/searchable/DeprecatedSearchableList.kt | 41 + .../mm20/launcher2/ui/theme/ContentAlpha.kt | 8 + .../launcher2/ui/theme/WallpaperColors.kt | 18 + .../launcher2/ui/theme/colors/ColorScheme.kt | 12 + .../launcher2/ui/theme/colors/ColorSwatch.kt | 47 + .../ui/theme/colors/DefaultColorScheme.kt | 18 + .../ui/theme/colors/SystemColorScheme.kt | 90 ++ .../ui/theme/colors/WallpaperColorScheme.kt | 15 + .../ui/weather/AnimatedWeatherIcon.kt | 653 ++++++++++++++ .../mm20/launcher2/ui/weather/WeatherIcon.kt | 456 ++++++++++ .../launcher2/ui/widget/CalendarWidget.kt | 253 ++++++ .../mm20/launcher2/ui/widget/ClockWidget.kt | 85 ++ .../mm20/launcher2/ui/widget/MusicWidget.kt | 164 ++++ .../launcher2/ui/widget/PlatformWidget.kt | 46 + .../mm20/launcher2/ui/widget/WeatherWidget.kt | 465 ++++++++++ .../de/mm20/launcher2/ui/widget/WidgetCard.kt | 48 ++ .../launcher2/ui/widget/parts/DatePart.kt | 14 + .../res/anim/activity_start_fade_enter.xml | 7 + .../res/anim/activity_start_fade_exit.xml | 5 + .../activity_start_slide_bottom_enter.xml | 14 + .../anim/activity_start_slide_bottom_exit.xml | 14 + .../res/anim/activity_start_splash1_enter.xml | 9 + .../res/anim/activity_start_splash1_exit.xml | 13 + .../res/anim/activity_start_splash2_enter.xml | 8 + .../res/anim/activity_start_splash2_exit.xml | 8 + ui/src/main/res/drawable/anim_ic_edit_add.xml | 45 + .../main/res/drawable/anim_ic_menu_clear.xml | 103 +++ .../main/res/drawable/anim_ic_play_pause.xml | 45 + .../res/drawable/anim_weather_sun_moon.xml | 60 ++ ui/src/main/res/drawable/drag_handle.xml | 6 + ui/src/main/res/drawable/ic_add.xml | 9 + ui/src/main/res/drawable/ic_arrow_back.xml | 10 + ui/src/main/res/drawable/ic_arrow_left.xml | 10 + ui/src/main/res/drawable/ic_arrow_right.xml | 10 + ui/src/main/res/drawable/ic_arrow_up.xml | 9 + ui/src/main/res/drawable/ic_attendees.xml | 9 + ui/src/main/res/drawable/ic_call.xml | 7 + ui/src/main/res/drawable/ic_delete.xml | 9 + ui/src/main/res/drawable/ic_description.xml | 9 + ui/src/main/res/drawable/ic_done.xml | 9 + ui/src/main/res/drawable/ic_edit.xml | 9 + ui/src/main/res/drawable/ic_info_outline.xml | 9 + ui/src/main/res/drawable/ic_location.xml | 9 + ui/src/main/res/drawable/ic_mail.xml | 7 + ui/src/main/res/drawable/ic_message.xml | 7 + ui/src/main/res/drawable/ic_more_vert.xml | 9 + ui/src/main/res/drawable/ic_open_external.xml | 9 + ui/src/main/res/drawable/ic_pause.xml | 16 + ui/src/main/res/drawable/ic_pause_to_play.xml | 11 + ui/src/main/res/drawable/ic_play.xml | 18 + ui/src/main/res/drawable/ic_play_to_pause.xml | 11 + ui/src/main/res/drawable/ic_search.xml | 9 + ui/src/main/res/drawable/ic_share.xml | 7 + ui/src/main/res/drawable/ic_star_outline.xml | 9 + ui/src/main/res/drawable/ic_star_solid.xml | 9 + ui/src/main/res/drawable/ic_telegram.xml | 7 + ui/src/main/res/drawable/ic_time.xml | 9 + ui/src/main/res/drawable/ic_visibility.xml | 9 + .../main/res/drawable/ic_visibility_off.xml | 9 + ui/src/main/res/drawable/ic_whatsapp.xml | 7 + ui/src/main/res/font/inter_black.ttf | Bin 0 -> 294396 bytes ui/src/main/res/font/inter_bold.ttf | Bin 0 -> 293448 bytes ui/src/main/res/font/inter_extrabold.ttf | Bin 0 -> 294072 bytes ui/src/main/res/font/inter_extralight.ttf | Bin 0 -> 288400 bytes ui/src/main/res/font/inter_light.ttf | Bin 0 -> 288088 bytes ui/src/main/res/font/inter_medium.ttf | Bin 0 -> 292140 bytes ui/src/main/res/font/inter_regular.ttf | Bin 0 -> 287928 bytes ui/src/main/res/font/inter_semibold.ttf | Bin 0 -> 293028 bytes ui/src/main/res/font/inter_thin.ttf | Bin 0 -> 288568 bytes ui/src/main/res/layout/activity_launcher.xml | 203 +++++ ui/src/main/res/layout/compact_music.xml | 62 ++ ui/src/main/res/layout/compact_weather.xml | 7 + .../main/res/layout/dialog_edit_favorites.xml | 24 + ui/src/main/res/layout/edit_favorites_row.xml | 30 + ui/src/main/res/layout/unit_converter_row.xml | 32 + .../res/layout/unit_converter_show_all.xml | 12 + ui/src/main/res/layout/view_application.xml | 22 + .../res/layout/view_application_detail.xml | 93 ++ ui/src/main/res/layout/view_basic_grid.xml | 50 ++ ui/src/main/res/layout/view_calculator.xml | 69 ++ .../main/res/layout/view_calendar_detail.xml | 67 ++ ui/src/main/res/layout/view_calendar_list.xml | 65 ++ .../main/res/layout/view_calendar_widget.xml | 146 ++++ .../main/res/layout/view_contact_detail.xml | 76 ++ ui/src/main/res/layout/view_contact_list.xml | 73 ++ ui/src/main/res/layout/view_date_time.xml | 46 + ui/src/main/res/layout/view_favorites.xml | 21 + ui/src/main/res/layout/view_file_detail.xml | 84 ++ ui/src/main/res/layout/view_file_list.xml | 76 ++ .../main/res/layout/view_information_list.xml | 28 + ui/src/main/res/layout/view_list_item.xml | 16 + .../main/res/layout/view_permission_list.xml | 37 + ui/src/main/res/layout/view_search_bar.xml | 73 ++ .../res/layout/view_search_category_list.xml | 24 + .../view_search_category_single_item.xml | 16 + ui/src/main/res/layout/view_unitconverter.xml | 98 +++ ui/src/main/res/layout/view_websearch.xml | 40 + .../main/res/layout/view_website_detail.xml | 15 + ui/src/main/res/layout/view_website_list.xml | 71 ++ ui/src/main/res/layout/view_widget.xml | 95 ++ .../main/res/layout/view_wikipedia_detail.xml | 15 + .../main/res/layout/view_wikipedia_list.xml | 71 ++ ui/src/main/res/menu/menu_launcher.xml | 15 + .../res/raw-night/lottie_play_to_pause.json | 1 + .../main/res/raw-night/lottie_skip_next.json | 1 + ui/src/main/res/raw/ic_menu_to_clear.json | 1 + ui/src/main/res/raw/lottie_menu_to_clear.json | 1 + ui/src/main/res/raw/lottie_play_to_pause.json | 1 + ui/src/main/res/raw/lottie_skip_next.json | 1 + ui/src/main/res/raw/play_to_pause.json | 1 + ui/src/main/res/values-w312dp/integers.xml | 4 + ui/src/main/res/values-w400dp/integers.xml | 4 + ui/src/main/res/values-w408dp/integers.xml | 4 + ui/src/main/res/values-w480dp/integers.xml | 4 + ui/src/main/res/values-w504dp/integers.xml | 4 + ui/src/main/res/values-w600dp/integers.xml | 4 + .../main/res/values/AspectRatioImageView.xml | 7 + ui/src/main/res/values/BottomSheet.xml | 10 + ui/src/main/res/values/EditFavoritesView.xml | 9 + ui/src/main/res/values/LauncherCardView.xml | 7 + ui/src/main/res/values/SearchGridView.xml | 7 + ui/src/main/res/values/dimens.xml | 8 + unitconverter/.gitignore | 1 + unitconverter/build.gradle.kts | 50 ++ unitconverter/consumer-rules.pro | 0 unitconverter/proguard-rules.pro | 21 + unitconverter/src/main/AndroidManifest.xml | 5 + .../search/data/CurrencyUnitConverter.kt | 7 + .../launcher2/search/data/UnitConverter.kt | 51 ++ .../mm20/launcher2/unitconverter/Dimension.kt | 17 + .../launcher2/unitconverter/MeasureUnit.kt | 7 + .../unitconverter/UnitConverterRepository.kt | 30 + .../unitconverter/UnitConverterViewModel.kt | 8 + .../mm20/launcher2/unitconverter/UnitValue.kt | 8 + .../unitconverter/converters/AreaConverter.kt | 58 ++ .../unitconverter/converters/Converter.kt | 59 ++ .../converters/CurrencyConverter.kt | 98 +++ .../unitconverter/converters/DataConverter.kt | 84 ++ .../converters/LengthConverter.kt | 59 ++ .../unitconverter/converters/MassConverter.kt | 54 ++ .../converters/TemperatureConverter.kt | 8 + .../unitconverter/converters/TimeConverter.kt | 44 + .../converters/VelocityConverter.kt | 34 + weather/.gitignore | 2 + weather/build.gradle.kts | 55 ++ weather/consumer-rules.pro | 0 weather/proguard-rules.pro | 21 + weather/readme.md | 50 ++ weather/src/main/AndroidManifest.xml | 5 + .../mm20/launcher2/weather/DailyForecast.kt | 121 +++ .../de/mm20/launcher2/weather/Forecast.kt | 135 +++ .../java/de/mm20/launcher2/weather/Weather.kt | 47 + .../mm20/launcher2/weather/WeatherProvider.kt | 51 ++ .../launcher2/weather/WeatherRepository.kt | 90 ++ .../launcher2/weather/WeatherViewModel.kt | 23 + .../launcher2/weather/here/HereProvider.kt | 420 +++++++++ .../launcher2/weather/metno/MetNoProvider.kt | 381 ++++++++ .../openweathermap/OpenWeatherMapApi.kt | 130 +++ .../openweathermap/OpenWeatherMapProvider.kt | 269 ++++++ .../src/main/res/values/config_example.xml | 6 + webdav/.gitignore | 1 + webdav/build.gradle.kts | 48 ++ webdav/consumer-rules.pro | 0 webdav/proguard-rules.pro | 21 + webdav/src/main/AndroidManifest.xml | 5 + .../de/mm20/launcher2/webdav/WebDavApi.kt | 192 +++++ .../de/mm20/launcher2/webdav/WebDavFile.kt | 11 + websites/.gitignore | 1 + websites/build.gradle.kts | 57 ++ websites/consumer-rules.pro | 0 websites/proguard-rules.pro | 21 + websites/src/main/AndroidManifest.xml | 5 + .../de/mm20/launcher2/search/data/Website.kt | 175 ++++ .../launcher2/websites/WebsiteRepository.kt | 52 ++ .../launcher2/websites/WebsiteViewModel.kt | 10 + websites/src/main/res/drawable/ic_website.xml | 5 + widgets/.gitignore | 1 + widgets/build.gradle.kts | 58 ++ widgets/consumer-rules.pro | 0 widgets/proguard-rules.pro | 21 + widgets/src/main/AndroidManifest.xml | 5 + .../java/de/mm20/launcher2/widgets/Widget.kt | 32 + .../launcher2/widgets/WidgetRepository.kt | 33 + .../mm20/launcher2/widgets/WidgetViewModel.kt | 40 + wikipedia/.gitignore | 1 + wikipedia/build.gradle.kts | 54 ++ wikipedia/consumer-rules.pro | 0 wikipedia/proguard-rules.pro | 21 + wikipedia/readme.md | 4 + wikipedia/src/main/AndroidManifest.xml | 5 + .../mm20/launcher2/search/data/Wikipedia.kt | 135 +++ .../mm20/launcher2/wikipedia/WikipediaApi.kt | 42 + .../wikipedia/WikipediaRepository.kt | 98 +++ .../launcher2/wikipedia/WikipediaViewModel.kt | 10 + .../main/res/drawable-hdpi/ic_wikipedia.webp | Bin 0 -> 1114 bytes .../main/res/drawable-mdpi/ic_wikipedia.webp | Bin 0 -> 1114 bytes .../main/res/drawable-xhdpi/ic_wikipedia.webp | Bin 0 -> 1592 bytes .../res/drawable-xxhdpi/ic_wikipedia.webp | Bin 0 -> 2498 bytes .../res/drawable-xxxhdpi/ic_wikipedia.webp | Bin 0 -> 3380 bytes 938 files changed, 50475 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/deploymentTargetDropDown.xml create mode 100644 .idea/gradle.properties create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/kotlinScripting.xml create mode 100644 LICENSE.txt create mode 100644 app/.gitignore create mode 100644 app/build.gradle.kts create mode 100644 app/proguard-rules.pro create mode 100644 app/src/debug/google-services.json create mode 100644 app/src/debug/ic_launcher-playstore.png create mode 100644 app/src/debug/ic_launcher-web.png create mode 100644 app/src/debug/java/de/mm20/launcher2/debug/Debug.kt create mode 100644 app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/debug/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/debug/res/mipmap-hdpi/ic_launcher_background.png create mode 100644 app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 app/src/debug/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 app/src/debug/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/debug/res/mipmap-mdpi/ic_launcher_background.png create mode 100644 app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 app/src/debug/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 app/src/debug/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/debug/res/mipmap-xhdpi/ic_launcher_background.png create mode 100644 app/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 app/src/debug/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/debug/res/mipmap-xxhdpi/ic_launcher_background.png create mode 100644 app/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/src/debug/res/mipmap-xxxhdpi/ic_launcher_background.png create mode 100644 app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 app/src/debug/res/values-de/strings.xml create mode 100644 app/src/debug/res/values/bools.xml create mode 100644 app/src/debug/res/values/strings.xml create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/ic_launcher-playstore.png create mode 100644 app/src/main/java/de/mm20/launcher2/LauncherApplication.kt create mode 100644 app/src/main/java/de/mm20/launcher2/activity/AddItemActivity.kt create mode 100644 app/src/main/java/de/mm20/launcher2/activity/SettingsActivity.kt create mode 100644 app/src/main/java/de/mm20/launcher2/content/GenericFileProvider.kt create mode 100644 app/src/main/java/de/mm20/launcher2/fragment/PreferencesAboutFragment.kt create mode 100644 app/src/main/java/de/mm20/launcher2/fragment/PreferencesAppearanceFragment.kt create mode 100644 app/src/main/java/de/mm20/launcher2/fragment/PreferencesBadgesFragment.kt create mode 100644 app/src/main/java/de/mm20/launcher2/fragment/PreferencesCalendarFragment.kt create mode 100644 app/src/main/java/de/mm20/launcher2/fragment/PreferencesCardFragment.kt create mode 100644 app/src/main/java/de/mm20/launcher2/fragment/PreferencesEasterEggFragment.kt create mode 100644 app/src/main/java/de/mm20/launcher2/fragment/PreferencesLicenseFragment.kt create mode 100644 app/src/main/java/de/mm20/launcher2/fragment/PreferencesMainFragment.kt create mode 100644 app/src/main/java/de/mm20/launcher2/fragment/PreferencesSearchFragment.kt create mode 100644 app/src/main/java/de/mm20/launcher2/fragment/PreferencesServicesFragment.kt create mode 100644 app/src/main/java/de/mm20/launcher2/fragment/PreferencesWeatherFragment.kt create mode 100644 app/src/main/java/de/mm20/launcher2/fragment/PreferencesWebSearchesFragment.kt create mode 100644 app/src/main/java/de/mm20/launcher2/helper/DebugInformationDumper.kt create mode 100644 app/src/main/java/de/mm20/launcher2/ui/preferences/AppStartAnimPreference.kt create mode 100644 app/src/main/java/de/mm20/launcher2/ui/view/PreferencesView.kt create mode 100644 app/src/main/res/anim/app_to_launcher_in.xml create mode 100644 app/src/main/res/anim/app_to_launcher_out.xml create mode 100644 app/src/main/res/anim/fast_out_extra_slow_in.xml create mode 100644 app/src/main/res/anim/ic_pause_to_play_path.xml create mode 100644 app/src/main/res/anim/ic_pause_to_play_rotate.xml create mode 100644 app/src/main/res/anim/ic_play_to_pause_path.xml create mode 100644 app/src/main/res/anim/ic_play_to_pause_rotate.xml create mode 100644 app/src/main/res/anim/ic_skip_next_arrow1.xml create mode 100644 app/src/main/res/anim/ic_skip_next_arrow2.xml create mode 100644 app/src/main/res/anim/ic_skip_prev_arrow1.xml create mode 100644 app/src/main/res/anim/ic_skip_prev_arrow2.xml create mode 100644 app/src/main/res/anim/preference_fragment_child_enter.xml create mode 100644 app/src/main/res/anim/preference_fragment_child_exit.xml create mode 100644 app/src/main/res/anim/preference_fragment_parent_enter.xml create mode 100644 app/src/main/res/anim/preference_fragment_parent_exit.xml create mode 100644 app/src/main/res/animator/card_raise_animator.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_wikipedia.webp create mode 100644 app/src/main/res/drawable-mdpi/ic_wikipedia.webp create mode 100644 app/src/main/res/drawable-night/ic_account_owncloud.xml create mode 100644 app/src/main/res/drawable-night/ic_permission_calendar.xml create mode 100644 app/src/main/res/drawable-xhdpi/ic_wikipedia.webp create mode 100644 app/src/main/res/drawable-xxhdpi/ic_wikipedia.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_about_mm20.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_wikipedia.webp create mode 100644 app/src/main/res/drawable/ic_about_fdroid.xml create mode 100644 app/src/main/res/drawable/ic_about_github.xml create mode 100644 app/src/main/res/drawable/ic_about_telegram.xml create mode 100644 app/src/main/res/drawable/ic_about_xda.xml create mode 100644 app/src/main/res/drawable/ic_account_google.xml create mode 100644 app/src/main/res/drawable/ic_account_microsoft.xml create mode 100644 app/src/main/res/drawable/ic_account_nextcloud.xml create mode 100644 app/src/main/res/drawable/ic_account_owncloud.xml create mode 100644 app/src/main/res/drawable/ic_arrow_drop_down.xml create mode 100644 app/src/main/res/drawable/ic_cancel.xml create mode 100644 app/src/main/res/drawable/ic_clear.xml create mode 100644 app/src/main/res/drawable/ic_description.xml create mode 100644 app/src/main/res/drawable/ic_drag_handle.xml create mode 100644 app/src/main/res/drawable/ic_expand_more.xml create mode 100644 app/src/main/res/drawable/ic_file_android.xml create mode 100644 app/src/main/res/drawable/ic_file_archive.xml create mode 100644 app/src/main/res/drawable/ic_file_code.xml create mode 100644 app/src/main/res/drawable/ic_file_document.xml create mode 100644 app/src/main/res/drawable/ic_file_folder.xml create mode 100644 app/src/main/res/drawable/ic_file_form.xml create mode 100644 app/src/main/res/drawable/ic_file_generic.xml create mode 100644 app/src/main/res/drawable/ic_file_markup.xml create mode 100644 app/src/main/res/drawable/ic_file_music.xml create mode 100644 app/src/main/res/drawable/ic_file_pdf.xml create mode 100644 app/src/main/res/drawable/ic_file_picture.xml create mode 100644 app/src/main/res/drawable/ic_file_presentation.xml create mode 100644 app/src/main/res/drawable/ic_file_spreadsheet.xml create mode 100644 app/src/main/res/drawable/ic_file_video.xml create mode 100644 app/src/main/res/drawable/ic_location.xml create mode 100644 app/src/main/res/drawable/ic_more_horiz.xml create mode 100644 app/src/main/res/drawable/ic_open_external.xml create mode 100644 app/src/main/res/drawable/ic_open_in_browser.xml create mode 100644 app/src/main/res/drawable/ic_permission_calendar.xml create mode 100644 app/src/main/res/drawable/ic_precipitation_none.xml create mode 100644 app/src/main/res/drawable/ic_precipitation_rain.xml create mode 100644 app/src/main/res/drawable/ic_precipitation_rain_snow.xml create mode 100644 app/src/main/res/drawable/ic_precipitation_snow.xml create mode 100644 app/src/main/res/drawable/ic_pref_about.xml create mode 100644 app/src/main/res/drawable/ic_pref_account.xml create mode 100644 app/src/main/res/drawable/ic_pref_appearance.xml create mode 100644 app/src/main/res/drawable/ic_pref_badge.xml create mode 100644 app/src/main/res/drawable/ic_pref_calendar.xml create mode 100644 app/src/main/res/drawable/ic_pref_plugins.xml create mode 100644 app/src/main/res/drawable/ic_pref_search.xml create mode 100644 app/src/main/res/drawable/ic_pref_weather.xml create mode 100644 app/src/main/res/drawable/ic_preference_websearch_new.xml create mode 100644 app/src/main/res/drawable/ic_resize_drag_handle.xml create mode 100644 app/src/main/res/drawable/ic_settings.xml create mode 100644 app/src/main/res/drawable/ic_skip_next.xml create mode 100644 app/src/main/res/drawable/ic_skip_next_anim.xml create mode 100644 app/src/main/res/drawable/ic_skip_prev.xml create mode 100644 app/src/main/res/drawable/ic_skip_prev_anim.xml create mode 100644 app/src/main/res/drawable/ic_today.xml create mode 100644 app/src/main/res/drawable/ic_website.xml create mode 100644 app/src/main/res/drawable/ic_widget_resize.xml create mode 100644 app/src/main/res/drawable/ic_wind.xml create mode 100644 app/src/main/res/layout/dialog_websearch.xml create mode 100644 app/src/main/res/layout/fragment_card_settings.xml create mode 100644 app/src/main/res/layout/fragment_easteregg.xml create mode 100644 app/src/main/res/layout/fragment_license.xml create mode 100644 app/src/main/res/layout/permission_view.xml create mode 100644 app/src/main/res/layout/preference_icon_shape_row.xml create mode 100644 app/src/main/res/layout/preference_start_anim_item.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_background.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_background.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_background.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100755 app/src/main/res/raw/app_start_anim_default.json create mode 100644 app/src/main/res/raw/app_start_anim_fade.json create mode 100644 app/src/main/res/raw/app_start_anim_m.json create mode 100644 app/src/main/res/raw/app_start_anim_slide_bottom.json create mode 100644 app/src/main/res/raw/app_start_anim_splash1.json create mode 100644 app/src/main/res/raw/app_start_anim_splash2.json create mode 100644 app/src/main/res/raw/license_apache_2.txt create mode 100644 app/src/main/res/raw/license_bsd_2clause.txt create mode 100644 app/src/main/res/raw/license_bsd_3clause.txt create mode 100644 app/src/main/res/raw/license_glide.txt create mode 100644 app/src/main/res/raw/license_gpl_3.txt create mode 100644 app/src/main/res/raw/license_mit.txt create mode 100644 app/src/main/res/values/arrays.xml create mode 100644 app/src/main/res/values/attrs.xml create mode 100644 app/src/main/res/values/bools.xml create mode 100644 app/src/main/res/values/donottranslate.xml create mode 100644 app/src/main/res/values/integers.xml create mode 100644 app/src/main/res/values/licenses.xml create mode 100644 app/src/main/res/values/styles.xml create mode 100644 app/src/main/res/xml/motion_calendar_event_view.xml create mode 100644 app/src/main/res/xml/preferences_about.xml create mode 100644 app/src/main/res/xml/preferences_appearance.xml create mode 100644 app/src/main/res/xml/preferences_badges.xml create mode 100644 app/src/main/res/xml/preferences_calendar.xml create mode 100644 app/src/main/res/xml/preferences_cards.xml create mode 100644 app/src/main/res/xml/preferences_main.xml create mode 100644 app/src/main/res/xml/preferences_search.xml create mode 100644 app/src/main/res/xml/preferences_services.xml create mode 100644 app/src/main/res/xml/preferences_weather.xml create mode 100644 app/src/main/res/xml/provider_paths.xml create mode 100644 app/src/release/ic_launcher-playstore.png create mode 100644 app/src/release/java/de/mm20/launcher2/debug/Debug.kt create mode 100644 app/src/release/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/release/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/release/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/release/res/mipmap-hdpi/ic_launcher_background.png create mode 100644 app/src/release/res/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 app/src/release/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 app/src/release/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/release/res/mipmap-mdpi/ic_launcher_background.png create mode 100644 app/src/release/res/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 app/src/release/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 app/src/release/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/release/res/mipmap-xhdpi/ic_launcher_background.png create mode 100644 app/src/release/res/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 app/src/release/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 app/src/release/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/release/res/mipmap-xxhdpi/ic_launcher_background.png create mode 100644 app/src/release/res/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 app/src/release/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 app/src/release/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/src/release/res/mipmap-xxxhdpi/ic_launcher_background.png create mode 100644 app/src/release/res/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 app/src/release/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 applications/.gitignore create mode 100644 applications/build.gradle.kts create mode 100644 applications/consumer-rules.pro create mode 100644 applications/proguard-rules.pro create mode 100644 applications/src/main/AndroidManifest.xml create mode 100644 applications/src/main/java/de/mm20/launcher2/applications/AppRepository.kt create mode 100644 applications/src/main/java/de/mm20/launcher2/applications/AppViewModel.kt create mode 100644 applications/src/main/java/de/mm20/launcher2/search/data/AppInstallation.kt create mode 100644 applications/src/main/java/de/mm20/launcher2/search/data/AppShortcut.kt create mode 100644 applications/src/main/java/de/mm20/launcher2/search/data/Application.kt create mode 100644 applications/src/main/java/de/mm20/launcher2/search/data/LauncherApp.kt create mode 100644 applications/src/main/res/drawable/ic_app_placeholder.xml create mode 100644 appsearch/.gitignore create mode 100644 appsearch/build.gradle.kts create mode 100644 appsearch/consumer-rules.pro create mode 100644 appsearch/proguard-rules.pro create mode 100644 appsearch/src/main/AndroidManifest.xml create mode 100644 appsearch/src/main/java/de/mm20/launcher2/appsearch/AppSearchRepository.kt create mode 100644 appsearch/src/main/java/de/mm20/launcher2/appsearch/AppSearchViewModel.kt create mode 100644 appsearch/src/main/java/de/mm20/launcher2/search/data/AppSearchResult.kt create mode 100644 assets/icons/MM20Launcher2-alpha.svg create mode 100644 assets/icons/MM20Launcher2-beta.svg create mode 100644 assets/icons/MM20Launcher2-debug.svg create mode 100644 assets/icons/MM20Launcher2-release.svg create mode 100644 assets/icons/MM20Launcher2.svg create mode 100644 badges/.gitignore create mode 100644 badges/build.gradle.kts create mode 100644 badges/consumer-rules.pro create mode 100644 badges/proguard-rules.pro create mode 100644 badges/src/main/AndroidManifest.xml create mode 100644 badges/src/main/java/de/mm20/launcher2/badges/Badge.kt create mode 100644 badges/src/main/java/de/mm20/launcher2/badges/BadgeProvider.kt create mode 100644 badges/src/main/res/drawable-hdpi/ic_badge_gdrive.webp create mode 100644 badges/src/main/res/drawable-mdpi/ic_badge_gdrive.webp create mode 100644 badges/src/main/res/drawable-xhdpi/ic_badge_gdrive.webp create mode 100644 badges/src/main/res/drawable-xxhdpi/ic_badge_gdrive.webp create mode 100644 badges/src/main/res/drawable-xxxhdpi/ic_badge_gdrive.webp create mode 100644 badges/src/main/res/drawable/ic_badge_nextcloud.xml create mode 100644 badges/src/main/res/drawable/ic_badge_onedrive.xml create mode 100644 badges/src/main/res/drawable/ic_badge_owncloud.xml create mode 100644 badges/src/main/res/drawable/ic_badge_suspended.xml create mode 100644 badges/src/main/res/drawable/ic_badge_workprofile.xml create mode 100644 base/.gitignore create mode 100644 base/build.gradle.kts create mode 100644 base/consumer-rules.pro create mode 100644 base/proguard-rules.pro create mode 100644 base/src/main/AndroidManifest.xml create mode 100644 base/src/main/java/de/mm20/launcher2/graphics/BadgeDrawable.kt create mode 100644 base/src/main/java/de/mm20/launcher2/helper/NetworkUtils.kt create mode 100644 base/src/main/java/de/mm20/launcher2/icons/LauncherIcon.kt create mode 100644 base/src/main/java/de/mm20/launcher2/view/ElevationImageView.kt create mode 100644 base/src/main/res/color/chip_background.xml create mode 100644 base/src/main/res/color/chip_stroke.xml create mode 100644 base/src/main/res/color/chip_textcolor.xml create mode 100644 base/src/main/res/color/text_color_primary.xml create mode 100644 base/src/main/res/color/text_color_secondary.xml create mode 100644 base/src/main/res/drawable/ic_permission.xml create mode 100644 base/src/main/res/drawable/ic_weather_broken_clouds.xml create mode 100644 base/src/main/res/drawable/ic_weather_broken_clouds_night.xml create mode 100644 base/src/main/res/drawable/ic_weather_clear.xml create mode 100644 base/src/main/res/drawable/ic_weather_clear_night.xml create mode 100644 base/src/main/res/drawable/ic_weather_cloudy.xml create mode 100644 base/src/main/res/drawable/ic_weather_cold.xml create mode 100644 base/src/main/res/drawable/ic_weather_drizzle.xml create mode 100644 base/src/main/res/drawable/ic_weather_fog.xml create mode 100644 base/src/main/res/drawable/ic_weather_hail.xml create mode 100644 base/src/main/res/drawable/ic_weather_haze.xml create mode 100644 base/src/main/res/drawable/ic_weather_haze_night.xml create mode 100644 base/src/main/res/drawable/ic_weather_hot.xml create mode 100644 base/src/main/res/drawable/ic_weather_mostly_cloudy.xml create mode 100644 base/src/main/res/drawable/ic_weather_mostly_cloudy_night.xml create mode 100644 base/src/main/res/drawable/ic_weather_partly_cloudy.xml create mode 100644 base/src/main/res/drawable/ic_weather_partly_cloudy_night.xml create mode 100644 base/src/main/res/drawable/ic_weather_showers.xml create mode 100644 base/src/main/res/drawable/ic_weather_sleet.xml create mode 100644 base/src/main/res/drawable/ic_weather_snow.xml create mode 100644 base/src/main/res/drawable/ic_weather_thunderstorm.xml create mode 100644 base/src/main/res/drawable/ic_weather_thunderstorm_with_rain.xml create mode 100644 base/src/main/res/drawable/ic_weather_wind.xml create mode 100644 base/src/main/res/values-night/bools.xml create mode 100644 base/src/main/res/values-night/colors.xml create mode 100644 base/src/main/res/values-night/styles.xml create mode 100644 base/src/main/res/values-notnight-v23/colors.xml create mode 100644 base/src/main/res/values/attrs.xml create mode 100644 base/src/main/res/values/colors.xml create mode 100644 base/src/main/res/values/styles.xml create mode 100644 base/src/main/res/values/typography.xml create mode 100644 build.gradle.kts create mode 100644 calculator/.gitignore create mode 100644 calculator/build.gradle.kts create mode 100644 calculator/consumer-rules.pro create mode 100644 calculator/proguard-rules.pro create mode 100644 calculator/src/main/AndroidManifest.xml create mode 100644 calculator/src/main/java/de/mm20/launcher2/calculator/CalculatorRepository.kt create mode 100644 calculator/src/main/java/de/mm20/launcher2/calculator/CalculatorViewModel.kt create mode 100644 calculator/src/main/java/de/mm20/launcher2/search/data/Calculator.kt create mode 100644 calendar/.gitignore create mode 100644 calendar/build.gradle.kts create mode 100644 calendar/consumer-rules.pro create mode 100644 calendar/proguard-rules.pro create mode 100644 calendar/src/main/AndroidManifest.xml create mode 100644 calendar/src/main/java/de/mm20/launcher2/calendar/CalendarRepository.kt create mode 100644 calendar/src/main/java/de/mm20/launcher2/calendar/CalendarViewModel.kt create mode 100644 calendar/src/main/java/de/mm20/launcher2/search/data/CalendarEvent.kt create mode 100644 calendar/src/main/res/drawable-hdpi/ic_calendar.webp create mode 100644 calendar/src/main/res/drawable-mdpi/ic_calendar.webp create mode 100644 calendar/src/main/res/drawable-xhdpi/ic_calendar.webp create mode 100644 calendar/src/main/res/drawable-xxhdpi/ic_calendar.webp create mode 100644 calendar/src/main/res/drawable-xxxhdpi/ic_calendar.webp create mode 100644 compat/.gitignore create mode 100644 compat/build.gradle.kts create mode 100644 compat/consumer-rules.pro create mode 100644 compat/proguard-rules.pro create mode 100644 compat/src/main/AndroidManifest.xml create mode 100644 compat/src/main/java/de/mm20/launcher2/compat/PackageManagerCompat.kt create mode 100644 contacts/.gitignore create mode 100644 contacts/build.gradle.kts create mode 100644 contacts/consumer-rules.pro create mode 100644 contacts/proguard-rules.pro create mode 100644 contacts/src/main/AndroidManifest.xml create mode 100644 contacts/src/main/java/de/mm20/launcher2/contacts/ContactRepository.kt create mode 100644 contacts/src/main/java/de/mm20/launcher2/contacts/ContactViewModel.kt create mode 100644 contacts/src/main/java/de/mm20/launcher2/search/data/Contact.kt create mode 100644 crashreporter/.gitignore create mode 100644 crashreporter/build.gradle.kts create mode 100644 crashreporter/consumer-rules.pro create mode 100644 crashreporter/proguard-rules.pro create mode 100644 crashreporter/readme.md create mode 100644 crashreporter/src/main/AndroidManifest.xml create mode 100644 crashreporter/src/main/java/com/balsikandar/crashreporter/CrashReporter.java create mode 100644 crashreporter/src/main/java/com/balsikandar/crashreporter/CrashReporterInitProvider.java create mode 100644 crashreporter/src/main/java/com/balsikandar/crashreporter/adapter/CrashLogAdapter.java create mode 100644 crashreporter/src/main/java/com/balsikandar/crashreporter/adapter/MainPagerAdapter.java create mode 100644 crashreporter/src/main/java/com/balsikandar/crashreporter/ui/CrashLogFragment.java create mode 100644 crashreporter/src/main/java/com/balsikandar/crashreporter/ui/CrashReporterActivity.java create mode 100644 crashreporter/src/main/java/com/balsikandar/crashreporter/ui/ExceptionLogFragment.java create mode 100644 crashreporter/src/main/java/com/balsikandar/crashreporter/ui/LogMessageActivity.java create mode 100644 crashreporter/src/main/java/com/balsikandar/crashreporter/utils/AppUtils.java create mode 100644 crashreporter/src/main/java/com/balsikandar/crashreporter/utils/Constants.java create mode 100644 crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterException.java create mode 100644 crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterExceptionHandler.java create mode 100644 crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterNotInitializedException.java create mode 100644 crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashUtil.java create mode 100644 crashreporter/src/main/java/com/balsikandar/crashreporter/utils/FileUtils.java create mode 100644 crashreporter/src/main/java/com/balsikandar/crashreporter/utils/SimplePageChangeListener.java create mode 100644 crashreporter/src/main/java/de/mm20/launcher2/crashreporter/CrashReporter.kt create mode 100644 crashreporter/src/main/res/drawable/ic_menu_delete_white_24dp.xml create mode 100644 crashreporter/src/main/res/drawable/ic_menu_share_white_24dp.xml create mode 100644 crashreporter/src/main/res/drawable/ic_search_white_24dp.xml create mode 100644 crashreporter/src/main/res/drawable/ic_warning_black_24dp.xml create mode 100644 crashreporter/src/main/res/layout/activity_log_message.xml create mode 100644 crashreporter/src/main/res/layout/crash_log.xml create mode 100644 crashreporter/src/main/res/layout/crash_reporter_activity.xml create mode 100644 crashreporter/src/main/res/layout/custom_item.xml create mode 100644 crashreporter/src/main/res/layout/exception_log.xml create mode 100644 crashreporter/src/main/res/menu/crash_detail_menu.xml create mode 100644 crashreporter/src/main/res/menu/log_main_menu.xml create mode 100644 crashreporter/src/main/res/values/colors.xml create mode 100644 crashreporter/src/main/res/values/strings.xml create mode 100644 crashreporter/src/main/res/values/styles.xml create mode 100644 currencies/.gitignore create mode 100644 currencies/build.gradle.kts create mode 100644 currencies/consumer-rules.pro create mode 100644 currencies/proguard-rules.pro create mode 100644 currencies/src/main/AndroidManifest.xml create mode 100644 currencies/src/main/java/de/mm20/launcher2/currencies/Currency.kt create mode 100644 currencies/src/main/java/de/mm20/launcher2/currencies/CurrencyRepository.kt create mode 100644 currencies/src/main/java/de/mm20/launcher2/currencies/ExchangeRateWorker.kt create mode 100644 database/.gitignore create mode 100644 database/build.gradle.kts create mode 100644 database/consumer-rules.pro create mode 100644 database/proguard-rules.pro create mode 100644 database/readme.md create mode 100644 database/schemas/de.mm20.launcher2.database.AppDatabase/0.json create mode 100644 database/schemas/de.mm20.launcher2.database.AppDatabase/1.json create mode 100644 database/schemas/de.mm20.launcher2.database.AppDatabase/10.json create mode 100644 database/schemas/de.mm20.launcher2.database.AppDatabase/11.json create mode 100644 database/schemas/de.mm20.launcher2.database.AppDatabase/12.json create mode 100644 database/schemas/de.mm20.launcher2.database.AppDatabase/13.json create mode 100644 database/schemas/de.mm20.launcher2.database.AppDatabase/14.json create mode 100644 database/schemas/de.mm20.launcher2.database.AppDatabase/2.json create mode 100644 database/schemas/de.mm20.launcher2.database.AppDatabase/3.json create mode 100644 database/schemas/de.mm20.launcher2.database.AppDatabase/4.json create mode 100644 database/schemas/de.mm20.launcher2.database.AppDatabase/5.json create mode 100644 database/schemas/de.mm20.launcher2.database.AppDatabase/6.json create mode 100644 database/schemas/de.mm20.launcher2.database.AppDatabase/7.json create mode 100644 database/schemas/de.mm20.launcher2.database.AppDatabase/8.json create mode 100644 database/schemas/de.mm20.launcher2.database.AppDatabase/9.json create mode 100644 database/src/main/AndroidManifest.xml create mode 100644 database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt create mode 100644 database/src/main/java/de/mm20/launcher2/database/Converters.kt create mode 100644 database/src/main/java/de/mm20/launcher2/database/CurrencyDao.kt create mode 100644 database/src/main/java/de/mm20/launcher2/database/IconDao.kt create mode 100644 database/src/main/java/de/mm20/launcher2/database/SearchDao.kt create mode 100644 database/src/main/java/de/mm20/launcher2/database/WeatherDao.kt create mode 100644 database/src/main/java/de/mm20/launcher2/database/WidgetDao.kt create mode 100644 database/src/main/java/de/mm20/launcher2/database/entities/CurrencyEntity.kt create mode 100644 database/src/main/java/de/mm20/launcher2/database/entities/FavoritesItemEntity.kt create mode 100644 database/src/main/java/de/mm20/launcher2/database/entities/ForecastEntity.kt create mode 100644 database/src/main/java/de/mm20/launcher2/database/entities/IconEntity.kt create mode 100644 database/src/main/java/de/mm20/launcher2/database/entities/IconPackEntity.kt create mode 100644 database/src/main/java/de/mm20/launcher2/database/entities/PluginEntity.kt create mode 100644 database/src/main/java/de/mm20/launcher2/database/entities/WebsearchEntity.kt create mode 100644 database/src/main/java/de/mm20/launcher2/database/entities/WidgetEntity.kt create mode 100644 favorites/.gitignore create mode 100644 favorites/build.gradle.kts create mode 100644 favorites/consumer-rules.pro create mode 100644 favorites/proguard-rules.pro create mode 100644 favorites/src/main/AndroidManifest.xml create mode 100644 favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesItem.kt create mode 100644 favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesRepository.kt create mode 100644 favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesViewModel.kt create mode 100644 favorites/src/main/java/de/mm20/launcher2/favorites/SearchableDeserializer.kt create mode 100644 files/.gitignore create mode 100644 files/build.gradle.kts create mode 100644 files/consumer-rules.pro create mode 100644 files/proguard-rules.pro create mode 100644 files/src/main/AndroidManifest.xml create mode 100644 files/src/main/java/de/mm20/launcher2/files/FilesRepository.kt create mode 100644 files/src/main/java/de/mm20/launcher2/files/FilesViewModel.kt create mode 100644 files/src/main/java/de/mm20/launcher2/media/ThumbnailUtilsCompat.kt create mode 100644 files/src/main/java/de/mm20/launcher2/search/data/File.kt create mode 100644 files/src/main/java/de/mm20/launcher2/search/data/GDriveFile.kt create mode 100644 files/src/main/java/de/mm20/launcher2/search/data/NextcloudFile.kt create mode 100644 files/src/main/java/de/mm20/launcher2/search/data/OneDriveFile.kt create mode 100644 files/src/main/java/de/mm20/launcher2/search/data/OwncloudFile.kt create mode 100644 files/src/main/res/drawable/ic_file_android.xml create mode 100644 files/src/main/res/drawable/ic_file_archive.xml create mode 100644 files/src/main/res/drawable/ic_file_code.xml create mode 100644 files/src/main/res/drawable/ic_file_document.xml create mode 100644 files/src/main/res/drawable/ic_file_folder.xml create mode 100644 files/src/main/res/drawable/ic_file_form.xml create mode 100644 files/src/main/res/drawable/ic_file_generic.xml create mode 100644 files/src/main/res/drawable/ic_file_markup.xml create mode 100644 files/src/main/res/drawable/ic_file_music.xml create mode 100644 files/src/main/res/drawable/ic_file_pdf.xml create mode 100644 files/src/main/res/drawable/ic_file_picture.xml create mode 100644 files/src/main/res/drawable/ic_file_presentation.xml create mode 100644 files/src/main/res/drawable/ic_file_spreadsheet.xml create mode 100644 files/src/main/res/drawable/ic_file_video.xml create mode 100644 g-services/.gitignore create mode 100644 g-services/build.gradle.kts create mode 100644 g-services/consumer-rules.pro create mode 100644 g-services/proguard-rules.pro create mode 100644 g-services/readme.md create mode 100644 g-services/src/debug/res/raw/g_services_example.json create mode 100644 g-services/src/main/AndroidManifest.xml create mode 100644 g-services/src/main/java/de/mm20/launcher2/gservices/DriveFile.kt create mode 100644 g-services/src/main/java/de/mm20/launcher2/gservices/GoogleAccount.kt create mode 100644 g-services/src/main/java/de/mm20/launcher2/gservices/GoogleApiHelper.kt create mode 100644 g-services/src/main/java/de/mm20/launcher2/gservices/GoogleAuthRedirectActivity.kt create mode 100644 g-services/src/main/res/values/styles.xml create mode 100644 g-services/src/release/res/raw/g_services_example.json create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 hiddenitems/.gitignore create mode 100644 hiddenitems/build.gradle.kts create mode 100644 hiddenitems/consumer-rules.pro create mode 100644 hiddenitems/proguard-rules.pro create mode 100644 hiddenitems/src/main/AndroidManifest.xml create mode 100644 hiddenitems/src/main/java/de/mm20/launcher2/hiddenitems/HiddenItemsRepository.kt create mode 100644 hiddenitems/src/main/java/de/mm20/launcher2/hiddenitems/HiddenItemsViewModel.kt create mode 100644 i18n/.gitignore create mode 100644 i18n/build.gradle.kts create mode 100644 i18n/consumer-rules.pro create mode 100644 i18n/proguard-rules.pro create mode 100644 i18n/readme.md create mode 100644 i18n/src/main/AndroidManifest.xml create mode 100644 i18n/src/main/res/values-de/defaults.xml create mode 100644 i18n/src/main/res/values-de/strings.xml create mode 100644 i18n/src/main/res/values-de/units.xml create mode 100644 i18n/src/main/res/values-en-rUS/defaults.xml create mode 100644 i18n/src/main/res/values/defaults.xml create mode 100644 i18n/src/main/res/values/strings.xml create mode 100644 i18n/src/main/res/values/units.xml create mode 100644 icons/.gitignore create mode 100644 icons/build.gradle.kts create mode 100644 icons/consumer-rules.pro create mode 100644 icons/proguard-rules.pro create mode 100644 icons/src/main/AndroidManifest.xml create mode 100644 icons/src/main/java/de/mm20/launcher2/icons/CalendarDynamicLauncherIcon.kt create mode 100644 icons/src/main/java/de/mm20/launcher2/icons/ClockDynamicLauncherIcon.kt create mode 100644 icons/src/main/java/de/mm20/launcher2/icons/DynamicIconController.kt create mode 100644 icons/src/main/java/de/mm20/launcher2/icons/DynamicLauncherIcon.kt create mode 100644 icons/src/main/java/de/mm20/launcher2/icons/Icon.kt create mode 100644 icons/src/main/java/de/mm20/launcher2/icons/IconPack.kt create mode 100644 icons/src/main/java/de/mm20/launcher2/icons/IconPackManager.kt create mode 100644 icons/src/main/java/de/mm20/launcher2/icons/IconRepository.kt create mode 100644 ktx/.gitignore create mode 100644 ktx/build.gradle.kts create mode 100644 ktx/consumer-rules.pro create mode 100644 ktx/proguard-rules.pro create mode 100644 ktx/readme.md create mode 100644 ktx/src/main/AndroidManifest.xml create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/Address.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/Context.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/Double.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/Drawable.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/Extensions.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/Float.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/Fragment.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/InputStream.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/Int.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/List.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/Notification.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/Rect.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/RectF.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/Resources.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/SharedPreferences.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/String.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/TextView.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/UserHandle.kt create mode 100644 ktx/src/main/java/de/mm20/launcher2/ktx/View.kt create mode 100644 ms-services/.gitignore create mode 100644 ms-services/build.gradle.kts create mode 100644 ms-services/consumer-rules.pro create mode 100644 ms-services/proguard-rules.pro create mode 100644 ms-services/readme.md create mode 100644 ms-services/src/debug/res/raw/msal_auth_config_example.json create mode 100644 ms-services/src/main/AndroidManifest.xml create mode 100644 ms-services/src/main/java/de/mm20/launcher2/msservices/DriveItem.kt create mode 100644 ms-services/src/main/java/de/mm20/launcher2/msservices/MicrosoftGraphApiHelper.kt create mode 100644 ms-services/src/main/java/de/mm20/launcher2/msservices/MsUser.kt create mode 100644 ms-services/src/release/res/raw/msal_auth_config_example.json create mode 100644 music/.gitignore create mode 100644 music/build.gradle.kts create mode 100644 music/consumer-rules.pro create mode 100644 music/proguard-rules.pro create mode 100644 music/src/main/AndroidManifest.xml create mode 100644 music/src/main/java/de/mm20/launcher2/music/MusicRepository.kt create mode 100644 music/src/main/java/de/mm20/launcher2/music/MusicViewModel.kt create mode 100644 music/src/main/res/values/dimens.xml create mode 100644 nextcloud/.gitignore create mode 100644 nextcloud/build.gradle.kts create mode 100644 nextcloud/consumer-rules.pro create mode 100644 nextcloud/proguard-rules.pro create mode 100644 nextcloud/src/main/AndroidManifest.xml create mode 100644 nextcloud/src/main/java/de/mm20/launcher2/nextcloud/LoginActivity.kt create mode 100644 nextcloud/src/main/java/de/mm20/launcher2/nextcloud/NcUser.kt create mode 100644 nextcloud/src/main/java/de/mm20/launcher2/nextcloud/NextcloudApiHelper.kt create mode 100644 nextcloud/src/main/res/drawable/ic_nextcloud_logo.xml create mode 100644 nextcloud/src/main/res/layout/activity_nextcloud_login.xml create mode 100644 nextcloud/src/main/res/values-night/styles.xml create mode 100644 nextcloud/src/main/res/values/styles.xml create mode 100644 notifications/.gitignore create mode 100644 notifications/build.gradle.kts create mode 100644 notifications/consumer-rules.pro create mode 100644 notifications/proguard-rules.pro create mode 100644 notifications/src/main/AndroidManifest.xml create mode 100644 notifications/src/main/java/de/mm20/launcher2/notifications/NotificationService.kt create mode 100644 owncloud/.gitignore create mode 100644 owncloud/build.gradle.kts create mode 100644 owncloud/consumer-rules.pro create mode 100644 owncloud/proguard-rules.pro create mode 100644 owncloud/src/main/AndroidManifest.xml create mode 100644 owncloud/src/main/java/de/mm20/launcher2/owncloud/LoginActivity.kt create mode 100644 owncloud/src/main/java/de/mm20/launcher2/owncloud/OcUser.kt create mode 100644 owncloud/src/main/java/de/mm20/launcher2/owncloud/OwncloudClient.kt create mode 100644 owncloud/src/main/res/drawable-night/ic_owncloud_logo.xml create mode 100644 owncloud/src/main/res/drawable/ic_owncloud_logo.xml create mode 100644 owncloud/src/main/res/layout/activity_owncloud_login.xml create mode 100644 owncloud/src/main/res/layout/activity_owncloud_login_username_password.xml create mode 100644 owncloud/src/main/res/layout/owncloud_login_drop_down_item.xml create mode 100644 owncloud/src/main/res/values-night/styles.xml create mode 100644 owncloud/src/main/res/values/styles.xml create mode 100644 permissions/.gitignore create mode 100644 permissions/build.gradle.kts create mode 100644 permissions/consumer-rules.pro create mode 100644 permissions/proguard-rules.pro create mode 100644 permissions/src/main/AndroidManifest.xml create mode 100644 permissions/src/main/java/de/mm20/launcher2/data/search/MissingPermission.kt create mode 100644 permissions/src/main/java/de/mm20/launcher2/permissions/PermissionsManager.kt create mode 100644 preferences/.gitignore create mode 100644 preferences/build.gradle.kts create mode 100644 preferences/consumer-rules.pro create mode 100644 preferences/proguard-rules.pro create mode 100644 preferences/src/main/AndroidManifest.xml create mode 100644 preferences/src/main/java/de/mm20/launcher2/preferences/LauncherPreferences.kt create mode 100644 preferences/src/main/java/de/mm20/launcher2/preferences/PreferenceDelegates.kt create mode 100644 preferences/src/main/java/de/mm20/launcher2/preferences/SettingsSerializer.kt create mode 100644 preferences/src/main/proto/settings.proto create mode 100644 readme.md create mode 100644 rssparser/.gitignore create mode 100644 rssparser/build.gradle.kts create mode 100644 rssparser/proguard-rules.pro create mode 100644 rssparser/src/main/AndroidManifest.xml create mode 100644 rssparser/src/main/java/de/mm20/rssparser/Article.kt create mode 100644 rssparser/src/main/java/de/mm20/rssparser/DayOfWeek.kt create mode 100644 rssparser/src/main/java/de/mm20/rssparser/Exceptions.kt create mode 100644 rssparser/src/main/java/de/mm20/rssparser/FeedInfo.kt create mode 100644 rssparser/src/main/java/de/mm20/rssparser/RssParser.kt create mode 100644 rssparser/src/main/res/values/strings.xml create mode 100644 search/.gitignore create mode 100644 search/build.gradle.kts create mode 100644 search/consumer-rules.pro create mode 100644 search/proguard-rules.pro create mode 100644 search/src/main/AndroidManifest.xml create mode 100644 search/src/main/java/de/mm20/launcher2/search/BaseSearchableRepository.kt create mode 100644 search/src/main/java/de/mm20/launcher2/search/SearchRepository.kt create mode 100644 search/src/main/java/de/mm20/launcher2/search/SearchViewModel.kt create mode 100644 search/src/main/java/de/mm20/launcher2/search/WebsearchRepository.kt create mode 100644 search/src/main/java/de/mm20/launcher2/search/WebsearchViewModel.kt create mode 100644 search/src/main/java/de/mm20/launcher2/search/data/Searchable.kt create mode 100644 search/src/main/java/de/mm20/launcher2/search/data/Websearch.kt create mode 100644 settings.gradle.kts create mode 100644 transition/.gitignore create mode 100644 transition/build.gradle.kts create mode 100644 transition/consumer-rules.pro create mode 100644 transition/proguard-rules.pro create mode 100644 transition/src/main/AndroidManifest.xml create mode 100644 transition/src/main/java/de/mm20/launcher2/transition/BackgroundColor.kt create mode 100644 transition/src/main/java/de/mm20/launcher2/transition/CardCorners.kt create mode 100644 transition/src/main/java/de/mm20/launcher2/transition/ChangingLayoutTransition.kt create mode 100644 transition/src/main/java/de/mm20/launcher2/transition/Elevation.kt create mode 100644 transition/src/main/java/de/mm20/launcher2/transition/OneShotLayoutTransition.kt create mode 100644 transition/src/main/java/de/mm20/launcher2/transition/TextResize.kt create mode 100644 ui/.gitignore create mode 100644 ui/build.gradle.kts create mode 100644 ui/consumer-rules.pro create mode 100644 ui/proguard-rules.pro create mode 100644 ui/src/main/AndroidManifest.xml create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/AppTheme.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/ColorSchemeTest.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/Extensions.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/InformationText.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/LauncherCard.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/LauncherIconShapeAmbient.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/LauncherMainScreen.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/LegacyAppTheme.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/Resources.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/SearchBar.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/SearchColumn.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/ShapedLauncherIcon.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/WidgetColumn.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/activity/ComposeActivity.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/compat/AnimatedVectorResourcePolyfill.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/component/Chip.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/component/ChipGroup.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/component/Clocks.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/component/DefaultSwipeActions.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/component/LottieIcon.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/component/Spacer.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/component/TextClock.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/component/Toolbar.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/icons/Icons.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/icons/Searchable.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/ktx/Float.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/ktx/Int.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/ktx/Modifier.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/ktx/Offset.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/activity/LauncherActivity.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/component/ApplicationView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/component/CalculatorView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/component/CalendarView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/component/ContactView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/component/EditFavoritesRow.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/component/EditFavoritesView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/component/FavoritesView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/component/FileView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/component/SearchBar.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/component/UnitConverterView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/component/WebSearchView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/component/WebsiteView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/component/WidgetView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/component/WikipediaView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/data/InformationText.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/fragment/SearchableBottomSheet.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/helper/ActivityStarter.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/helper/BitmapHolder.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/helper/WallpaperBlur.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ApplicationDetailRepresentation.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/BasicGridRepresentation.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/CalendarDetailRepresentation.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/CalendarListRepresentation.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ContactDetailRepresentation.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ContactListRepresentation.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/FileDetailRepresentation.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/FileListRepresentation.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/InformationListRepresentation.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/PermissionListRepresentation.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/Representation.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/SearchGridView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/SearchListView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ShortcutDetailRepresentation.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/WebsiteDetailRepresentation.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/WebsiteListRepresentation.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/WikipediaDetailRepresentation.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/search/WikipediaListRepresentation.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/searchable/SearchableView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/transition/LauncherCards.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/transition/LauncherIconViewTransition.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/view/AspectRationImageView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/view/BatteryChargingView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/view/BlurView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/view/InnerCardView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/view/LauncherCardView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/view/LauncherIconView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/view/SwipeCardView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/view/ToolbarView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/view/WidgetResizeDragView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/widget/CalendarWidget.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/widget/CompactView.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/widget/ExternalWidget.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/widget/LauncherWidget.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/widget/MusicWidget.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/widget/SmartWidget.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/legacy/widget/WeatherWidget.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/locals/CompositionLocals.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/search/ApplicationItem.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/search/ApplicationResults.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/search/BasicGridItem.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/search/CalculatorItem.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/search/FavoriteResults.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/search/FileItem.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/search/FileResults.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/search/GridItemLabel.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/search/SearchableGrid.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/search/SearchableItem.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/search/SearchableList.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/search/WikipediaItem.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/search/WikipediaResult.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/searchable/CalendarEventItem.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/searchable/DeprecatedSearchableList.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/theme/ContentAlpha.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/theme/WallpaperColors.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/theme/colors/ColorScheme.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/theme/colors/ColorSwatch.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/theme/colors/DefaultColorScheme.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/theme/colors/SystemColorScheme.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/theme/colors/WallpaperColorScheme.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/weather/AnimatedWeatherIcon.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/weather/WeatherIcon.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/widget/CalendarWidget.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/widget/ClockWidget.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/widget/MusicWidget.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/widget/PlatformWidget.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/widget/WeatherWidget.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/widget/WidgetCard.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/widget/parts/DatePart.kt create mode 100644 ui/src/main/res/anim/activity_start_fade_enter.xml create mode 100644 ui/src/main/res/anim/activity_start_fade_exit.xml create mode 100644 ui/src/main/res/anim/activity_start_slide_bottom_enter.xml create mode 100644 ui/src/main/res/anim/activity_start_slide_bottom_exit.xml create mode 100644 ui/src/main/res/anim/activity_start_splash1_enter.xml create mode 100644 ui/src/main/res/anim/activity_start_splash1_exit.xml create mode 100644 ui/src/main/res/anim/activity_start_splash2_enter.xml create mode 100644 ui/src/main/res/anim/activity_start_splash2_exit.xml create mode 100644 ui/src/main/res/drawable/anim_ic_edit_add.xml create mode 100644 ui/src/main/res/drawable/anim_ic_menu_clear.xml create mode 100644 ui/src/main/res/drawable/anim_ic_play_pause.xml create mode 100644 ui/src/main/res/drawable/anim_weather_sun_moon.xml create mode 100644 ui/src/main/res/drawable/drag_handle.xml create mode 100644 ui/src/main/res/drawable/ic_add.xml create mode 100644 ui/src/main/res/drawable/ic_arrow_back.xml create mode 100644 ui/src/main/res/drawable/ic_arrow_left.xml create mode 100644 ui/src/main/res/drawable/ic_arrow_right.xml create mode 100644 ui/src/main/res/drawable/ic_arrow_up.xml create mode 100644 ui/src/main/res/drawable/ic_attendees.xml create mode 100644 ui/src/main/res/drawable/ic_call.xml create mode 100644 ui/src/main/res/drawable/ic_delete.xml create mode 100644 ui/src/main/res/drawable/ic_description.xml create mode 100644 ui/src/main/res/drawable/ic_done.xml create mode 100644 ui/src/main/res/drawable/ic_edit.xml create mode 100644 ui/src/main/res/drawable/ic_info_outline.xml create mode 100644 ui/src/main/res/drawable/ic_location.xml create mode 100644 ui/src/main/res/drawable/ic_mail.xml create mode 100644 ui/src/main/res/drawable/ic_message.xml create mode 100644 ui/src/main/res/drawable/ic_more_vert.xml create mode 100644 ui/src/main/res/drawable/ic_open_external.xml create mode 100644 ui/src/main/res/drawable/ic_pause.xml create mode 100644 ui/src/main/res/drawable/ic_pause_to_play.xml create mode 100644 ui/src/main/res/drawable/ic_play.xml create mode 100644 ui/src/main/res/drawable/ic_play_to_pause.xml create mode 100644 ui/src/main/res/drawable/ic_search.xml create mode 100644 ui/src/main/res/drawable/ic_share.xml create mode 100644 ui/src/main/res/drawable/ic_star_outline.xml create mode 100644 ui/src/main/res/drawable/ic_star_solid.xml create mode 100644 ui/src/main/res/drawable/ic_telegram.xml create mode 100644 ui/src/main/res/drawable/ic_time.xml create mode 100644 ui/src/main/res/drawable/ic_visibility.xml create mode 100644 ui/src/main/res/drawable/ic_visibility_off.xml create mode 100644 ui/src/main/res/drawable/ic_whatsapp.xml create mode 100644 ui/src/main/res/font/inter_black.ttf create mode 100644 ui/src/main/res/font/inter_bold.ttf create mode 100644 ui/src/main/res/font/inter_extrabold.ttf create mode 100644 ui/src/main/res/font/inter_extralight.ttf create mode 100644 ui/src/main/res/font/inter_light.ttf create mode 100644 ui/src/main/res/font/inter_medium.ttf create mode 100644 ui/src/main/res/font/inter_regular.ttf create mode 100644 ui/src/main/res/font/inter_semibold.ttf create mode 100644 ui/src/main/res/font/inter_thin.ttf create mode 100644 ui/src/main/res/layout/activity_launcher.xml create mode 100644 ui/src/main/res/layout/compact_music.xml create mode 100644 ui/src/main/res/layout/compact_weather.xml create mode 100644 ui/src/main/res/layout/dialog_edit_favorites.xml create mode 100644 ui/src/main/res/layout/edit_favorites_row.xml create mode 100644 ui/src/main/res/layout/unit_converter_row.xml create mode 100644 ui/src/main/res/layout/unit_converter_show_all.xml create mode 100644 ui/src/main/res/layout/view_application.xml create mode 100644 ui/src/main/res/layout/view_application_detail.xml create mode 100644 ui/src/main/res/layout/view_basic_grid.xml create mode 100644 ui/src/main/res/layout/view_calculator.xml create mode 100644 ui/src/main/res/layout/view_calendar_detail.xml create mode 100644 ui/src/main/res/layout/view_calendar_list.xml create mode 100644 ui/src/main/res/layout/view_calendar_widget.xml create mode 100644 ui/src/main/res/layout/view_contact_detail.xml create mode 100644 ui/src/main/res/layout/view_contact_list.xml create mode 100644 ui/src/main/res/layout/view_date_time.xml create mode 100644 ui/src/main/res/layout/view_favorites.xml create mode 100644 ui/src/main/res/layout/view_file_detail.xml create mode 100644 ui/src/main/res/layout/view_file_list.xml create mode 100644 ui/src/main/res/layout/view_information_list.xml create mode 100644 ui/src/main/res/layout/view_list_item.xml create mode 100644 ui/src/main/res/layout/view_permission_list.xml create mode 100644 ui/src/main/res/layout/view_search_bar.xml create mode 100644 ui/src/main/res/layout/view_search_category_list.xml create mode 100644 ui/src/main/res/layout/view_search_category_single_item.xml create mode 100644 ui/src/main/res/layout/view_unitconverter.xml create mode 100644 ui/src/main/res/layout/view_websearch.xml create mode 100644 ui/src/main/res/layout/view_website_detail.xml create mode 100644 ui/src/main/res/layout/view_website_list.xml create mode 100644 ui/src/main/res/layout/view_widget.xml create mode 100644 ui/src/main/res/layout/view_wikipedia_detail.xml create mode 100644 ui/src/main/res/layout/view_wikipedia_list.xml create mode 100644 ui/src/main/res/menu/menu_launcher.xml create mode 100644 ui/src/main/res/raw-night/lottie_play_to_pause.json create mode 100644 ui/src/main/res/raw-night/lottie_skip_next.json create mode 100644 ui/src/main/res/raw/ic_menu_to_clear.json create mode 100644 ui/src/main/res/raw/lottie_menu_to_clear.json create mode 100644 ui/src/main/res/raw/lottie_play_to_pause.json create mode 100644 ui/src/main/res/raw/lottie_skip_next.json create mode 100644 ui/src/main/res/raw/play_to_pause.json create mode 100644 ui/src/main/res/values-w312dp/integers.xml create mode 100644 ui/src/main/res/values-w400dp/integers.xml create mode 100644 ui/src/main/res/values-w408dp/integers.xml create mode 100644 ui/src/main/res/values-w480dp/integers.xml create mode 100644 ui/src/main/res/values-w504dp/integers.xml create mode 100644 ui/src/main/res/values-w600dp/integers.xml create mode 100644 ui/src/main/res/values/AspectRatioImageView.xml create mode 100644 ui/src/main/res/values/BottomSheet.xml create mode 100644 ui/src/main/res/values/EditFavoritesView.xml create mode 100644 ui/src/main/res/values/LauncherCardView.xml create mode 100644 ui/src/main/res/values/SearchGridView.xml create mode 100644 ui/src/main/res/values/dimens.xml create mode 100644 unitconverter/.gitignore create mode 100644 unitconverter/build.gradle.kts create mode 100644 unitconverter/consumer-rules.pro create mode 100644 unitconverter/proguard-rules.pro create mode 100644 unitconverter/src/main/AndroidManifest.xml create mode 100644 unitconverter/src/main/java/de/mm20/launcher2/search/data/CurrencyUnitConverter.kt create mode 100644 unitconverter/src/main/java/de/mm20/launcher2/search/data/UnitConverter.kt create mode 100644 unitconverter/src/main/java/de/mm20/launcher2/unitconverter/Dimension.kt create mode 100644 unitconverter/src/main/java/de/mm20/launcher2/unitconverter/MeasureUnit.kt create mode 100644 unitconverter/src/main/java/de/mm20/launcher2/unitconverter/UnitConverterRepository.kt create mode 100644 unitconverter/src/main/java/de/mm20/launcher2/unitconverter/UnitConverterViewModel.kt create mode 100644 unitconverter/src/main/java/de/mm20/launcher2/unitconverter/UnitValue.kt create mode 100644 unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/AreaConverter.kt create mode 100644 unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/Converter.kt create mode 100644 unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/CurrencyConverter.kt create mode 100644 unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/DataConverter.kt create mode 100644 unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/LengthConverter.kt create mode 100644 unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/MassConverter.kt create mode 100644 unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/TemperatureConverter.kt create mode 100644 unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/TimeConverter.kt create mode 100644 unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/VelocityConverter.kt create mode 100644 weather/.gitignore create mode 100644 weather/build.gradle.kts create mode 100644 weather/consumer-rules.pro create mode 100644 weather/proguard-rules.pro create mode 100644 weather/readme.md create mode 100644 weather/src/main/AndroidManifest.xml create mode 100644 weather/src/main/java/de/mm20/launcher2/weather/DailyForecast.kt create mode 100644 weather/src/main/java/de/mm20/launcher2/weather/Forecast.kt create mode 100644 weather/src/main/java/de/mm20/launcher2/weather/Weather.kt create mode 100644 weather/src/main/java/de/mm20/launcher2/weather/WeatherProvider.kt create mode 100644 weather/src/main/java/de/mm20/launcher2/weather/WeatherRepository.kt create mode 100644 weather/src/main/java/de/mm20/launcher2/weather/WeatherViewModel.kt create mode 100644 weather/src/main/java/de/mm20/launcher2/weather/here/HereProvider.kt create mode 100644 weather/src/main/java/de/mm20/launcher2/weather/metno/MetNoProvider.kt create mode 100644 weather/src/main/java/de/mm20/launcher2/weather/openweathermap/OpenWeatherMapApi.kt create mode 100644 weather/src/main/java/de/mm20/launcher2/weather/openweathermap/OpenWeatherMapProvider.kt create mode 100644 weather/src/main/res/values/config_example.xml create mode 100644 webdav/.gitignore create mode 100644 webdav/build.gradle.kts create mode 100644 webdav/consumer-rules.pro create mode 100644 webdav/proguard-rules.pro create mode 100644 webdav/src/main/AndroidManifest.xml create mode 100644 webdav/src/main/java/de/mm20/launcher2/webdav/WebDavApi.kt create mode 100644 webdav/src/main/java/de/mm20/launcher2/webdav/WebDavFile.kt create mode 100644 websites/.gitignore create mode 100644 websites/build.gradle.kts create mode 100644 websites/consumer-rules.pro create mode 100644 websites/proguard-rules.pro create mode 100644 websites/src/main/AndroidManifest.xml create mode 100644 websites/src/main/java/de/mm20/launcher2/search/data/Website.kt create mode 100644 websites/src/main/java/de/mm20/launcher2/websites/WebsiteRepository.kt create mode 100644 websites/src/main/java/de/mm20/launcher2/websites/WebsiteViewModel.kt create mode 100644 websites/src/main/res/drawable/ic_website.xml create mode 100644 widgets/.gitignore create mode 100644 widgets/build.gradle.kts create mode 100644 widgets/consumer-rules.pro create mode 100644 widgets/proguard-rules.pro create mode 100644 widgets/src/main/AndroidManifest.xml create mode 100644 widgets/src/main/java/de/mm20/launcher2/widgets/Widget.kt create mode 100644 widgets/src/main/java/de/mm20/launcher2/widgets/WidgetRepository.kt create mode 100644 widgets/src/main/java/de/mm20/launcher2/widgets/WidgetViewModel.kt create mode 100644 wikipedia/.gitignore create mode 100644 wikipedia/build.gradle.kts create mode 100644 wikipedia/consumer-rules.pro create mode 100644 wikipedia/proguard-rules.pro create mode 100644 wikipedia/readme.md create mode 100644 wikipedia/src/main/AndroidManifest.xml create mode 100644 wikipedia/src/main/java/de/mm20/launcher2/search/data/Wikipedia.kt create mode 100644 wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaApi.kt create mode 100644 wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaRepository.kt create mode 100644 wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaViewModel.kt create mode 100644 wikipedia/src/main/res/drawable-hdpi/ic_wikipedia.webp create mode 100644 wikipedia/src/main/res/drawable-mdpi/ic_wikipedia.webp create mode 100644 wikipedia/src/main/res/drawable-xhdpi/ic_wikipedia.webp create mode 100644 wikipedia/src/main/res/drawable-xxhdpi/ic_wikipedia.webp create mode 100644 wikipedia/src/main/res/drawable-xxxhdpi/ic_wikipedia.webp diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..085797df --- /dev/null +++ b/.gitignore @@ -0,0 +1,304 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/androidstudio,android,gradle,linux,macos,windows +# Edit at https://www.toptal.com/developers/gitignore?templates=androidstudio,android,gradle,linux,macos,windows + +### Android ### +# Built application files +*.apk +*.aar +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +.idea/jarRepositories.xml +# Android Studio 3 in .gitignore file. +.idea/caches +.idea/modules.xml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ + +# Android Profiling +*.hprof + +### Android Patch ### +gen-external-apklibs +output.json + +# Replacement of .externalNativeBuild directories introduced +# with Android Studio 3.5. + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Gradle ### +.gradle + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +### Gradle Patch ### +**/build/ + +# Eclipse Gradle plugin generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +### AndroidStudio ### +# Covers files to be ignored for android development using Android Studio. + +# Built application files + +# Files for the ART/Dalvik VM + +# Java class files + +# Generated files + +# Gradle files + +# Signing files +.signing/ + +# Local configuration file (sdk path, etc) + +# Proguard folder generated by Eclipse + +# Log Files + +# Android Studio +/*/build/ +/*/local.properties +/*/out +/*/*/build +/*/*/production +*.ipr +*.swp + +# Keystore files +*.jks +*.keystore + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Android Patch + +# External native build folder generated in Android Studio 2.2 and later + +# NDK +obj/ + +# IntelliJ IDEA +*.iws +/out/ + +# User-specific configurations +.idea/caches/ +.idea/libraries/ +.idea/shelf/ +.idea/.name +.idea/compiler.xml +.idea/copyright/profiles_settings.xml +.idea/encodings.xml +.idea/misc.xml +.idea/scopes/scope_settings.xml +.idea/vcs.xml +.idea/jsLibraryMappings.xml +.idea/datasources.xml +.idea/dataSources.ids +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# OS-specific files +.DS_Store? + +# Legacy Eclipse project files +.cproject +.settings/ + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.war +*.ear + +# virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) +hs_err_pid* + +## Plugin-specific files: + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Mongo Explorer plugin +.idea/mongoSettings.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### AndroidStudio Patch ### + +!/gradle/wrapper/gradle-wrapper.jar + +# End of https://www.toptal.com/developers/gitignore/api/androidstudio,android,gradle,linux,macos,windows + +*/**/output-metadata.json diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..529374ff --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..6e6eec11 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml new file mode 100644 index 00000000..03f0f267 --- /dev/null +++ b/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.properties b/.idea/gradle.properties new file mode 100644 index 00000000..e69de29b diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..d606a954 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/kotlinScripting.xml b/.idea/kotlinScripting.xml new file mode 100644 index 00000000..bc444dea --- /dev/null +++ b/.idea/kotlinScripting.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 00000000..a89593bd --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,153 @@ +import java.text.SimpleDateFormat +import java.util.* + +plugins { + id("com.android.application") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + packagingOptions { + resources.excludes.add("META-INF/DEPENDENCIES") + resources.excludes.add("META-INF/LICENSE") + resources.excludes.add("META-INF/LICENSE.txt") + resources.excludes.add("META-INF/license.txt") + resources.excludes.add("META-INF/NOTICE") + resources.excludes.add("META-INF/NOTICE.txt") + resources.excludes.add("META-INF/notice.txt") + resources.excludes.add("META-INF/ASL2.0") + resources.excludes.add("META-INF/LICENSE.md") + resources.excludes.add("META-INF/NOTICE.md") + } + + compileSdk = sdk.versions.compileSdk.get().toInt() + defaultConfig { + applicationId = "de.mm20.launcher2" + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + versionCode = versionCodeDate() + versionName = "1.0.0" + multiDexEnabled = true + } + buildTypes { + release { + isMinifyEnabled = false + isShrinkResources = false + proguardFiles( + getDefaultProguardFile("proguard-android.txt"), + "proguard-rules.pro" + ) + applicationIdSuffix = ".release" + + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + + versionNameSuffix = "-" + buildTime() + } + debug { + applicationIdSuffix = ".debug" + } + } + configurations.all { + //Fixes Error: Duplicate class: com.google.common.util.concurrent.ListenableFuture + exclude(group = "com.google.guava", module = "listenablefuture") + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } + + lint { + isAbortOnError = false + } +} + +fun buildTime(): String { + val df = SimpleDateFormat("yyyyMMdd") + return df.format(Date()) +} + +fun versionCodeDate(): Int { + val df = SimpleDateFormat("yyyyMMdd00") + return df.format(Date()).toInt() +} + + +dependencies { + implementation(libs.bundles.kotlin) + + //Android Jetpack + implementation(libs.androidx.appcompat) + implementation(libs.androidx.preference) + implementation(libs.androidx.cardview) + implementation(libs.androidx.browser) + implementation(libs.androidx.palette) + implementation(libs.androidx.fragment) + implementation(libs.androidx.core) + implementation(libs.androidx.exifinterface) + implementation(libs.androidx.media2) + implementation(libs.materialcomponents) + implementation(libs.androidx.constraintlayout) + implementation(libs.androidx.gridlayout) + + implementation(libs.bundles.androidx.lifecycle) + + implementation(libs.androidx.work) + implementation(libs.androidx.multidex) + + implementation(libs.glide) + implementation(libs.glidetransformations) + + implementation(libs.lottie.core) + + implementation(libs.textdrawable) + + implementation(libs.bundles.materialdialogs) + + implementation(libs.bundles.groupie) + + implementation(libs.draglinearlayout) + implementation(libs.viewpropertyobjectanimator) + + implementation(project(":applications")) + implementation(project(":appsearch")) + implementation(project(":badges")) + implementation(project(":base")) + implementation(project(":calculator")) + implementation(project(":calendar")) + implementation(project(":contacts")) + implementation(project(":crashreporter")) + implementation(project(":favorites")) + implementation(project(":files")) + implementation(project(":g-services")) + implementation(project(":hiddenitems")) + implementation(project(":i18n")) + implementation(project(":icons")) + implementation(project(":ktx")) + implementation(project(":ms-services")) + implementation(project(":music")) + implementation(project(":nextcloud")) + implementation(project(":notifications")) + implementation(project(":owncloud")) + implementation(project(":permissions")) + implementation(project(":preferences")) + implementation(project(":rssparser")) + implementation(project(":search")) + implementation(project(":transition")) + implementation(project(":unitconverter")) + implementation(project(":ui")) + implementation(project(":weather")) + implementation(project(":websites")) + implementation(project(":widgets")) + implementation(project(":wikipedia")) + + // Uncomment this if you want annoying notifications in your debug builds yelling at you how terrible your code is + //debugImplementation(libs.leakcanary) +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 00000000..57980d1a --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/debug/google-services.json b/app/src/debug/google-services.json new file mode 100644 index 00000000..5e4205a4 --- /dev/null +++ b/app/src/debug/google-services.json @@ -0,0 +1,59 @@ +{ + "project_info": { + "project_number": "1080818269583", + "firebase_url": "https://quaesitio-1523049861646.firebaseio.com", + "project_id": "quaesitio-1523049861646", + "storage_bucket": "quaesitio-1523049861646.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:1080818269583:android:c37c9bbefdd27e04", + "android_client_info": { + "package_name": "de.mm20.launcher2.debug" + } + }, + "oauth_client": [ + { + "client_id": "1080818269583-br291p34djunaf9katvhomj5u5qcvr8c.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "de.mm20.launcher2.debug", + "certificate_hash": "af1d5f4a72fbaf9fce328142d1ed4a3ea4e77574" + } + }, + { + "client_id": "1080818269583-4nll59hn87841bivhi9actpijmvpn6r0.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "1080818269583-36kh12cv92opc17pa49umltcih0qfh2q.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyDmEgLMLFnwYQviEE05mCg8kD6ik0gcWw4" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "1080818269583-4nll59hn87841bivhi9actpijmvpn6r0.apps.googleusercontent.com", + "client_type": 3 + } + ] + }, + "ads_service": { + "status": 2 + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/src/debug/ic_launcher-playstore.png b/app/src/debug/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..dfcf788879b24b015994a84f47e2575bc3da4b4f GIT binary patch literal 46331 zcmZ^Lc_5VC`}aL&G8BXCyHb)RWM60Gv1H2{MF@o?vX_`aBH1cJ5<<4JWTJ&;$WqxR zOQfU>%zf^2uIsa1uG?6fa_$t_2|*C&QM147AczJ0 zmjz;D1^=u>4sJpa4m$dmp+ltG&-`^PpJ~I|hS6Bm@Yw2?m;Ui;UsNX$W+#;=5VtUh z+k8aa+ZI-@!+Z1Yg``<~#Mh;!hB!y%M=a<}SZRL0_xjRS#gF?H2B#ELOLE_vg~U)| zQus~23^{(k^yA)-N(pq(uYE;%H=jxOth-EyZ^m58%o|vFYjd_=x5uiP#ZLJ0!`T_P zqnq‹K}I(I(2eB(JHe?!CY7^4odj?OQ9U7)Sz6}5jzQOE>-!16(5-9Y)d&(g^S zDS6gtVM*oqn8T-8Idhb6vs!ZG7{d)|KRGx<_|`{!@8yY8*QtFDIoPhPPtUHctVRxB z^LH2)aczv?YN1Vxv07ir`7tw0T6ydkI?HSN6c(6mH{H#d_`x;U_qR^=ZpWjWr_Iy% zq?D3Ies|_+vl@8~h}F}zW~N+X>m;uuPj@@-zOH&Cy9~*QBXKITDoT=gmKud{AKJ&y zLakqGI4~z}U0w^Rz~)=%j$4!7EAg%j1{TNGIA~MqjPl&f`)lXCd~KrR58Sd>^<;-< z2r%Y^myU@-FDW6DGp*=y{wIf7F3J!I1_3OGhcArr{<>&m#LJm)aOZ>j(f9Q9duA^) z<%C2{38B90Q7FgM@QO#+e$l8`u-Y74EzCEOovj1A?@$s@YA?i|L16awacgT!B=bgc+rIF za)0Af0(vjq^>g0LQ_c%JQ~EBbi*n~c18lXH(638_Ud6t>_svHy>sO3kZL~iarxHxp zWIwqcXS6DR>V<##d}=@r?z}4O8qb>!L`f=?&gF?Php9kcc6i}_w}`gyMg;Gp%aIE# zZ>f?>uQ{R~KbQ4mKuMn~0^{)59YZ~mCh!h@vD<6w_H!qa&;_L~K3lF6F7cu`E$NIb z_Qh046$bA(R{G*njP``RQx?39&DRFyywzlM`@^$Lv2WS%cWnr^=f)hEh!&xHS0+r_ z#yz3Yy>wwX(T9F;q5k$@J;7q#GRW`KJiE^C=iO8|357HW_njKz1xjj+DWg{Z8)Ix${T0F>m&JLN9bW3ctZMlFv*u+ES;^(#>qgzW%^7 zM%q!2pG}aSyI-du3lUGIdiLpGE=Q-jpPXmM%8+ZWj`Lx$Gh%F5@0oEAL3-qSuB&dU zk+;O@A@BR%pDi3Zj~PXDC-*S-`H7^PZpx{{_XNbsS1i5EpYjJ1?EcDvH#In~rWj3S z&jv#sIhpXQu6tMu<-IumGB7K4W{r9S;oYe>?>6nvEg9s_{^7JY>>zj@U)XVKEUM$87jYD+bHa zaE>KKB|nutBN+2minXxHlLKR*^+|>vpaR)0arM)SCBl5?gcEyX4t$ji>h-^oA6Vx5 zzK-rdcru!|;nGi!r8I`kEbI!px9GEQS>_nTK-x7GZ8cBJNTEj5l-Nb$Ki8n$Q7Zc=iL`rMm-Uc zSJ*^h5>9Pwp@N`VsHe1aAU*%0dU0S(+tE22(qwo2fudBU{j;R9!^>PmOHMzAW8dea z>h<%!W&;TQmj@GlQ2HCmMz4M5gK=&`IginXqfVp6ac_@bKfBTh}=6G=WC+9&Vl%npAe!qNMPrdCarSAZGP4owj*7O+R%}G zl75UA8>q*Gy?$8UBy>N$t8d)uX~?zj2gu{rOAFgI|BaQ5ls&3E|CFgk-oQM^H=(X);7vtGEgpb5b4`L zjax=J9*d~xi?%~?KQZON)Om93H6T6!6;t}pQNC%{CQuSN#Fa2YW6bfxBagiBW~+216eZ9;XZCkZ;m%Cl`v<(vyv*?k{%1DtZ->N$j#Q+aFpu34bR? z7l3Enz@r7I&*NQ1To)?&q~jhZ5m@VwHGNNaqOF|6HhnLM#}kz9DBi$Iy$ax_CrbK@ zQrD+aAQ#aLcizG-n6pq)BYZJgYK=XkCr+*9$Y9nn-B9=278Na*PZxqBtOXY1?eLg{ zA51<)QTZN`PCHd_2LHhPy3aF!TMrSI&gN?6u9PYsPAn@&M zi(0%IEP4jld1iM4kRXr(YegQ*q+ZABkjMKQEzE6Fxyhn`QT1o%&x~{ z4bIYC-+bD|_^j~H4u*5IHMD5yLP)ly?~chHk0dre7lkQlsa<@22uW2g?FAtniX zvb<3Dgpzl71ue#UF>kc5Hd#{QTlms3p0m5A(HAR%@fKADS$G2b*<7t5F67p1Gwp^J zxzD(%c(HFGlsDrba^fcxh9j9WdneLst(#LQGLbbc?5%L2;Ig?kMj71Qk3#WrTsg9L zS_F54yF0#@ZpL$GPMj{{q>GL_F@JAT@zuuW`*wZi?_eTA4^Ay;sa;Y;aR(0x^|@$E1+Yeb-YsqOC1n+FL9@D zN=Lt3?JjuDkkEt#cr%7AaYrQgyK2^416{g=CaE3zV}3RS^r>sOq{FCa%5c1d7-@l=L+g=u^IyfulZ2M6k`RLGxz3c&fFD@CkU z4as26P=DK^-t;5Dxj=z-z-sdSJ6t;X3@)J!?K0VT9eeB5_xD?F*>^u^;lDQ{@)=fY zxXi;$*qB1*S6jKMa{mp*534#op6iD_)UC*!6y2~m9>^1ODwfAXM#_B}7j)D^PG2iP z!D{@Zu}jSX>Aw2R=M$;qXtWjS_54L#MpZ}5S}rb9nmA=Q!nPPGV^Wv{3YBjPst;+z z*Hw7Vk@}joa6hv(b2HQhc|)m!cDkIHof{LKsGcuQhGENz?idry zn4~0Z3ha&XsN^q3!>j7c!`S)NtCthZ8ytIZc*eu*TL2`Ba|#JsZ$>}VE_TJzlh4Q(BGO<0Jq+pR|QIFo&9zN z`m2tkB!l83Pt{l&hQI2IK4f(=v3`El{IrCuxo}CE&-t2?(*vE6ECcETbk{cBoeZio zWtG(gmy0`+319qq&3bqm*E4K)^SkRikM0GUl%c)GSJ~4Ck(qMfQDWcMT`EiW6xMs3 zj2(gSOsqZbbOXEGj>xB$meAl1qvLssyEd39ViYa>a*mGNS7E zMiGc<=mJZVk5_Q#mH5J_xc`luvu)43=jS$LsdZ{C5 zAeNXWcI7VE`rxsLFE-TAbz;0(O2jf?yWkt2U>_HReB1V^z)tN{fi_xao0_~#nKb3m z*YO`e3;L+`=jQu7Zc8?OuW?(wjmL_x!TRay^&Oj~rT9*7)f_bI$U_r2R(m8?OKo5n$5GYNvywO&#UoE;^Gl2Vxg zG5{R6<>2r<6Blh$Fh$((42zVsce)q(x;%WDH(Sb@dh^BWo3FO}fJY$q{6ZJ<5rB)CvS8u^5!$I@3wQ_^}oL>ct0wLX5Tj@RURfI3UUK_rna#ZB$j47sZ0@$jl`Ytwk< zUSDLBc{YRdfCfw2=Xpq_f2P>QDs=za@9~;ED{|K zNbD1sNGAM4^8~3x{ZJ|Eyk;`De;Xo{3-kuQV%6ViBHP<|f;z?A#xk}WVVBn;{DPV- zT-+^4H~Y@aNa7N#oR^v>%>zYrqSZJr+ynGpueC<^j@VsXS|)r2c7(HXAAT*t0Bokg z>n+=ZaYDi0LJ}JYlcZ>?7h;R>r^=*s{cgl!gR}&6Ql#0%tg+f~eaeZ&-{MSHU zaPtq$2{@I}Iu(L_H!dCB$}6|B9y5RMWx6?V^^AH+`pVN%T3O?z86`hPBxSX;_0#Kf zh~QJG#R5%vWTAVCi_Sd>eTO-AH}h2Ku>y0Rwp$@%S|>N~01kq#;4K;g*y@t5^!U95;Ho$7JhN0ux+roViaF8N%|zs#V2`0g4P z2PUnJkm*Agi;=9upMy^JBJiIOIjrEIc2^Y%18&AhJgU8aVB%*g;xXZ?jeWgE^sj;a zovxYiNLM~y8-NVKdIL)+VIGDMoLh;08qR%y4(+06tYL#HEsnj4lRnSf<6J-q{11&8 z4yl^*5&PeV6=Jkc{Rzv`gxrS>>x&AJaUn{T@^rW7eD5JD*H`b1qx`N4Qf(tumW!w7qBMDkIh;mjIW+; zKq=}^kv=Z`1r!qjpmDvQI1xkq0ERiNkQTkFsJN`K^{6t&6a z#@c)7ck4C9Mrjw4@7$ZSNUd7BPbgTEbRpsiN!Y5zt*5P@9uHVTn<>4B!J5j42dr1i zPA!x;xKGh)_zZu>?l~FLTQpf8CLk7r4FN5%IA~&=~nLV}8ib&c(5h+o>`A?yqg_Nf`UW(F{+QlnnA{EsJwVe)?8hic# zZ~CX`4@J?B=C4VvmK)6Oyc%*KF2g~m~_BWXTW!$;< ztLz=@$(ji-*vY0@Qcl7PF;YV`OG=KcOkrUXEa~;EUX#~}cF;qEv(}DrUYw&Ro_abe zX=vK!9-rGe#&rw-we`#3h=L!Znl%t9-=weGgSgrS>*`%~JaGW9**MG3$;&9t{2z7& zQ7&%0#3}cCuv#YU_{O0Ei4_klqWIenQH7WKj#G-ircY_sHP@JL)r9&ktLqafM0w*{ zm0fPeNAv~!7$*adA78{14j$P{Ctqt!14jFc~l$vJoFV1Th$U0Ahs`sd_uaIyjFadAEW)i*88MO z>bKSwzAlE>wSLOZ!^NFpIXsQy2}vB7>XaWmYdE5ku|B|5{tO~~X=f5Ul-^I{Bp-%a zcJ(5J>g+5rV?1yV&(Pgtv44)6+16Aor%&OwB-*~ibPTOQ2HP8_U)XS}67Vi^E zXfJh|q8}}W|59U8GfUSKR#o?(Tafkl=(3)3(4H`9^%qRMrUt~<5R)U~b=yZY#bwbfS0p503eT21+n7T3~1 z8EhKkrtB8lnD~*fZ@V&;#Vg(C>`q`0BKbtYhxZb7lO3swtWtu0*luc<+^HY# z-|d!-LGzh7zZPHc4F4-w^6s;}BV$he^e_Bl7r+IHxXVt&hRJwPpPW1yG8f)gF5_4* zO?$>?$e|nZnn{}-)JIGIa=f(-p)cCJU?t8Xn&SVe3vKwD8?3V$DK)=#_o_G29sf|F zYLH=mYkV&BN1EnswGh)CoOvqmc0G$6O7Gd+PaY>vkbM%+iO$j)--9tP_9#epNPm46*LhKewR47CdeEVs{<<%&i#XTETa)W*WQAaZm;y z1D>*j6_t#@@tVOVQ<`Ja5d~gpGoEKVD%ABbHPNX z_jI>psSn}|X<9h(^?a!qDHqhni3owQ*#58M0&rrxdl@Zoe!en-)<`JYaqJvj7=`^o zVp7{5bvM;KsNOu-J)D8xos-)UDS0!ilLys)zqiHH&680vdjYy{YqB4oHSUYxTDHWX z40E zMMibD78DGzeJc_prUFq8kn@q0IyEnU{(x69V3nuOhijI?F>w#i17_w!YUac(No^RV zdCnP{R1ZN3DHr1V^w-l8g_` zeze|HP{@nj_qPE1`g_{LC>f1Qh66J6m`cYs_Z+)68L8Q_&xpwA&HLAH1MUf^D~^3) zs?fnuL2t<-aV>f_8@?$Tg#c_N`~(!h^2h}94(9iw45ln0fvp9IcAquXg%9A_sQhON zN27lYMQsIMa_pyz{n27O%V@NZStYa`aJrc1WZ%#>imiRLYjJZ8$8MhqG*a+!!g2Km zL3H+B6h<2(DShu>gzK`+Nt41YVQ4CLDG{L8gTQvvT4C{HxbYuSSD$B=cJuF$s`XVU)@szG^g4$%C^qu3Jm_vVaEGb^x-|}fD{IEv>bY9?UBM?egKf*pr zN_n^W0+q_Pm6eF41PEaBU=ym?=0-GXZO&!BzdC|9^yl}(%bG1!F+g2!DX84A7>&qkX9`W& zy*}4G3SfzWGkGg@Y)nv>diu5`joB9yJ`)S<6&{P=T)kPoT7iQ`G=PmLu<5dTe)0$l z$9eDMmqmK8sQW#(_=O>eaeD)`>D$%asu11i7w!)YEoM2_irq6&z7*FEZqHm8&wV6q29D zeSZHG+!bX?e6r`Ii2y#vvs|>DJv!*t4|T%DV_Nv%eu=#_=B1g_6PvqXPYbF|ANvHJ z>qyK*pQ6AR*CUfc=KKiz0(`;Nl8_*$KZnvM<+}3pq)d+|3A^N5RVXcg2wUnJp;6`F& zn1tv_9|GTr$eD)j?M(_MrUZ;A$X0ZudBfF!xe#S2x&L!i(xImSMNDPlV2hG6SfVyJ z7T11If9qvv;O(lhWi&Mxw4y~fB@Mk35zo0m7ffc=v`#s?<->skZ^X5wz)L|}4fZh` zPL1iaBu;d&0vej4dGpz+EV%MD$}>4*Zu`0z-%s->{l(ftNU3=dC#H5H-Jr@#4BO)c zoz#Etz@0Aa6*?w|hT*Jt!b+SOgPgBmS+KIknTj~5uf;y7)dyg0C{U>BFslxeyT0Xfe zfp#l0Kj#zpraD$i7O8;#PI|LLOBdtA^3@X4a004gS61KuK=afm1RnvWERv-e`Cp*) zha>vuVqNAMsK1hhO zLsS)bdG4jO+dAa*b?Q=O!K1Z4oB~@v-J*BJ@vD*_wn)BpE1rL>=A<%gqX*&r4v1FA z?)|6%tZ=zpMix22Z}^kfhGQD%sR=tZ0#FH2j%n$;Qdg^DM*gnZphgfM!53s|u52DU z=c))k)Bp8W0{<96=MNcF`fL?7X3A`;Qg19hw=K1Rc$dq@%AXW)Lx|A$V9b&bRD3fM zZc?`kYeKE(rv)|rkh_K&|*KOqqcms zXpgP8&@m4RMwxEUk%+i;Uil`%MP8d)rs1vZc|lQ8=XjCEuGX~JE1&P|P5QWP(oOOi zX2N4~`c({cc|CkhH8o(Ty1`}#@G}~OphF-H6T<9o{kXbn;F3L6=fQHGr6Uc3{d(GU z%iRKg$CIOX-CNUSQRHgfOFwdGg4VusAtL`QlWty@Kj*tzA5p3J>`W&nb*upK@|KR8 zZye}z&E+ltl;{jYLa8mHa>=FdjsCVb{jep8wQrBTJ_n?}B73tYqpZDJX?#AnrQ4I5 zX;nUHEB^EymRpT7RInCmlGd?v0b6HM7&MLhAzbX&u_hOWL*X=86_(6LakGRMYR8^A;W#Z^&kh$+GlIH z&ROx^nFIS6Q=6VdARS$d@lIIp>^%(Ina?iN7^?L+x`<1hI+Eo)1C))$WDnzfi!)gr z4=^{F5fKs(ugzUn;xoT7DO&|j(Z2!q*z1L?5`*;@gM&Xw)e&KpNfGz#cFesd2k!Ih zNZSdT9sU3{W^|k{1~j8DVq*o57Gs3{5x-qgX4!~`{Y-^>&ZWwCel;_bg~LYV<5sK@ zS548G4Xwn7ksFv=HaES5;2cB zA?(lZk*~Xoj`~xBF@e}4Ia%z=_=lo7xV`F7O!3k+%Ntl14oHwm0;5J6*TRNg(wT7x z?8UdjY2~lgZvojnvJZW4jjM-hOikDwyI$k0^B%FtERV&4sU@pUcA>NOA7y2sI*Dmq zK+S>KU#AbP{>*l`Ym+>UYXuN;hLUPpr{-Y>ZZOkDLVo#fkS$Xa;@h`%Ywi2rw9j$p z(t)YNehmC##M#jTuY&MB-A1=4CBGX=-o(Boi3o9A7nGq7Moxd|U-@0fHIQL69+&E*38@tQUCk|}VG zG0EJ1jE_-rDM8wgvZtjQjYqa!^#i>{W6a!wyl$Xj5AK z1hdz8Hdy68mjzFL15r|6iz+DoDSTn0l1vg4@hZRYw)@+2Sl$x0n`@HKJ@#>mp%2Q@ z%_+vz9kJ^#oprhpwPCO~EFAY1l|lOMF{rGYSE$)?B%HmwE`Ce@6!710c5=vXpiXPH z1S%(&79C+;X^<FZIjSuMoh;PcWnMClk_Fu;h68tTlKSw0v&aEO zs<v{!fcIF|zGo>$eHB)94CMHyTcqC1$V zzk0j;LQJX-hHX2jQpjHR3P@lTd9FV`4l5zOo4pE)tODM#2I(t2VxRSfPiP*|_&Qz* zoA_EIdEb0%e4&4gYd3J2Z>?!+Q?6TY4fhILty`Y|9oNS9k^;C3_oGF+kVTOdM)`u` zG%o7L0sEexi6DjH&l2e>>sZLy$#_#lliWXer$rW7!U-&B@9Z7EEeJkCZK+0ZnVn;# z!DZfkfUdZcU`1U$bPNb^X5*<&tE}AHl=vwDB#q+xk;JIK$g&ee0A>phdk(cI7rYsH;V%U%yuWxFaOX$wM+RH*X5$aF>6RR;k%CCWD0pXaM=2P zXyC%0thxB;&EdHrx+x)3oNj5eZZwJ=Sy53df7P;!RYjeNn2+R5(A4h4<=}q4gGCw# z&92mK+iaJbz-|hg652k&{tmp}3Y8%ai=1WgtcO0{#aYWCLq9`8g6&FL$idn)n`fG1rNEURu(JOjA& z7>f82_SP^)TT1I_?TtJfi!j7^s3Y}%rBsLLy`~1)xmaj)lGy3YmiwS6J*B9Hb9ISn zQMz*nvn&wm_&{XJ9z}#5NnM%1*D*sLj;IWwN<&?-jn*RZKpnM`meME5CH?n)*`Rz2 zV9InYLapo_(euTW&K~N`zCcaozKIMFz~5K#|@N0*GS8o=r!>(>`ESK@~3t#ywjK08#wWOk1spX}~j9G;R>O zsXO-QMq<;L7J9UdR21(-I`%k<_!W~0ul1Usr{4!6*i|W zI1N>&Cme=dQiJI2%cAdMDR6}j%qRo60IR(-lYQPZl2BPPr@;pUtS*x=UQ@G9c}V;C z6R_X749Q4B+WtftsjXnBi-jWNVP+pMsM}$)+^!3}y32;@>8hn{YBP)9Q326CMMd_1 zOtYsIjsQ}f(vXqr0}l&C7OtfmzIl|!cIdBmwAohr<-wv-A2FS+{3I;#`9f`3Sh9fl z{ION}s^(k*I5tUGQ6Hu{adoX_D?;H)(;vEM;%X)e1H7iT>6(B?6 zPV*l5UR|}bMHUgLp5ZyZT}& zNL5g=t9*3uOZ&x6%LA~bWmiIT(hoT>)LmV?$BK8QyFe7zYrb`Zvg=3*!DVvlp}0&T zNaPGaQoPC~75C4bO?|+B>Ed7ts|D2{*URi5JIqcS}%Swc+~cW%3oAjs zzbqgS4?HpP7vVav%Yqzh+P-l;;Iqx|^S`0LPzL14ogxM`CZ#m_p?ST_z=p;e${Iv6 z8e2PU|F$RffIVptq1E%*_UOxTi;CY>maS^auspX9(>Ps8%YH>`iE-4MR)n`d;wLMP zc9=17P&tgU;Rk2A}uVou%jTXF`j+DRR3MeEH^wv_r@T0UpUjCd|0$cU`jNWhQy#4rY}UJWipU zYe9UQ=Qngi{dWMA()3RN)V2w9K$vn2ju8w3jA%QVAS-a)PJAPc3VPp` zZSVT*=Hk-ng!FoWfAWj}BtWen{qF>5TP`jfKtryR?F20)f%?_W}AG9*fTI zPzv+y+0S&qkXuq@!mKm)>FWZZDHm`yK~0V5rEW?B&5eozWmLo)UrjBPyS6xAN#K_l%#B*TQS zOOpLMwxKnU2U~LF9mrLtR*8=;-2th!j3Y*!AD7PBi8GbUm8zKh+iTxrO~ASNy-U*c zI3+S=+cKQsGZfq3L|)BIK5d605xL2iJB!}>Ged@WrNC(%UU=fN#6y1g1eT34AtFyQ^ z0H#akNM+UDuq$!F#E03t2WyV!L0Rm5rrN+?=k|gWB>DbkhetQ>n*zb`+{r1@{q^0U zbj|nIU7ENai&Qb>9_5mBuUlL?r_Qn=kGyKZ zVh=1YFTc$Xa$v+q_p?uACqGF++l@1{S07Op+b?=HPh3k`3|n1)_M z&M|wKeUu5zQ&&L>*#n+mCdh}C@rn@EStvk>hF{Lzp zC%veMgp*`+`KvuT%TDz%hk$n`c-1Z;oxmi}+Pw&b3{DBPVo5B#CgX_8d0KBCa6bG; zm9L(Habdy%LO)2df|%q^N7Srs2DtJ0Yn|PaUzMt8s#5!};jv$+wrWAG`vPm*k_h11 z8!AssDc?Dghbt5#T0&zSLRw3wMGOK{=EO9dsZ$Sz8 zTH_wbc6_UNMk(sA#E+XI8XFZQquRyIE%A2=XW}K@Df_{Wuq38;I#VbOBC)R7aD{o6 zCLztBR=&zyT854;(A0$ERNZT3q#|`OguOWdGKFLlDNpF605fUB&v1J`M1P_g3WgB4 zqJNav<8Q7Jd_3(fN71@W??5W^&=uEhj%O?*H3rOsZQAvPD)Q7z>M+a>^#+#^OC{re zMsTR%b-;x<3>{wz3|s3117Auzjs$r}~}1FwTg zOwqiY9iu&QfAc7ysE@#wSV8HxOLyXT|2srEVucxnoZAV;+u`V-TCTOV&b3z~lVHrj zY!v;vVgBgd=CU}GLqc<9wAF5AuaF8w7+8BvPL2IJE6S&L0-kMEAK?gO1EBjA_QoyY zdACg?%ZID5BS4)0nt~ZY$ihian321Z(gf?8`CqmPVbk30YW5|U?Tw%@}r z^W;2#gJ{cx>cx~vd2Ewp9FQq0`peG?WWqkN7K`EFSA&wJ(suWr(JnM~X+wKKl^#umZ$o$(D1L298|IS3Zb6-ts=?qW+vC@xOK(3kqlW!HlP&h~CqdJ+tBbSp; zcIKZ64AGYAKu}nTGIWjYZHk%LH{>5Pm8}DsgsJ$P{MQ;oU5`M<26A)2C_Q~qNB0jk zW7~*NA9s$_s}+fD1-T!DiIT|Ct0u3hiJrdTfecNZl_>T=G1$4g7i%icBlrgdjU}lg^;f0u|qbiFN zoz-$kGYw39F!6<;*j;=_>7WPgp}5*JX&EUmX!Pb<=Yy;FX{W$MNdJf7-J#z?ayO73 zFy!%u3(xxVK-|JAUi5%DAZ1tMT029S9+Wun?BG{L94bP|yP||N)~B*uh9aEq+P*Q=)qU|(@+)2&tY*pux49mt)bEb?kzkG%we)_&~DWQ7Ycvm zO8c1&T=g9-7@{$2$B{h~Q+jt$cX4Z3@a;&w(zOrvt10jCn;&)cc?(s0^nTshOL7&H zBuqOdR+eo~kj?ssRmn(!OlUo$9H?mwTD4~Ve`fb^4I>-j$r9-I=|7xK&lDWpa+#+m z2rv{lAubVcBPA)3jjdBAAf_m+diq~6#nPdF#T0i_{}WTJUVa6eXqDt=NRb17Ek?|k zti+k~=m945szAn%iTG6rI=9yv#eWS5|LHzgD9^wOrhg-Tv4npI5_P-2*~h>A-cbc+ z^EzNhPF4jTs?UQiAo`hk=y+zf6HuAaApV+$qf^MjrJMhqIt1+jQDinTHNG=lnyd`d zehW$k7&-#hLjUhw5d*Kd!dUNiPTB2~MV`O>5pbCpZDML4B8S;VUsC=RE^J5szr%&{ zfUs~Ui%K9LEk^eH$v|}uJ}f_+90z#IHV3LB?BrONpSdPuZl6v9V|Bii^ZD#=9*U8q zFEGQBwXfZ2I}Qk#KIX1>K$;8N8_INayQAXUZeWmZIP39d(r&-&xo z-)zXosZ8T0Lrlu21|yn5T<~^3G~0i>W;U3ZZ}jw9<4QPapp)Z>>5 zAd)c%R6+OfhML_>2`P@`IC-&UXS$MN4vxw+D8O*X{<5u#VBDjl1BhuH;IDu@A41~s z$1v&TlzBY%T$05)`e)@3-PaY^9%3K<6%hmv|H44ezS;07& zH#Rj|3nWqnm}=FGvBGpfQsi;@iiUMZKSmZR;MGHP5+l|QA!;*`wQDdxW36WOvk#a- znDO9U5=87tT+}+9&?$O>rGMMi82uHLaF6za&=3S> zYD#wgN&j(gr~k-^f2IGx`~TE3mB23%@eh0#Qi;&gk7+s*AaGS2eu;X>7zFb|5e#PQ}GGM}Ur-+7E=5XC&rDWbJQz*W=8`sG%uV>IM604UD z@82I=p%%I+bhqwtazpd|{!)-Gl?4V2?Y`0N;pcG_Ui8VPCl2(9*kRZ!5dIp$gn?2_r<*&Xv;YYi%ic@c{1db@ z($84SUYAi712cLD+^zec?SHN6$MEC<@4|B;-iaGgR1Bs9EqItk6SzIrA20gDNR^R6`R+dx*iGLcUgxM5qImEl4Bx2h{c8Jv2ik1KWsTdQ zNHO|gk(JVZzdQ@ri8A%AUmTH%6jw21Bx!;Q!8^ z<+J^rJrhJ2vb9XvDCkw6NX1CJgMDW6u)_JUT%ETV@*+^I^$hYVh$t4%h+^diQy;dBDo?chk6A>JU?xN$eBf zb_{e9t*ErjTR?PTi{qEb_B@DZXZxzD33Gcy@cHUpnXhV+1+vZ03^I4__k%LFVY_6( z+WM&yCQj!*d9}05IkKt#f8cZu+M|{44Cqy|L&2D%!cYm*kY@@hL)`@ni^HscdUf2j z&WeAdzwOw7GcQ2MrN>dS2jMs;8dxe0?YPY3=WIuMCzj;?7T>pjYTHV$7X6ZYO9k52 zB&wWf4x{dWkTQ2LGTo(aQOkFGPUW98mAPMi;jaKFrfmIxknrrEN)nf$6iH3~_c+wZ z0WjAKW>o{?hd#o-P@$p=0Z8v!L1)aiG(p1Kv9FS;%O3H= zx_luN`Z3XwqyqSqFPswVj}|E(QM{yI!8kvNZ$QtgqU=X*vG0&-#3f#XJ*ZMC9`bcPcGe@A1h=2?*DP_OXKqg%@_nEnA+ezyeI>~nTA(b(23I8fN_@^+9Ts~SfS6w03$a9ClTSZUND zGbq<%&TR({0X7Saq~cqrlr>>Hx|l&5=^4&4<4(ChqXqLP>TCyDAyo!k@;QFytGGjr z#zxE9s~y%`qTLgADRd$abH&Q7$vArZol+5@_P5d<7xj55WF~gM%+qK+;MmK=WScT?^WTiMmBQ#&W@%zuM3;Rmm?=@F&ILn`xj6$ep61mb2lOfhDP; zrb}AKiGwd6MvTftqOD1HZ%nIQcxzL0v8HL@=aY|=g<#Kd7u00t@zFp@_KHa+pV5@9#ZQT zcQ#p-f9JRS^!Ee#9ek-Ut?4panf?iD$KtNp&c_9ME-GZ&;vR3#+V9+ ztfdUG*++<0eoWYLFl!()bsEo*&V)bM96rJP4C;wE4cCKZ1(9DF%`RQD>oE={z4}A% zb3sB4eD$}jQx_ESeOXwfA-DI3=u!)xgH8~ z?ROhNkVZfc7B1=NllycXIht4tLvX^;@DnrqJAj5YUC z>RcPp3*ri68M|Bn~L|TIvT7o!1^A#zkLJDS(BdR8t9s3HhD5AN|Q#qV_x04PW{Q$9Hj ze_N311A%v1h3sjy8qpjG;bUa*lm{$c@iwL3L0?zs#}W9qIw26^kpU@}4|F&kUQmHW z7|{9@cpH%KHjuo!u$)lpEdE_ll@K;5um%sL1oDL^jhKpr_Bryz9Di<|zr`N93JO86 z7Jk?cNIATo>F*2R3;>0tZR#hN;TAoDwegJ>XWeVz!RGL7ix#G0NSp1wg5A%p{q1K> zjac0G&LH?Awv=K*2z7P1To+PXv3%~E_aBH_kngP3hgmi7CO^SX;y3w&Szi&bk`G)i4_y#O$ zCq;xub}xXco}bU`?Y(nTqqgK?mD5xhJ01Mzog#4g;{5NV0bw#6DH2dvs!ixZw zC}*1#7RsEkM^DO@$IKwzv=n8WzmOk(oJLr?rdeLD|5iwG5DU;CDgvFU+vv1O=w4QK z8VJS^JQ06JzLL&MxpxGfVPi8own4@PPyi{uimnB6@y{c_PPNT zL|l6T;nUf_J>h&7REJmVyw{conuUM25?_$MU5ITWcOSESjD*tWA#Z~?9G3!1M1gw{ zJZv+cj+Itoa3sWGX9lPPFvaRu-WyDOOTj*+$y)*S6!}N#1eR+l&P# z!IE)iwrJGk<^Wrzp(XPEsZZ*UkzN>GAd*y$TT6yR_| z^ce@jUDPn(-Tp&kGpIBw!*k6|C;scQud-?i22g;5$C5W5%^$LV><}1VMyCNcQ#Y92=k!EMyDP$IXqt85$07&l(*Pnjnm{@QbLa8h3O`EH0@KEJj z&%o4F!rEgt2ZWg#ZSgd0pc4$)$;c6IBY2;kTHSSb9YscWV8IU@B?q5601sY0Xz<%l z+cBn6z&0`CfcXjg0x*pRqfL#Bzn-5!_ud7o>}webhZzv@`fup&zSFe(+zC>f44$YL zsomqGAWS(+iIz!ppcv=>BYqwYDmuTt$0E?-At@;E9Rq1xhtw@Q8(lp=F!vSo(ikD# zl>y9iDw$gVRik*AcWTFSGp&n`a^YgF&!bCQ6ZwD!eE!rYhssiFv%^M_6u>`97|*e} zs>7`anb4RU2v{hn<9Nl4`)pg6e~bdNI`sXOAmx>}syPCct*!fbD!_`>f3^oN!vYNX z!8ADL9rHF-#@PWm$(8s`t4G@9srZdDBHkd%JBoJOf3cvW&FwqJR2CVbF)#4tM`cd5 zttBos_6u?7$*}q&(mQPUj?L#%hIN;7!P*0<@?ZKr4q$Q65(AjjH$0`}8&E&EWI5vJ zXv4b)bK6~FoIpq^m-;g9u>4gUl!|ih+J6xU`)d`E19w7+E2KyW0qmf}0^BQfEFkR^ zJo+%%SDVO;n9a*uUJ!te#xQe{TJ(e8OZc!#DpFOj_PTkO4>N)nn0`y9hx4ca^l%Nn z_t&}dFr-w@K=QMS}Wiq_k}&Qr+>wUYwAWK3&+peN7=o|S_|g|~WD|4x(tOgX|| zF^Q>z4N$P!1D=onvG14fcEX7(ltR#Z$>7kh)O>(0qMtIBP0?gOi5qVQIvOQ>cHdp| zlm8@+9Q6|{wvDTWTnH!bnS|o+^=I6BZ}r0_$}dP4xGyN~z@n0+UY$Wx8qvo9vSgI(q1(Ji(ag)B>j)(x zsNm3QQ1KG~-hl!w^9aB}L8v_867f3Y<;gD!;i|H({xrb}@!NMRcKRr#XE^Z?-=jlx z+)hKHzIYvIX|FV_+HIT{Al}dj3HyqsZ|~UNJ#@jJ{gCV2gw@*uo_TspxT+we!V^T8 zB4>F`4v*STK>=ox`2b54k=##QGj^M<*%S5iYZ?jG%7wH(cxiLQ68?jM@e5RWY{x{6-XN(7mk0G?*xD9R6`I76l=!BK#6 zzd7I?^5T7J&L>5)Ms#B%%#R4dZ_Y%mhLJ)i4V^cK8C9NQf@dnhQ(cb(7Nia_yID}_ zI!HfU=3RBvLga&C3}+oj{5O9By8vHMyL}5VTf$ZPI{^ArhK?^Hz*a*EkHY!#JWl09 z-)31(v%2zn6L=`^hmXFk&em|EoeCIVeFBQ=xbl0jEYrK_&g2eq1Y>=C$IohT_aryM(z;8A?3sh@Z?cbIA?#%H1`EO`WMk){_9 zTh4Aj(-)w89k1%(1`uhSK^Mc$!#Ltt+3`^eWi>fGZM^GU7p)Pf?eAI!fD6(eSmNH= zkBx3>xPTiIP>R6}%PD@_zFS zWSje^9$I>Xrbbd1XuyRW7M$+xM)IbzfFqs~5DPZp22$99M>v0x19uP1LdxM@x&j}) z-5N6a8XKtPwgq-)`f8I>z`C!eU_IC6z{27MBEN!&XN1_UC<0>a<>3i(GO3Awx%R=~ zxh5Y_g~{`_B^m|J-vTlO>dK&Bq{|J&o%qU|`ORZ(mFARlT~UtOmL^ipp&%Oj((OH$ zY$bYpXA>~wSkoj99!Dgd@<(i=Y-Vx3ukEqfJn7=GTyRcpaO73?_Qh5uyf1Xz|Mk|zs31$AO=M+Z1jQP&7JUZA;oX;I@P3I! zzc$JpUhbsfX=E17MOj)oBe^WOMeU*V`j;NxVf&uzVNPS;bogzKD2T7S`}J}%1xz2k zZLL6?Dm|kFjD%7k!xWU2?S+Fgwdr7O5Ty4;{3NMtd59f}l!Oc5B28?Dkj?LP(qbJ8 z-k@|7}+5 zaF4HB??THqUmLVuzEFmg;~hduR>&SvYoxs@dLSx0_*zyu^Ouyke!Uro!;D|-mwmAp z*D)E{K|QJ^XG;#_^KBx_hE78b9<`7e1c%Xv;@2raE!c;&0`2O}X%u6^#gN^??&)4e zNlad4-ca5_Qa86)xhaJyr74x^e5!+wbv5a=rME0vH6B-7y^;14dqCEs4REaOmVF_< zdaw5kxU!=|%x^p42%Zt`*Y{Rwm1 z-)XUED6?RNQ3PMuJ^hSURC4xgw}@BIeVA~E@8wE|<|`qW>OQnXcQ#AMX50Y-*%;Q> zdHZHRE1GLv?jbWCn_*2;ABt4RYz`Xpum^pHzRikx94t7-G-%Ip?^8R#zlf*p&UuK} zjhI%J4_jJ)t+?p7vl5k1Hq7ecHu~yTwEc5)Y;(3j`{eXQ=e%1yKck6qYonq^(3IMA zfv!>n4-p?zTck0Aq~e!!-a4yhcUHZf_G5b>cLrQ~v>h8AyW1>vUHcT@`uvpuh`dlx zgAtYi)?pWjQrD4#vBggU8z60b!N16Wg$4uU{AaX>mXMWoj-pShJYzQ>DXp(F{lGRz zCW2mnkTQfkCMl^%H*Ma=3ZKu_xE?LLxNeQgc;8(ropfLhdH6VH0q0RZazgrWvdnX= z_N|w$7h#MTKIdbjr+U^+jA(?k1`>6dam;5#M?o>d`vc}lYRzDVPs<^DePGsls>RQB zdt5=#Wxckk)hg2~HPi2m{B$nJ$`E5{-go^UAu@wF1o6!@dSp#m&TfrNP=5Md{qYsQ zox%tu#GGxIe}~p!>vW>7*7ZBdLCS%)ADJKd5S1UL-yGFO!C`-Zz~Ir`2u_%v=YCjl zSoo~_L4lWHRQl(Jltn1Sk`tA@X*{ktl6buL|pI2xBMBECJn7=Tlw_V7Ud| zX-b+y_B)5HLF|#AzeFLVE!xTqw8!pGh66&qOb?405lXY>WM@4NavsaYcvIieXNdU7 zB8y=~{7f>+F4Z>5PcrfkH}qPwXmZ~p_TH=0{wh-0;;lT-v435VR8iuN_72kiCxP^N z42_U1c%rW9%};mT@o#b6K9-8eC`vx0h{e~B^til6n00pttld3_eDB+Xwng!F5o$cv z`YcZSofg}<<$MnLw%;t#I8`=S%?Zr_4guA8Xb3a5ivdA~vwNBjVPDFQEQ7n6XpkNS ziwc;uJA2Q4U2xNLFBbLb|B4$XcI9k*GTZ|`NrxlY3MbI&{dG4tZnt2jkSU$%a$KI= zt0?_C8y2?1s5UhYclT2QV?Gq|Eo1Od%3f;PV>Z9W3T%(d>lw#F#UX>>BK|I~U&udS@JaH& z8pGXOiGj&x`Cp;?o&730=_1V<1Nk?y*Y39rZptj*Kh?IVwLP{9W ztkI6}FZwFC-knx%*0jyJL=t=DTg}#ZMcC%tKVkL)7Q*`NZ|Im7zFfq;Fd;Lu|Qg4UF&3cEz&$ zn0kSn@<<81;qzhC4OXMd$xtBG!Y~Pasx4-*= zckF+=c9W61fAFH~2h6we=f{BJ)K-2>DqgE_#n%Ua-Ys?h_+ZNbBt^ENoW~fSpH3Nl z5{CyIS;5YVPPAdCNhedVTkR8m*zwd!J?xtQ!~=F-Z*zBda9^QkvOu{{3~IdlW3hW~ zVop4Iyr=j)bQowj$ue}$(}iH~&!`2ZDZi2PO(GX-BeMxsCq z`gWWyr@#)|uN5!wPoSO+Vskr2cWXw)8-^uARR3aN%k_Q;1*zvcD1y{8cn)5F#ue&x z>=*h@dA#>WRaCK2&8Q=ZM@ZxLBo;eQb1QR)mPLm{N0OXipwXDppxpp&LJZMwLu8F4uCaimK;$c6Jf%7j?m3DB$uXqWP`E29fBTJOcR>{lHVd_Z0Ac z(#N#BumR?~8`DuzV}7770PI~B+KNB?5gV}8(y4v-@vcWTJsYzH4N8Vo10V&8A9Bgx z9nszg++Akfhu@tV-3Q;@F5a^Zz;1e4laShP?`B(XS#E!}-f-WJiQPWF{VsOnH-l$9 z2~QymWU_oIk1w_|fTxlvD6><)m-{28vJk%}Q94s5r>zXBM!d{;;j<4%hM=GBfPUOy zH^{!lhNJQ(+&Mx7x_u7M^@alt?*#~`1u3)Hh8e6WqcCeVje(I6Yo%JI1&Zk=tE{O> z)$LtXM$o5!;XA$Kvl+A*x%pu;t-w5D}%| z(`4rNc-;6x_);-m)vhMTA6^}FX?5z{u|}kjV8e5Q3L(AuyACK&L!U7aa6WY9RHP3J2{CWwpf^aPaAB_`@ zlk*Ogr=Yu?*1HdP8?ZaQqt^Rtu_x&Jx8UE_SKyw)qenEkh-|%Z`N$`&Cewr$2w!r% zS!FuC8eZ*56Iv5lzHYX9KP6~1Yp`(%db)b*QW{YD#I>hC&%nHKKJ04Vis)zpU8+Xaapks~=H=`86* zX<8X0nJnxmnoJ+g!L^i~=cG|Co#MhSozk7M;8n5n1$b5K)cD5UV7+HaA@tU(Nci|{ z)DSV23*x{J1fYkFxr~AJNqBdhlp|tzEojHMU`b;!3h11X6Al|-0VKB|NX_KipHs3L z%utRL(`ZdbheET1P&veiE$ z({Ep3W-YuuZ@^o&H%p-|71=^T z)?!?imdi0DeASQm7;g1mCpqgC3zd+`f9HVSwfQt`bzEh79CePBnS!S&|FR?RjqX+KKrV26Dh5yaJ#22GKR ztkVRO0Msc8CYE7>%$Bc{i60$i`fM}Jv>ZSO46AdpD^YVv(8nOh)d#&?r~T@!(pRKz z%K83I&B+s~2+$$nh`p4&fq$=YAUxR3+OGBzp>j`-)RFFK=H)F-jrN;t>TBFFugZfQ z++2=a)!ets-Xq#0R%>a0Rih+pBNi+Y!A4)L|ts$*6)T|)J zY~iGA>qvs`MZ%#H*?Z9ENP+Ma;_YC0!bgWKfBINXPPm7Luc&=&F3O(R=HS6(F9GhM zPfC*`lg+4GqsK|(EXv6Rrg5~kztBt@487L(5FIR2#cG+rRgC)5;<-iOa5UrJ7hktf z^R%B^ZjnOt7=2=MI6a6#!6Y91DpFcuejq>RQT-X_y`Ndpbnp``CkPY0>N(ckFvaiy0taIoON4 zA>fTQuq16&J}p0sOQpUXLtKWY;aIuZmfW7;JkOzx^hpv4r7T~zgxF28A8pZV!$HvH zw@qs6l>&tjOlFmQ4KaPG!Rx@pd%IC-`y+3eZVv*3K53?x+IF1PfY>j*H}F4?bG@X^64$S?!6q+U14Ph?QHHK>vav90B|pW0z$4(`&!%O(PG zRlqRe;`}%emH9PY%l?-#X{Miwn;NT5M-)_N7nT2~P~)&a0wJ874*_ws60MV?`A>x4 zYaF?s>t7k7cxjK*>Yvhi?ww9e2=#leOAHuU!W*CT0?2j80txU;ZDMaFeyrVa4|#KWBtLeKP?TPGmEogdC?=(WEP@p_{xJymDJ8rLfQWT!z zk^@FJU_6r$2HHWGw25Hiz8#ed20-_~~rx8^E zggXdx$S^;c^2^5Q1ScAHpXiPUt;Y3NR2ySqmz3KLTy9MG7o0vZPZUoQZ&mos#Jkew zA){IoMg!0N_^GLrG@4D&zJG3E6ia>r1-o_X#ksZ5V0P-8n`6{%);GIO1Dqn*J7u_2 z7DlT%^CoODsYr{!MF(x{9rB%$J0hMl`F5c1_`Qb_B0!hiYRQt}eU+=5Phfu%1bwoc zWi;H9>?MJ9xWH*?&e7r=t#$BRJa2<7Mz~ZzK-suz(0FQOtk2b$0fP?B@F>A5Q*8K4 zd#S0lORY#IB(qBjQ){@2{IV21Pc`Rl^Ti)t&2_0POw0Q}{l{{~2zW1pq5}{6RYgy! z298ZROgUj%UD7I8@SGz2a?QnhYZu)iuvIdc2omWKv%k?u-9$}8fv$2Y_5)GL%?UJ% z4Ic)Ln#yeB(G<}zV7c+(*1ZYap|8PNVL}T6~*#5We-3 zLxQS{Th6|VLCJG{gS{pD-;|1Ci_5-co1w~EGeLCx$_JPn@x!=ozEk;gZ3m8@D}D!} z(!WM&+0QEtHD7#AC5AplE)96{bd@#?Vi}}E7UI{r=y^>&<9vw3%i6bC;<(GpX>+72 z=zaJ46oZTpY=aO#)%r)(7yvigzqq-|IC*=2HYn5*ajf|LbH=u3#=3pFGT$$A{$I-J z%IUj>4!aHfsaw<|rJDmM>?NBBboA=<^mx=G$_LaV)mzQyZ@y=Ev>r#xMy(s7L+ZB% zuX{dZA&xe4Z(uEYwFolv2e6>H33i)WYovO2Pp(zmBw58o`Xa_WxgPzZNXe4tH@bdC zCMa9i9zeNwjOtGeN3g5lZ?SEBiIAHPyNJ~GyU!-P^*F=)aa^;H2QP8(-ZpISC}4r2 zwoT*0oJ2c2?{&t2Ntz?~G!cRy-2wlcH*Xp+0c4s{5O z%kN{UR)1RIq~d~hxV5td!y+V(&jVv^3t|Tn6g$UCM)eeL5;`!p#b#> zQcf`Lt*P^xJ=5P>kvBSwP1h@uRMcZ}sHkloMGX?)mW=$7+gVsxY-)jmN4Y?Z$mjDDMoT zLs@Oz=(^%8i$fK+_HXGnr0lkANP z|Am%#D;A%r3jM7-015X;V{Q3p_kZWfE8q{^qPIikkpKbS$D$1*IPmo5B&4^+QL?#94(EL66yvTe|Bl^_%VO_og(=m4iAcHa zPAVxBSP-&^6wIVmntLS(8jT9%AL)aT0;l#rxMd+|qVAB`e?*N5xSTTONO!waM>sqX zSiPnjY`t#AL^jrM@+RZg03_ir0r&l1K~4}7cwf0sw#6=Izi#cT4gL3z3qj6&1?2w` zr0~`56b=~OP=c`DpD1-8Fs2@`A9vtDhTf;{YPM#$8RDET#g{uaCV5_k%%5D0vL$k!;%n3 zS|Y$h65=Tdc?z%rgJLJa|HuChTLCm8co9gO?tf@&$AgRA40Ikz4dNC8$!dTkCUTn} zI21Z^8!kvpix?5G!@&duhy2$c`Gb>=E&_y+|Le$T3E<+#A{^@cw}%WN#3CXuF=Yeq zNHE~n*+2u~!(?{<=kx#Y!dZ{KT;Ls0 z1hDEhqKL@SBfF0G(5eBCbb!7R(_y+dJnL+FGJi>*j&DYE`mqHuz|v~6v3g}Ug-K}D z-?cSX?m~v9%^fNeCKyA~4tNgPO!))X`l&JHl^*(8ss4`~X`%D=@yY{zd&GrN(KS z2pKy49C>N{(+7Et#;1$9X?@Ca;CwL2FSmp&bN#zlUw^QAkANq<3-WBweq#Mg7ycaNC$I9{Kz{iVIF@p$9Zw7=qeyxM1{B@BrpS!y-r zLPFVml`b&%;%7H05ytaCD zQYGap8g>)*_+NB@gbWQt7B8Nu;15ww4#b{yR|5j0cv`e7mzLu4$oL&_Qcua(Kjw)K zJ4ip9v|orIEK&vSRG0zqK1(oIlaha@W4B)w8f#^DbHYj`?EON_1suqP6n?5#Z?<{y zqj-kzTRnh51V`a6< za}v{&{)EMGsaJiTGXF0mp7Q8Qq7#*_U5D!bO=)4B-02t9Gn7#_hzR^Z|54` zpU%2(=IXP_qXvap_s$)^Z@zZRlZK$-Bm(mNHJZDGj6;&m?Co(awtLsv^KfYnk^1C;ju)%kYITR%b-l^FF6)#FW8pCZ|XR>Uh5uhjgiGyI}6j^;_Yp%LJTnyL8zxFcA_@ z6v_(D7u}bvU)fUMd|1*wp$>@h$||usFyBw5T^Dhbw|}yc=Dt7DVeq6s=Dtn};usEu zMQgnoQ#x1t@`;8n^c`Z5AKAs`kC!DE9FoV=za@5%1>+9(p1ZR0?M(;OY;ZUdLB(~N zVOwr*HRI>KbTs5$*oX8(?7^2fLhs8_QLbCveq4stZZ$=-VTiymG2lu_;!nrQFm-Xm zo}B9moqGd@`Jxu$!tjUEy=p2+o!bZJ}dBar=tZIo;1{ztBiJRk3Ky zb;wPi^!sWctpsmZ>d#4I@f4e<)V<284;U-vv1d(uI~Tz+Rq=a4gdOdKzi9(@UU60L zuUz-(we^G;O#z~3uQn7!9r8fFNh~T);W_6#cZ2sWMLp6LdHBYFUcJ+Dzd%%$fKeq< zVi#;sABphtuFx;Uq`v_mj6#oyRk$JAy6B*I07or*B1cB7JX>k=tYtjOkcjQJzx;hS!k(uKC`h zgLea;z!Y}*{fMM&2L9%8&lWdXdNnR_yvu9CVY?HgT_IPbqzXfe!CQFeVPD zOM}IWA}XgC8)jYWd)^6wf6mS2=iaR_hp=d}GWP4jLqwKOuK6SQak31vqwdDHgvG-b zrBCfE#)gW8OZzWB&R_*#4XBA-Ll zpdLMoR-}To<~gmW2Ipw(0?g6qyh6+2hir`cin>6J)_N>k~17E1gG6j|2HWIbRlL@WR*si`#u=>2QiLO)Q7MfYIWIEg3@ z#*zuW%7A1kVQhWdN;}hJ{hM(<8&Uh>XCJYq7^=#R zYYRO$^}W;C9H2oTU|n^~o%lHNUOp}0q*1BL@sz=S(L;Q8KfGqc@pm7wexpA%|C{-a za-n)+5B-3-gNYsf6QUoVTu2=cFPL_(Za5R)&b>zHG}Pf9pAy zr_0=yTPUVog$^YU;V{G=m0}o~)v08?ONogqBBSOC$FZLeE_HjV!&gq9U%K?FSuyTG z1SfO0#1n+E8N};MumvYiIw`k8KC>ubL}x*0U$_>^b(Y*zBi^;(=L3z^zuRI>_ZC2a@`91`Y<)eH!g|!MnC2>e;f>OSkM%FH1>)Y<`vaz-JLMmoJBv=P@=1+J zZgZXHtW5C9pDup>nn_Ty$c7W`jWdE=*%%RULZ)|i{9d&)XJ36^bN}_efhn{)Hd)?2 zLN^9%G5G7Fl}@IGSK->P#D5cc9-*3_>mn*y?=(88kjChcRU6Rm8!A_o1JZ>?_pSjC z^t|ABl3|$tpMUY~=|ukHzPil_60jj4FFOt5a?c}CFNAo8LX(bsSMwJ?IukyP=}Xvp zqr|^tq%)b5?tDU@vk|LqO({e)F>}({!ru6mV=|M4_rTF1d@C{`Xu_$W+n$pEY2h`N_8vg_3cVEfWLT9QNQuiF(m?$fY1JPabnr z0_N^sWCLD)+$_UtzBdT%V$O2T?`s2 z@hP9OTpG_;(FxsH=@|&p-us1arYrULeS&ewCF8v^_5TOv+8TEfE* zzQLUi{87O(WL8+Wheey(yV{#-SC!W4cMd$ouLvx*c_e;Zm492-ef1!)JQ_EpU-sXN z2FV#u7jwz0C`j1aGBYSEN_k&_%Iz6>N{I=`73M7MUa?wcWLbVq-G7fv{STkyzWe#iXd zG1GQSQ6uYyb8Eayqepl^r=;nl>Nv7f&Fc7Mh204K!JT^9#+m5hVpFFRp98Bf*zqjc z`HXh#)m4rBbx)1_eGhl?pKk8nacPs($XHO0vdT<372*m_pQ*>)TxoONlqU^e2N~rm zx+=3l1{+8;p91V8@a2-DE1gq)u`LmxIw+LR-!+ikUSl+3&EiR=b}7C#eBFaKEJN6ux<70p%7oiBGqa>DL}%`FDncYoEm2E=x7a%rC8 z^}O%OHhDbEyEn<2RGfG>EqjbheGWGw&i>trAQW(ziMtUS6H0cLe%!U#&X-o zpusU<@aspHcJdgc>g7=cY%eX$@4U!bdpX9Nz92cG)Z<$u4iP|#0q1%3 z%-lfv^dil5Kd1r!NS-XM%;#qGIB!UKqjHY&Mp}l% z$L*)2Vv6Of-MU3z$q$~!!j1>Swnj+}u5r6L-I_;hh<~MVnR!=i{n46cRo_=;H4Dv{R?7(t zYol!#*XkZE^oVv*E!gPPdyyw{&R6p!srXUDa?sPVJ5G!pv4{?ou5MHHX?nL>A{&(?Cmgi^L1X~f6H<@kh1 zt;ppV96-E18_dbDag_#@`1Zqe6;-ZqXx_)6qbGBk1P(TaMV+|Hyvbh*Kir*;i=O}L zv2~qD*1)vkT&Xqq1cd-IJiJrZlwKf79U7j|>1f_Tc0&7%s!r41LYggo5;>u43WK*q zOZJ-JK8WtEAMJ`JhgQgCfmJEyy^m$}2UCtNN4XzQcbSNT{b=P(T9=PU)t}@2?iKOZ zn)$ZT95KIrSD(aLAZ{)n+4faE`TPqLZqf?56EUi0w*9KHa{S%lrT@(n zlEiM%`H>~brP2FTvD2ir^=A^t7N`@Fo~H}C5%k2+j&(_5ydw&A+nL-pG25c_P(%98(7gA%u=;@RBZGQPQ{S#g=CUKr) zwFVJ86T7y5??*KH^E>90b6*K3!})s~;!r|n8aYkeF&S-Ga=O;1O%-QpGarC^OX}vX~_#zdGv)#Ra zvh}j*P~p4h?G^9bH!sA8kjCC@z2nBHO?oaboDbH>d5|!wQ1!$; z4z1&Sormx&@KI!sMz^Z?ki>Vq`7fw_JbSwzBaJm$yhwQt7HDrjCcKc(NM`YURzQdF zxY?i)w{-L8t-)7!V*WS!Q|Eh8a`fx-Ua-KwxRH*l+Fac9NuCLTPX`ll=~Cq9nG6Wm~lm# zJZyzU{;G$6xa>x#n1VVw{#4Ir?QuHjJIDj(IT|a2)nU}nBsnbOWtBp%En4(KE($J` zOlmM%CoYr_FS|(357&~6mhNsj48rHGn|(Z7g2m0-m@(vHXhx7tuFo7An!>UGlZK>sU#~T*h;&HGRkK4mO>0 zj=!*!UiQyh>HkO=fnIoZr>^Nf=q$%gY?mvxhTtFE0V!`qBO4EdgFcp6JFuAGxX`M1 z?dq%iZq-^dX>TpXSRv)jJuFa)^J%sEfEY(d&V+}Lr!J_|o&G4+4e=Qp=k=Zy=Vi3j z$azp!oFVsr3;bGd1SN_hLQycA15Hy?hzJ+24@JM z^U(IVO4sqtk65&+z3A(0vw!0IZ050uHhEtnKGu)M-l(wX=4tK?+&G=-f;M(IfmC=w z=POedu0$P=eX&;@pFdJPKPIp)k)D#A{ak5waV^$STi=?i-KbQlxqeo_$P)Y_Vy3*r zGUqzC>GjP%3(ZsE5$R><(Zy%)_js@PSDtjxk0Czeg+8lWmwJiB(0uB4!g8j>^XVck z9&TwUfUXTf20S3;IthVDh?R!vlTA}7vrF<<_r39E|CGhV%xE6_mIzVfSgKu__ILJu{+XlV>>0PJkC6~{CO@QE=*jx~Lmal% zh{r!d&D7Uvb4j9T?OkQl%4gR)R+SJ=^sDy~2Sg z=-ToV^KV?mB>7CQBz16fDUpvmU3*TQ$?gLhK? zoerI898(GbIIr>#uU%?5>DZ_G6`;cR*_i0E+3(3 zG;DNNx=25l^EHb`li#HeAy5kc)Tsp~o--4;WO_t(*PuF>2n82E{qI!@?gO0;{7`Ea zZkZ?j%}HS7f0DW!p0%;KU=Dnj&ni{tymvjY|pZM)4RnWdxZ z7!-MlSU1AYA(HNxVMx7?sxL9u%{r%@t}b*0Xxmcq#MRVK?`FGkWS8H)Vm17xv!UW-*|@w)&8v^pVK(f&y{fnB)Q@&!)@A~~|-IwM}f$6(mV1lPBVA+CewjjHYM zRwq@hWtK@3d(X1ikoy;QJV*pJI0^X4(PM2OCh<=~>cSL_&1sX38vsDGuVtx*)Fh!`q+SV=6NziKjfOA_2qxzY)|7Peo@+7hul1A#W1Qf2I;K_;3vW zDvo26lSPm&k*-*s1IS4dKdf0=TIBf5p@|T$^s7U|w)lW>Fx<1y5$v5)6m`+t;%+yc z)wKxIS!M(Y{n^S9e)$aKfv~8�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�dk$zToqa1Vcrnm;3w}Jyz<>7w@nR^NV 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 EKRCAqL;wH) literal 0 HcmV?d00001 diff --git a/app/src/debug/java/de/mm20/launcher2/debug/Debug.kt b/app/src/debug/java/de/mm20/launcher2/debug/Debug.kt new file mode 100644 index 00000000..69fac8f7 --- /dev/null +++ b/app/src/debug/java/de/mm20/launcher2/debug/Debug.kt @@ -0,0 +1,22 @@ +package de.mm20.launcher2.debug + +import android.os.StrictMode +import android.util.Log +import de.mm20.launcher2.BuildConfig + +class Debug { + init { + Log.d("MM20", "MM20Launcher2 is running in debug mode.") + StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder() + .detectAll() + .penaltyLog() + .build()) + StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder() + .detectAll() + .penaltyLog() + .build()) + } + companion object { + const val DEBUG_MODE = true + } +} \ No newline at end of file diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..4ae7d123 --- /dev/null +++ b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..4ae7d123 --- /dev/null +++ b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher.png b/app/src/debug/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..160f9a503ee5eaeb2ef34ef8531686ddad1185e0 GIT binary patch literal 2880 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 literal 0 HcmV?d00001 diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..9c13d2c9d022cd316acd45792dec70108cf14ffc GIT binary patch literal 4823 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 ZSnGKA?TKl;;cQrOSy@~_QW2iF{|`)P2S@+_ literal 0 HcmV?d00001 diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..e95ac65876ea7dd35eec90c2c089d9c5b2287a0d GIT binary patch literal 4716 zcmV-y5|izTP)z+i}=W!&=Hzg&dAtFM3 z0kZF#4o`2sWD9(AXOH$7WAY=?hK`BIdavVZM$IEz;JBqIm?-)`;xDu7Z+Yj%b9qySMH>nX}J@w^(`vA zvSeTPGwk{?sqIYkg$|`oK1O!!qw%4OM2a&Fet9Cc&Grk{qHX7`yO@)gx1P5U$2kja z`OZQM%X?79m;y@6pG3V!O>qLBGsYCso9sH;$^E#Fia~5Gsg+*ArtGh5@{3u7S{7H`91yig06i%z|TQrTo z_9>XkzwbXjUszP$owwh5&Rh*0984c{Z@*|+TXNFcRbh#5y^AR2ozq+5Q+kbvJGkc5 zZBp2)08&7X963w&b)!C`idm#gCPoJJEt*d0#WS41XME4Evvy1NcH{kIf}vAM)|%6n zgS~U&Q{36~Fa;6AqbAIh7#?m)+}bDCMk|k6rD##zb%`9LP3E{!)QdUaTS?C7ne^0m~3JWb|kpObi@+L`^v9Dny^~tl5-Wb0ZHY#ndbCCy%F#SB1kyBwXCs5ijn|Kklg`yc+sw8N-rw%I3d>-?j~;NU09y%F_Jh*$E*UWF7@aA#pW+sBBTz&l7QPtc~@B(8rUy9wSRiPemea~xSz zkrW$uF4Smjsr2z~g?DC=vj=H~3EA76gD}TZo_QbB$`h37hH)?+RyS+`yrtX9V^%(K z>y6gm=){`w?g(7r-kRACe7ZJ(nqA07lbGSno=Wyu>}(hd8hRV))yhg+*cqM|w~ zeXgBIjl+!zTTniD+adFP&c51}4|N62>c;EBe^4B`3{f>ucPhL#ox5AUWK^ zjn*D0JW*R|luNjq@}aKaE|O|0OSWCGRIfa0qU6k&bfxb#37@psHvIsqV`a6hgjhZ# zOB3>)l@E0VO(fM&mdL&>DUba^SE~tI3bU?*%!&Lwhbz)XkXSx+zo+t{uAq)2XIZkx zUAmd8SrpNxznW;yrl=lD4%~_yV(%R>{|bjt?xF{G0y|`E0_$p@bFXl>PvT|CE^Q?7 zgS-ketv(*TLCJwT;o-GtpS2b1ctz|io&YK%2esAxEFbC$?)FLjS-RooDkBL#B8t}k z$@s5H?{)|Q2X3F1$joj0+FXd}*-~M0C5M*}-T0lTZ)xQT?$V8yC8~W=**TNrlW$G* z)`6%A-~f-5T-jk#R>pJBhc(=(PA_?%Q8J>INb^RYrGv)L` zr#TxfWK3qe&GhFUX>w$!NrGSSZ_Wk_m40UtO3#{U`DdM9XH@veoN$pM$amw{#tl4j zDlEc+Z5A^1uu%Ib3&m$-KXPg13CeVXU+~Y!{9I6mpfJW z*wDeDHznY?5{1Qx1gQrA@@6iebXE?C?!f*YBj zVXx@*(OqpJqf>@|TaNa{aKqkP2l`E=~$DXOfzN;hs?_lWD)tLTR-KhWCJ z?fh=b(HM1tURf`j`W z9ak9?9^Oeqkil$-WR0rA&56Xwsg@t@ApG1BH2>WX>1t&qIUGW*@Rz@RL;0^PbUL`> zv(HHo?3#A&fOcpv6AV4jr*7v7uoL#Gec%Me;%P^M)8{awt*T{6xQP>ll0&XL0>e9X zV&qgAEM3UJ8XGu4i5>}BvF;Oghi+($@q`PP{y}5sE#rL$O`Jnt96zmT;|*w!e&|1T z-g2%_-NqAOC+t=GaGRh+r_XF^sqzmFH-i(MRG;#H+Jr_L45p+?5X6{t@K@)lPQ$X9$XeB{>6$=auYsVP89AWMHL=(j3-|zkX+v+8P zI5%?5(ac~;`i2u^N__GNl6+>L&oV9o)C2*3EP{@LAibrF@T3tW`^+|uCm9#b6};%= zW5%DcRczA}O%R);LpqzbfeV+TSLv|qsrFIkxlb}dcx`9sya-&l9=(ceHEe5$$^=1t zsjq>cB_C{Ho3!g1V?3ea;@_!g?o!@&;ES_q|Ch%#ZM*^P(GUHh2l~`)JOOsXUbPQS zg4jk;oS+k^RdDzV;6kT5o92%^{QdpI43@-8@>1`Opn^F|=C%*c#NBsT|f22*j57N--uJuqqRzxmew}lR${)48E62D{D z-(v01UfCb|Htjjc^{e|R0sCO5+IA&DVWvbr{{%k=}+RT?yDu191|oa?cg2L`gDQ{6`iTvzvz0lm<#?xO_kQ}>ghNHOkfD}9Goz=5Y9 zS0SVtHGSrpD1$lSdnqR!H4*eE1Ac|Z#EF3r6L-bW-yc7$0y(Pmk|ZW3<|+0P5E^aS zz&A}S^t=&-JNUws*C==90*}alr?6o~DlyxIVyb6wIW-g)h>`ynM>d z%kzlrQKPBf=;?0yJ0nQITF4VV5|6JDiN>o(G%BS$rTlsNR|5#A2%mHkKM45!Tl zgX!p?rgUPkpIaOs-3Pe#%VjSq?ahgI`AC?<{qS_Kb*?{4QG7wN$NMvIke zalZvYp^Ra71dV-dDV_Q5JGR2y(ySV4++_QZ{a+lW=O@o~lDqta_4LzEl{FrD;5yo& zy*!saUo?R}8xlk}M(Rk&^^RLPI{G&IX&RgRg2o5t7tfHOjxk4=&+R1NYY>saW^6x@ z%@53&0x+#szGThER8@6N2z|JK2|@LA_!a@LAo-Fsi7p4&LaE&>?QlrO=Lf^frQy+ z5(=JnOAvfv1d*k8R%KIv|5W&@!|wS`L@zShHZZiqd^{duN`-n!5Yp-6iHw|mB)r|9 zgtz*WeeW`||86Y_lY%@EB;dgz+Z-;2k0Bu;KU4c(8}ZQv27+0Gu}8*!ma{x8>nA~z z#Hd;JED0YLlJM)%WdHJGMo>EqLEHyC@)~aPu4N-XKXcC!;&rCkZQulJG`%7EK#TD1VcL32hVv zr3tV-WyH9jf;vQJ!KWb6w|kd>4jpj97SbsxwXpZ7$$WxavGx<;;SrQa=as7%FwH>1 zj`<|)e}{yHJ;lh9u2tpu3M=x{C@pt_qhnmRvCNnDl23P?T59p}*X#Y+t3LYYy(i(X zk>{^mOV_Hd*=yS9$@uHp3d((1d@hCp^3#KdG;O@$(*uW`g=cQj82UOl(3K!LnhKtB zezpTSI(t=KLz*@$-4WTP=S%RRMQA8e>%NgAi?5{E5Sf@<+<(kVS5JQRXS#Xomi^YP zn|w#@3D>Hs=)-dZZzQtl`z-RW4frJU4QwfR$;wse)|JO?P4vy|Tcs7%vG30yCjtv&4O$4Hu1 zJc3ph_NJ1;R2RHonC2)cN^`6%NTmtS_c|LA5jhw(OSab;IkNalcOk4%qecl`297v@ zpSAJHTUF_Hq zDWXJ2K-RrP79!;~DSW$uh{0GGGu#?q1RG$BWYeA1S9K^nn*99y@V~Jb7)+tjaXAV7 zhLoiYAM;PtJtPyC7KJopsSp`1u zVs!UDpFt;dV+@RiF)=o5aIvVK3Nw-wfnr3pXTUGEjEx&NP7VlcQydr)wybqn#P+t~ zosS1aMExzeWAvrq$k=~{M8;kP@IJU>%q3hy8?+5nLo8c@t=KBSX}=aN`Zj6aG6TSSe8;ZEqYc_heb6@qy0}iJ zTN>lB5IFJ_ju)CYZywOJY17v1b6XwGf<@vm$%je6dwhp$ZP6Beq@xKub$Dz@@h1Fn uw@zGXaO8k!z#$!g_xKLi+&l(;aPt4+#wAKn$1qO-0000f)FD(wy^s=R3dO zx#yhkw(ZvKg+0qQWlDFe)!L)ISarnO;|O>I`>m_I;Scit5G!#`RPTk1`r(<>&>Pia5KVZ1QbE2jrIH*)aB&7aYL z!7Te~q+!&@5&HOl!*%fvzhEo`oFh4xb=02KId)uQL~K6JInrkrI1=;bInqmPj?F)F zGJoywI!8(#OUKhZ%G@3x2jqgBq#XSjbw(KCKOum&pwe3)T*m<+58KWeVCOZxh?=8j z$S5m-q=Na7Tv$OrGsWNzd@7fi?995!M^h?J|$=!qvbbc8-irH^lN4>I%0mB5u!={ZQu z#+7efO~i)7@sKq?AChf$czf0&NG)D00^8W1Ri01dlusflhoyOyPeR}j&X}EpB4b8M zxynrF9HP;viGW650tE6&WZTr9k#8GH-k#@^yi&x(`Q9SxNIu+-N)d7akug%PGE)ff zdIAD@+_RsbR<69o@5(D7J_#QpFfZnpijWEjj)>4oxyr090Rs7Cl6>naIr*9+7MM^T z7oNJS{OQ{8!n$1^cJmURY)1|mR31Tb+;`1r8M zNR#1#d)z($C`iwQ^k8GL!%CkxLm#++#2jN24#L4gBD^Zwd)u@Z;uR|?c9g^4dieA= z#>rR%5WuBt)U~JeP2ylm-hYLC(ooh3LBLm+80YNw0uZotyGFg^icS=;?ob?A`P?oc zFQD~Fu zH$yn7#8Lr;%h$l2<~zXiE-y4V9Mq3!bUMBK2<#VPFkb0LV1D#V1*m8xjuY#E&oh8O zci<;2WTX<)cOf4Fnwy)wCPmzo2x#&n0Q-d)j92;*nBSKG`Yse`AroKbo;3l1z6QmoTpO+2*@B4WR@)xfuzz( zTK#DlQ|co|k^|$tVm!nJbcz5hG+2NtN#PMtK?|98k!mYu>da!O$Q}=s+36w{lJ<04 z5%E`cjsT%Z-M!lk_wL;lf$d|z){_GDE=;z&i{dGpS>a5$k<%Mox!u6ESOczkA>dl7rS03FM^g`tH3okCCkR>&tOr+Z z5xDlRBLW6>t^h1Tjs6N*Lxa0WRlQHogVK+{7dZ46%qoKGx&7Q{pyYLMEz^_F0krMN z=|%k=6+nO7RSLiY^X2Y?aFG<9KdxL8aq;q1Ql$2~3mGkT2WX!%2K&*UQM?dpZ3%E8 zKLjr24-#>aw71(vLuPS>PXPh_xI0)KtMT^)5NRb#VB);xFsXcnh%C~^{x%w2~HvIdhSJ+774$m*c5N)>7UkGsZx*9ky&L7TjFjS-4 zMY#3$aP({F?B26)-6yk>mx#z@7ws2wr>2Kn}%)B< z3pKSBa)YGQH@sV9!jqHnfOW=0#u<&r#rJJ`sb5x*8GO9eO1qJ~~3Lam@v;-{vPWDiYL_p0>5I ze)Cx~=R(1OzNEi-2mff7nDA^6xOVvedL8?eEnf~gMX1kV>u>FvW;R1;8n>Ws?1se= z%il)EE?l^jQ|ypc^7&U>oVvCf1j724A531jU@5B@=jqmlX?zhWI^nM}G8U7tsAM@5@B48Aqe0L=@#$SMHk7_(!qW`<=nMZ=0vijpg>` zUvqE!v+whx4?n&2=-=swbz&9y=hoJ5sCqxi|JKve#IX7MwzHi!of(?8E^OwxxNFuC zn|JBlxFn*?G&%BZ+_wLs%MVnS_*EqoZ8Vftyy5fxk+bNVr*GEDJc`cV5HmS4H7#I6 z(vgHJak*a!vjrP-IoL$;#XZ&pK}hfu!((%UYG1(|(MD1Y$3rseugc?LQPpg*_m#q7hu;w}sHTn*B22xyn+F SC3?V2$KdJe=d#Wzp$Pz&*_tc> literal 0 HcmV?d00001 diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..6ba5318029f74ca07845451dab674f8e14bb1835 GIT binary patch literal 2632 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 literal 0 HcmV?d00001 diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d76b67553bf9b8bc6686a3ab0ab93f0126e8ec95 GIT binary patch literal 3934 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 literal 0 HcmV?d00001 diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher_background.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher_background.png new file mode 100644 index 0000000000000000000000000000000000000000..7a56ec15f7a6150f286e29d64d4e84f3e3eca179 GIT binary patch literal 2456 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 WF_xT3USsA9V}>9z-D+*;7ykoXrX?W& literal 0 HcmV?d00001 diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..a110392633ba5fc71588e536b3cabef04c4f8ac2 GIT binary patch literal 6681 zcmV+!8s_DRP)A7L()lbPT_d2BhTOtYI^>oiLj>Tx^ju1ZJW4$cqESHLUg~ly08*8oYZc^`^gyTb z^9B(GtrcWF{lC`6o(SLZoweDDce8@BCk>D2I$}b6*ZfJz9PB-w;WMOB=95HTMnex_ zg7C3mZP8J~BiTW;Y`-Sa2^ z(!F5HuN>?>p5=_sKVAG*)&ZnN9^{1%=z>mp89c19vQY>5^FM@i>Edem_S2SNM%aDZ zPFtRO?TquEEUo;1^ncQdKqq&(`DJBb+K)T3Yu^>}cq0QMfw@L3K^v*Mjh zo(FjWQP)4A8)cv@dD*B7>Lh=5a|%e85x@P1_!h67u?%A5Y})#*`J6;J$0-q~&GuEh zGi^B&b8U=ZTaWz7_MQ(;wckB*nxogK=^S{EXZQ?h`m(euSsvuA)FbE;Ae6NUWup%6 z>UZ;q!my3|3{i}b1&o9vE~JArhSF0e$JSG(ie)>psNY1i{6gN!8PRn2sOfa~=pyPh z`e6>d$1{8;^n^mnVR?`jI=C)TuhJQ)1M0GIUwV|g`qpRhk$3H^KGx_J?^xC|!!K15 z&j~VLl?ayzDm`H)*78O2g|Op9{+Jm~aCO2Y?gZ8oxUQ<6AUFhNR+c9?I#Yd-AmY_F z)ERY$4X}kUet1Nyy zi|(0(m@jYn;!LL+645RR6!vb^;KpAkHij$1UfdH`WZ7;v4#jzoTq9pEk;v9b`3l=X0j~ZQ6Qnw%UU=i-KmR3LBp#*B|7 z@=N}1ajCdFtolbs)v$A|$(Nws6WC&n4a}={cXX+rJFtZ$RXV|t_OV`X_Lp;V{Y9Dq zAm$r0-jT>(_nt-5zy>VaaXVk7DhJ%_MZN^LPT<=Xx6@V)_2=t^_svyKNJ-nJlfuvq zm>pHlff@NPRkeH;cC=I;+Y7Aa_nYY2@@q5TYK5nP8Fj+eht_bk;vI7Zn;qFA4QUjS zVtqwdwMe^;>jY_$Wk{bR4Y-K;$_lyzwihr1xn2XVR;c#`VV!_6K}$~n_?W;3W)_Ml zJSla7oTFJ?fEUoi=PumMOBJ8+>r*M?5aoa*d@mm%<7HbhHmNOwS#!xwD z*Fq56IP}J}%~+bR{)aa5(TcFbMc4kl!;U+s_k=>;U{H50Jpov~$85{YNu}^)(`!0m z9FTacom9^EQ_}Afwihs%R%3gia73L6JMN(Fgh?!9j^H+E=?U1jSi9H4LbHX!?8WEh zBZmI`3JF)JKJVwZ7gYE2Zw~S$xN`#Ews3Q(gLMJ;d-?y2w0vjmaBwPe>t1kSRlF$M zXj8fA6|{b}19fS8K{&}$lJEWYLVbg$aDKmYfJdQZT%MJ_++SMmhz&9mg)82c1Wqt z{aApz^X$dvr7w+x)xwXkz0gTzo`3U`FG1Z2EF2XIdq?t~AP+-JUysWNw`^>6x+d== z;+G>Dy>i63L9O+#-knO#FI6q2P|8ZCI$=)bTtu<-_%;ZvT}bEsPzPJWM*6~d4utHqG}v?{tLt9 zsT=fWa4d#;XOSH6%x5VBr1*~o*z%nq-%MeNCVnyLkKXc?Q1%3H1g^|o%(VP1fkR;& zFwnfsF}dJaVYN7BXz)X>*j0W;mAa?>d0$1P+4Hq0r ztKwyE$1VoPF5`e~etPRGhYioYirLN%i`~-ALJ`TS9N|f+6lLo8hZ=CTLOw8qBXA}0 z1`dmNn(dp8v4MDZ{9hTzja}9QvhUC~+4PNR*U~pFl-fPj9+84`P7%qX0~&~T3vdID zIQcT}ER4&=ZzdBo;-)f&snBPC(C^vV8D=ztcTEDh4Ww7HZ6^7fzP+-gxq z*$4Z0nboZ)Y}@?$o7`5V{SvqX7+0J#a0d=Kmuz;FV7?F*9hTzifKp)w)FC1^G18cJ z2_re1&pIMmcY*@JA)XyDeqITEbF7>k4ttGo@#00Az4}j1Iv;v`6`j9uo)e%9qnjj$^he?UzvL09<1Sci@n7nZZJn`EuLfh*WSbaxRE?SkNda zGTz8C{eP4hnbIAIXOa`VIiRR`JzeDg;qVc$w}wjwNKN(|NU>5v|53@ z&>`1}GEkN>tyaLsqUGyd?3YmG0D*kYA-H6mh9{@~+$L1t5nMYT&I{Vi}kN zGAYWK;lQg_2h3Qpfqtk(%K|BDUfklObHTbl)33k%>XKF~kQX}SI#EV#8~__(tI~c6 zE)I}!8If%LIUq!z39g-QONb6=A0Cs;GF^g_1_!94rVhY!HYL5>wnJE&TL-iaiHc)A z;9Rt&Q-cHCpvDehoU+-`1^=KBlUoM_1cZdL3@4B{(wu2;aDXdn;s9_7PTBh4RLhou zG28))t14KCHPW?g8Nf1q(cl30sEq@{k}L;x&6~Fu`Ihwnop27!(2`A`b~iY{J!h!3mtpkhqjBjAQ(NBEI!6cYt(rt66xm zWgZ+5ZR%LTGFLP>z!}wb07C_P#(4~mPQ-=XW+KnC#xLHi}yjBv<(lpvhH$G$dsYk z0h1T6rSHyPaH+6XTw$av{?m(2Iv+0HK)=*tC5*g9#ZNlvL>VYcnN}-c<3r3=rTr3I z98k3exYW8shdaTsY7fu}F$o_8vP_>=*8xMPEuj5heeF_VtvGw`JdK{~yvH(l>O4C6 z?OB(!T7kTSr_6KGi84@@GObqZ|LPF8RcXHjcMb?kFn`Io4G}q3zoHlu6LTATY0VZ4 zPgmCgz_|IV=*z=LS=axKY&Ps5dIwtL@iS*>?z-nxx1%3k@h3WX^f+C)a>XUDJ0LCc z%vj;Pxd)((Ijk&{iL#Y>-2pbiws8xbH}{lKI)II%4z>`Z;Fu(whP4(sRu}O@zmVwU zd?@0xAY(>_RtLbi;WHM|thLY5vS(hQWzTxYy!Fr1$T`KTM{Py1YxMzZMWSP9bvE@;!2DecC8K=G_h*(ga`D!)*w!;fG6ba4-@Fg(f5~lVTa08DT}|QlK289$QS?-rT_t zvCGSk(edNu9#LLiPG5h0h<5GS!yK`avQ-AShZdC3_MLCj!9xc%>O98NA`kK^^<|GK zqG>~W(7V~q=}`YhbZnrG%Dv#&Kp*<@-dku}c552-z!0X#{nZ?B#KJcwE)KN*c9F5` zz@^BkMiIhL%a(20WJDO!&%p^1NfthzRTd~=(BwI^_063%K9t}0;fFM0#^X*p2R<~L z%HIEg{`IfR)z*nT$O|2Eog)eg=!1Lx=;z@&x;jEfwp?vM+Mn*%QR#qC8Z>gEi~SO~ z0~lA}EHuG%!O!3SE^w*S7=9%22@DMMW3Ry>u_;gS>lI!cFmb_3I)3Vu%fdw~K-l~z zo^jGSb;(-#@#2pzX|)1*r!HCJq;viQJ?Zi=9ochhhVuKG(uDl`TXlk2NO?`!lPbS02hga}5sV zot?TWw=-QC;YzIi!Q03_KA`IFiEYVV(AXv2`Qc5ds9=zb{Sv?txC)Io{@~}|wktT) zd71mW6CYjMw%9riZ6A}g3b(zYJviWc?1o;K%{X@sSlWebAG}ERSq8GtwUTZBHnPu% zcS(1FIbend2e2OC2v0Oq+lZKr;7;VQnytS|Fct)`Q9LC)-guaYpmC4bl!XGFd5y?E+eEhWC&_m76SAH7f^1*ENA@XU!e<&C&}YmHJ3>%!bkfnrjT>X(g~OL> z#eSvuv=uxU5)>9S5nHNQinHmROHZh`4j9vtYwt0oWc%VBviEx4c3! zcFA4~ZbXh$PrPa?=uX&9#l4n<^ql**^kgxQRlw1E%#4b9>wx@Q$UZ%iZ0H4t_L6Dh0c-v5lYM0$vQG|SJ;2A+0nBFDj-Sx7^c=7aoQT{YTwk-$qm)MC$d02mL)L)Y zUHFP72B3#aHdNHw0cwZxzt+M5zgA-%fH+^$i|pH%kbT>H7LLq|fTH|C6|foK zicHHMTE@6Af)kM&_2b5CD7Q%C0fRYfVBUvtLgC`I_Osugt5A}l6<4nOMhiDQ??l%0 zW$SBwC_io4IwzeA3;NP;BdS_-b#dl_<`t7hjvYIioukgJ-D<-{$zg0UbOs4TT#gdDq6A8JxVsx*m)}{XToDHL^+W*{!tn=w}iav z<2pC+w8*2>BS+5IA{sk-Bo&Pw&M?d~#tgHK96Oe}=N3|G&;A&2@h=CWMBA}lbHmA( zggb#vz0d{G6AY$qeOIzM@l`fU;DfW;wncpbpJs}y{?5b3T}`>G7p9s9*ecrmyOJ*n z4@WIpv}oh&>kB6&$5=B*-#)nDIA#fat)N&n*9V}^s5@-vG%)`}v?&9NtYp{-TVbU zp`*`LZefg@S;1S|?>I(q(uEd}RV@!UcMKhM&e*xnM${Q~hYhd=Hi@>?3z0%ni>9qx zW5I^rU&0dfFWfP7E0LsL_7(*Qlo%^f{=n&c)bweFdS8BCHgl<#U9AagiM42d?)dAmJM~jc=brZr3V*L$W+1?02{6tq~#xWy(ef2saG$k_< zOuoK;UHyYPOb-f;e5HLv>}SEzi9dwICObmo4ES*z3QI6icp_l-0uxUgmSCjNcmsvR zrZ||c3(yJOC;y!-q6H;G7&z_x80UDKxc(Hp5(q<|QP^cJmK_w#Sp zVMEUF^QkIkBr~T z@@zp~reiX6L8nMM{#yykiW1AloE~+#>4{g0G)D06BrpWBnMb%dbHbOIvA+(ojEp!l zHvJGT*h1*8=FMC61gS0j+uX~+-s2hj>`tT+(_*^-F&#RfD+anz2Feo4MA`K|#@AM0 zJX#)NE(k#wXpJv4GqS>bg;0ea0>>a^q4k1L0GD1R;BS0}G)NnTywD+jIU3u=(2X)s z7RvlX6Yoys4#8dvPT@ctem)jA4chX75~MlQhUbgq00000NkvXXu0mjfh*SmG literal 0 HcmV?d00001 diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..a1098cb27645c663206dea6cd4bf2dd7d8173ee1 GIT binary patch literal 6485 zcmaKRbyQSe)b_wgcStjU#DIu25(A8MNC*;wh;%ndHw+<-Qo_(B-Hp;c5<^RaLwDzk zzx928y=%RH+ZofPE(=AOn1XRah@sF!E-76<5D`1WxvlgW<>*w^pf_pwWB2iiL@gPgK4a-Uc#MvA{P z;Z>3TW6etTx&^*a&jJ&uZ$aDoCs^*fPeE>8L@}>%HId~o&%4o|ylq*ACSDTAX_VMXD=UfU3+R#hG7#WQ1o0YPHFTBs5>~P|x=yE7 zd7J19Fr#|DsWyDo9rO#C>_zcZ^Nft}hq!5>oxJq)_Cm0wsTKx7hHxSkk3gxd(1Nx^ zAM-84&%Pz6KR*nD^F)KjD9o2m&qDuvoEh1(m=4_GEhm`fFz^lup*pK1qB4##xQ(<2 zv}_)+F=r6pEXe-FcJF>Sx|CtW{xkEt$WjIz>uHPkQ_7~ovWAvd{#wh6pIKz+UpiJS z3XYC=QCyUfnKy>1dX0{Ja^koA)1U=GzBu>41^M?mF&An#>QtT7gU&T90F!)TC9i}V z7b-Iiv06YMlAWR(KMr6~^q6G#^@NiF-4jG$yxej+*l8+GGxP37Hn0?UxjP9L=xuZ<+%#<39$9(5?147l+tc&(J()kJUrdj ze=`BE#3uS4R$9iO!2GCp=NYn#ZL0NSRT&grW|9{7>a3e0hZH=g zypYV{;;QSZY7jx9Y1Ignk8%4won|sD6O=qDQRo}#+M(!uqQUZ`50BpB6(;kT5E)d zpa2ppY1__1o?d2q{uDYEx;2y3@+$BegO3bF4m{^S^ztEq8A-?+&( zS)ODLHDB~21_WT`3TWg2c(Dy#?S{?lQD6wpJ{iaZok;(g@IB+PV0w084PRC7nTmma zf5odbVzW(N zs!%vj;meN@!BYw(S-t}WfKK9YR*7;kJ>pcf=6V}z9Ag^VfLggZchw!M-%;dHzvYf@ zXBrptz1Op29pNa?pmd%uS(loh!bi!rsBO4EW0007S0Hi(s)T^9D#`o7#G3l5s$#r< z;<+)Q#o~0S@qm6Jh)*$sZTLIan-pJG9O=}PCkfc`5O7+@@+4z5U%D09&gP3FeRQN5 zkMZ0!;25ZO;3umGn;q0xAcCx%Lvm!oThC-5vyJmKGP-s-STU~Eyo&%gY+6Ri!)3BQ z{$ayGLIRd29A6=^*db3bFyi6hYIKr>d|h8T(r4TNH%oB%y4hzXMLzia|4P!H7JE&?PsngNiOcg2mNKPzR(b5P3ESn2$+2(^B(bW4WhA4m#wWe!> z2Eq9@_;T8b(Gf)I#D}`??XHwbd%Zg8N?F45BHTQvZA{!Kl;G@DXvaXJ-#isHDX5iK za%A2!$E+mJ9J;8>hn#ywyG>12(bus+aHC$6B5l6mYwIDDNf8cyZ1yi=EBB-6_df7& zi_&SVSLv*5kJ=d9|DnI*2~i{UQY5XX@W97_u@6VpNu*;mf64TCge>okUpG2#;HCyk z1>@-VrGw)hlleQM)S@&xqV6ZFALe-mD?W4v9|Z)lw3NP_jP}<5m}z8hCLYU<^Q;&= z&Ob#F#(xhP^I96{b+ubFMhE0g?Bc;4LDF^zo`!v+p=6nG9|8*>RZ}D5=*6MwwJHGi z?Z3BahL z*|W2BNvk1^<<04T$$e%gTyyJD>Ia-^J$OOjeKZ>;aSzBwb$lUwqwH3qD3y1juM86z zCuK@N$rMD@g~AnvbqiNQ33sKJ#OF!m18}cj9l_hrijyps!dUOS*R=Ri*B?g=Q5DR~4nyexJ+_ z^2qCv%4n#W4(f*SRAEBrV`sOT?nDqd^Q(YSI};81sB5B0bkoz!<&!C;mEh_V}oT0bskjyfF72Ncl8VlNcH1U%KU0!-=x z`Yqt{9b{i0gIg)R>dDMGko5!>xxU8-{P5~Phdx)N;#DM8B(qsN(tV)b(FPJXPkeJY zI}t^_J=E3YAzqo}d-ey{fhSP7$jOjKhr0aK?J7-XL*> zwD`p(P-~kM1jaqaAh{*@+4Uguf^ew5K7`!}t+TRch1#~ym!bguGn4csWm3_kwkwO& z6UnZnL$TXf;dR1lm+&asN*o+wHrt^FAP5W2Z#2$0_`FH$y8x$04jaj{OIp9M2NJAq zpi@T}t&@^)z$avN8R4LjT!A?yUoaEq+oa%XOu`L=k|1z1pY*>l4}YE;y$81L`MaN$KmhrG8VXy_0K+lbRZTT>j!w`*T+ zmlQp#&j#<$RRFj_L8t0Gk-8L3;0P=rY+w2ua`3x`COMkztnnD z!HdKh82cmnC}~OnUr%TsTUbxj-R=bUZ;?C!Lcm=U+hn}Ei&3JgKzlQZzHBY{{z~%! zG(H>pr?+|X7hpv1`bpbG2iED?G}cykMCRsj>I4%WieA_;VEY_7@ik0mOo4k3Izd&0 zJ=_s&7&=LTkdJ$tIfEH;ZiKZPXs}X?@$;ADVd6~8zZk+E#2M^ed=9fMp!ug9j-Lm@ zfzaaFVnTBnvZ&>y8!KDAHSd^9Rf3GuWdP0DGU?pK;*IMzQqfuT`{t@_vf0XQrr&k^ z;!Jzuy|7Efbp2=KUUni_O(u^E8GJ40X@$4Jl+!TJ}}dfz*yhB4iMfG*e4xJ z6dMn!A)nwM%dhfC&sFkI&vkhYWUt`%F9O|k@?FNr?~>RoexkyK@+XxSnk>;VCl7Im zAR?D{VKB_JM#p^Q)K^xUtgz!yLjnw@aC-51{3DFIBJBJ^%E@%FQKOM(VK-;J!JreT{t1MNXHg$uZ!^L((#F8 zhv6fQR~QQZnXfxrKcF389k+f`LPJX7;KhMz8iCzQO%{IAspv(aq%@4J*)QOLl{@G- z=RP@<>+2z)=Z?S(m+>WrdZjGdx)yHMr!VOJCFIZ!3XC`%j({L8T*e9w0ERv}^kY5% zxBeCrphv|Tpk6VPgs$-7ExP4^A-vG?58n5B*I?vS`lOmX1|yQQKJ+}-i5vf(be@lM z3-H|A<8i;_rsS+Ge7${0Lc{OSgq*s>;M|%&@Q;fjk}o1Ui?qwW3zm;jdSv(#=l>0( z5wRoL^La3_wzmFCXD%BZ{g9(l*Rok|eRXAdyMJbI^K436QAfNr@hNnK&kHqi=X(7|1Vj#jJpHk}oM?ei-&-WwYM+f&w4>@=`s{nj zks<1xl*v3r7Uc*+ERAemouv2W3k}=Zl|KZ`o3)j(iTa+?T@c-})H6NjKnvQ>XbSo3 z-~oqQWX?^0ZX>G}YHvz3FY|-#;~HD^E=*8P{Bip|?()5?F7(7vVw+@#0>j&!zvWZr zV*v`1{9EB4vww6vQ~HXP3#t?!kFLP+f+OH-XTrY*s~}YOTHTD)6-Kt z?y)I)hqng4;%)Rn+vmI4b9-W&1JT^0A)d;2_OD=rlrIVk;r{XUS!LqRzV{((P9TK5 zxbcE{bg3SctT#3vEN`y9cUWS1L7%L-Ku;e`5wpa1tK{V+g1`fMw-Z?Oz2V*8Cj?NnyD44}%ffiWRbK{k&JQ&9xM<}xvQTi!K~I``mo zTveHs9y&wuq2YkO9gD6-DIP*rQ`yn4aCmpvb#Z!~vyX&son=WpDqAqyn9ujVP}%cPmc104k-q(b z^atGWC-OhV)o;sPS7Du+FV+i9mqs9_%>NC@wZ%UWlp*dMn{~3=jK^WluOC(t4e+bc z|2`x)k;=>W1f*r4QZA3%ul2e3zC~zVFI~BOYkHt>TU4^0fGa~-%6h1D9mozN`r30E zA%i*k-X9?D%ODcV@NBvuf<|E{Eb*i;ciqcPw27@QK6V;SuK-|OoQGlgnbKKk7i^(P zCh|Lj#lnPH#9vsUJk9k=7bDIm8w02Qm*XGN@&MLMr^5P_HsYhlf$KglAQ8!VMTDd!1I_^C_+LO(JZ+BoqsLOl;?W#0nu32c!%m(dOX6|0CZ zF_LV$_oi(UisD(Ai_3qch1Gw7{F}GYqckLI*+lifzpa?jcI$Z}%C8N3bRku-jzz~0 zRc2dYt~1jfM|)Rg%9R13pA}mVi}jT@K@AaoQ~6LE+5T`Ozjf(&dUv$;@0_FR+r-vr zBPT0%=UBwiUXk&!X)#w#0wE(r|HH$WKkUwz6sG=r$$j^zxr6v;N!ddHJy|L$F#RQX za{um^x4zcNW`1=ClI2BD8SWWUZ7^R92$@QA1-v%VbvRn_zYF%eI@94$$lkONO7_~h zi`}{pOH#6CMvHwS2Z1E-~@KABI^N9PPs4xvIG~UL~5Wh_!Xg!r% zdpMwSI;ii>^{<597hy4c+oo`H&?1<87n7UJX`j9K)OV{aay6?inEpYjXQb9+5a_yu z6wz2%{8|g(kd?z_%&*J?Xy7X25UD34L7~F{i%$WOpk*aG`_w9cKnR%UJC=^Eev#g3SiU!I#%Mekf?(R- z&?q2GcHc&ZQah2$@dUq2Lt7=L25dYJSY;xDUcVAz2Fcl;3LD7mHlJ}^mfvaVoI&5% z>7j_voN+Ye3wFlwA|-=Jwqi(=u^3yZv)XUboynoRl#gHYHh?obOzfjEmtT=X>)PrZu-+04y@=3|k^-uZ$?l97Dad z2hG^e0S;}T>hvhp@Y?X1FE?m;(LG*3U=(hL#G|qD-uR}IGru!cY@;Wb6iL;|XOzN= z#YbRr$%3D$A?^MSp%WRu?63&j>Y~hqnoybWN|nz12`g_ap8wFT9?Qx$%KKOo$~bN5 z#R@7j+w2wgs+zs)4Q`aIgz5|CZ(35uC{vH@&v6_rgfSayuoe)m-^FaQ$`g$CC0M1u zJ!83?IOMv;|F_+c{L&?h8An0sqvLbnv;8%p9j(fQcemH1xJQ5uko~DtCfPls%=hNa zIe*xhjEkn%hVQ)i{7P!2Rac4x<+zUNW?9xgv6wc8)cDyRm)Cu z-V2%G(;0)pK0dY+hTKD=;evSY+%kg}iCv42eKGXPqJ?>qzM67Vk`CLc-s$o8HcFZ3 zl!^TLYV9>P2axiYZO-~XEMgM0aM}fY@cj!B*Rwpu;I(f+Nue9hJC8s13B5@%FnrGVK)W%^{y{#9#k<>XQ zDWd#F#u*6Yca9;<+;6MkZa|_L7h5=iafogYJt&uoct!5dhR}%co8j^XE0bN7W@wwn zuIVK`FZ2a*Vu_tz6mdyNKp#6#Hvor>FRDZ#x=YuzjOoqPS7UJ3K{(nYFh(IvVH{v= z6BW!z1G7Un|8gi?zB6d8gbI_hN9RXTPUQ3dWk|KJ{5O}#iDzFTUp@XV0Vv9<%9Kle G3i==UZ-0RR literal 0 HcmV?d00001 diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher_background.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher_background.png new file mode 100644 index 0000000000000000000000000000000000000000..2fe8f6c3be8a7333356f89bf1f90700ddd77f190 GIT binary patch literal 4442 zcmeHLYdDnq7T0btG%>?$#{H#eDq>RO(qwwKOP3L*I?|11hM8<~4Wq&XjDS3UZi9gYlIq;#H zgH}JXjSk(fJ@ms7H}M^ht!2(K=OYH~+d4-^eV_c)d~d_2seCX&_tk)qts@zX+RWc= zU*?DZa6IMfa;>h~xB0>2BbsVOdLBP{qu1>O5p4(zp#NVJ3cJb@=3aEAZpV036hrTW z3jLB-hsU3*BdzkjwxK&QUKO6`dVy*6&L|3GmA4rZ1nC?%5Ui1ekQQct)g>k&^-I`-Me@bP&tmf4q{UkD-o}-w zl8+15$ShOw3Vte$ignQhhsShUqOg^r$fk(9L6_!M)6O83iou2yWmBv8wsLe(5^=xB zt9qqzXQr6>OHtbqPFuGCqLH`T{;=3`{5)!!{B7~`;HV{c2>(9iTj`SEOZ^-T>$$@n zo!D59o04dY38p{vs84+*Ck@!Yy2>C#sc* zkA8Zl7)jZJ5#%y+CcS4Sui$x@hMcMPN_AaF>KoL!P7?{OW!H560D0o7(AfQSRA+U4&^8D z`Lxof)}yg=UExYuQCkeB?JNxyq+BUU%XSpc?65v_+nW+g9X^f7dE1QR>Hb*9yI}6x z-*}7n$Db*x!QJ0@qyOdS9@#L3(lL6WKL+^_Kf8;A>%WehN?v8w1|Kdt_~O8s{;2`W zezn5YGoCpmdT<*Lj&gGJCL}4v%KX+b{@oKlW)$(Qe61m>T{^AyN)1;P@>`dEI!5SAP5=Ogz$*+rPXa){5s1onSslZXipyujO4+er(9XN_T_sNwi%|IpxTIo zs08w3R<1NQOmg(zVSshp#SDha(yq@_&tSw)5-5~@|x`?PPYJz{%cU?ahh*0^I{OFl@wemM92Hp&vs=;&n8OliI{c)kn_0Oq{@XxYRTWNX%-0E^FGEQ2uOLpDs!>K#DeUV zptAW1p%l}%6Fgm}3O>>Av9d?`Xx5d}YYl}xbl|@3QehBw)l$$>CJhxiS*;8knV37T z2k$$s1?_9@54XR8yjO2b@y1Rtn)t_tWQR&MvEnT|;R3RrcM z3%J!<6OsnQXHR}%-R{3G4CO2ec(B7FLwUT^Me*%vScZuW)D=QQ-8vjtGS3rO6p%Mj z`^>>4oehjn;j>TfZHa8KW)&c2-Wjx3@r_n(xnWL%%LXlW7&z>to_qGnfZ$=0PeCB@ zuJdc=BfqNwz{`Jk}j8I!h?uwMx+h*PouZoA`5aU+R zXw_O-WDK!siDu6)BIQR^Agh&|$F87Nx&4NSPPZm}=}FOE8ApacB$j&C)&N_QojfleAFR8vhV))xa;7ZYAenajA3 zz?24kIZ;*J_PqAfvQHCp;n=yly~ZuG3cvxJ7=BK$5Ji-duBlio)6nkvD_QZ4PC7cl zOh1DiWo0na6^QECv+)5_`019LY)wWWoQtTccd?M zx`B%eW4gJZ#;NPApLaoXLby$;ryh-k$I@@i6HUP2@qFN8Z`5*F`j8n53`f#qg6jo^ zn`fuG$-Wx6@Po!8(b4EE`vP@B{!7h2Y612t5WP7p_+s_0ANuTdaUh0&9rWF?2R<&pCAx9}D4bY)xvdswo=sAm+Gw9y& zG)|jck1l-=<$*T63a?E!gl)Lpz-m1Zv;GavhmlBx5;#2=*a}ieb=7lvUun*C%-b&y zx)uRhTJln0butVLp34WOT5AAv!#&W-S9fsu@h$Bl{q1&O-v)YO$2B0vL`U;A<{1Gb z8N*JTw!=6M`%|C?=#8khfs}YC{FJeX!_Eh^t=1vwW+2w%J2dRd!9xfDibGJ*TKmxB z$Ra@RC*Uo{1Kg|zKG+0t4ybVg_M_Ev)DYEK|9|-Z;QTK!hkh7M*0Sp5wN?Rzsy3F& zwukzzT0#pP_SJt=wQqymsT)Y%3WW#h!}g<`o~%n?xE9^8#R>|#3p_gjCT)Vk15Mzh zl_(o%Xj&c2EJDf4Leae;b7=Si<|JD7WEWSl@i$XAxt^p$UoJ$$fcf_@t>lF2WCge{ zy90D$;2n^?80>5Ms_GO^o&r9^V~A?~?NBOMbT62s(Yh58#?=sMqNyq`(v=n*j_Vb zZ@ebU!T9J<^<$Gup{Oizu#-UmTHI*YORwH-$U1xtbHRu@zeffGdg`24Lt z&CZ76r+&@qH-^HA98csZliPB{WqHCu%K~OlCn~oXP)XQUX8QHtQorvECB4l`iz0K= zDoo}HsxOJAJO7&e+$--QXTw(hzUF_T7*Tr7sjA;GAcICh=N?3l7CCDV1)46b*Xm;J zvJo@lOq=<*dC<1W{yRO`#~eGdkFPt__n`Wij=ytBOk+qf(m%})b&1nsv@t8@)eWjZ zH<*xmwalpXtczP29S`jVZOt<2Izu_mKI^se{*u)cipY&I-7sj$aUMN~{l(eeLvKfg zX?E_+Htuf3%uQ0%i*<1UF8D#?7tzkXNMC)}xcK=oot*)~nVwsjVK#`F>K7pKh>lks zH^wyML${V(Z{$PjZoGKwKmV(bccX2bPQdRBLV^n(hbtxJ+zbzJxZ5!gVJ3R(%mH>U1x*u zdw%D9Ie);J>*5Qu_u6Yc>t1&}dqP!|WS%}Dc>)4~p32Eez5#(ANZ$QnpacKW-ID15 zfrNPEB*osk=0tV)VbsoqXS-2%2Vm#R;s`l(J|ab-%5(&piNds@2;erHxIrHUhKB)=06%p zSvkG-NQ?Ts(${b_aC9|rvpxDyZ^mY#Spc(g5n_W%^*9~Kvjk=Cg91p=UrGN#zdI%Et4~tO)*J9f}*wHU74$}1QKpT-G zI=`lZgFu^~aO|E-6kuzieNPV7MEi_MBZ&jTW~u?%QgO8p;fX^qX-l`+cDQ~8dDoiQ zaBdHVH**9I`*h zS%mi{=)a?GeBxN?HxQ%}l6W_$rz>1E_$?w4`sUnAEcLrV>>SY{(RH|&$yG$^t3zY1 zyLmi$Z#mO8guq9L%Zg9PzuUjk%QmG9;h_N$8x410{OU-L2rlOiY95w z-WDD~Pz&R5k=jd ztVu|0$T>i9gM2I=q#3_G_2YATG|J@bG=Iq6>nk>Sn86H zrp95$xQJ60abU+agTsQ8iL_)s@3#F(@f9C>VzG3p(Iisg8+cWa}mA-Wp|;`&K!;zp`q$-9cJ6SK!ONw<6Js`=do z-mD}}zX`~PO<0sM@wU+Ij@Euf=sc5Htw(aM!#Ky_}wZ7 z&N$;HF(uZ;^$u7QdXp4h#{X|DbSx)cj=%B51Z+_e&+I_$C1xnB;&?Gk4?$NgD2=oF ze3T{IITglsAxTucp0Aa5nB>!`0r-E!#QMa*@z;_*c7X_mapV%*7+$7EV*2=_6ecLc z|3~1E99S44@tnlKJfdQFrHUj1`YVn!{RnURGcdAU%8qf6eC0J5CEesp-mp-6%W7$` zsWol2qz3mn+>j7S~GzzN}%W{-SrgF>#1Bmj`g735rhfo?ZTdzB1BIn(1 z5wHzX2)F5`>uXEmv|xw^EdJc04@&Dd^qfGPrI(kc>;0C#PUE8M9Ld?Bhg+FJ0b&cG z6p{t%3hdP*l|xf=#hp_w{oZyujR>dRfsrW_ok46V5H%{6l6_A|Dv_IU(CEBzp`!H) z^7FQ|JwxdUu}>r>)@xPl-Mj6h<@mPA3}CCrg1%XI2uXbFDym-FxL~TM=%D~YVFgAK z4|w!7`ED34AMtJ90@`O8Dg**e^fvj}i%jAH)1KIB$%;ZW5h*5Asw>A>FjPuZ7wo#w zOl{-bP^w*%cNLr`HGO&9qaLj9(===+*WEnm_Ysg{@_H_HW_$h&9y$}X9l3OibYQo! zVJ?4+XoI*8&A}O6ec)ty^^P2HUBe|l63kJi{B+Z+wrh%B4qpoKx%}mgkJ0uk0N(tl zSONfcEa|nYu1l8napKJSSK>>{!k#FxU>nu6nWHS_JY{{4!`{6@x?Y7~9OSs>d#H;H zS%ZLZd>(wx;<{V^ju2*oG|2uA>|?823Q9{%ER56`nxFE3 zV$;CBDd4Eufx4c$MAzP(Oj#60t@O-Mh)Uz%letv|{n6)EaG2T2dxYWj4J{c;ACKW-1=K0s~yAU>PBItlTF?+ zBF29q7awj5ZptTAhEv#)gH?4pqS;R?3PXeufy)?Ymwt>7qtRSc?x*l2&4iH~^3wEW zQG~L9&Bw&B_+@dIrte1q$41g3U+2wQTu#nEupz$F>nKeuOg!zW@J&FR5gEckl9=QQ z#G#o+)81yW#d2Nrg%OD!7aeowJ+@S|iY zw!Z0Pay^a|DE)(0|1L$aaB}xW)$J4r&IQZt9?aTa!Y#R4+O(Cjd8T?Iqc8K&J`x!Q zCXSEkyW+i$ya@s%=+}PPU^+R=heTa^*AbUkCsMH!-lRarhePNte^gZX?+R-=d7?Wd z{gjJHJZbTV9~+mCzHy+jv?+W+Q<~rXh4ihm2&8?ukEqh};Obo>W`qK4%ih!?{o0Cb zErDt3F%9Sk3OQFc<<$KiO-59uXU?!TXfY<&Tfg0xkH>9~<`jYSQa|`sc48{56o2Yz%$H2iA7~Nw94;kxAPi`?~flqj>_PKO#y`wrlIM_;Xl?L*bN|BxhN!4~&WE8DCW<^-#k?Xc^#3u#v=}y{WB#Aw8(i zeYF19!??r6?>9>j;~IX7Eo^eiV+vb*N;C#IcCI^A>7H=lfKOpyJ-I(ZIn~#RE^l*r zDvcEz&JNjA(sD>Ch=eLF<0V8YI<0p=!Ildm zHNCDDu3!t;jE>h9N4=7oD@CTR%^BpKIG|`@7wmtvLSgKvx;?xYHMKgN`W*oEtb6tr z7^pnd(pW*=_dBG35z%jYGs9pA7rt~Ld!<*j!^Dr)uPG4ebNN6L$AP)#sQw;OFFRkb z+Jmy-PDMESrb$}M3B-R)Oil%k+AEwpum{h;5m9IeC zQSX&)3f&n{Hiup?H^;YYa71PI2&aVk=U*WBu!-phxJe>$Qc`9t(Mb7pQY_;|H{Re^ zsFcC$$cAAP#OG!2T}TO50plH<$)4@4?BYu_CKXy;mORan-2o1KcHa<@qEinTg5PQx zvUJXMVX4p^qa`vmwce*uA&Ms!93)uHy-`c##uLkG5t|i3pKK8u;Tq(HuCjyZ))Cv! z@p3sz3e$JX2wYnu(Kb!pZ> z>d|UVjB`_y8#qtC%shz8d|CnXFXfqJg8UWqS;AXUT2t2*Qe8n|-Bf^SUpCO|ymWo7 zgnVasph;v}TEGsnd$!l6^K+LrXcH(|_axGHqs+s1UaqaM7$ zU*ph{2>jQx$}!n1g?-*&Pk2UoYwCou%5O5@*(Ih}ax#${^Y zT|{>hI^@>9+HIxZgblvAc7)FxRCb~%82O`2J~EjRc@7o;&uI3GIz7>6;84KI*qFZQ z#ncg4jO3J%02`SEk>2pL!~VuI>0Af?I$~6foAMyg<#gDCplgYm_zLD}|G5)wh4Bgc zUR()#bH!%Ndhi>+Zt!&2yxVaq(@Aa3_7CiFD6g>@0FeoWEdn3Wg=bp?6-xTKSQ!`` zq}4BPGWv||@D+gDonrw|596mIJq|IhSA94uv+juBm zErI}|!zQk=7eFfjbWPQwLSiM0XWexA_A1oU{3(`tNglUYXy)VQv_!@_#csJUYQ^s1 zM_Pu)OK71;{8E|nXr@Df2b2yqswqC0P^DUhTCU3YkCaFAF`_93P6Belq#{L`jBYbb{VuonF2Vd~9_a{QlSSb2_(v zLx!{Ca%y-5Ek{7*U}y{C3~pp zmE408#b+d5BKJG==4g^HQglu<3|nfK&#xgoXXOvDOqH6}Bwtg)06@tB{R{=n+Vm}W zf*#-*%d`4VZWQ-uW+?X5A!Pq7M8^=5#l$}&((1yHf%_x14=kFqVEu|CmHe z&LMgu0()k8nJ%wy+S2=r0Ort#XZF~jEsDm6@uAr#6Hb(VrmmDW6--V~t1=!RYWA9N zt6Q_{;-wVgH7&^GV;wf2BELMK>C#IPO#GaXi|m(VGC#IyU+-hCV9$ik`1Jzu#`2QA z^sA0;a!QWP;wqQVXE|dcwW}uH5(N(7Ax_5eRq=CGyC9w+z=mrF*BZgE8l}!>eKA^6 zJHan=GVGqWE+=K4Rh7gw?0Rq3xwuMB&_8<0o|DLU(Sw)QtJhNJ@_4LB(esf$7g^Xv zatxE-_XQ;b^tcbNT<_f}O|V_EcEzrDLj#Ipks13HnLwjV8q=X~0&dRA=$dkLG7|wU zT)l&$Qxr4$sWSm2I>oO`4*V-AlRn7qk;-4I+vf4i?C}ah9cy5vu}oW?I04^Jk|cqs z6^-Wn9O5(M*Qo^lL&5+mHrT$Ru+RS!L1=s&}>0ej;yrEn{}_ zphWS$ZRnM6f?u7_IMg}78C(W(1_T;Vkz->!9k;tMV`x9^ZIDLADdB#0T+w}TaQ%)% zqy!>GwJ%@VzaBX8qp9e_vPc)p{*!2f9`r%wtOr~fL9+5J!_*?y9lj%JOVFy2J?^lQ zu`swv2e($W8$tdq(3rTXaQt#7ll_LG*%(up-kZjvd}#jn%%NALcw=d8c}bymc63h| zfIk4v#EN6`7mM_${kt4w?e$~(8{|wZC#cyHf<5E+19rz3fQth3w2Ei)_vRE6B!K9` zU&L8mGA7axW0~(^)d?{YN6z-vf*c9}GVdf{rcWhZOTzttY2c0~ zB>0>yT&@AIXlH*RkP@h)3a=bowphZt?8BSA3YvI)Mc=u=KImX6m~*Fm-r>;7>;(+x zx{UCL!3eF2z{VwZprXcDI?7f!Qk@Dh<4M7<=++q(ySrO$QBw(SMihdb?2H*WfJj;a z&<03q&NQ=@XA)Z?rw_Y-<>TV*bg$vTc7?w&wrMlzuCvkPH zEe+^n#2#06AGQt6oXnfj3qH*-{7mduwK$R9z}fy+6q7q$;@#y;E`V;2*UwHF&fZEc zvUfXtWLS56F5wBv5`7`+DryX=g=dc!M#Sq|*?n6${)bkp3?s#cFLSjK6sY>*cu{we zJ2gKlmfbnsPWk`dm59#vkHK*JTs(> zF~3oAcp2RoKp$yEGp$ixWx4Pd#1ds+f-7Bv{<-<8j|z28&m|0l7F)J|MWvtj;I;Ba z`m8Z&p=<42fs^!~wde*>FjV5UI;1cDUUl`32Dp6s=0)$pkU}I+gU_7YY0gVIB?@0AEFOT^T(CfX0iTyhRw{)DviCoqyZvfPImV;4L-!#E$>zX_ zE$W}<=!N*bweWwM=IY4@nC_0XVx=T1bQNTjuEv{*R(@S6%PF`r~%`TuL`d|AOyf(jyHkc0O?kOWF4m;v2@t zI^EeQQ~c>AecwO1T2`z)z_YR{v!#rSB{Kd@+;YCsx(#UIEN6Sg-63*7qyuuMb$KOA zz$vA}@TV8FfWNcikftwtNgPzU+id$bH$9IVyLHC5Ik+kWX^<@|lM?j(L{lV^@)BSs zg2njlHA}!8@h3O&Xuc4Q`E8PGb7@0Ly&p%6OiG#lbGmqsO^ITxRX#ssCsN-20jzwbcNx`MKZr*E9y&Mpt1OU)x#@*03NA>hdTc;X$UgWOc=Mxz4b z!F@-I;fJ5D$w41D%I1>2oIg{!D6?o0AGm?I*=tWw^_!Y9RiUT#6VqLYik7!(4S#4X z5g@dqCN)AQDyt`%BZ%%6t?xXsW?q5Z2OOI3F8j8> zskXATcJm8CB&^Jzl2NlegR)91c)Z{E+*R?>rq7?#?SC4E!pcP#T=gt=Cg%&I7LGd? zW)gKttkUBWRNkCV7uln2(I=3XTEy<}4@XI?tT?N#AMN@yo3O(i4sUTKISQ9CfPQb< z-`bPO{#(~n2j;WZ>Q3Un&{n_V>xg>|fF4ZOKRJ8I8QAHkD)eU!LJ06???QpwKjV90 zi`sP8?M1oyZp}U~2d=b{^qlr)g~ZVZsR74b8bX{z)rEd_?H-D>c+VKbsB>8fF^FUO zJUDe`ODT<%O`^_TIrA6!_DlYsm~BJirWDAhM1#8wtT-_+$X_TO{Jh6z%ajR;X98e- z#XR9ernbQ%Dbu|GEAGBfN!F71+I{DFmsrN>{e2d10NpIMMSekH?Tn&{P81pv?S=pT z`sK3$mN#Qg;!YBA_BwHoN9`RMeY40hwxdtd3w9l*G^o-2kCe4T37yGuu_|PDRTYMW zqzvH_=J`tIouRJ2{NpX%&@&H&xHdK6OYflFDGacYReJR!%t(2t{pB87FMl{aiql z{jaISU)jMYIhU7Y=_v=?_sSMtS=BcExZgx8k)ma4UBGkKUO?61xm1ozd06Gpas**e z7!|QM|ESCl-N-5)*O{`rK9Wm_M|>#{z-a!YewT^&sUUdP!8rUx6_11JfX~nG!nXHA z6b)t30@8HrHLbzGcKh#8s{M^!33-p#>YZj*WerW>11nj<52u^I95ej-uvf@+N+;G7 zN+)VRTmpCyv+qXWjzhJVqbgeD@MwtLmDt-a#QSiJRKe~~wpn*x>aDMMsslD7*UsIy z0_fgaUSio%?AZXM#iFWE;+c@dw6zXR?!<`}jpd1!j#mGI+~7L2B=pW)_{*816A()j znIPnYEK=m*lwjcKGc;E;Wk+=osF`;SMNY@1R4~5DChD+7Z3o^M;Dvv}ld5iOV|$jR ze@cBOzrge|+Dq#$fXFJ^k%)3!fH^AB9Z+j-fgws1VlMCii{uf282t7W z4($@x`e`ttpt7?RGZDy@qT{2Xs#$P$#ZrIFQW!eVUOlLyn`{|QFK85mEfF2|Nu&?X z7~}vHVe#-vq%GLF{s$vgJjlu4(w);FXUdx!#Dorn* zr;=XB*XmY`!RIpTem5&C2P@BU851m*Sg>(PU>d+%xX#S$?@O6||L`Z{E@R*5s}8)y*WA+vy6*3g!y# z%8Q2gRsolJKn;Kn~W$?V(H?ftJCIN&mRGg+r3k{%$9tKA=w4?>jN-d7rnHLr7-qf z_Qt>Zl+qJkH|Mqa7@r)sMwz1A)~bc&zdTZ1vBx50*P`wc`BejOsJ%7+=1;5*T|rhBJmgra3eyzMtmd z`6zFYoXvi;&p#N(!5U4=gd(I4|%a_&73+Cez{ z>A|J!suejW_E$oq@?8-d`lC^8IMJIE4(pj3Drz4KDfOW;mji0Y-6`%ZZJWkC>&e5& zI>&7S$LXpfU!o80M{Ct%PhX4|^d_?7`ko;LjC$f4+uPf{JuWtLHa#nesdhZ&TQ`T@1cn3;4u^S)X-(nblVsbJN{iNP+3|^^~u6CS0jpC7`ZoFFN4{VC0{Vs(?k3e`Ci1*VgbgZ zrlTWZHjpwII#!^OH(lMFL(cbIx2Qlce>Qkta9a8_!+m$YS-GA!Ud&MQ@$gLQD1hcP z&z6A4BFMT7fy}y00c73hgO`T?MdAw0pz!H?$F~qEILB6c;+RMFUKM=m$~~NOwU{!^ zStqKzqonKBb_i*zRK%gwiAfFV!`NB|aG}-CQu0!v@pP_!D~2X`OcJT7NUtp^l>JSU zIev6c&bP_^R7Ojk)BU8+3YS5t0iHPd*fukknVGribkMtLR9utfv7WtstkZ`&XtRlm zF%)z5a^WY6R)?(7^Fh1%DRkc)6T<#|=RCGtrCe)65lrIQSPwrBe~$oX$1kGJl^(#FH>H4KGKi>_JI2kGzOP_j234tZe zO=7qbPxL33^tkJI<9&O?jh<_6_r8_Zv1Ztq%zKs#OtQs;fb;O_A&g4}Ud2i6UH4Ugh@K6?lS%wYethNDD|!g~D_9A)qUa#GvgR12A)Zy3{p z;v!zYulW=_uOKPZ`)6xwB2uONb3lkLJVzo_X3r2PFE?!3jvR%7OL3+3T?96vGA8haO!w#<`4E}wxiY8?Ck9ObuI@> zho|%N=R+mr24)&G*vKw%$3V8gIa@{poq~xR^|DhZ8CJS_18y@rfZ06hcl<4tC%)sJ=f!))p6U* zf~c412uC3tU_H+iX>caXR#Qih21UH-*z3v0#>SfGOP8-@je@gbNx7E2i5jP_M6}H?gtnbV-58iKV6N^I6TpMvf3qzMZ~}~ z5T6#D{xY2xyw)koUlU-x;$5aH;vurHxspp(jj9cHr^XCUb|%T>Y;!naleysyGfqPg z?>|2y@x~rBtN)g65{F7ONdR?ALvtZLdgo*g6@)JhEef-Z4|-bb9wlW@T+J{2W;cE0 zx!Q)>SzN1Nv|LL1jd}R!YQ`?=YgwP?5n@~%us+_<^Y%mW3X59FQo~~Z2N5T+c zcf9fsZUlEnHe6m2ul>F6kIL&%#rJP>jCgXp?XL8&dO*7prD6*uY&;H5A}Yotx{!qr{5=+-B* zN^jRyC%B16$4MY!9XMzyLk_`yUvPxjrfA^C&H4h1wR(nwVt~v+ zdnce4suuan%_I(4PKG{{$E&nKO29L|JbPOGWF@{+&`=S^#j=mf^`P8TZAO4D$8{uG zf_{eYwIDK@d4iOW;}UaIV67`wJBwc}C9cQ+zD^x~&i*`?f)w`?)2)3BBc9fpUkZt7 zyij&qexo&vRP4@Nb*XsR{>8!SNha|$&Va)jSzE;K&Rc)1yg+KZTYG0)}PIAv)DtRoAm`Ju9 z!4~1a2hms3rCFh@dlAj$PxRblQ-&6|EQ^H7mu4h~3xy!Fdx|f_g&O~SX7^n89@tgB z2g@itj_X7;!uiqDCGznkD&zOsv7`k=pOXZh zZ|)2Q5W>Td={;Y2UbPPo>@fG@iPt}|&*$0{#a<;w<=EzHuT*VW#+w#OE3(}sm>#EP zU}zg^1K!t2#)Ol9(MZ$uo#~rTiZ{~GbDAfQ$>sXDHZpP%r_x&gh4f6pMX1%|`=kJ-L`mBU$oCu9LgilM^LKwh)`9^R?SQ zcGF^mq>f^=^HdZ|Mvo{NG#lNHJFBdwFD%;zjg-#%#d)lz{`OyAo@OD>UcRMgPVUNY z`UxnbMM>j$a++|ypDeW8YC54g7VDr4CA!>lw(eMcai$Z=NUf+CK^qdL8AIl}p6aZB zdAdKFpios&2grfZRI~T3Ix`J>HLjrZ-usc6nb9pRy=1D8&MM1Ez_b{H>=0JdRmc1u zXG7XByqKV%i;fTVPP6U3I{{tlb^FuRB`@Kc`0=4V(nc$*PWq>Nvm%6!LqAIJt71x` z2Q6`%T6@8w1NMIwZYbai-y#LQzDSE{XlU>%8_GF!1lmB)7W~5kLp<=Z!V`O_dn()q z(}W2R4(q6zzyk!1f5bga$3P~A`Wc44}@r*Rk!(tB5WU$CRUnX)y1 z&~X05$X-CPwH0|q8Dl!tUh*QLvVzOoVdr-Y#gW}bX7Sz+Y#5;pEcMTJkY0-nBA`S_ z5!%}OL^ODD)_Hz+YIO9^mrrN@LKub!3=a~`55SG3z3o_2m9IrT_C_x*E=n+|rpS%6 zWlsr=TuSotZU_!grrzZDhYY6DmbE?1$IXA?g_R`TMCGw^KqlxkG=2ecund^+Zm6w2 ze~r_oD)px^FTcC7C70;~MG9T_v(QT`8#6Ok#hKe2Aaw~RCl(cjnDEz@mfB`?;iY3w z=+NxY*rwAI?WbGGljEFDNuA=IdZqM;8ZE9`u;jb_wS@6HzafKtfTeCacJ8MuesxZt zk#C~((1+LD=5))rOoBFsFmgoHP!9t`C6~}~d}snaX+&`+ReSrTzYp`*XiAMi3hmp= z-!tqT<0oI5OgB;QIkL{i?)CS^GQE{?`%pRaa?dfW|GM0aru$JBVHbmoCg}h9X-MKQ z=;$;VO!Ex%H;}^L{ItioAK_S1)0x_0jSzIU93&==2hT8#H~05O;X zA>?{kk4vv`R?gVDppe)YYT;pdb#^eJ_BKvUDT&j_#Ar3V4#@c22S7bG$Q{`0Ok7j%5(h0G!Ct7E-DtpGAE%cYC}@4KVIc z`^+sS@(MIc-XyXWsT02ZQu0LaZEw%(iAXZRlZz8Lb!g^y!NBkPIFpFFPm8_giv4zu z{fM!my>zRu?n8|mY2q7du%KDwOA$ik?_#h#ZmV9;vC#zMcDASzN_z9(feBQ+1eus#i7mj&yxnl|I@g6O90FE2f7Fvhlh1A zuJ^Scw;X$#Z&^88tg1JbMqTQW^m{6!E2k^(!oC~ZHeXCT=+VN`oc%}mY-KHT7E2nvM^d-f| zk6O~{F~F#OmrkRN9Bi0TfGG5qDTDLSPLK-Gb;^2y{Ood@Tt``nq4(O8U5EyFRfFF$HjB=@Kn@Uzmqn~-1CtN)4Be5vO|mpgokkTxL5!`nx`kSjgRFgNeFXag_r#j zo>vEYQ!Gz`Ln$JifFS_pYFvxg&|&)qZDo9nK^zJR{R_wEW>w8mIv&%l{P z0`NwZT7i0BmAFt~uxJ?QXu< z5;$>KHcf@NF>uX9+PivH>2jd2>-SDuThMsEsWIk%0#BztRR~4on>YP9 z_>zdvN8_?aKanxml0#xcXtuu|nj@rvc!Dhg#FPBGx~W7;pz)>G=ZkVlbABiIFXCf> znwe9D)K9U5i10}%-mKDdAD`O5Z$&DP_q)BEf)I$47u=46KI6?tubxYq_t^MR`bDBX zURvuXlf&j+^P#i`t7A8Ne@0wPYmBXA&YG9)Wgy&6rp57;p$9g5P#HxvW%XEAR&%>$ zac)PGsPPF~4h-szXu$6zaf#8oZ@lKN^=y3IImLkGk(*6_lZf2`GcVh4H{Z%*&&$J) zH~&mO$2(S#7q~n+ibYl?`^cZt@7ZI(k;&mpmUWhB#9oSwN)7*fBi}PNI_O?~Qy46U zEf$Ol_^)qImIen@VsG7~ZJvisGvGevu3Z=AuC*vf2-WE-!YRx}<{Q=aEmqP`U;Sr7 zni+m)*Rf-xQT1CVv3pv${F5pT&_X&0AY!!E1W3^JqB8)0Xj{E$g*L;g`z3S$YA_bX! zvrj+;4|W{Jn>4sM3X>lj#S$66fDrt5WoXPHQ92X(-mhFg<{(Iphz~AfwQhxp75iP! zCztz8e$5pVXzC}9d)xfCkifiM21U7#M|8(V{Y*wz=63787rBe2``J5FaI4)wxcQGK z%;c|+doR{yWuta}-Foc`U6aJ_Ii=N`5~1Qa$O2CErHUBsA`MRcB`Iqwjjw#;S=^1_ zaEj92bSAy_^G>Tx!)xbevO;sW=T6H!*Q*3QCQ zN|E_Hf325`m??!RlF!9fjTT5~H6zDGB9-Di58Us+J0s?EfDB6MY z#FnaMg&B`q`bRD7Jgk^9Xu)tU*YyB%2p5J#y7%^c8cqLyFaL;UTZ6DPe;eB$QTIJm zI~y$q@rz6L&>$@7YnQs%w_3PE1g}mYrQg6ER)C6D@VH))U0k((*iT^$Y2GVQ9w|<9 zm|@-<@ixJ4qr<=8PP+Q`pG52w-bQaJm+p+Hy ziE<6eDE|+dt8e!T=r#IBq)O@Xtez)(nA9R>{vx3H3dtfN`gB-c-?Ev#ik15pCqDfb zl1XpHXzRCMa`Rv6S;Yq9N`L7_BnR-Z!Bmy!HZ1bUdGCO;;aB4dMdfgbWeYJJjmxeL zl5)z$Te*%jM*}fVnNVsk7SLYz-oxh&CLg}{rGsd{LZER^6ZVHM!gBF z?J#Jf>qK!XD<&b^PAf1%wqmmNbhEcEZF*o2ZBK&Rc2RZH9rO1j$J;wczR^N5hd8hu zC1}SX6RqFW{Q2GzCZPN`Z!q-j@ua@h)`p@USRF*g0ujvRbhf`WU}Gn2sT^3M z^F9!`ikDWa3_2KBtL}tz zXR6YIi@h9oy4q&H(Pn;bU0dsUQ89kiU4!txB@Dev;H+EZ$qED{Y1}RtNnWQmd9cPC z7v^$j4gwvCKvp5gX{&en*!zkqXz3)BI8?IrQzhAC9R9&MoA`o_B7JN@t0{E5CG8zu&mXb_ z|8Qx2IS-Pi9I*0C@!Kabg-@BE^~9r{x#u#kUUMyaMx3Dx>0v*ZQN#Fk+-(MeFi0YV9O)|Fw|IpLBfd3CLKLd4#%M|F)!%6%R>7I}NY zJ)b#4)n==3c-d3l^2EQajjZR_60{kINBr7%Bt8-C|Cx>c|NatN@kV6N?|SL!9EbDW OO68=KB#XsA`u#8N=)JoD literal 0 HcmV?d00001 diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..bf4b4ed670ebe16df1ffe8759892c8337afcd366 GIT binary patch literal 11173 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

Xf${(V literal 0 HcmV?d00001 diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..7c53c7c7cf6808973531431e05a1bbceab8727bc GIT binary patch literal 9365 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 literal 0 HcmV?d00001 diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_background.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_background.png new file mode 100644 index 0000000000000000000000000000000000000000..b597dbdaa14f92d7ac8333ec9d7d4353b309f24d GIT binary patch literal 6619 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 btI%bo4ofwei`L+!jg-?NSNn=@;P3tgey@YJ literal 0 HcmV?d00001 diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..2c90b97683d15298050724c4e1c3c9524abbb1d7 GIT binary patch literal 23735 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 literal 0 HcmV?d00001 diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..da772cecb35c8e9cd2e00f2fbd3bc18cdf508418 GIT binary patch literal 16190 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Μc#_Ws=Mr$zfwnfYV6LO!vwDgH6x=R!!Ij4<`*7Y~Mpq~_W$9%Xq$gEr(#wv}^xa_Wm+oxlpCu?(r;eZhs^6gbur<{Fn5;wZ~2; zk6qO|5D}{ZyrnMl{oj>Wb;LRN|IYKc<+pQ|{aY+m38WESprh?i0(ONSDX1xukSWjzfZmNCY{;pLMti+#h( z?}ZBFa6*;q#LrIn-_2RP|Lu(hy^G@;f!5|I`JXc|jYQVwV|f;G>Xn`K!OP6@7AwR0X3cE`KVOix-R)0zhH{fLa4CxJJ&x_e;Yn;|vtv tP%+9M$di0V;%ue + Kvæsitso Debug + diff --git a/app/src/debug/res/values/bools.xml b/app/src/debug/res/values/bools.xml new file mode 100644 index 00000000..b73515b5 --- /dev/null +++ b/app/src/debug/res/values/bools.xml @@ -0,0 +1,4 @@ + + + true + \ No newline at end of file diff --git a/app/src/debug/res/values/strings.xml b/app/src/debug/res/values/strings.xml new file mode 100644 index 00000000..e5a39862 --- /dev/null +++ b/app/src/debug/res/values/strings.xml @@ -0,0 +1,3 @@ + + Kvæsitso Debug + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..d80ac9b9 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..35adae677639c3e4dfddae69f7b3b3269487fa45 GIT binary patch literal 42258 zcmZ^Lc_5VC`}aL&G8BXCyHb)RWM60Gv1H2{MF@o?vX_`aBH1cJ5<<4JWTJ&;$WqxR zOQfU>%zf^2uIsa1uG?6fa_$t_2|*C&QM147AczJ0 zmjz;D1^=u>4sJpa4m$dmp+ltG&-`^PpJ~I|hS6Bm@Yw2?m;Ui;UsNX$W+#;=5VtUh z+k8aa+ZI-@!+Z1Yg``<~#Mh;!hB!y%M=a<}SZRL0_xjRS#gF?H2B#ELOLE_vg~U)| zQus~23^{(k^yA)-N(pq(uYE;%H=jxOth-EyZ^m58%o|vFYjd_=x5uiP#ZLJ0!`T_P zqnq‹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 AppCompatDelegate.MODE_NIGHT_NO // light + Themes.DARK -> AppCompatDelegate.MODE_NIGHT_YES // dark, black + Themes.AUTO -> AppCompatDelegate.MODE_NIGHT_AUTO // auto + else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM //system + } + ) + WallpaperBlur.requestBlur(this) + @Suppress("DEPRECATION") // We need to access the wallpaper directly to blur it + registerReceiver(WallpaperReceiver(), IntentFilter(Intent.ACTION_WALLPAPER_CHANGED)) + } + + companion object { + lateinit var instance: LauncherApplication + + val collator: Collator by lazy { + Collator.getInstance().apply { strength = Collator.SECONDARY } + } + } + +} + +object PermissionRequests { + const val CALENDAR = 309 + const val LOCATION = 410 + const val ALL = 666 +} + +class WallpaperReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent?) { + WallpaperBlur.requestBlur(context) + } + +} \ No newline at end of file diff --git a/app/src/main/java/de/mm20/launcher2/activity/AddItemActivity.kt b/app/src/main/java/de/mm20/launcher2/activity/AddItemActivity.kt new file mode 100644 index 00000000..f8b0e196 --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/activity/AddItemActivity.kt @@ -0,0 +1,28 @@ +package de.mm20.launcher2.activity; + +import android.app.Activity +import android.content.Context +import android.content.pm.LauncherApps +import android.os.Build +import android.os.Bundle +import de.mm20.launcher2.favorites.FavoritesRepository +import de.mm20.launcher2.search.data.AppShortcut + +class AddItemActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val launcherApps = getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps + val pinRequest = launcherApps.getPinItemRequest(intent) ?: return run { finish() } + val shortcutInfo = pinRequest.shortcutInfo ?: return run { finish() } + val shortcut = AppShortcut(this.applicationContext, shortcutInfo, + packageManager.getApplicationInfo(shortcutInfo.`package`, 0) + .loadLabel(packageManager).toString()) + if (pinRequest.accept()) { + FavoritesRepository.getInstance(this).pinItem(shortcut) + } + } + finish() + } +} diff --git a/app/src/main/java/de/mm20/launcher2/activity/SettingsActivity.kt b/app/src/main/java/de/mm20/launcher2/activity/SettingsActivity.kt new file mode 100644 index 00000000..dc51b4b7 --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/activity/SettingsActivity.kt @@ -0,0 +1,86 @@ +package de.mm20.launcher2.activity + +import android.content.Intent +import android.os.Bundle +import android.view.MenuItem +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import de.mm20.launcher2.R +import de.mm20.launcher2.fragment.PreferencesCalendarFragment +import de.mm20.launcher2.fragment.PreferencesMainFragment +import de.mm20.launcher2.fragment.PreferencesServicesFragment +import de.mm20.launcher2.fragment.PreferencesWeatherFragment +import de.mm20.launcher2.ui.legacy.activity.LauncherActivity + +class SettingsActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + if (savedInstanceState == null) { + val fragment = getStartFragment() + setupActionBar() + supportFragmentManager + .beginTransaction() + .add(android.R.id.content, fragment) + .commit() + } else if (!savedInstanceState.getBoolean("theme_change")) { + val fragment = getStartFragment() + setupActionBar() + supportFragmentManager + .beginTransaction() + .replace(android.R.id.content, fragment) + .commit() + } + findViewById(android.R.id.content)?.setBackgroundColor(getColor(R.color.settings_window_background)) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putBoolean("theme_change", true) + } + + private fun getStartFragment(): Fragment { + return when (intent.extras?.getString(FRAGMENT, "")) { + FRAGMENT_CALENDAR -> PreferencesCalendarFragment() + FRAGMENT_WEATHER -> PreferencesWeatherFragment() + FRAGMENT_SERVICES -> PreferencesServicesFragment() + else -> PreferencesMainFragment() + } + } + + private fun setupActionBar() { + actionBar?.setDisplayHomeAsUpEnabled(true) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + val id = item.itemId + if (id == android.R.id.home) { + if (supportFragmentManager.backStackEntryCount == 0) { + finish() + startActivity(Intent(this, LauncherActivity::class.java)) + } else { + supportFragmentManager.popBackStack() + } + return true + } + return super.onOptionsItemSelected(item) + } + + override fun onBackPressed() { + if (supportFragmentManager.backStackEntryCount > 0) { + supportFragmentManager.popBackStack() + } else { + finish() + startActivity(Intent(this, LauncherActivity::class.java)) + } + } + + companion object { + const val RESULT_NEED_RESTART = 0x09 + const val FRAGMENT_WEATHER: String = "weather" + const val FRAGMENT_CALENDAR: String = "calendar" + const val FRAGMENT_SERVICES: String = "services" + const val FRAGMENT: String = "fragment" + } +} diff --git a/app/src/main/java/de/mm20/launcher2/content/GenericFileProvider.kt b/app/src/main/java/de/mm20/launcher2/content/GenericFileProvider.kt new file mode 100644 index 00000000..183f52e9 --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/content/GenericFileProvider.kt @@ -0,0 +1,5 @@ +package de.mm20.launcher2.content + +import androidx.core.content.FileProvider + +class GenericFileProvider: FileProvider() \ No newline at end of file diff --git a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesAboutFragment.kt b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesAboutFragment.kt new file mode 100644 index 00000000..0df25401 --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesAboutFragment.kt @@ -0,0 +1,176 @@ +package de.mm20.launcher2.fragment + +import android.annotation.SuppressLint +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Bundle +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.preference.Preference +import androidx.preference.PreferenceCategory +import androidx.preference.PreferenceFragmentCompat +import com.afollestad.materialdialogs.MaterialDialog +import de.mm20.launcher2.R +import de.mm20.launcher2.crashreporter.CrashReporter +import de.mm20.launcher2.helper.DebugInformationDumper + + +class PreferencesAboutFragment : PreferenceFragmentCompat() { + + private var easterEggCounter = 0 + + @SuppressLint("ResourceType") + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.preferences_about) + val versionPref = findPreference("version")!! + try { + val version = requireContext().packageManager.getPackageInfo( + requireActivity().application.packageName, + 0 + ).versionName + versionPref.summary = version + } catch (e: PackageManager.NameNotFoundException) { + //Should never happen + versionPref.summary = "Ich mag Bockwurst-Bananen" + } + + versionPref.setOnPreferenceClickListener { + if (easterEggCounter in arrayOf(3, 4, 7)) Toast.makeText( + context, when (easterEggCounter) { + 3 -> R.string.easter_egg_1 + 4 -> R.string.easter_egg_2 + 7 -> R.string.easter_egg_3 + else -> 0 + }, Toast.LENGTH_SHORT + ).show() + if (easterEggCounter == 8) { + easterEggCounter = 0 + requireFragmentManager().beginTransaction() + .setCustomAnimations( + R.anim.preference_fragment_child_enter, + R.anim.preference_fragment_parent_exit, + R.anim.preference_fragment_parent_enter, + R.anim.preference_fragment_child_exit + ) + .replace( + android.R.id.content, + PreferencesEasterEggFragment() + ) + .addToBackStack(null) + .commit() + } + easterEggCounter++ + false + } + + val licenses = findPreference("category_licenses") as PreferenceCategory + for (l in LICENSES) { + val license = resources.obtainTypedArray(l) + val preference = Preference(activity, null, 0, R.style.Preference_Material) + preference.title = license.getString(0) + preference.summary = license.getString(1) + preference.onPreferenceClickListener = Preference.OnPreferenceClickListener { + parentFragmentManager.beginTransaction() + .setCustomAnimations( + R.anim.preference_fragment_child_enter, + R.anim.preference_fragment_parent_exit, + R.anim.preference_fragment_parent_enter, + R.anim.preference_fragment_child_exit + ) + .replace(android.R.id.content, + PreferencesLicenseFragment().apply { library = l }) + .addToBackStack(null) + .commit() + true + } + license.recycle() + licenses.addPreference(preference) + } + findPreference("crash_reporter")?.setOnPreferenceClickListener { + startActivity(CrashReporter.getLaunchIntent()) + true + } + findPreference("export_debug")?.setOnPreferenceClickListener { + Toast.makeText( + activity, + getString( + R.string.debug_export_information_file, + DebugInformationDumper().dump(requireContext()) + ), + Toast.LENGTH_SHORT + ).show() + true + } + findPreference("export_databases")?.setOnPreferenceClickListener { + MaterialDialog(requireContext()).show { + message(res = R.string.debug_export_databases_warning) + positiveButton(res = R.string.dialog_continue, click = { + Toast.makeText( + activity, + getString( + R.string.debug_export_information_file, + DebugInformationDumper().exportDatabases(requireContext()) + ), + Toast.LENGTH_SHORT + ).show() + it.dismiss() + }) + negativeButton(res = android.R.string.cancel, click = { + it.cancel() + }) + } + + true + } + + findPreference("license")?.setOnPreferenceClickListener { + parentFragmentManager.beginTransaction() + .setCustomAnimations( + R.anim.preference_fragment_child_enter, + R.anim.preference_fragment_parent_exit, + R.anim.preference_fragment_parent_enter, + R.anim.preference_fragment_child_exit + ) + .replace(android.R.id.content, + PreferencesLicenseFragment().apply { library = R.array.license_mm20launcher2 }) + .addToBackStack(null) + .commit() + true + } + } + + override fun onResume() { + super.onResume() + (activity as AppCompatActivity).supportActionBar?.setTitle(R.string.preference_screen_about) + } + + companion object { + + private val LICENSES = intArrayOf( + R.array.license_accompanist, + R.array.license_android_jetpack, + R.array.license_suncalc, + R.array.license_crashreporter, + R.array.license_draglinearlayout, + R.array.license_glide, + R.array.license_glide_transformations, + R.array.license_google_apiclient, + R.array.license_google_auth, + R.array.license_groupie, + R.array.license_gson, + R.array.license_jsoup, + R.array.license_kotlin_stdlib, + R.array.license_lottie, + R.array.license_mdicons, + R.array.license_material_components, + R.array.license_materialdialogs, + R.array.license_msal, + R.array.license_msgraph, + R.array.license_mxparser, + R.array.license_okhttp, + R.array.license_retrofit, + R.array.license_textdrawable, + R.array.license_viewpropertyobjectanimator + ) + } +} diff --git a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesAppearanceFragment.kt b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesAppearanceFragment.kt new file mode 100644 index 00000000..9d2528dc --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesAppearanceFragment.kt @@ -0,0 +1,182 @@ +package de.mm20.launcher2.fragment + +import android.Manifest +import android.app.WallpaperManager +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.app.AppCompatDelegate +import androidx.core.app.ActivityCompat +import androidx.lifecycle.lifecycleScope +import androidx.preference.ListPreference +import androidx.preference.Preference +import androidx.preference.PreferenceCategory +import androidx.preference.PreferenceFragmentCompat +import com.afollestad.materialdialogs.MaterialDialog +import com.afollestad.materialdialogs.customview.customView +import de.mm20.launcher2.R +import de.mm20.launcher2.icons.IconPackManager +import de.mm20.launcher2.icons.IconRepository +import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.ktx.checkPermission +import de.mm20.launcher2.preferences.IconShape +import de.mm20.launcher2.preferences.LauncherPreferences +import de.mm20.launcher2.preferences.Themes +import de.mm20.launcher2.ui.legacy.view.LauncherIconView +import kotlinx.coroutines.launch + +class PreferencesAppearanceFragment : PreferenceFragmentCompat() { + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.preferences_appearance) + findPreference("theme")?.setOnPreferenceChangeListener { _, newValue -> + val theme = Themes.byValue(newValue as String) + @Suppress("DEPRECATION") // Still using MODE_NIGHT_AUTO + AppCompatDelegate.setDefaultNightMode(when (theme) { + Themes.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO + Themes.DARK -> AppCompatDelegate.MODE_NIGHT_YES + Themes.AUTO -> AppCompatDelegate.MODE_NIGHT_AUTO + else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + }) + requireActivity().recreate() + true + } + if (WallpaperManager.getInstance(activity).wallpaperInfo != null) { + findPreference("blur_cards")?.apply { + setSummary(R.string.preference_blur_cards_summary_lwp) + isEnabled = false + } + } + findPreference("blur_cards")?.setOnPreferenceChangeListener { _, newValue -> + val newVal = newValue as? Boolean ?: return@setOnPreferenceChangeListener true + if (newVal && requireActivity().checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE)) { + ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 0) + } + true + } + + findPreference("wallpaper")?.setOnPreferenceClickListener { + requireContext().startActivity(Intent.createChooser(Intent(Intent.ACTION_SET_WALLPAPER), null)) + true + } + findPreference("cards")?.setOnPreferenceClickListener { + requireFragmentManager().beginTransaction() + .setCustomAnimations(R.anim.preference_fragment_child_enter, R.anim.preference_fragment_parent_exit, + R.anim.preference_fragment_parent_enter, R.anim.preference_fragment_child_exit) + .replace(android.R.id.content, PreferencesCardFragment()) + .addToBackStack(null) + .commit() + true + } + + val manager = IconPackManager.getInstance(requireContext()) + lifecycleScope.launch { + val packs = manager.getInstalledIconPacks() + findPreference("icon_pack")?.apply { + entries = packs.map { it.name }.toMutableList().apply { add(0, "System") }.toTypedArray() + entryValues = (-1 until packs.size).map { it.toString() }.toTypedArray() + if (packs.isEmpty()) { + isEnabled = false + setSummary(R.string.preference_icon_pack_summary_empty) + } else { + isEnabled = true + summary = "%s" + value = packs.indexOfFirst { it.packageName == manager.selectedIconPack }.toString() + } + setOnPreferenceChangeListener { _, newValue -> + val index = (newValue as String).toInt() + IconRepository.getInstance(requireContext()).clearCache() + if (index == -1) manager.selectIconPack("") + else { + manager.selectIconPack(packs[index].packageName) + } + true + } + } + } + + findPreference("legacy_icon_bg")?.setOnPreferenceChangeListener { _, _ -> + IconRepository.getInstance(requireContext()).clearCache() + true + } + + val shapePreference = findPreference("icon_shape")!! + shapePreference.summary = getShapeName() + shapePreference.setOnPreferenceClickListener { + val launcherIcon = LauncherIcon( + foreground = requireContext().getDrawable(R.mipmap.ic_launcher_foreground)!!, + background = requireContext().getDrawable(R.mipmap.ic_launcher_background) + ) + val iconShapeList = LinearLayout(requireContext()) + iconShapeList.orientation = LinearLayout.VERTICAL + val shapes = arrayOf( + IconShape.PLATFORM_DEFAULT to R.string.preference_icon_shape_platform, + IconShape.CIRCLE to R.string.preference_icon_shape_circle, + IconShape.ROUNDED_SQUARE to R.string.preference_icon_shape_rounded_square, + IconShape.SQUARE to R.string.preference_icon_shape_square, + IconShape.SQUIRCLE to R.string.preference_icon_shape_squircle, + IconShape.HEXAGON to R.string.preference_icon_shape_hexagon, + IconShape.TRIANGLE to R.string.preference_icon_shape_triangle, + IconShape.PENTAGON to R.string.preference_icon_shape_pentagon + ) + val layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT) + val dialog = MaterialDialog(requireContext()) + shapes.forEachIndexed { i, shape -> + val view = View.inflate(requireContext(), R.layout.preference_icon_shape_row, null) + view.findViewById(R.id.icon).also { iconView -> + iconView.icon = launcherIcon + iconView.shape = shape.first + } + view.findViewById(R.id.label).also { labelView -> + labelView.setText(shape.second) + } + view.layoutParams = layoutParams + iconShapeList.addView(view) + view.setOnClickListener { + LauncherPreferences.instance.iconShape = shape.first + shapePreference.summary = getShapeName() + dialog.dismiss() + } + } + + dialog.customView(view = iconShapeList, scrollable = true) + .title(R.string.preference_icon_shape) + .negativeButton(android.R.string.cancel) { + dialog.cancel() + } + .show() + true + } + + val systemBarsCategory = findPreference("system_bars")!! + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + systemBarsCategory.removePreference(findPreference("light_nav_bar")) + } + } + + private fun getShapeName(): String { + return requireContext().getString(when (LauncherIconView.getDefaultShape(requireContext())) { + IconShape.TRIANGLE -> R.string.preference_icon_shape_triangle + IconShape.HEXAGON -> R.string.preference_icon_shape_hexagon + IconShape.ROUNDED_SQUARE -> R.string.preference_icon_shape_rounded_square + IconShape.SQUIRCLE -> R.string.preference_icon_shape_squircle + IconShape.SQUARE -> R.string.preference_icon_shape_square + IconShape.PENTAGON -> R.string.preference_icon_shape_pentagon + IconShape.PLATFORM_DEFAULT -> R.string.preference_icon_shape_platform + else -> R.string.preference_icon_shape_circle + }) + } + + + override fun onResume() { + super.onResume() + (activity as AppCompatActivity).supportActionBar + ?.setTitle(R.string.preference_screen_appearance) + } +} \ No newline at end of file diff --git a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesBadgesFragment.kt b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesBadgesFragment.kt new file mode 100644 index 00000000..9e137b75 --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesBadgesFragment.kt @@ -0,0 +1,47 @@ + package de.mm20.launcher2.fragment + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import de.mm20.launcher2.R +import de.mm20.launcher2.badges.BadgeProvider +import de.mm20.launcher2.notifications.NotificationService + +class PreferencesBadgesFragment : PreferenceFragmentCompat() { + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.preferences_badges) + findPreference("notification_badges")?.setOnPreferenceChangeListener { _, newValue -> + if (newValue as Boolean) { + de.mm20.launcher2.notifications.NotificationService.getInstance()?.generateBadges() + } else { + BadgeProvider.getInstance(requireContext()).removeNotificationBadges() + } + true + } + findPreference("suspended_badges")?.setOnPreferenceChangeListener { _, newValue -> + if (newValue as Boolean) { + BadgeProvider.getInstance(requireContext()).addSuspendBadges() + } else { + BadgeProvider.getInstance(requireContext()).removeSuspendBadges() + } + true + } + findPreference("cloud_badges")?.setOnPreferenceChangeListener { _, newValue -> + if (newValue as Boolean) { + BadgeProvider.getInstance(requireContext()).addCloudBadges() + } else { + BadgeProvider.getInstance(requireContext()).removeCloudBadges() + } + true + } + } + + + override fun onResume() { + super.onResume() + (activity as AppCompatActivity).supportActionBar + ?.setTitle(R.string.preference_screen_badges) + } +} \ No newline at end of file diff --git a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesCalendarFragment.kt b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesCalendarFragment.kt new file mode 100644 index 00000000..621847fa --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesCalendarFragment.kt @@ -0,0 +1,121 @@ +package de.mm20.launcher2.fragment + +import android.Manifest +import android.content.res.ColorStateList +import android.os.Bundle +import android.widget.CheckBox +import android.widget.LinearLayout +import android.widget.ScrollView +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import androidx.core.view.setPadding +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import com.afollestad.materialdialogs.MaterialDialog +import com.afollestad.materialdialogs.bottomsheets.BottomSheet +import com.afollestad.materialdialogs.customview.customView +import de.mm20.launcher2.R +import de.mm20.launcher2.ktx.checkPermission +import de.mm20.launcher2.ktx.dp +import de.mm20.launcher2.preferences.LauncherPreferences +import de.mm20.launcher2.search.data.CalendarEvent +import de.mm20.launcher2.search.data.UserCalendar + +class PreferencesCalendarFragment : PreferenceFragmentCompat() { + + private var hasCalendarPermission = false + private val calendars = mutableListOf() + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.preferences_calendar) + init() + } + + + fun init(requestPermission: Boolean = true) { + val context = context ?: return + hasCalendarPermission = context.checkPermission(Manifest.permission.READ_CALENDAR) + if (hasCalendarPermission) { + calendars.clear() + calendars.addAll(CalendarEvent.getCalendars(context)) + val unselectedCalendars = LauncherPreferences.instance.unselectedCalendars.toMutableList() + findPreference("calendar_calendars")?.apply { + var count = calendars.size - unselectedCalendars.size + summary = resources.getQuantityString(R.plurals.preference_calendar_calendars_summary, count, count) + isEnabled = true + setOnPreferenceClickListener { + val sheetView = LinearLayout(activity) + sheetView.setPadding((8 * context.dp).toInt()) + sheetView.orientation = LinearLayout.VERTICAL + sheetView.setBackgroundColor(ContextCompat.getColor(context, R.color.bottom_sheet)) + var owner = "" + val padding = (8 * context.dp).toInt() + for (c in calendars) { + if (owner != c.owner) { + owner = c.owner + val text = TextView(activity) + text.setTextColor(ContextCompat.getColor(context, R.color.text_color_secondary_normal)) + text.setPadding(padding, 2 * padding, padding, padding) + text.text = owner + sheetView.addView(text) + } + val checkbox = CheckBox(activity) + checkbox.text = c.name + checkbox.buttonTintList = ColorStateList.valueOf(CalendarEvent.getDisplayColor(context, c.color)) + checkbox.setPadding(padding) + checkbox.isChecked = !unselectedCalendars.contains(c.id) + checkbox.setOnCheckedChangeListener { _, checked -> + if (checked) { + unselectedCalendars.remove(c.id) + } else { + unselectedCalendars.add(c.id) + } + LauncherPreferences.instance.unselectedCalendars = unselectedCalendars + count = calendars.size - unselectedCalendars.size + summary = resources.getQuantityString(R.plurals.preference_calendar_calendars_summary, count, count) + } + sheetView.addView(checkbox) + } + val scrollView = ScrollView(context) + scrollView.isNestedScrollingEnabled = true + scrollView.addView(sheetView) + MaterialDialog(context, BottomSheet()).show { + customView(view = scrollView) + title(R.string.preference_calendar_calendars) + .negativeButton(R.string.close) { + dismiss() + } + } + true + } + } + } else { + if (requestPermission) { + ActivityCompat.requestPermissions( + requireActivity(), + arrayOf(Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR), + 0) + } + findPreference("calendar_calendars")?.apply { + isEnabled = false + setSummary(R.string.preference_permission_denied) + } + } + } + + + override fun onResume() { + super.onResume() + (activity as AppCompatActivity).supportActionBar?.setTitle(R.string.preference_screen_calendar) + hasCalendarPermission = requireActivity().checkPermission(Manifest.permission.READ_CALENDAR) + && requireActivity().checkPermission(Manifest.permission.WRITE_CALENDAR) + } + + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + init(false) + } +} \ No newline at end of file diff --git a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesCardFragment.kt b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesCardFragment.kt new file mode 100644 index 00000000..38371b77 --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesCardFragment.kt @@ -0,0 +1,199 @@ +package de.mm20.launcher2.fragment + +import android.animation.Animator +import android.animation.ObjectAnimator +import android.app.WallpaperManager +import android.graphics.* +import android.os.Bundle +import android.view.View +import android.view.ViewOutlineProvider +import androidx.core.content.res.ResourcesCompat +import androidx.core.view.doOnNextLayout +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import de.mm20.launcher2.LauncherApplication +import de.mm20.launcher2.R +import de.mm20.launcher2.ktx.castTo +import de.mm20.launcher2.ktx.dp +import de.mm20.launcher2.ktx.translate +import de.mm20.launcher2.preferences.CardBackground +import de.mm20.launcher2.preferences.LauncherPreferences +import de.mm20.launcher2.ui.legacy.helper.WallpaperBlur +import kotlinx.android.synthetic.main.fragment_card_settings.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.io.File +import kotlin.math.roundToInt + +class PreferencesCardFragment : Fragment(R.layout.fragment_card_settings) { + + val preferences = LauncherPreferences.instance + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + previewCard.strokeOpacity = 0xFF + previewCardBlur.clipToOutline = true + + previewCardBlur.outlineProvider = object : ViewOutlineProvider() { + override fun getOutline(view: View, outline: Outline?) { + val radius = preferences.cardRadius + outline?.setRoundRect(0, 0, view.width, view.height, radius * dp) + } + } + + val context = requireContext() + + + val previewCardBlur = previewCardBlur + val previewCard = previewCard + val prefFragment = PreferenesCardInnerFragment() + + prefFragment.onPreferencesReady = { + findPreference("card_radius")?.let { + it.summary = preferences.cardRadius.toString() + it.setOnPreferenceChangeListener { pref, newValue -> + val value = newValue as Int + previewCard.radius = value * dp + previewCardBlur.invalidateOutline() + pref.summary = value.toString() + true + } + } + findPreference("card_opacity")?.let { + it.summary = preferences.cardOpacity.toString() + it.setOnPreferenceChangeListener { pref, newValue -> + val value = newValue as Int + previewCard.backgroundOpacity = value + previewCard.cardElevation = if (value == 0xFF) resources.getDimension(R.dimen.card_elevation) else 0f + pref.summary = value.toString() + true + } + } + findPreference("card_stroke_width")?.let { + it.summary = preferences.cardRadius.toString() + it.setOnPreferenceChangeListener { pref, newValue -> + val value = newValue as Int + previewCard.strokeWidth = (value * dp).roundToInt() + pref.summary = value.toString() + true + } + } + findPreference("blur_cards")?.let { + if (WallpaperManager.getInstance(requireContext()).wallpaperInfo != null) { + it.isEnabled = false + it.setSummary(R.string.preference_blur_cards_summary_lwp) + previewCardBlur.visibility = View.INVISIBLE + } else { + previewCardBlur.visibility = if (preferences.blurCards) { + View.VISIBLE + } else { + View.INVISIBLE + } + it.setOnPreferenceChangeListener { pref, newValue -> + previewCardBlur.visibility = if (newValue as Boolean) { + View.VISIBLE + } else { + View.INVISIBLE + } + true + } + } + } + findPreference("card_background")?.let { + it.setOnPreferenceChangeListener { preference, newValue -> + val background = CardBackground.byValue(newValue as String) + var color = when (background) { + CardBackground.BLACK -> context.getColor(R.color.cardview_background_black) + else -> context.getColor(R.color.cardview_background) + } + color = color and ((previewCard.backgroundOpacity shl 24) or 0xFFFFFF) + previewCard.setCardBackgroundColor(color) + true + } + } + } + + childFragmentManager.beginTransaction() + .replace(R.id.preferencesView, prefFragment) + .commit() + + + } + + private var blurBitmap: Bitmap? = null + + private var animator: Animator? = null + override fun onStart() { + super.onStart() + val content = activity?.findViewById(android.R.id.content) ?: return + animator = ObjectAnimator.ofArgb(content, "backgroundColor", ResourcesCompat.getColor(resources, R.color.settings_window_background, null), Color.TRANSPARENT) + .apply { + duration = 200 + startDelay = resources.getInteger(android.R.integer.config_shortAnimTime).toLong() + start() + } + + if (preferences.blurCards && preferences.cardOpacity < 0xFF) { + lifecycleScope.launch { + val wallpaper = withContext(Dispatchers.IO) { + WallpaperBlur.getCachedBitmap(requireContext()) + } + LauncherApplication.instance.blurredWallpaper = wallpaper + } + } + + + content.doOnNextLayout { + WallpaperManager.getInstance(requireContext()).setWallpaperOffsets(it.windowToken, 0.5f, 0.5f) + } + + val activity = requireActivity() + + lifecycleScope.launch { + val viewPosition = intArrayOf(0, 0) + val rect = Rect(0, 0, previewCardBlur.width, previewCardBlur.height) + val screen = Point() + activity.windowManager.defaultDisplay.getRealSize(screen) + previewCardBlur.getLocationOnScreen(viewPosition) + val file = File(requireContext().cacheDir, "wallpaper") + if (!file.exists()) return@launch + blurBitmap = withContext(Dispatchers.IO) { + val wallpaperWidth: Int + val wallpaperHeight: Int + val decoder = BitmapRegionDecoder + .newInstance(file.absolutePath, false) + wallpaperHeight = decoder.height + wallpaperWidth = decoder.width + + if (wallpaperWidth >= screen.x && wallpaperHeight >= screen.y) { + val translateX = (wallpaperWidth - previewCardBlur.width) / 2f + val translateY = (wallpaperHeight - screen.y) / 2f + viewPosition[1] + rect.translate(translateX.roundToInt(), + translateY.roundToInt()) + } + + decoder.decodeRegion(rect, null) + } + previewCardBlur.setImageBitmap(blurBitmap) + } + } + + override fun onStop() { + super.onStop() + if (animator?.isRunning == true) animator?.end() + activity?.findViewById(android.R.id.content)?.setBackgroundColor(ResourcesCompat.getColor(resources, R.color.settings_window_background, null)) + } +} + +class PreferenesCardInnerFragment : PreferenceFragmentCompat() { + var onPreferencesReady: (PreferenesCardInnerFragment.() -> Unit)? = null + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.preferences_cards) + onPreferencesReady?.invoke(this) + } + +} \ No newline at end of file diff --git a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesEasterEggFragment.kt b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesEasterEggFragment.kt new file mode 100644 index 00000000..c7ae44f5 --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesEasterEggFragment.kt @@ -0,0 +1,29 @@ +package de.mm20.launcher2.fragment + +import android.animation.ObjectAnimator +import android.animation.ValueAnimator +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.Toast +import android.widget.ToggleButton +import androidx.fragment.app.Fragment +import de.mm20.launcher2.R +import de.mm20.launcher2.preferences.LauncherPreferences + +class PreferencesEasterEggFragment : Fragment() { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val view = inflater.inflate(R.layout.fragment_easteregg, null, false) + val root = view.findViewById(R.id.easterEggRoot) + val toggle = view.findViewById(R.id.magicModeToggle) + toggle.isChecked = LauncherPreferences.instance.easterEggEnabled + toggle.setOnCheckedChangeListener { _, isChecked -> + LauncherPreferences.instance.easterEggEnabled = isChecked + Toast.makeText(requireContext(), if (isChecked) R.string.easter_egg_activated else R.string.easter_egg_deactivated, Toast.LENGTH_SHORT).show() + } + return view + } + +} diff --git a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesLicenseFragment.kt b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesLicenseFragment.kt new file mode 100644 index 00000000..d14527f3 --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesLicenseFragment.kt @@ -0,0 +1,60 @@ +package de.mm20.launcher2.fragment + +import android.annotation.SuppressLint +import android.net.Uri +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.browser.customtabs.CustomTabColorSchemeParams +import androidx.browser.customtabs.CustomTabsIntent +import androidx.fragment.app.Fragment +import com.bumptech.glide.Glide +import de.mm20.launcher2.R + +class PreferencesLicenseFragment : Fragment() { + var library: Int = 0 + + @SuppressLint("ResourceType") + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + val view = inflater.inflate(R.layout.fragment_license, null, false) + val license = resources.obtainTypedArray(library) + (activity as AppCompatActivity).supportActionBar?.title = license.getString(0) + val icon = view.findViewById(R.id.icon) + val iconUri = license.getString(2) + if (iconUri == null) icon.visibility = View.GONE + else { + Glide + .with(icon) + .load(iconUri) + .into(icon) + icon.visibility = View.VISIBLE + } + val url = license.getString(6) + val website = view.findViewById(R.id.website) + website.setOnClickListener { + val intent = CustomTabsIntent.Builder() + .setDefaultColorSchemeParams(CustomTabColorSchemeParams + .Builder() + .setToolbarColor(-0x9f8275) + .build()) + .setShowTitle(true) + .build() + intent.launchUrl(activity as AppCompatActivity, Uri.parse(url)) + } + val description = view.findViewById(R.id.description) + description.text = license.getString(1) + val licenseTitle = view.findViewById(R.id.licenseTitle) + val licenseText = view.findViewById(R.id.licenseText) + val licenseCopyright = view.findViewById(R.id.licenseCopyright) + licenseTitle.text = license.getString(3) + licenseCopyright.text = license.getString(4) + licenseText.text = resources.openRawResource(license.getResourceId(5, 0)).reader().readText() + license.recycle() + return view + } +} diff --git a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesMainFragment.kt b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesMainFragment.kt new file mode 100644 index 00000000..0b46bf74 --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesMainFragment.kt @@ -0,0 +1,60 @@ +package de.mm20.launcher2.fragment + +import android.content.Context +import android.graphics.Color +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import de.mm20.launcher2.R + +class PreferencesMainFragment : PreferenceFragmentCompat() { + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.preferences_main) + findPreference("screen_appearance")?.setOnPreferenceClickListener { + setSettingsScreen(PreferencesAppearanceFragment()) + true + } + findPreference("screen_about")?.setOnPreferenceClickListener { + setSettingsScreen(PreferencesAboutFragment()) + true + } + findPreference("screen_weather")?.setOnPreferenceClickListener { + setSettingsScreen(PreferencesWeatherFragment()) + true + } + findPreference("screen_services")?.setOnPreferenceClickListener { + setSettingsScreen(PreferencesServicesFragment()) + true + } + findPreference("screen_search")?.setOnPreferenceClickListener { + setSettingsScreen(PreferencesSearchFragment()) + true + } + findPreference("screen_calendar")?.setOnPreferenceClickListener { + setSettingsScreen(PreferencesCalendarFragment()) + true + } + findPreference("screen_badges")?.setOnPreferenceClickListener { + setSettingsScreen(PreferencesBadgesFragment()) + true + } + } + + private fun setSettingsScreen(fragment: Fragment) { + parentFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.preference_fragment_child_enter, R.anim.preference_fragment_parent_exit, + R.anim.preference_fragment_parent_enter, R.anim.preference_fragment_child_exit) + .replace(android.R.id.content, fragment) + .addToBackStack(null) + .commit() + } + + override fun onResume() { + super.onResume() + (activity as AppCompatActivity).supportActionBar?.setTitle(R.string.title_activity_settings) + } +} \ No newline at end of file diff --git a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesSearchFragment.kt b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesSearchFragment.kt new file mode 100644 index 00000000..9681c3a4 --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesSearchFragment.kt @@ -0,0 +1,176 @@ +package de.mm20.launcher2.fragment + +import android.Manifest +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import de.mm20.launcher2.R +import de.mm20.launcher2.gservices.GoogleApiHelper +import de.mm20.launcher2.ktx.checkPermission +import de.mm20.launcher2.msservices.MicrosoftGraphApiHelper +import de.mm20.launcher2.nextcloud.NextcloudApiHelper +import de.mm20.launcher2.owncloud.OwncloudClient +import de.mm20.launcher2.preferences.LauncherPreferences +import kotlinx.coroutines.launch + +class PreferencesSearchFragment : PreferenceFragmentCompat() { + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + lifecycleScope.launch { + viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { + updateGoogleDrive() + updateOneDrive() + } + } + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.preferences_search) + findPreference("search_activities")?.summary = + getString( + R.string.preference_search_activities_summary, + requireActivity().componentName.flattenToShortString() + ) + findPreference("search_files")?.setOnPreferenceChangeListener { _, newValue -> + if (newValue == true && + requireContext().checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE) + ) { + ActivityCompat.requestPermissions( + requireActivity(), + arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), + 0 + ) + } + true + } + findPreference("search_edit_websearch")?.setOnPreferenceClickListener { + setSettingsScreen(PreferencesWebSearchesFragment()) + true + } + } + + private suspend fun updateGoogleDrive() { + val googleApiHelper = GoogleApiHelper.getInstance(context ?: return) + val account = googleApiHelper.getAccount() + val pref = findPreference("search_gdrive")!! + if (account == null) { + pref.apply { + setSummary(R.string.preference_summary_not_logged_in) + } + } else { + pref.apply { + summary = context.getString(R.string.preference_search_gdrive_summary, account.name) + } + } + val isSignedIn = account != null + pref.setOnPreferenceChangeListener { _, value -> + val newVal = value as Boolean + if (newVal && !isSignedIn) { + googleLogin() + } + true + } + } + + private suspend fun updateOneDrive() { + val oneDrivePref = findPreference("search_onedrive")!! + val user = MicrosoftGraphApiHelper.getInstance(requireContext()).getUser() + if (user == null) { + oneDrivePref.setSummary(R.string.preference_summary_not_logged_in) + oneDrivePref.setOnPreferenceChangeListener { _, value -> + if (value as Boolean) { + lifecycleScope.launch launch2@{ + MicrosoftGraphApiHelper.getInstance(requireContext()) + .login(requireActivity()) + updateOneDrive() + } + } + true + } + } else { + oneDrivePref.summary = + context?.getString(R.string.preference_search_onedrive_summary, user.name) + } + } + + private fun updateNextcloud() { + val nextcloudPref = findPreference("search_nextcloud")!! + val client = NextcloudApiHelper(context ?: return) + lifecycleScope.launch { + val user = client.getLoggedInUser() + if (user == null) { + nextcloudPref.setSummary(R.string.preference_summary_not_logged_in) + LauncherPreferences.instance.searchNextcloud = false + nextcloudPref.setOnPreferenceChangeListener { _, value -> + if (value as Boolean) { + lifecycleScope.launch launch2@{ + updateNextcloud() + } + } + true + } + } else { + nextcloudPref.summary = context?.getString( + R.string.preference_search_nextcloud_summary, + user.displayName + ) + } + } + } + + private fun updateOwncloud() { + val owncloudPref = findPreference("search_owncloud")!! + lifecycleScope.launch { + val client = OwncloudClient(context ?: return@launch) + val user = client.getLoggedInUser() + if (user == null) { + owncloudPref.setSummary(R.string.preference_summary_not_logged_in) + LauncherPreferences.instance.searchOwncloud = false + owncloudPref.setOnPreferenceChangeListener { _, value -> + if (value as Boolean) { + lifecycleScope.launch launch2@{ + client.login(requireActivity(), 0) + updateOwncloud() + } + } + true + } + } else { + owncloudPref.summary = context?.getString( + R.string.preference_search_nextcloud_summary, + user.displayName, + ) + } + } + } + + private fun googleLogin() { + GoogleApiHelper.getInstance(requireContext()).login(requireActivity()) + } + + private fun setSettingsScreen(fragment: Fragment) { + parentFragmentManager.beginTransaction() + .setCustomAnimations( + R.anim.preference_fragment_child_enter, R.anim.preference_fragment_parent_exit, + R.anim.preference_fragment_parent_enter, R.anim.preference_fragment_child_exit + ) + .replace(android.R.id.content, fragment) + .addToBackStack(null) + .commit() + } + + override fun onResume() { + super.onResume() + (activity as AppCompatActivity).supportActionBar?.setTitle(R.string.preference_screen_search) + updateNextcloud() + updateOwncloud() + } +} \ No newline at end of file diff --git a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesServicesFragment.kt b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesServicesFragment.kt new file mode 100644 index 00000000..2fb04beb --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesServicesFragment.kt @@ -0,0 +1,174 @@ +package de.mm20.launcher2.fragment + +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import de.mm20.launcher2.R +import de.mm20.launcher2.gservices.GoogleApiHelper +import de.mm20.launcher2.msservices.MicrosoftGraphApiHelper +import de.mm20.launcher2.nextcloud.NextcloudApiHelper +import de.mm20.launcher2.owncloud.OwncloudClient +import kotlinx.coroutines.launch + +class PreferencesServicesFragment : PreferenceFragmentCompat() { + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + lifecycleScope.launch { + viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { + updateGooglePreferences() + updateMicrosoftPreferences() + updateNextcloudPreferences() + } + } + } + + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.preferences_services) + } + + private suspend fun updateGooglePreferences() { + val pref = findPreference("google_signin")!! + val googleApiHelper = GoogleApiHelper.getInstance(requireContext()) + if (!googleApiHelper.isAvailable()) { + pref.isEnabled = false + pref.summary = context?.getString(R.string.feature_not_available, context?.getString(R.string.app_name)) + return + } + val account = googleApiHelper.getAccount() + if (account == null) { + pref.apply { + setTitle(R.string.preference_google_signin) + setSummary(R.string.preference_google_signin_summary) + setOnPreferenceClickListener { + googleApiHelper.login(requireActivity()) + true + } + } + } else { + pref.apply { + title = context.getString(R.string.preference_signin_logout) + summary = context.getString(R.string.preference_signin_user, account.name) + setOnPreferenceClickListener { + googleApiHelper.logout() + lifecycleScope.launch { + updateGooglePreferences() + } + true + } + } + } + } + + private suspend fun updateMicrosoftPreferences() { + val pref = findPreference("ms_signin")!! + val msApiHelper = MicrosoftGraphApiHelper.getInstance(requireContext()) + if (!msApiHelper.isAvailable()) { + pref.isEnabled = false + pref.summary = context?.getString(R.string.feature_not_available, context?.getString(R.string.app_name)) + return + } + val user = MicrosoftGraphApiHelper.getInstance(requireContext()).getUser() + if (user == null) { + pref.setTitle(R.string.preference_ms_signin) + pref.setSummary(R.string.preference_ms_signin_summary) + pref.setOnPreferenceClickListener { + lifecycleScope.launch { + msApiHelper.login(requireActivity()) + updateMicrosoftPreferences() + } + true + } + } else { + pref.setTitle(R.string.preference_signin_logout) + pref.summary = context?.getString(R.string.preference_signin_user, user.name) + pref.setOnPreferenceClickListener { + lifecycleScope.launch { + msApiHelper.logout() + updateMicrosoftPreferences() + } + true + } + + } + } + + private suspend fun updateNextcloudPreferences() { + val nextcloud = NextcloudApiHelper(requireContext()) + val user = nextcloud.getLoggedInUser() + if (user == null) { + findPreference("nextcloud_signin")?.let { + it.setOnPreferenceClickListener { + nextcloud.login(requireActivity()) + true + } + it.setTitle(R.string.preference_nextcloud_signin) + it.setSummary(R.string.preference_nextcloud_signin_summary) + } + } else { + findPreference("nextcloud_signin")?.let { + it.setOnPreferenceClickListener { + lifecycleScope.launch { + nextcloud.logout() + updateNextcloudPreferences() + } + true + } + it.setTitle(R.string.preference_signin_logout) + it.summary = context?.getString( + R.string.preference_signin_user_nextcloud, + user.displayName + ) + } + } + } + + private fun updateOwncloudPreferences() { + val client = OwncloudClient(context ?: return) + lifecycleScope.launch { + val user = client.getLoggedInUser() + if (user == null) { + findPreference("owncloud_signin")?.let { + it.setOnPreferenceClickListener { + OwncloudClient(requireContext()).login( + requireActivity(), + REQUEST_OWNCLOUD_LOGIN + ) + true + } + it.setTitle(R.string.preference_owncloud_signin) + it.setSummary(R.string.preference_owncloud_signin_summary) + } + } else { + findPreference("owncloud_signin")?.let { + it.setOnPreferenceClickListener { + OwncloudClient(requireContext()).logout() + updateOwncloudPreferences() + true + } + it.setTitle(R.string.preference_signin_logout) + it.summary = context?.getString( + R.string.preference_signin_user_nextcloud, + user.displayName, + ) + } + } + } + } + + override fun onResume() { + super.onResume() + (activity as AppCompatActivity).supportActionBar?.setTitle(R.string.preference_screen_services) + updateOwncloudPreferences() + } + + companion object { + const val REQUEST_OWNCLOUD_LOGIN = 581 + } +} \ No newline at end of file diff --git a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesWeatherFragment.kt b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesWeatherFragment.kt new file mode 100644 index 00000000..17da14e2 --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesWeatherFragment.kt @@ -0,0 +1,166 @@ +package de.mm20.launcher2.fragment + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.SwitchPreference +import com.afollestad.materialdialogs.MaterialDialog +import com.afollestad.materialdialogs.list.listItems +import com.afollestad.materialdialogs.list.listItemsSingleChoice +import de.mm20.launcher2.R +import de.mm20.launcher2.preferences.LauncherPreferences +import de.mm20.launcher2.preferences.WeatherProviders +import de.mm20.launcher2.weather.WeatherProvider +import de.mm20.launcher2.weather.WeatherViewModel +import de.mm20.launcher2.weather.here.HereProvider +import de.mm20.launcher2.weather.metno.MetNoProvider +import de.mm20.launcher2.weather.openweathermap.OpenWeatherMapProvider +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class PreferencesWeatherFragment : PreferenceFragmentCompat() { + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.preferences_weather) + findPreference("location")?.setOnPreferenceChangeListener { _, newValue -> + lifecycleScope.launch { + val locations = withContext(Dispatchers.IO) { + WeatherProvider.getInstance(requireContext()) + ?.lookupLocation(newValue as String) + } ?: return@launch + onLookupCompleted(locations) + } + false + } + /*findPreference("weather_provider")?.setOnPreferenceChangeListener { pref, newValue -> + val newProvider = WeatherProviders.byValue(newValue as String) + LauncherPreferences.instance.weatherProvider = newProvider + WeatherProvider.getInstance(requireContext())?.resetLastUpdate() + ViewModelProvider(this).get(WeatherViewModel::class.java).requestUpdate(requireContext()) + updateProviderPreferences() + true + }*/ + val providerPref = findPreference("weather_provider")!! + val context = requireContext() + + val providers = mutableListOf>() + OpenWeatherMapProvider(context).takeIf { it.isAvailable() }?.let { + providers.add(WeatherProviders.OPENWEATHERMAP to it.name) + } + HereProvider(context).takeIf { it.isAvailable() }?.let { + providers.add(WeatherProviders.HERE to it.name) + } + MetNoProvider(context).takeIf { it.isAvailable() }?.let { + providers.add(WeatherProviders.MET_NO to it.name) + } + + if (providers.isEmpty()) { + providerPref.summary = context.getString( + R.string.feature_not_available, + context.getString(R.string.app_name) + ) + providerPref.isEnabled = false + } else { + providerPref.setOnPreferenceClickListener { + MaterialDialog(context).show { + title(R.string.preference_weather_provider) + listItemsSingleChoice( + items = providers.map { it.second }, + initialSelection = providers.indexOfFirst { it.first == LauncherPreferences.instance.weatherProvider } + ) { dialog, index, text -> + LauncherPreferences.instance.weatherProvider = providers[index].first + WeatherProvider.getInstance(requireContext())?.resetLastUpdate() + ViewModelProvider(this@PreferencesWeatherFragment) + .get(WeatherViewModel::class.java) + .requestUpdate(requireContext()) + updateProviderPreferences() + dialog.dismiss() + } + } + true + } + } + findPreference("auto_location")?.setOnPreferenceChangeListener { _, newValue -> + val autoLocation = newValue as Boolean + val provider = WeatherProvider.getInstance(requireContext()) + provider?.autoLocation = autoLocation + provider?.resetLastUpdate() + provider?.setLocation(null, "") + ViewModelProvider(this).get(WeatherViewModel::class.java) + .requestUpdate(requireContext()) + updateProviderPreferences() + true + } + updateProviderPreferences() + } + + private fun updateProviderPreferences() { + val provider = WeatherProvider.getInstance(requireContext()) + val autoLocationPref = findPreference("auto_location")!! + val locationPref = findPreference("location")!! + val unitsPref = findPreference("imperial_units")!! + val providerPref = findPreference("weather_provider")!! + + + + locationPref.parent?.isVisible = provider != null + unitsPref.isVisible = provider != null + + provider ?: return + + providerPref.summary = provider.name + + if (provider.supportsAutoLocation) { + autoLocationPref.setSummary(R.string.preference_automatic_location_summary) + autoLocationPref.isChecked = provider.autoLocation + if (!provider.supportsManualLocation) { + autoLocationPref.isEnabled = false + autoLocationPref.isChecked = true + locationPref.isEnabled = false + locationPref.setSummary(R.string.preference_location_disabled_summary) + } else { + autoLocationPref.isEnabled = true + autoLocationPref.isChecked = provider.autoLocation + locationPref.isEnabled = true + locationPref.summary = provider.getLastLocation() + } + } else { + autoLocationPref.isEnabled = false + autoLocationPref.setSummary(R.string.preference_automatic_location_disabled_summary) + autoLocationPref.isChecked = false + } + } + + private fun onLookupCompleted(results: List>) { + MaterialDialog(requireContext()) + .listItems( + items = results.map { it.second }, + waitForPositiveButton = false + ) { dialog, index, _ -> + val provider = WeatherProvider.getInstance(requireContext()) + ?: return@listItems dialog.dismiss() + provider.resetLastUpdate() + provider.setLocation(results[index].first, results[index].second) + findPreference("location")?.summary = results[index].second + ViewModelProvider(this).get(WeatherViewModel::class.java) + .requestUpdate(requireContext()) + dialog.dismiss() + } + .negativeButton { + it.cancel() + } + .show() + } + + + override fun onResume() { + super.onResume() + + (activity as AppCompatActivity).supportActionBar?.setTitle(R.string.preference_screen_weather) + } + +} diff --git a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesWebSearchesFragment.kt b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesWebSearchesFragment.kt new file mode 100644 index 00000000..ec3d48bb --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesWebSearchesFragment.kt @@ -0,0 +1,229 @@ +package de.mm20.launcher2.fragment + +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.content.res.ColorStateList +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.PorterDuff +import android.graphics.drawable.Drawable +import android.graphics.drawable.GradientDrawable +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.widget.EditText +import android.widget.ImageView +import androidx.appcompat.app.AppCompatActivity +import androidx.core.graphics.scale +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import com.afollestad.materialdialogs.MaterialDialog +import com.afollestad.materialdialogs.bottomsheets.BottomSheet +import com.afollestad.materialdialogs.color.colorChooser +import com.afollestad.materialdialogs.customview.customView +import com.bumptech.glide.Glide +import com.bumptech.glide.request.target.SimpleTarget +import com.bumptech.glide.request.transition.Transition +import de.mm20.launcher2.R +import de.mm20.launcher2.ktx.dp +import de.mm20.launcher2.search.SearchViewModel +import de.mm20.launcher2.search.WebsearchViewModel +import de.mm20.launcher2.search.data.Websearch +import java.io.File +import java.io.FileOutputStream +import java.lang.ref.WeakReference + +class PreferencesWebSearchesFragment : PreferenceFragmentCompat() { + + private lateinit var rootView: View + + private var sheetIcon: WeakReference? = null + + private val viewModel by lazy { + ViewModelProvider(context as AppCompatActivity)[WebsearchViewModel::class.java] + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + preferenceScreen = preferenceManager.createPreferenceScreen(activity) + val searches = viewModel.allWebsearches + searches.observe(context as AppCompatActivity, Observer { + updatePreferenceScreen(it) + }) + } + + private fun updatePreferenceScreen(searches: List) { + preferenceScreen.removeAll() + + for (search in searches) { + val pref = Preference(context) + pref.title = search.label + if (search.icon == null) { + val drawable = resources.getDrawable(R.drawable.ic_search, requireActivity().theme).mutate() + drawable.setTintMode(PorterDuff.Mode.SRC_ATOP) + drawable.setTint(search.color) + pref.icon = drawable + } else { + Glide.with(requireContext()) + .asDrawable() + .load(search.icon) + .into(object : SimpleTarget() { + override fun onResourceReady(resource: Drawable, transition: Transition?) { + pref.icon = resource + } + }) + } + pref.setOnPreferenceClickListener { + editSearch(search) + true + } + preferenceScreen.addPreference(pref) + } + + + val newPref = Preference(activity) + newPref.setTitle(R.string.preference_websearch_new) + newPref.setIcon(R.drawable.ic_preference_websearch_new) + newPref.setOnPreferenceClickListener { + editSearch(null) + true + } + preferenceScreen.addPreference(newPref) + } + + private fun editSearch(search: Websearch?) { + + val websearch = search ?: Websearch("", "", 0xFF555555.toInt(), null, null) + + val dialogView = LayoutInflater.from(activity).inflate(R.layout.dialog_websearch, null) + val nameEdit = dialogView.findViewById(R.id.websearchName) + nameEdit.setText(websearch.label) + val urlEdit = dialogView.findViewById(R.id.websearchUrl) + urlEdit.setText(websearch.urlTemplate) + val iconView = dialogView.findViewById(R.id.websearchIcon) + iconView.apply { + if (websearch.icon == null) { + setImageResource(R.drawable.ic_search) + imageTintList = ColorStateList.valueOf(websearch.color) + } else { + Glide.with(this) + .load(websearch.icon) + .into(this) + } + sheetIcon = WeakReference(this) + } + + val sheet = MaterialDialog(requireContext(), BottomSheet()) + .cornerRadius(8f) + .customView(view = dialogView) + + val radius = 8 * dialogView.dp + dialogView.background = GradientDrawable().apply { + cornerRadii = floatArrayOf( + radius, radius, // top left + radius, radius, // top right + 0f, 0f, // bottom left + 0f, 0f // bottom right + ) + } + + var newColor = websearch.color + var newIcon: String? = websearch.icon + + + sheet.noAutoDismiss() + .positiveButton(android.R.string.ok) { + val newUrl = urlEdit.text.toString() + val newName = nameEdit.text.toString() + if (!newUrl.contains("\${1}")) { + urlEdit.error = getString(R.string.websearch_dialog_url_error) + return@positiveButton + } + File(requireContext().cacheDir, "websearch-tmp").takeIf { it.exists() }?.let { + websearch.icon?.let { File(it).takeIf { it.exists() }?.delete() } + val newFile = File(requireContext().filesDir, "websearch-${System.currentTimeMillis()}") + it.copyTo(newFile, true) + it.delete() + newIcon = newFile.absolutePath + } + if (newIcon == null) { + websearch.icon?.let { File(it).takeIf { it.exists() }?.delete() } + } + websearch.urlTemplate = newUrl + websearch.label = newName + websearch.icon = newIcon + websearch.color = newColor + viewModel.insertWebsearch(websearch) + sheet.dismiss() + } + + sheet.negativeButton(android.R.string.cancel) { + sheet.cancel() + } + + @Suppress("DEPRECATION") + sheet.neutralButton(R.string.menu_delete) { + sheet.dismiss() + websearch.icon?.let { File(it).takeIf { it.exists() }?.delete() } + viewModel.deleteWebsearch(websearch) + } + + sheet.setOnCancelListener { + File(requireContext().cacheDir, "websearch-tmp").takeIf { it.exists() }?.delete() + } + + dialogView.findViewById(R.id.websearchIcon).setOnClickListener { + MaterialDialog(requireContext()).show { + @Suppress("DEPRECATION") + neutralButton(R.string.custom_icon) { + val intent = Intent(Intent.ACTION_GET_CONTENT) + intent.type = "image/*" + try { + startActivityForResult(intent, 24) + } catch (e: ActivityNotFoundException) { + } + dismiss() + } + title(R.string.websearch_dialog_choose_icon_color) + colorChooser( + colors = context.resources.getIntArray(R.array.color_chooser_presets), + allowCustomArgb = true, + showAlphaSelector = false + ) { _, color -> + iconView.setImageResource(R.drawable.ic_search) + iconView.imageTintList = ColorStateList.valueOf(color) + newColor = color + newIcon = null + File(requireContext().cacheDir, "websearch-tmp").takeIf { it.exists() }?.delete() + dismiss() + } + } + } + sheet.show() + } + + override fun onResume() { + super.onResume() + (activity as AppCompatActivity).supportActionBar + ?.setTitle(R.string.preference_search_edit_websearch) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + val dataUri = data?.data + if (requestCode == 24 && resultCode == Activity.RESULT_OK && dataUri != null) { + val stream = requireActivity().contentResolver.openInputStream(dataUri) + val icon = BitmapFactory.decodeStream(stream) + val scaledIcon = icon.scale((32 * requireContext().dp).toInt(), (32 * requireContext().dp).toInt()) + val out = FileOutputStream(File(requireContext().cacheDir, "websearch-tmp")) + scaledIcon.compress(Bitmap.CompressFormat.PNG, 100, out) + out.close() + sheetIcon?.get()?.apply { + imageTintList = null + setImageBitmap(scaledIcon) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/de/mm20/launcher2/helper/DebugInformationDumper.kt b/app/src/main/java/de/mm20/launcher2/helper/DebugInformationDumper.kt new file mode 100644 index 00000000..20ac7979 --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/helper/DebugInformationDumper.kt @@ -0,0 +1,45 @@ +package de.mm20.launcher2.helper + +import android.content.Context +import android.os.Build +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.io.File +import java.text.SimpleDateFormat +import java.util.* + +class DebugInformationDumper { + + fun dump(context: Context): String { + val df = SimpleDateFormat("yyyy-MM-dd-HHmmss") + val file = File(context.getExternalFilesDir(null), "kvaesitso-log-${df.format(Date(System.currentTimeMillis()))}") + val fos = file.outputStream().writer() + fos.write("Device: ${Build.DEVICE}\n") + fos.write("SDK version: ${Build.VERSION.SDK_INT}\n") + fos.write("====================================\n") + Thread { + val input = Runtime.getRuntime().exec("/system/bin/sh -c logcat").inputStream.bufferedReader() + var line = input.readLine() + while (line != null) { + line = input.readLine() + fos.write("$line\n") + } + fos.close() + }.start() + return file.absolutePath + } + + fun exportDatabases(context: Context): String { + val df = SimpleDateFormat("yyyy-MM-dd-HHmmss") + val exportFile = File(context.getExternalFilesDir(null), "room-${df.format(Date(System.currentTimeMillis()))}.db") + GlobalScope.launch { + withContext(Dispatchers.IO) { + context.getDatabasePath("room").copyTo(exportFile) + } + } + return exportFile.absolutePath + } + +} \ No newline at end of file diff --git a/app/src/main/java/de/mm20/launcher2/ui/preferences/AppStartAnimPreference.kt b/app/src/main/java/de/mm20/launcher2/ui/preferences/AppStartAnimPreference.kt new file mode 100644 index 00000000..4be04ac1 --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/ui/preferences/AppStartAnimPreference.kt @@ -0,0 +1,99 @@ +package de.mm20.launcher2.ui.preferences + +import android.animation.Animator +import android.content.Context +import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.view.postDelayed +import androidx.preference.Preference +import com.afollestad.materialdialogs.MaterialDialog +import com.afollestad.materialdialogs.customview.customView +import com.airbnb.lottie.LottieAnimationView +import de.mm20.launcher2.R +import de.mm20.launcher2.preferences.AppStartAnimation +import de.mm20.launcher2.preferences.LauncherPreferences + +class AppStartAnimPreference @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = R.attr.preferenceStyle) : Preference(context, attrs, defStyleAttr) { + + init { + summary = getNameForAnimation(LauncherPreferences.instance.appStartAnim) + + setOnPreferenceClickListener { + val anims = mutableListOf( + AppStartAnimation.M to R.raw.app_start_anim_m, + AppStartAnimation.SLIDE_BOTTOM to R.raw.app_start_anim_slide_bottom, + AppStartAnimation.FADE to R.raw.app_start_anim_fade + ) + + val dialog = MaterialDialog(context) + val layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + + val list = LinearLayout(context) + + list.orientation = LinearLayout.VERTICAL + + anims.forEachIndexed { _, anim -> + val view = View.inflate(context, R.layout.preference_start_anim_item, null) + view.findViewById(R.id.icon).also { iconView -> + iconView.setAnimation(anim.second) + iconView.addAnimatorListener(object : Animator.AnimatorListener { + override fun onAnimationRepeat(animation: Animator?) { + } + + override fun onAnimationEnd(animation: Animator) { + iconView.postDelayed(500) { + iconView.frame = 0 + } + iconView.postDelayed(1300) { + iconView.playAnimation() + } + } + + override fun onAnimationCancel(animation: Animator?) { + } + + override fun onAnimationStart(animation: Animator?) { + } + + }) + iconView.postDelayed(300) { + iconView.playAnimation() + } + } + view.findViewById(R.id.label).also { labelView -> + labelView.setText(getNameForAnimation(anim.first)) + } + view.layoutParams = layoutParams + list.addView(view) + view.setOnClickListener { + LauncherPreferences.instance.appStartAnim = anim.first + summary = getNameForAnimation(anim.first) + dialog.dismiss() + } + } + + dialog.customView(view = list, scrollable = true) + .title(R.string.preference_app_start_animation) + .negativeButton(android.R.string.cancel) { + dialog.cancel() + } + .show() + true + } + } + + private fun getNameForAnimation(anim: AppStartAnimation): String { + return when (anim) { + AppStartAnimation.FADE -> context.getString(R.string.preference_app_start_animation_fade) + AppStartAnimation.SLIDE_BOTTOM -> context.getString(R.string.preference_app_start_animation_slide_bottom) + AppStartAnimation.M -> context.getString(R.string.preference_app_start_animation_m) + else -> context.getString(R.string.preference_app_start_animation_default) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/de/mm20/launcher2/ui/view/PreferencesView.kt b/app/src/main/java/de/mm20/launcher2/ui/view/PreferencesView.kt new file mode 100644 index 00000000..3579c3e7 --- /dev/null +++ b/app/src/main/java/de/mm20/launcher2/ui/view/PreferencesView.kt @@ -0,0 +1,73 @@ +package de.mm20.launcher2.ui.view + +import android.content.Context +import android.os.Bundle +import android.util.AttributeSet +import android.view.View +import android.widget.FrameLayout +import androidx.annotation.XmlRes +import androidx.appcompat.app.AppCompatActivity +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import de.mm20.launcher2.R +import de.mm20.launcher2.ktx.castTo + +class PreferencesView @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : FrameLayout(context, attrs, defStyleAttr) { + + private val fragment = PreferenceViewFragment() + + init { + if (id == View.NO_ID) id = View.generateViewId() + context.castTo().supportFragmentManager.beginTransaction() + .add(id, fragment) + .commit() + attrs?.let { + val ta = context.theme.obtainStyledAttributes(it, R.styleable.PreferencesView, 0, defStyleAttr) + val preferenceScreen = ta.getResourceId(R.styleable.SearchGridView_columnCount, 0) + setPreferenceResource(preferenceScreen) + ta.recycle() + } + } + + fun setPreferenceResource(@XmlRes resId: Int) { + if (resId == 0) return + fragment.setPreferenceResource(resId) + } + + fun findPreference(key: String): T? { + return fragment.findPreference(key) + } + + var onPreferencesReady: (() -> Unit)? = null + set(value) { + field = value + fragment.onPreferencesReady = value + } + +} + +class PreferenceViewFragment : PreferenceFragmentCompat() { + + private var isInitialized = false + var onPreferencesReady: (() -> Unit)? = null + + @XmlRes + private var preferenceResource = 0 + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + if (preferenceResource != 0) addPreferencesFromResource(preferenceResource) + isInitialized = true + onPreferencesReady?.invoke() + onPreferencesReady = null + } + + internal fun setPreferenceResource(@XmlRes resId: Int) { + preferenceResource = resId + if (isInitialized && resId != 0) { + addPreferencesFromResource(resId) + } + } + +} \ No newline at end of file diff --git a/app/src/main/res/anim/app_to_launcher_in.xml b/app/src/main/res/anim/app_to_launcher_in.xml new file mode 100644 index 00000000..34fcc81e --- /dev/null +++ b/app/src/main/res/anim/app_to_launcher_in.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/app_to_launcher_out.xml b/app/src/main/res/anim/app_to_launcher_out.xml new file mode 100644 index 00000000..5af47f21 --- /dev/null +++ b/app/src/main/res/anim/app_to_launcher_out.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/fast_out_extra_slow_in.xml b/app/src/main/res/anim/fast_out_extra_slow_in.xml new file mode 100644 index 00000000..b70fb29a --- /dev/null +++ b/app/src/main/res/anim/fast_out_extra_slow_in.xml @@ -0,0 +1,3 @@ + + diff --git a/app/src/main/res/anim/ic_pause_to_play_path.xml b/app/src/main/res/anim/ic_pause_to_play_path.xml new file mode 100644 index 00000000..025974cd --- /dev/null +++ b/app/src/main/res/anim/ic_pause_to_play_path.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/ic_pause_to_play_rotate.xml b/app/src/main/res/anim/ic_pause_to_play_rotate.xml new file mode 100644 index 00000000..e4174dce --- /dev/null +++ b/app/src/main/res/anim/ic_pause_to_play_rotate.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/ic_play_to_pause_path.xml b/app/src/main/res/anim/ic_play_to_pause_path.xml new file mode 100644 index 00000000..3b0c626e --- /dev/null +++ b/app/src/main/res/anim/ic_play_to_pause_path.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/ic_play_to_pause_rotate.xml b/app/src/main/res/anim/ic_play_to_pause_rotate.xml new file mode 100644 index 00000000..4f7e9603 --- /dev/null +++ b/app/src/main/res/anim/ic_play_to_pause_rotate.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/ic_skip_next_arrow1.xml b/app/src/main/res/anim/ic_skip_next_arrow1.xml new file mode 100644 index 00000000..341036d1 --- /dev/null +++ b/app/src/main/res/anim/ic_skip_next_arrow1.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/ic_skip_next_arrow2.xml b/app/src/main/res/anim/ic_skip_next_arrow2.xml new file mode 100644 index 00000000..31c62ac9 --- /dev/null +++ b/app/src/main/res/anim/ic_skip_next_arrow2.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/ic_skip_prev_arrow1.xml b/app/src/main/res/anim/ic_skip_prev_arrow1.xml new file mode 100644 index 00000000..0674c678 --- /dev/null +++ b/app/src/main/res/anim/ic_skip_prev_arrow1.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/ic_skip_prev_arrow2.xml b/app/src/main/res/anim/ic_skip_prev_arrow2.xml new file mode 100644 index 00000000..4d4029e4 --- /dev/null +++ b/app/src/main/res/anim/ic_skip_prev_arrow2.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/preference_fragment_child_enter.xml b/app/src/main/res/anim/preference_fragment_child_enter.xml new file mode 100644 index 00000000..2b84e6c0 --- /dev/null +++ b/app/src/main/res/anim/preference_fragment_child_enter.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/preference_fragment_child_exit.xml b/app/src/main/res/anim/preference_fragment_child_exit.xml new file mode 100644 index 00000000..321e7762 --- /dev/null +++ b/app/src/main/res/anim/preference_fragment_child_exit.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/preference_fragment_parent_enter.xml b/app/src/main/res/anim/preference_fragment_parent_enter.xml new file mode 100644 index 00000000..bcaf0193 --- /dev/null +++ b/app/src/main/res/anim/preference_fragment_parent_enter.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/preference_fragment_parent_exit.xml b/app/src/main/res/anim/preference_fragment_parent_exit.xml new file mode 100644 index 00000000..850ec0da --- /dev/null +++ b/app/src/main/res/anim/preference_fragment_parent_exit.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/animator/card_raise_animator.xml b/app/src/main/res/animator/card_raise_animator.xml new file mode 100644 index 00000000..c957f60b --- /dev/null +++ b/app/src/main/res/animator/card_raise_animator.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/ic_wikipedia.webp b/app/src/main/res/drawable-hdpi/ic_wikipedia.webp new file mode 100644 index 0000000000000000000000000000000000000000..3679bc0b37cd1cf2288812ed76f5b45f6d072382 GIT binary patch literal 1114 zcmV-g1f}~@Nk&Fe1ONb6MM6+kP&iCQ1ONapN5Byf88vMius9u8BKjZSJ7d7Mk)$xw zf7|@y13q*RK>@aHQ5UYb z3L!zB`-IPW}n56mHV?M*GT2})+pu(C*x!F(0~HZ?F#7WDG3768O@xd0QW0yME^!(;^o=!B%?nFYX${%&ZN zf%1yW0N_zxU9?22$_=`$XbEA2U^_D)kXU(jP$>zs=P%v2fc^=r-z{%q)xz6 zZ2&kUP!a99!Q^e{U169my(9L(UZyXMj5Ip|_}{YyA=6piZ=tjkG-#fdHBE} z8Fr5W&`7U^fH2+_#C1Uc<^v!gM6Ko?1t7~*^vXOS#Rdc; zP~uepdTRy(LI8Vp7Y6KiV#9GR%j74RBBCx50A>>q0s?n5_B>#^GDQuK6LdRq3w+}n z0LDrq1O$MMt^zb)I@Y*fiAmZ;m@`6d0AMwPfPerV;0Iuqas>sk8xA1Kl!50|0x*k( zfB>pJ2X6y(-#T1)-v_!IaTAY+0mv{01O%=N>!i7l0rQp1S?pvu$ef`<|y6Fmum%M6QYevs!Iz%per0EfA47ayWh#3fMx z77`F2X=ngoycvL4f7k;a$0gf@yWlq;0?05lJw_N+XW0CY3z1rCn`;BvkZ*3lLK4l?Jc1H=_!zy-Dj9+c-U z6kCKRm3S5IcmTbSmiEFO&=3Xl#iDiX92Z@`yYd5K_?M`LGbuL!FmARFKk z!7Je5C0mCNfrl4EfR}Fy@bXUqb^KHCvkMFXehz^lAiya&1O&K*hJYZq&=4RZggSLC g5h2tJAfljV01+kB3?K|pGk`Ea%>Xb!4FQ6K02yrA%K!iX literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_wikipedia.webp b/app/src/main/res/drawable-mdpi/ic_wikipedia.webp new file mode 100644 index 0000000000000000000000000000000000000000..afd78fad9b57e99afa640985c80900dcafe525a3 GIT binary patch literal 692 zcmV;l0!#f;Nk&Gj0ssJ4MM6+kP&iDV0ssInFTe{B83kz@u&~{U=zpX=*tTsWi2JX7 zIr~24R}qq6+cs@yKI8kxww?5B+qRvSZQHhO+qU^_=JRPy00Vxy&bZ`+ownQKge$Hw zyXu@nw%hK6OV09h-B|~1x7R5*IbCwzLEG(f)|tj)choW5NJI1xW+kVEzUXPBkve;9 zHG?H0^fyu`Z`IJp0{@IKNMlb`V=sJkRxe?IPgV<)-!HqY0;D-DR({{Cl_A$42L+3_ zUQxdV`XyeF{A?MQC`pzAiZB5JW!UE*dEZ3KYXXq{990RBQ*BXEhb7cdL}1UfDZnyN-h>R|;J2yoLwb20K0 z!Wx0g4hc|AZ2;i33owRE@!4e%0cLBg$plV8ED@wT1Q>FV)8e3ozFJvEL_5y|x2{pnZk&c7Ocp@XrL&@5}78y`SEg0{o zD`X=#bXQD}?wU0$l{hB>!xYfp0zMMyELcp?Uo>OGT)_rDXk)p%o)f_aKImhV&G^iF zcfqWoWkeu9TmdVy;}ugyn(G2)gK-+7G0AH(zITqgmz$0BI?E?lVgO literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-night/ic_account_owncloud.xml b/app/src/main/res/drawable-night/ic_account_owncloud.xml new file mode 100644 index 00000000..15eaacfb --- /dev/null +++ b/app/src/main/res/drawable-night/ic_account_owncloud.xml @@ -0,0 +1,4 @@ + + + diff --git a/app/src/main/res/drawable-night/ic_permission_calendar.xml b/app/src/main/res/drawable-night/ic_permission_calendar.xml new file mode 100644 index 00000000..616d46e0 --- /dev/null +++ b/app/src/main/res/drawable-night/ic_permission_calendar.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/app/src/main/res/drawable-xhdpi/ic_wikipedia.webp b/app/src/main/res/drawable-xhdpi/ic_wikipedia.webp new file mode 100644 index 0000000000000000000000000000000000000000..36cc5b48369437c9815a210f62ae8716c1fbbfaf GIT binary patch literal 1592 zcmV-82FLkQNk&F61^@t8MM6+kP&iB^1^@srU%(d-8HH`zptvwzME`^QkhuxQZQDp< z|7|;SWH#?b1O;l_&PLk(HR-TzIpHgG0^(BYxLkP=5e`RKmCL1V z7U4>9B_$O&9FB;nv};=Yt|gAulaf%0eMwQBeT!|<+&?Z9!mgimXwlJ7eN5GA#|NJD zjhCejFv1|yl`SMZ;uAl3SKegpO*USjkoTAmeJBaz^fbym!9v2rKJ=O(uS0!N2$PznoLAO}k|Sb*WEWj6`f8gP|l0Y(F^tlP!HcRV0$HOW+~61ppvnWjgf&|3 zfDsX8_o%Qp;C#gjj0Rjcasj{;?Li=p^>EzHAb?Oi9V{nfpAi;ix4>4>@U^EGU^H;p zy&+&DfnX~KE|n&LK!x``4b$~I03*aYTY6W0H$k0(8zN5)%^qz2&K~% z;cOf;LL#gV<<4Y>7ksh;BkrMK*#y|D0xskM1OkPBTo1iWKLev$nAHFiObfmXHwbRx z0f5f&GW$q z-+3Sq2p|A(3WV&#ZANti%R!C83KjqY2n2EsAwA*;0)YSm0p9T}tkd!uI-spBp{WS~ z0R#fUcU~`~2?PQN1i-0$mQ!(GRfjTXsssS5K>&e30J*ZTL_-1r1Oma|u7~ORe*=bE z5PItg5C{YieCmTxZUYD)5D0LQIGlqaPaAP@*35d0OeTs?q5AVAhR(97(v zz(Pa9B+Wn|fI!gNtU{#?AP@*3cli!Z7wk~cqsFPhvXnpofj|zh3s@xz0s#akLlYbE z838-B0sxDJ2p|wh50l|8KZI)Q2?P+x^RSom9cX5E2$>5I2n2^1X_vY#U@eCL0%^_{ zasaK$o7hej0RU@Q1OmYz!{8c6J?f7TwhkZ=tb?^$eF=J66`C6h0s#)eVK)=s^#L0= z1cC~SSWZW;vMv?Q30O-Yz$AU(LTSK#4u^=9AdtmsOf%?PFr_d+F9HPYhwnWEkb)Zm zHme3P*1|bx7HkvpRlsV1%+(&w=7T`)5DPU{5scCS=6;QFTVa|eBxonR>opJv(ryjd zrW)46e}Y}?Hn_*pU|S9=wNP>v2n2$gr9!RcS{VyB;&YC>J7Agmw!)*nBoGJ?cSpc> zI|O9h`8~cH6oTUc_2peiAP_)soqT9uIou=O#_EG#JsHpeZuU0_1Q5s(_XMEi1`NP+ zeE~_=fIt9&K(10KJm*(x6L`RHHu)e_NXTK6>vRu=4i2NAc|g(iP7CdPgKf`RuLM9B ze?9P|&#d$Z!0|7CPs|}t0~$%=LFNT{7rGMA%#S$qxD5pT{X7J`W|=yT{e)A8?e(g# z0DlUuldbfoe~#cGGri(VOhLZ#gzs_A^|E((V?a)fcj4JbNJfk|8%V}+#n}K^F~#8^ zE3P;kli<`NKh89e+3|Nb!e*f;4|P@MM&R15ZY2IPheZ qh67JdX*ej#D-8!l1*PG@SD@j*SD@jb#G~P$q)5Yo_%s}dPY(d1nW3%# literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_wikipedia.webp b/app/src/main/res/drawable-xxhdpi/ic_wikipedia.webp new file mode 100644 index 0000000000000000000000000000000000000000..e6d316a75326fe7eb2ddebb1a703dc6cbfaa3b08 GIT binary patch literal 2498 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 DWK?r+ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_wikipedia.webp b/app/src/main/res/drawable-xxxhdpi/ic_wikipedia.webp new file mode 100644 index 0000000000000000000000000000000000000000..ffaa39670defa69d728f0d106d24e506edddbb8b GIT binary patch literal 3380 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 OIEKMJj4DqJfd2!zjC+0n literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/ic_about_fdroid.xml b/app/src/main/res/drawable/ic_about_fdroid.xml new file mode 100644 index 00000000..3d750757 --- /dev/null +++ b/app/src/main/res/drawable/ic_about_fdroid.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_about_github.xml b/app/src/main/res/drawable/ic_about_github.xml new file mode 100644 index 00000000..38af1f5c --- /dev/null +++ b/app/src/main/res/drawable/ic_about_github.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_about_telegram.xml b/app/src/main/res/drawable/ic_about_telegram.xml new file mode 100644 index 00000000..be85023e --- /dev/null +++ b/app/src/main/res/drawable/ic_about_telegram.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_about_xda.xml b/app/src/main/res/drawable/ic_about_xda.xml new file mode 100644 index 00000000..907afbf1 --- /dev/null +++ b/app/src/main/res/drawable/ic_about_xda.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_account_google.xml b/app/src/main/res/drawable/ic_account_google.xml new file mode 100644 index 00000000..89d2b99d --- /dev/null +++ b/app/src/main/res/drawable/ic_account_google.xml @@ -0,0 +1,30 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_account_microsoft.xml b/app/src/main/res/drawable/ic_account_microsoft.xml new file mode 100644 index 00000000..2eed828d --- /dev/null +++ b/app/src/main/res/drawable/ic_account_microsoft.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_account_nextcloud.xml b/app/src/main/res/drawable/ic_account_nextcloud.xml new file mode 100644 index 00000000..50ae6b4a --- /dev/null +++ b/app/src/main/res/drawable/ic_account_nextcloud.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_account_owncloud.xml b/app/src/main/res/drawable/ic_account_owncloud.xml new file mode 100644 index 00000000..b61d7b3f --- /dev/null +++ b/app/src/main/res/drawable/ic_account_owncloud.xml @@ -0,0 +1,4 @@ + + + diff --git a/app/src/main/res/drawable/ic_arrow_drop_down.xml b/app/src/main/res/drawable/ic_arrow_drop_down.xml new file mode 100644 index 00000000..ce583469 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_drop_down.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_cancel.xml b/app/src/main/res/drawable/ic_cancel.xml new file mode 100644 index 00000000..7d2b57eb --- /dev/null +++ b/app/src/main/res/drawable/ic_cancel.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_clear.xml b/app/src/main/res/drawable/ic_clear.xml new file mode 100644 index 00000000..ede4b710 --- /dev/null +++ b/app/src/main/res/drawable/ic_clear.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_description.xml b/app/src/main/res/drawable/ic_description.xml new file mode 100644 index 00000000..0fcd6f4a --- /dev/null +++ b/app/src/main/res/drawable/ic_description.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_drag_handle.xml b/app/src/main/res/drawable/ic_drag_handle.xml new file mode 100644 index 00000000..68a71905 --- /dev/null +++ b/app/src/main/res/drawable/ic_drag_handle.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_expand_more.xml b/app/src/main/res/drawable/ic_expand_more.xml new file mode 100644 index 00000000..8d57dbc1 --- /dev/null +++ b/app/src/main/res/drawable/ic_expand_more.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_file_android.xml b/app/src/main/res/drawable/ic_file_android.xml new file mode 100644 index 00000000..8d3fd387 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_android.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_file_archive.xml b/app/src/main/res/drawable/ic_file_archive.xml new file mode 100644 index 00000000..f38c06fe --- /dev/null +++ b/app/src/main/res/drawable/ic_file_archive.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_file_code.xml b/app/src/main/res/drawable/ic_file_code.xml new file mode 100644 index 00000000..f7cc253e --- /dev/null +++ b/app/src/main/res/drawable/ic_file_code.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_file_document.xml b/app/src/main/res/drawable/ic_file_document.xml new file mode 100644 index 00000000..f55b73f2 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_document.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_file_folder.xml b/app/src/main/res/drawable/ic_file_folder.xml new file mode 100644 index 00000000..82347288 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_folder.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_file_form.xml b/app/src/main/res/drawable/ic_file_form.xml new file mode 100644 index 00000000..84cf107e --- /dev/null +++ b/app/src/main/res/drawable/ic_file_form.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_file_generic.xml b/app/src/main/res/drawable/ic_file_generic.xml new file mode 100644 index 00000000..a431a5e9 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_generic.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_file_markup.xml b/app/src/main/res/drawable/ic_file_markup.xml new file mode 100644 index 00000000..6720df48 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_markup.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_file_music.xml b/app/src/main/res/drawable/ic_file_music.xml new file mode 100644 index 00000000..69f0a3a4 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_music.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_file_pdf.xml b/app/src/main/res/drawable/ic_file_pdf.xml new file mode 100644 index 00000000..a73638b0 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_pdf.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_file_picture.xml b/app/src/main/res/drawable/ic_file_picture.xml new file mode 100644 index 00000000..0d8d5030 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_picture.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_file_presentation.xml b/app/src/main/res/drawable/ic_file_presentation.xml new file mode 100644 index 00000000..4d799702 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_presentation.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_file_spreadsheet.xml b/app/src/main/res/drawable/ic_file_spreadsheet.xml new file mode 100644 index 00000000..da1dbcc4 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_spreadsheet.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_file_video.xml b/app/src/main/res/drawable/ic_file_video.xml new file mode 100644 index 00000000..349fbf31 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_video.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_location.xml b/app/src/main/res/drawable/ic_location.xml new file mode 100644 index 00000000..e3291a94 --- /dev/null +++ b/app/src/main/res/drawable/ic_location.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_more_horiz.xml b/app/src/main/res/drawable/ic_more_horiz.xml new file mode 100644 index 00000000..da83afdb --- /dev/null +++ b/app/src/main/res/drawable/ic_more_horiz.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_open_external.xml b/app/src/main/res/drawable/ic_open_external.xml new file mode 100644 index 00000000..60b75a54 --- /dev/null +++ b/app/src/main/res/drawable/ic_open_external.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_open_in_browser.xml b/app/src/main/res/drawable/ic_open_in_browser.xml new file mode 100644 index 00000000..d597c37e --- /dev/null +++ b/app/src/main/res/drawable/ic_open_in_browser.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_permission_calendar.xml b/app/src/main/res/drawable/ic_permission_calendar.xml new file mode 100644 index 00000000..2b892e4b --- /dev/null +++ b/app/src/main/res/drawable/ic_permission_calendar.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_precipitation_none.xml b/app/src/main/res/drawable/ic_precipitation_none.xml new file mode 100644 index 00000000..a0df182d --- /dev/null +++ b/app/src/main/res/drawable/ic_precipitation_none.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_precipitation_rain.xml b/app/src/main/res/drawable/ic_precipitation_rain.xml new file mode 100644 index 00000000..ab227fa7 --- /dev/null +++ b/app/src/main/res/drawable/ic_precipitation_rain.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_precipitation_rain_snow.xml b/app/src/main/res/drawable/ic_precipitation_rain_snow.xml new file mode 100644 index 00000000..f9d9a1e0 --- /dev/null +++ b/app/src/main/res/drawable/ic_precipitation_rain_snow.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_precipitation_snow.xml b/app/src/main/res/drawable/ic_precipitation_snow.xml new file mode 100644 index 00000000..50cabe72 --- /dev/null +++ b/app/src/main/res/drawable/ic_precipitation_snow.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_pref_about.xml b/app/src/main/res/drawable/ic_pref_about.xml new file mode 100644 index 00000000..cfa4ef95 --- /dev/null +++ b/app/src/main/res/drawable/ic_pref_about.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_pref_account.xml b/app/src/main/res/drawable/ic_pref_account.xml new file mode 100644 index 00000000..157a0f45 --- /dev/null +++ b/app/src/main/res/drawable/ic_pref_account.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_pref_appearance.xml b/app/src/main/res/drawable/ic_pref_appearance.xml new file mode 100644 index 00000000..d0c84022 --- /dev/null +++ b/app/src/main/res/drawable/ic_pref_appearance.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_pref_badge.xml b/app/src/main/res/drawable/ic_pref_badge.xml new file mode 100644 index 00000000..0ea8130a --- /dev/null +++ b/app/src/main/res/drawable/ic_pref_badge.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_pref_calendar.xml b/app/src/main/res/drawable/ic_pref_calendar.xml new file mode 100644 index 00000000..2bd51fff --- /dev/null +++ b/app/src/main/res/drawable/ic_pref_calendar.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_pref_plugins.xml b/app/src/main/res/drawable/ic_pref_plugins.xml new file mode 100644 index 00000000..e2254d7f --- /dev/null +++ b/app/src/main/res/drawable/ic_pref_plugins.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_pref_search.xml b/app/src/main/res/drawable/ic_pref_search.xml new file mode 100644 index 00000000..69350a19 --- /dev/null +++ b/app/src/main/res/drawable/ic_pref_search.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_pref_weather.xml b/app/src/main/res/drawable/ic_pref_weather.xml new file mode 100644 index 00000000..81383eaf --- /dev/null +++ b/app/src/main/res/drawable/ic_pref_weather.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_preference_websearch_new.xml b/app/src/main/res/drawable/ic_preference_websearch_new.xml new file mode 100644 index 00000000..59514b94 --- /dev/null +++ b/app/src/main/res/drawable/ic_preference_websearch_new.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_resize_drag_handle.xml b/app/src/main/res/drawable/ic_resize_drag_handle.xml new file mode 100644 index 00000000..e9ba7540 --- /dev/null +++ b/app/src/main/res/drawable/ic_resize_drag_handle.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_settings.xml b/app/src/main/res/drawable/ic_settings.xml new file mode 100644 index 00000000..ace746c4 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_skip_next.xml b/app/src/main/res/drawable/ic_skip_next.xml new file mode 100644 index 00000000..eddfe018 --- /dev/null +++ b/app/src/main/res/drawable/ic_skip_next.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_skip_next_anim.xml b/app/src/main/res/drawable/ic_skip_next_anim.xml new file mode 100644 index 00000000..b4f3bd5e --- /dev/null +++ b/app/src/main/res/drawable/ic_skip_next_anim.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_skip_prev.xml b/app/src/main/res/drawable/ic_skip_prev.xml new file mode 100644 index 00000000..131ab07e --- /dev/null +++ b/app/src/main/res/drawable/ic_skip_prev.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_skip_prev_anim.xml b/app/src/main/res/drawable/ic_skip_prev_anim.xml new file mode 100644 index 00000000..d57e660b --- /dev/null +++ b/app/src/main/res/drawable/ic_skip_prev_anim.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_today.xml b/app/src/main/res/drawable/ic_today.xml new file mode 100644 index 00000000..77fd98c7 --- /dev/null +++ b/app/src/main/res/drawable/ic_today.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_website.xml b/app/src/main/res/drawable/ic_website.xml new file mode 100644 index 00000000..c9f2d069 --- /dev/null +++ b/app/src/main/res/drawable/ic_website.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_widget_resize.xml b/app/src/main/res/drawable/ic_widget_resize.xml new file mode 100644 index 00000000..06a54c71 --- /dev/null +++ b/app/src/main/res/drawable/ic_widget_resize.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_wind.xml b/app/src/main/res/drawable/ic_wind.xml new file mode 100644 index 00000000..308e94bf --- /dev/null +++ b/app/src/main/res/drawable/ic_wind.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_websearch.xml b/app/src/main/res/layout/dialog_websearch.xml new file mode 100644 index 00000000..e7372c83 --- /dev/null +++ b/app/src/main/res/layout/dialog_websearch.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_card_settings.xml b/app/src/main/res/layout/fragment_card_settings.xml new file mode 100644 index 00000000..bbcab1cf --- /dev/null +++ b/app/src/main/res/layout/fragment_card_settings.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_easteregg.xml b/app/src/main/res/layout/fragment_easteregg.xml new file mode 100644 index 00000000..740e89aa --- /dev/null +++ b/app/src/main/res/layout/fragment_easteregg.xml @@ -0,0 +1,26 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_license.xml b/app/src/main/res/layout/fragment_license.xml new file mode 100644 index 00000000..42825317 --- /dev/null +++ b/app/src/main/res/layout/fragment_license.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/permission_view.xml b/app/src/main/res/layout/permission_view.xml new file mode 100644 index 00000000..7b9021f9 --- /dev/null +++ b/app/src/main/res/layout/permission_view.xml @@ -0,0 +1,29 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/preference_icon_shape_row.xml b/app/src/main/res/layout/preference_icon_shape_row.xml new file mode 100644 index 00000000..b27538f3 --- /dev/null +++ b/app/src/main/res/layout/preference_icon_shape_row.xml @@ -0,0 +1,30 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/preference_start_anim_item.xml b/app/src/main/res/layout/preference_start_anim_item.xml new file mode 100644 index 00000000..5fef62eb --- /dev/null +++ b/app/src/main/res/layout/preference_start_anim_item.xml @@ -0,0 +1,30 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..4ae7d123 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..4ae7d123 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_background.png b/app/src/main/res/mipmap-hdpi/ic_launcher_background.png new file mode 100644 index 0000000000000000000000000000000000000000..e689775331ff5e057f6dcf267ce5842898a2a171 GIT binary patch literal 1648 zcmds2`#01H6rb@J!!-6wWhjsNI@xwBGHgZ+GZ--*vlKm!M@$hZj3{L=%#=w|IgZz4 zl;?Vlwat2qzNYa=Id+6p9y8vKD9xIrwPXK--5>Xdd++(2bMNQ#Irsi>Z+lT)&>DL* zU@#b(;z~ZQsKwiXL?|-!NoOw%hQL$E4nFaL%kKwbhK>PDdf>j>=oB|d($RX~=hyGN zQl>!&%=Pa9D+o^|hT%2CuC~UiLIL|(igG-c{@@&L#ixkFotGhtFF}cE16PoR zim|dt@e7vqId;8v-9m^%zwzh#yo_4Gmgaq<+wU-g1hcBk&whrfCKTD+rG3=x!;zq3qPfIn2Rr3Fv1?&>SUB z0zJ3)>&NslS;I^=JuEd^m zj+bP2=(jW^L|JV2at!_)eW1X}qX=b`oT-ky7noq7NjR*@xLn?^Bk2t9p~Z|zP$q4K z>E(d!z^#|zO)XpPc!Eu&h4e#Z*j7!L8qT;aeJeVExR?KdxQ=k?_6&Vze{f^;(K5Jb z67oAga032us5c{In8&|>2WG0|k#Q)YdY0acnl9N6;pZ>WXyNUPU|;2FYvEtT+F;Tm zIx~~oACD}afg~u&k{V|vC4TX|$RYw8s61$at-1w{!|16OIe1bwGYkXxk_0Hu0qmG6 zc{i}ERII_d3innew=#cGDh?p|zz5B+RsWZ^E+8t!+Tn=M59{U!j{mdOl+mA%bXDPx z8o(*i4jCA^hf|cmDv^F~$p<5ua!04O8jx2zOnQjTukW`ZwOs=HMoQP?3Vy%?1Bb1q zs!qRG-oDLJgdWFKSbeQ<=?w3z{Wn=u#l$?NY%N>wTHQ<*=;lXtzMk|Td2Zv+ilr8# zx~eF{)|#&=h&yZHvnEgOT*=OFa8EB1dwqiAcWvn-1F0{jMwII^roR&+x>Q3=3Uu4cBV y?7}Fwu7u%KN>eg!MslW&u~~PRu4exc%qK7Hj&;FDm&__Y35?=QCG#DFQvU`r+u+;) literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..f94556075d9b0c2add0679f383c6d18b04b82f50 GIT binary patch literal 4348 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 SC3?V2$KdJe=d#Wzp$Pz&*_tc> literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..679d7b1be68f710f5f538c80e4938aa446653b3c GIT binary patch literal 2346 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 OYnmWkRg|*LtN#a|1`b95 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png new file mode 100644 index 0000000000000000000000000000000000000000..2fe8f6c3be8a7333356f89bf1f90700ddd77f190 GIT binary patch literal 4442 zcmeHLYdDnq7T0btG%>?$#{H#eDq>RO(qwwKOP3L*I?|11hM8<~4Wq&XjDS3UZi9gYlIq;#H zgH}JXjSk(fJ@ms7H}M^ht!2(K=OYH~+d4-^eV_c)d~d_2seCX&_tk)qts@zX+RWc= zU*?DZa6IMfa;>h~xB0>2BbsVOdLBP{qu1>O5p4(zp#NVJ3cJb@=3aEAZpV036hrTW z3jLB-hsU3*BdzkjwxK&QUKO6`dVy*6&L|3GmA4rZ1nC?%5Ui1ekQQct)g>k&^-I`-Me@bP&tmf4q{UkD-o}-w zl8+15$ShOw3Vte$ignQhhsShUqOg^r$fk(9L6_!M)6O83iou2yWmBv8wsLe(5^=xB zt9qqzXQr6>OHtbqPFuGCqLH`T{;=3`{5)!!{B7~`;HV{c2>(9iTj`SEOZ^-T>$$@n zo!D59o04dY38p{vs84+*Ck@!Yy2>C#sc* zkA8Zl7)jZJ5#%y+CcS4Sui$x@hMcMPN_AaF>KoL!P7?{OW!H560D0o7(AfQSRA+U4&^8D z`Lxof)}yg=UExYuQCkeB?JNxyq+BUU%XSpc?65v_+nW+g9X^f7dE1QR>Hb*9yI}6x z-*}7n$Db*x!QJ0@qyOdS9@#L3(lL6WKL+^_Kf8;A>%WehN?v8w1|Kdt_~O8s{;2`W zezn5YGoCpmdT<*Lj&gGJCL}4v%KX+b{@oKlW)$(Qe61m>T{^AyN)1;P@>`dEI!5SAP5=Ogz$*+rPXa){5s1onSslZXipyujO4+er(9XN_T_sNwi%|IpxTIo zs08w3R<1NQOmg(zVSshp#SDha(yq@_&tSw)5-5~@|x`?PPYJz{%cU?ahh*0^I{OFl@wemM92Hp&vs=;&n8OliI{c)kn_0Oq{@XxYRTWNX%-0E^FGEQ2uOLpDs!>K#DeUV zptAW1p%l}%6Fgm}3O>>Av9d?`Xx5d}YYl}xbl|@3QehBw)l$$>CJhxiS*;8knV37T z2k$$s1?_9@54XR8yjO2b@y1Rtn)t_tWQR&MvEnT|;R3RrcM z3%J!<6OsnQXHR}%-R{3G4CO2ec(B7FLwUT^Me*%vScZuW)D=QQ-8vjtGS3rO6p%Mj z`^>>4oehjn;j>TfZHa8KW)&c2-Wjx3@r_n(xnWL%%LXlW7&z>to_qGnfZ$=0PeCB@ zuJdc=BfqNwz{`Jk}j8I!h?uwMx+h*PouZoA`5aU+R zXw_O-WDK!siDu6)BIQR^Agh&|$F87Nx&4NSPPZm}=}FOE8ApacB$j&C)&N_QojfleAFR8vhV))xa;7ZYAenajA3 zz?24kIZ;*J_PqAfvQHCp;n=yly~ZuG3cvxJ7=BK$5Ji-duBlio)6nkvD_QZ4PC7cl zOh1DiWo0na6^QECv+)5_`019LY)wWWoQtTccd?M zx`B%eW4gJZ#;NPApLaoXLby$;ryh-k$I@@i6HUP2@qFN8Z`5*F`j8n53`f#qg6jo^ zn`fuG$-Wx6@Po!8(b4EE`vP@B{!7h2Y612t5WP7p_+s_0ANuTdaUh0&9rWF?2R<&pCAx9}D4bY)xvdswo=sAm+Gw9y& zG)|jck1l-=<$*T63a?E!gl)Lpz-m1Zv;GavhmlBx5;#2=*a}ieb=7lvUun*C%-b&y zx)uRhTJln0butVLp34WOT5AAv!#&W-S9fsu@h$Bl{q1&O-v)YO$2B0vL`U;A<{1Gb z8N*JTw!=6M`%|C?=#8khfs}YC{FJeX!_Eh^t=1vwW+2w%J2dRd!9xfDibGJ*TKmxB z$Ra@RC*Uo{1Kg|zKG+0t4ybVg_M_Ev)DYEK|9|-Z;QTK!hkh7M*0Sp5wN?Rzsy3F& zwukzzT0#pP_SJt=wQqymsT)Y%3WW#h!}g<`o~%n?xE9^8#R>|#3p_gjCT)Vk15Mzh zl_(o%Xj&c2EJDf4Leae;b7=Si<|JD7WEWSl@i$XAxt^p$UoJ$$fcf_@t>lF2WCge{ zy90D$;2n^?80>5Ms_GO^o&r9^V~A?~?NBOMbT62s(Yh58#?=sMqNyq`(v=n*j_Vb zZ@ebU!T9J<^<$Gup{Oizu#-UmTHI*YORwH-$U1xtbHRu@zeffGdg`24Lt z&CZ76r+&@qH-^HA98csZliPB{WqHCu%K~OlCn~oXP)XQUX8QHtQorvECB4l`iz0K= zDoo}HsxOJAJO7&e+$--QXTw(hzUF_T7*Tr7sjA;GAcICh=N?3l7CCDV1)46b*Xm;J zvJo@lOq=<*dC<1W{yRO`#~eGdkFPt__n`Wij=ytBOk+qf(m%})b&1nsv@t8@)eWjZ zH<*xmwalpXtczP29S`jVZOt<2Izu_mKI^se{*u)cipY&I-7sj$aUMN~{l(eeLvKfg zX?E_+Htuf3%uQ0%i*<1UF8D#?7tzkXNMC)}xcK=oot*)~nVwsjVK#`F>K7pKh>lks zH^wyML${V(Z{$PjZoGKwKmV(##E8X24vZRCvEV+b)3oPB;lF}(H(ukCFN_TfZH~h{y z&ok#AcxPT_V1}LhzV7S##wWhJ8>Fl#h53x|*`r5~FlD60RUbWiEPnrsh6=o5v?fvP-gc#1ppL$c%WpW_rm zMaQ(&4@$|oGQT?lsXM7xU8lqe%;AZT!OG^}g9!8S104w9(;4}JpGCki4Qy=j@5%}n|LD-#>T(6j+U)eO+PJ$Mj;uo_)-!yE9x3q zK~dI|PIP#XI2~n_e51R$<>Gaa^(!TPx`paXWR^5q5UwipD}L{8c|4Y3kea&26#hQCm4 z)_!fy^xqdhnkxi>ermV4U3=EbH}5~Y4!dbMCz8UyDU`TB7{02hZ~I`uvsA<&j;L9@ z;&opJ)7Es9TI?fd0mymhhUZ)_4+4F-j~eIe_hlwHR<&G=r^nB) zB3h%iGH)~5_Y(r}`C}IYu=>!WpNfEmKHf14`rV%i%)odNNKxOQsgb{3F=F5+sW7j7 zVW40}FTrk5%W&}Hw+pC+(A(M@Rq)q_=$R&%A;dqTF_LnT4u0sF`A4Fr5ZGI2li6T? z{qLTDGN4860Wp4P=|FVRykoOCnqT(E?Ca6j687**eVwG@_zLh#iI3NUplzY?6SQE za%sV2#C180&#(aW>w1$r*K5-~Z=DXYE%;K@Ba#xh%quo?RDm>_7pwXVe=dxtIB3r_6gzt2GNmzx3Ro~grs!qckKRFDp9;YXKVHlXe=bnfNEIy^ z%TED`4JA=&0`ix&wGh`D4t+g0Z$1t9urh;YshmP0)@~|9FiYB)n!2;w^aNPrql5E! z(sFokdAjik{46aYYUv?O7tnMdq3u|SU%epfS_I}#l^NNqX%B2eCS>gywAxB$C3LS| z-^i;_Q%iO)Lvur1f!LS@p4pyGEh{oGu6#@o=&2s?KQ=H`<@a5u0dhytj`c?)=~VvB zxPn(+VHd~65suEEw8ED_(-hF)TV~SZ;81W7+_bZmakM{rHipbYL@X?V%iomaP=d3- zC+iRM#L}3{-NmL_lgJ0>+yR>t|12*m#D4q@#LV9A1?MUb3Q$li7CEbSH(2Q-@`iQi zG1|49*L00jZz`}nt;$`tk-ny0p0WUnpdFm(@g#^s(s1W7I6BtY#lYI3eEn&&G`ZC$- z1=QM5MMK_lch8EF2*;EouF+CY!CG~Hj)(ATR{6%IVpBx z#8$rV{J5!YPyzlo1E)cElHT~7;C3fT@6K{Ud^EMRbw2IPjfTTI!_0Frlag49JRMIW zGJ=vky7ztyZ8LAhvSW=kEIjobPGe0_^^NQ`(MnaFb1EW#?sHxz(}ctuaQ&?(NRA3U z2VKf8SGd64x@G4d5C0 ze7XNYTm!4j*hg6f9!3PL!a7|{u?MINRfs9h#eM7XHSDx-eTK-Zvt87A$vKQMx45WO zP)?Ibo;)r0`=M_biA#V#h30rm1}xUPw`eChdoF-S0}vD;NQrQ-B8-aumbMZsA$llG z`yxvA9#Ar^r@c*xxZbmaQhKE@ZeFBHwAgo^L0Hl()Mws|jEgz@s@W@|7T#^Qe} zHeeuZdSSie_`SI8no)w)7(iO`=uT{L*88?qnbp1`NSgim?dj-@ff2D_^P)bx_{y5P z4wi70Q$E{uAGXvr%PlP(BK`K~zWPnBDRaq!{cKbM&fk|4N|Dy-=)|v&wnbm9Jvo-aQwLs4=%!pVGJfrb4zAu;*A(&ttHHR@8QnU+2KyOH= zb${J|z?+ZL-hNVFbk(f6X{_xoXM-7;`opLj=JDz~2C*^ntkL~G{YbfUxiz3eoZDEV zxL+?*ELFs24edI@oOc{cRF7XuIH#kd2g##8G=Jr+srB@o{k#6%GQ zidy-Gk23J;kVA?%Rd(pei)mZde)wHH_2#rHW+C&cW>uq_6QTFayl2QK-QCVm$V3ga}^+w81Hke&CMF2@~iY|An^Nq z=(&@Nz-Q_tT`WubSMt((5h4J^WiqyXU=DhU`62_Fm29I0=Vyn$Nb4>f#1>pj%aJjL zx?~oFr20>ACP&Ah9$6Q!txO7Tyx)zh3r1ImZ zpLBv)Y6!1Ky=yKx6i`52BjDCkvyS3?r6r$O z+CZ-7z3yPdNRL`tp%d+WMbPIZL#j=ny9Qvgm!mi#;Z|wj?A9@dN3$m_Fa8T}m}}whFDg z0a4&5=yGnP1>@jeeyq>~HP!cgp{>036cXy)tps=IpIxjDNLHLq@`wGXmJ3U#ueg%G z1aWAmijUEhD}o%6=_hUPe$%ijeit*d49?u>Ri?aSkp_!tAZl-RC1Ufzq0Z@}@t}Us zRbh+`)^73>xj6tFW|z08OdTQX^2G#BJD)cvuve&Is7WW5NG^hZCQi84t38h_?K!EL?T4nnPVPcVpnzbwq z5V9j*@gX}|MMWUQbOM8r3Y=2e@gk+S>i)7Sm;Mpmks=%MUz;I;H=trSIBMS5n@Q{F z9hq@{_K-r3nC<~goDSvK#nkkHVo&bcK2{iUYj|)Oa0cMch*tcV6kYbd5`~(61#WEu z^V*yAPgfB{jEmz6V-!Ly^jU#4LD)r}idwbQdoSwx1_t(q>HP7fP%~K=kQ;iifW~rY zkWW%gkEOvTs5 zeO9wy;Zn>_3;)f_Q^(BJ&~69y3>Af+A|ybrQrAp`Vj=o?SF z$sKW&<30Gk4(6*6^1?kbO<*WG`C-_hcHhc#9TKl|V8PT0tUmuT%N6Tt@@HJq>GB6oSt) zKr+l2nIv$uOqKc2*J5paAc1G%W@S6xd_kFhHcEn7`NiOJfA-ZbY-Ml|yD1xAP_7&Y z5Du9*gJc`bj08Cr0B?)tiwCvfD`P+grg5ne#`ObQ#?*6ZQ6?2xdyK>OD8qF2?uK7O zB1D$s_5Y!3Ki17k>u9D*J=YCXpb3x1U!5bEnl<%{HFmZ={I2&@-)QKrzK7rRDwq;sVn_JN_DvTy z^*x~u?bXSq$1FM0(1@c3MDrTTyqr*|VQkxegeZ4`OUug_m%oS{cZ!~cOlURq@Gn=6 zzB8KijLS;c5>)yQ>&9Gts#`cES9My)`qo4h#Z>t`DAUL+VTV?0HFpf3NWmFUs%r!l z9LJJ#N)J_Pchx4x#O^k|j(A1$V{B^eXE;@a;0DG(BKR{1l}@EGin@#gTCBi55!zd# z&{>FhrU^HW7|=ay6w^XbNrH$;TJ2E`0GdMtfsp7#pgox{#lAtx!k%?Fy!At-?{z1K z-$1Pt>>|(bs1ccr62wyPf&S&Au}~Vde|@wxWuZ!cxF8U;5DU&^6tm7o1wd$QYB{rz zzhE%fm_p6^(m}QR3WOuk!obI;Ld;f~=Pwg{Vt}o% zx+k3_-1cq>(Kt`80@7emtfzA!_Ng1E?Vnn!ykfWnTr{zC`f&j7`gnK14z;flj%5ei zKgzR$qiT$HTEJk7Q4C?syX=W40k)?W19xEuRu6zB7K1&y6_H$@cmw^KWP3;FoWW5w zAiVHM_8@{iGZ^C`{zR4qDbNEaZ26w@Fe)@RNsNYp8~lq;sXxY_G%13LWyuAqJNdDC z6Y#m0W|oV463v=-y!9i!(C%T4GFK#1wY~DlHI0Tnkn(xSVhnMXn4mh{-zML8_ofNq zq@F+Y@v!|lG37vY4~GkVeg%r*KjQ1vtcSRT@vO8b{rJki5AYRWSfNVbn zHnu*F5v+fB5;{)DU~HBr5D-?CEneStF~ElqFN;BuW>~*BwX@Z7yG;Si+ouVi1XOBp zspz}{%U2H5PwC;uK&4!2Aox_;hcH!&Jk#c)JlITg*710T=|Yk@#01~cl{NYDmnDas zu>-eibeRpt_D1loTNz_qAwI=Ybd|h^t#B*a2VmJb@Z8ux&Q2|QeH^T z#M8(ZzgPqBrvlLcYWxn$tlVjtRT#s}0IL#2U84~xiNr!aW;~f)LmLxx(X~zo6b02% z5Ie5gvX}#sHeMq?d*Aa6bsc2-v*c}B9uuqItK#p*X}g|nv^>-f@W|w-YS&d#du1H8 zEY~b|1AybX6V3EHh%Y`}`*N3YROOCUQMcGVNRm|oL;)9vB?f^o#TRVuX49N}5=($C zwuOUxNToI0-K{qRD4yzSGsICGW}-a#27m{k-yc{`*j0sjBD%y0(%s9Z`&Fo|T_<0b z9kGPQrZmJgxQJ^}1pb9}!S)lS-~mAd@1>MEQ`>gU-KoP{=jf%-W9tN046wxOqhQl} zPkScHa%9pN`}mOJ{E5YTp2=$N$#dXZNlap42C|=>srRDoEx?^SW| zJnZ2OuU33*0>JH&QLVq~!glTWkg!{M zjLv{$?DrbzSLB+!2do{V4R3Jzae{mKI7XCF42Sh+r?qlU0+A&F?9XTG`%ZYK<{(=J zJn{2%q~hldUpYFs1(VoG@wn-yw9JaMOXi-=2El>HF<$;2f)D)O{w?m_4z|ZgrNp+Y zmsBkk9pG$7iMjhgvsm(xn8d6f0S=!6z|fZhrM?K>d{|vGEZ96$+YQQte47B;#3sd>6MP29g9QxJz z0qr}|R^ebLVxO(lta~x6Pd+T;C6av)5xtX+(V@)V4*y~>>W|BgAe;`i*hLLq2vT%y zXo;l?lK$7~fnmCKiHY~nS{BL>6^S2b!tcQT3aMvTRz#7oniKl!qoJd7i@ZK3@q$#r6XgwWcSqxOcbNmy`=TxF!= zjxP&;)E)&G>I$LPoldge<|tj_9IKPl`~&jtaE{@*FrUo5v01zH-> zRXB^#e<*CoqD{?V*fp=)=au*J?~8XffS8>B3>++k7MK;bwrkzrBH1Yh@X)Hv7tTS4 zQ(h#;4rEl_F*;I!EA5sQHb@=bnz^oe8 z_RCAOFsiaK!GM&gKWh>ybN=&Uqd?mzS5DvRA=7t~($?eWgZ@QY(W>N2)Y4$jqr9K5 zI+BG1OjEc9AvFP`W+YOB13ZbCS8S=h$_+>z)jKV%cx?c_m{j~*?xLfMO}u?p2zEqf z&Q?Y4oVsEl{7v`onN&wbAC-$=>eB*P;6KDQc||}t^Ya_v=!HgqRQFJ$c7F#5UO6_) ziMTb+lo>Khbt?yv{jBs}sN=S#r^u#gci~AhC%{?n^Q^O1Zra1(%sq)Ix?&J`AA^Bm zkF3h`suTg5JZ7%8V_^*Exm{Ld=88zK7YcOnIv4mD0X<;Igk6UeR;NsH#7v2w07>`K z_|Dh?H2YFW+-Dbr@uA!$wPttc#k<$D4Pk)UzJk7e5W5_4Z#Bx2Xxh{i}8?dh9pd%&i_@(>>nY#!g2%9;U;22fmS8!SGC9CPRcIl z*osuyY2CYqQ$8(*O>vcYJHxtHZp}TN((L{{0e&thhYQ-)(RsxrXT`LB0o^>)*3or4 zLJU-{3;=4yBnm4 zD<58}-Z1><8r=GmIlU~U*Hv7f4~74vF`6LL|DHKbj}hXSdvMVSdRI0Ts(oo!+r(0j zeBj)|R2jHo^qZ1tO^2*F&=+E775!s0pj z9rh^I#XWmk3S)>YBjd%`0LcS)tV4?Kr++<|79?4Tk_q4RZqgDJfreoR7Nf0p7I>pyxNRD7_9zC3g?X zywY1w+1!UuKMgPXPz{XKk3h-5(6lEJh~F`?sXpp`Du;BhX5x~3^bfmBqY{P#Pp3kA z_7ty2$iZxe8d+Llz1MHx@oi=<7r;WDcO0%?3qhFa$#2V%iMT%5a%@;EQz5*NnGbn= zb?-rk(VOgYgvhsdQF85x&woGb%-|9<)fyd&}tX^X|J zkLk1ervVp7Yu;7O&0foYlm?n=>*_A%N1OWM$60F@2*1B739`<{tT1SLEa+qRH+yyU z;>Ayk{nSP;cFX}%_RreF^5op64Ts{LAAtOo0Aeg*;RY;ZqyA-t+56K1F78?wFU%=9 zRi9Zl3@*Vx^&t>iHfoS|+=UmW9J}DTxhl^;l%#92XMx}F@o>QV_GpOHeB^e1veJSi zQRr@?FN!9Zb3m}+&C!hGfO(qtZCw<;;8mhUoT<96#cF@5V|`|(v6+Ix?t2WziEpqjbK?gWq{+N%1;Wmd55A!xpk$tlv>fxa{U;`igWyr}LE3Z&?dTT;rNh zoAXObZjn5Y$y&Acbqe;L{OedFqoX(K)k}nv&2J+Qbipd^UWnXrK_D7XRT?2e&@*IKRhvgp+NR zh=$IG(b?2gyU}1+I=e~d{13c+^oOL;?mhuVC9lb8v4{dA>C#*wPWns|%lLBx$(w>p zPSbrgBjY>Vv8CjOZYvRTd8JA>wgnPHiMl4~VOogZGV=W}>$yfZP9qQdjzpuo%bmW1 zB{K8jjorSDKkpdH2JkC6Efn>}#k0KkTdy=VDu6zktu+5#uTi5DaG8N&-k)+IrQk`(+*1n*wXC4kvvF(Fn1Ou8(K6IK*3ZZgH;QwAvn2TXsmt$J)L0=)Tni8Mt&U5tG8oD2x8Ow1XQ zm<_ok7E>TWdzg(QYs3NZ!<3}R^FeVwcV9MZ&3&_EIh#9D+r@}kKAn-AvC}`FwPK6^ zRc+Q6O~67q=VzLq>yQL)yiha$!HZ4Q@a|-mc3>oxXC73*r3%7G)Kx1}8~UPG58d$8 ztF6fyV&*Wwy(B<+G}?{8Wjciyi;pzTl)In&>KZEc$Lbjf=^cDkB~!iAMk5zD!!o_o z(HqMol*?G|vVeRp>SeAD#}_He-*t4~7^a!;TUI%^Bwyl}2G7Q3r9?%)l#uUX=%@pN zx?#<9hdyO&G;Yn3l`Rm7PPy-pza+mQZ6OvAZX{qls1aVO=w<5_$ta;A>oRIbXhd{o zdq+e>^t42`y6_;?<+Pn~)D8imbiFJ67&9;W z6jGz)^KolBOi}qo-G0dz6=jjLW-iZ(+I}S-zRQAURTubOZqSh$sa+`Kw?IV&QM*hY zJ=tPrg1m}FGxoNjId!<-ZDAqMd zTV*?2KRO==>~6Bctaf**`Uo^f)H{Hf!eYXIGgW5X#u-R4d5RQaZx~3wI>!L{U?Zlt zV4WVzO`P1!eNf!$S3Gbsy)3d?(Pv^JZBb9M;k@WZBtYNbVd^kA8h%@G`0cy>MhIJ+ zfe%Ns^Uv9}Kw}$JW$HpbZky>E+h1%}1htGPa1nGGN(=Sc3&lxiR&KjF-jg3bFCYxN zmM^E^+`Y~)ADGwKY?H-b}YBFGbVzX=ggPYvGV#4e6_r&)_)dc{VQ?vqK!blA9O z=W)`VcI>&-c7~DtN1vrp1IcMqmiX72E6y{}DLB^)BnaI9wSEoC_4zk{c`34qR-Q9WuyAzBduc@w9kXXU#f{7## za#yL;Ok>P&?!F5fW!b&o6gzn!N4%L}Kmj;1n9A6^Znh4Mo!xtG z=7diqf-)Rr_#CR#>hn`hr4h26AflzD7kyoeh0MGGuQC0-ce|1ki)%5D$FJxZKT=)* zBB2jl4e^~|G9^U`%4$%B8V056ZRZoGUa!T6sXgg?vs@x2*wC@xUF8&HH$7{b#{mtd zsA{t=4DH*9TB{%vc|PKqowD~9RMi-Bo%B)mc^ukrcP9_OU&i^*a{d4lcDYUAXIXsN z57-_{YDH@;MLYCzj@JLifN!0_~QEe_J)f2flHPtT+K52K4Rs8FurU zr;j^gqggBG3c91Bvl9cwisjwR#j`flEM6C4i7c*Fb$4JXWBn$0QciAacq|D{Jww#_ zu5q3{Cz+n$zGVm8Y?5vz|3!6qt+o^@Z)r0WlD~D}wgNRh=TqiaYL0LcAF`k8Vt-s| zE;js*CJ%PH_0rtqG@r<`4* z=YF?NY3N-dqw8F$Vfy)ewi;emm&Hoc4eTSuwr%7~KGoFOen;XpIz-q}iUW+7+us%1 zf!T7G&cjQJ-9!pS z!-inuTKu2HBtcG2>*Pc<66_&Ie?d~jO?|&~!m!-=BPCJ~n(AWU9?+j(JisaE0*zJRZoaL zM5#OSVp4BwT-pLfM@Qu06>W+m?rv%C&ksBFZzq0O3+37yyV?KRK!XjClYZHXl?kOW{d0SdDoK)!jKob4KfcUx-_YE)6Vf&IXsgYd(l{O=?7i*?5V8cZu(nDY3-YZ{J?}ewTft^GnaJDVEpc z{O|6cJXrr#Q9hb~x*27ZTovAyLtN867!v!o#daw}C zH5Zs22!*C>&a`;nX_+NE&(7lP-o6~E{I;+IOjjxZZe z3bTQs?0`F64ayyk{p>f?1~Ax_ss_zMNbq3e)o#N$6SkX>A};Gx*3@yU5AxHJ=$#WW zPXhg1kA0h&<{~U6sc_QE^ol0`WAx{wZ+VwT3Qnce7oB?lhX0KDoT53q!ZSS`t{7vB zDF(YbXpzlpIQ=yc+UxFKKQH8Vcs}3j49{%3=|))z=K1^bqE00N5y^tf6i#Vws)xS8 z*?4n0joeVr@0E{qn=g+Por^<8^^Hj>?X6q%rAVnJdVb5*M1fjfbB4dAQcNsmN%7SF zR!NDPcb3;@=A1*xo7pFYvPHg1e~H{Mqu~?E?1%evO|bH6K?+YI+>@5c*;&tE*~L1> z3&R0ZMKX1G$#KE(1wn59_zByV;)*Q?1>{+nG?>V7M1u%-02#7MpY@4=FG*ZkM#t^ey^6i)Bk>v{bu z%c0-B%|+YkuUHMQ?Q$xKt8tgNiyV}OmEEYjf| z|DDWp_TJI)PeDg8UQYaL9glkTT)`HLg`ZDSkJf}dw|nT)Jz$+71VWlS_*&}~JSPM# z?4Fm$4d(qemPvpaYzA!8PVg({?Wlhn^T~+`HlvFpMVgQ&%rHMzQt#`7-pk9&^dC~a zT4et*>4qIYWZID+Naa5Gkvmjh2Qra7KN%S8gaD7r@&t@A2b0zmU8*skInH9gR~ftcPXrAeDNs>0B(5dm!Cgrvjh!TVUd>fT5w1lgG~(%FjJH=0p;k#$Z&=#e?l4z0dC?EmT^8i6-frf^ zdzoFJG~f4%+iz8Z-6szrB9~0_bpEjRQmOAUeh%#|MLFO(`O?)N-)?Ccd3MNt&mW)? zL882v^K`LW)oRg=^}5{1Yhjo!S^_Gn(Y4h#mlmoQ*@#6$UEK(0XuE&*6I(0QjL?c% z{!e?~G>hc{TM)Jqoga=P3|KxJm6_~j4?%`$dFjcltZQGOCvFX_t*yIS{hk)}iqVRI zQ7J^?F%A07etDHMysW_a@bP1_{b-?4b3kEHW#v*M0B3`*piWvP*DDRduYQ*T76^%I z22m!A;ShMmg{*DKby(5)%pfj)a!PW^66}|^=b-oz< zhnFPam>}-84u@$Q{3Li$ouvME3C_UVvl++C!oqk%_l;y=&M>u^dvAB{oe)qj%@or+ zU);HQ7Uvf3-A3|}+2~Zg7sWs1WV+HD+h*u_GHTWp?v4Kq(aR1ZZ#?Qx<7Cn-`mR

zm?*g%qJlpCDn2`<~~a=zb-K`8cm z>Dc1!&!0pd6FkTWY3aVqdqG4=!5;A6D&ha(1t~@3yV{Z>ubWO3L}pq|q-Ewab8=Q7 zxFd3lEP<>o*N49W<RiM=vyXj1k)*R#iK=b86#EVrV2QAp?Mx0deh z^`k#kz>b2%3ZTan_aYKHh5>c_O4^0EdT-pE!t{2k!Q|zspXe5Y-noA{mJgT z65qgt$tIWu=0EutmpFR@^!HY}`X`qz74iCp2J%3=!n=z=n+81Kb(1KZR5AO;_#$%B zx8GrByLv$O5`?^OrOtL>c27OXg_x`=pOnj?FA5cs>5~IhFqy>|w-rt%)O>clk#>Y6 zWVN-uy*>%(6!4@u3G8&6;J%_Q+}(%QJT*t)+?lqP**3LdH&^q?NMzj)7QHK+Z2Afq zI2h3ZU8<8!c}*o1Mto7enUrJpSL(wltgy!O>NGUkb}aLDABiU84C>^R7NQm#Sd-74 z%Kz!_k3EaS{QQw7t)EZr>+$d*D--2ZFDyhgcY&`T0aLnbaVe=T)080Oa08 z%abA@?O*NebdhP`>cn67<;z#+=sB_9QAMvcJ<@3MHgrl>>IMchSX3k=bn#^T&~@2K zP0>pbol@GhxR%yrl27jO(3*XLlDr|J0r6g{js^EG0y&qx&dNF}0rK4~9M6*CIQ|Qg^>oslESM&_b3GbvB@sF4)BWjH2fm%-zqbEq>(1Gr}MZZ|}z* zSss5-$a<_3+1kZjT-L_1s46@GvQ|e0srR^?ZT#7=qlc*n$vUC$usanOynAP0q#4^q zKN%;g0;mBR*@8cL4R~${-jHK|v)Ts6+;lW0_}dEuaXr z0#b{TFhyz@0t7-CL{Uiq5g|ncA{bKxGTtY)@4fFI@UC~QyH~tD~a)26-tdDMhDm?T<-GL3GJSP8MA0aK&$vl3IVy$^PK+NYaO4m&aO<{gKh7 zoA;?9MK1`9#sUl7^umBsPQ%9;%;{~6^7~A~(AP^#Lu(bHoXAOk+`!$C5}Fnsx;#Pp zUB1~<$}G?C>Zhg0b5hUl_?0wCNo`lgOWoN5OPM>dq|SbiK+m>7(my$&WDop+RzM6f zI4KvE{~m)I(*vFp#5fInKGOW4PwvrDtMlYs>iWBmEV#nlLN(m-#DXY6;hiczPe0Xp z!#&^0UftytRtnN==Hu_w>i%6yvo%U~kC#YSl~1 zg6^!}vpN18N_NPj@>~6?vvtRIe`83-8^Uxdp5$}WN&3&XeC?*J{t@QN$Pl+=WPg_T z!eTnE1g*dryWtREMB06?R{VSV$eX4}QOKX*OJ?IWeBdQL!_%AK%b~9IeJnw0KgUhB z$a;0yuC9?8Cu((@yKrr3a%G$u*R*PGPx<`$`JBtrl1oS5{_K?IJIHg|?y*|ciijKc zb{2oUWLH14GC#2xxBOz{9&>iJUcGYkNx`g*+AeH-13JT2JA-Snh10T!JzZXR8uwjo z7T1s*_gkD$%#7?2&;60$%zj^hJTJP$--CUW-H1yFE*yzf%~g02`KV!87#Hxir8awH zuljCmyf-?d@vbs$(wl2R8XZ)oU0MAcS9~_p+E+D@JUVBoQ<-`r6=$u2U%07G3$L?X z7(21_Mwy0ZZfHB7{dn~Y*Z<0xh&I;C(*lXbgTzcX(3A^K)Qhjai|EeRUAefr>zbi> zh*M-l4%-0n#Rb|lbKmmF*p+Rh#if~{MQ+?u(b2o_`hIju6>z zEkIr%?OZeC(Zz4z952FaBU7Ws$i{{mk{MPS;!|&>a9g>_x4Dx(Rq>&A*CP}CVyI&- zokYxDtK^vOmZF1f7___*C6xEe)*`&ysC1&Ub=HPbxmugjjE0IsWX9Ah_EYh*rEFjH`&4UB=V@a9Z5#Q5~M<4WK z*RI;=$x1`THo(N5q?czO&W9oq<&tbZmXJnyQUg{$P zH3es*iXS|>3D6J(?1uUsD}Q;(REo6nhuPETcy!^)Hn(FHE?29n1Ip@- zqS`;JrFhmgzT*FU__xJ3mF}+%A-<(NdzxM7jlR)@Thk_&GX zS^_n+tm+Iu^>Ap8h^X|S?wUzOAFt{)BZ6-QOXIk4oTQ3cBZs%ocA3x0M0BigkBBtYoUwr{Z zg+>lwdWqCQJ>Q7xr&Myn06q?=_9zKH(~-Ye?xUt{z~IVEK-*fA_&=Z52qcG6?;E1p z&w&UO-pAXX%Pm1<;AT5I;YhHQm)X4YLX-R^H#RjN8)035#rs;O!tf zgMYZKD=G8-0I#_ggG&bC=cBI{=2k1_YwA-c0)r~-SUlQ+`lR8c(MaR6&XKgRwAehC zAzpJS2KNF)3FSrRMGery(Vb>JSFiU;ICSM2Y39FZQYiIHCIUQNlE);&4f~LTk@c0+p@+O*;XuXfcJF zMyV1$ZUWa5WESX@_b|y-J#czdZSms<6|<)Xu#K@gEzJ6s7u0=n(uLhNfnfk@IQRKT0tWXz_)wE4-I#G`Jf~!v?`e$Mf>iSK;78lkcWM!Sxsj>ujHPS*2aX5FGk6901p~_Tezg=X zeF(o%Y5aK9lm^%|p|jU7`dpMx#iDCC!{75Gim7<6U?TOi3+p^+>$IZQs2^=IAF45?*j7~*yP-VFzY(VO ziWWYK-n&Sh)F8dcDwxVfUi|_{tMa$jIb2_NbhK7mN$T49eU+Lh576YlriWeVDG3|J zx+sqy{;po31@%|(GyUIeSgLwxS-T0u7%xSzjv2z`A9iVLg;E)n{2UCgR#Jy26H(10 z9uhc)w?WMNU!9+^TO(@O^zfSmcR?#pxpGJk<{sB0fqj#0e_cC2Q(J~=bf14G9ZL`P zwji}8DKZuPi|%n;OZoZf%JkqoENwF^k_gh*DYAB5l!)&Zp1OQ1Q+p`j7<)<&&Lvop z%8{OwzmUnB@PEj@%@bs5GXvb&Q=8%3nj`hLCwFSuDst=^vWF5Ky_H8V2M9J$G z@}s#tH?*FLBCMzRGq27U;zbvWG;gWTsHPXC+W1@$c)bSx_TH^_r)F9z-7hqy^J8pCFOU!Rlo3TR?F z3agd)sz9b&~3)c{KFG~DSl!)O~p($G(r5VxT``d#XLRL1zJ7pA3sFi6Kp%8E+gdZX?@A*@&WW;ghh#7>Z{km@)T} z&*OSg)F>1q;b?sy@`zunP4^e8%QlyxWL+=;)P8_0g?hmAnz33f`tW!wUMdM$ zzGi*^iB2?Q>NG9pU>}c28|vpYW{2&9d|Iv}E<1JC>8`XxA5Eb6em1gv-U==oRyBmX zl<5YiWdw=H!GI*BL78r%N;fEhhN4Gj6nW>RlaYfl0JA~Y{a8BA4Dz{yjGt`4l?D1` zL;3SdCbFic2jy_)6J~JJ9eJK5`5cRYt-cws9+eX?ooKNiF4tU~677WI#R6GMbw=bx zmc3~@GM=YS@A>pI(j1bfNnf_Y%1|WsYBYCg@z_c(O11|RK*$)zC-q|T&altOd1a|e z6Kb#9v3{}{f~;zQLUgLoHxki;qN0(wg|REe5bC8##sHowBY1)~T2Nc(MhKP^NFJmS((RZHB|vW~~trA*K}66;5R(?S6??7&KqUWB=TU^0DVxaJ;ljGuy^g1B2E`ABS;L!2 zC=W2h|2^VNvGg@Xjq}TA5vkAeVCAlDs(W9w*$#Bg3H4TWKCoD zE7Q+lys{o3Hu;+LI@Rz!Qebuhv5Q^`rEb!}ctu`NrXK{u61%K#}bhM(ndkPlZ^4B%Wtcprn5Ad)7$=>WqsXA%frgf%=gt6e;&9;gbY zHcFg-g4JFW52W*)5zyVZHsnL2rX24R(7$smh92XJ$#MXTB#$!>l;eeMd3~DIxBbuQ zxx^1h>8$=;XI2hfl|JmvyUa>TX+vUlP~zn0QqYvTBu1nLPiCD6Y-?&b$?2Q~cJt^k&Jhi<5hskf~feO!*G;s6a5B$jri zFst!ETNBKzzR3N!zAO2!|4zy5ljm(CCrgW`R;oc?KsCkBK8*q&J&kAz7HIq<&2eLL zOb#|VMNxW_;Os)~>ilftGA8m?R<^Z&cLitGf;9PQ)V}_!^(}oUCWE)2bMudCI;YH> zHlygH`=^%909`0p$zA3QqKN_7rToad9-_Swd-wLR0O zD7I&~elrQ2tV^9`1?5}dH90DtU8x*>f_hczPo;g3!ei6eWjFLRTqkZ1)~?VeCTBxK zf3==>Oe`blFcpNc37dy>t6xUrL%o%by?3%)cVF+v!7Ps&@Adm|wDr|HA)i8I`#Ck* z1EeRNH}m~iXa~8w@A{g%+qM0eqjxlELP4+_t8Y&6sUf_U=tt-1GuA6uVTnNjG2e)B zHMObDn1>JBoh+~2Z`o5AQ7W{oxonZ;sla2$$WExzOI_;EW;;@Podw~9oCJ!)Gi0A( z`zuS|HT;OdZj*K*OT!2z`ZOGQp?Tp}{j<44lwGH@&9sylH*o~m(b83gU)u5@Ogk0$ zixeyh$`v4=!T$6;u~^*gkR0==8?sYgR2Ely?0wt2|&-g%&O6+D&8iMdJXIhz-TDhI1xfw1EX}Ix1a~>uiuud{f9dPZqUlNbS5jie;sr@Tq zE}X9J>5JgGb<%!Hg)qi_-34IpfgJk1)?_o9;skj%7JeOiu46GZ2}B$aH9e$l+$3 zeU!@mEXS**G`xQSnYmf=5K$?A+G6yHEyQG;-XuBmI$Dw(`M1+m=qF300}Qkeu*OUH zN9(|oR_)<6-3gcJP#KZ)s^s$3V=_?iLCZYkQ(ySE5oke9iL^3Uk7Cb%(OZN6i+3NB btI%bo4ofwei`L+!jg-?NSNn=@;P3tgey@YJ literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..ff5e86ac136f21b0a84fea3e72fec234a76ae76d GIT binary patch literal 22128 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/raw/app_start_anim_default.json b/app/src/main/res/raw/app_start_anim_default.json new file mode 100755 index 00000000..63689c94 --- /dev/null +++ b/app/src/main/res/raw/app_start_anim_default.json @@ -0,0 +1 @@ +{"v":"5.7.1","fr":29.9700012207031,"ip":0,"op":18.000000733155,"w":216,"h":216,"nm":"app_start_animation_fg","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"app_start_animation_fg Konturen","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[100]},{"t":17.0000006924242,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[44]},{"t":17.0000006924242,"s":[107]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[41.5]},{"t":17.0000006924242,"s":[108]}],"ix":4}},"a":{"a":0,"k":[44,41.5,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":10,"s":[100,100,100]},{"t":17.0000006924242,"s":[588.571,588.571,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Formebene 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[108,108,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,35],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10,"s":[-64.104,-66.442],"to":[10.684,11.074],"ti":[-10.684,-11.074]},{"t":17.0000006924242,"s":[0,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":10,"s":[98.504,98.274]},{"t":17.0000006924242,"s":[636.029,585.095]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"app_start_animation_bg Konturen","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[100]},{"t":17.0000006924242,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[108,108,0],"ix":2},"a":{"a":0,"k":[108,108,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":10,"s":[100,100,100]},{"t":17.0000006924242,"s":[80,80,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 5","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 6","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 7","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 8","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 9","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 10","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 11","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 12","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 13","np":2,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 14","np":2,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 15","np":2,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 16","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 17","np":2,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 18","np":2,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 19","np":2,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 20","np":2,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 21","np":2,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 22","np":2,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 23","np":2,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 24","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 25","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 26","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 27","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 28","np":2,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 29","np":2,"cix":2,"bm":0,"ix":29,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 30","np":2,"cix":2,"bm":0,"ix":30,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.42,0],[0,0],[0,4.42],[0,0],[-4.421,0],[0,0],[0,-4.421],[0,0]],"o":[[0,0],[-4.421,0],[0,0],[0,-4.421],[0,0],[4.42,0],[0,0],[0,4.42]],"v":[[83.996,92],[-83.996,92],[-92,83.996],[-92,-83.996],[-83.996,-92],[83.996,-92],[92,-83.996],[92,83.996]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[108,108],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 31","np":2,"cix":2,"bm":0,"ix":31,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/res/raw/app_start_anim_fade.json b/app/src/main/res/raw/app_start_anim_fade.json new file mode 100644 index 00000000..1b5dc720 --- /dev/null +++ b/app/src/main/res/raw/app_start_anim_fade.json @@ -0,0 +1 @@ +{"v":"5.5.10","fr":29.9700012207031,"ip":0,"op":18.000000733155,"w":216,"h":216,"nm":"app_start_animation_fg","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Formebene 1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":17.0000006924242,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[66.653,96.216,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[216,216],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.347,11.784],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"app_start_animation_fg Konturen","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":44,"ix":3},"y":{"a":0,"k":41.5,"ix":4}},"a":{"a":0,"k":[44,41.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"app_start_animation_bg Konturen","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[108,108,0],"ix":2},"a":{"a":0,"k":[108,108,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 5","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 6","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 7","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 8","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 9","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 10","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 11","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 12","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 13","np":2,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 14","np":2,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 15","np":2,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 16","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 17","np":2,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 18","np":2,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 19","np":2,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 20","np":2,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 21","np":2,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 22","np":2,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 23","np":2,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 24","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 25","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 26","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 27","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 28","np":2,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 29","np":2,"cix":2,"bm":0,"ix":29,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 30","np":2,"cix":2,"bm":0,"ix":30,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.42,0],[0,0],[0,4.42],[0,0],[-4.421,0],[0,0],[0,-4.421],[0,0]],"o":[[0,0],[-4.421,0],[0,0],[0,-4.421],[0,0],[4.42,0],[0,0],[0,4.42]],"v":[[83.996,92],[-83.996,92],[-92,83.996],[-92,-83.996],[-83.996,-92],[83.996,-92],[92,-83.996],[92,83.996]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[108,108],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 31","np":2,"cix":2,"bm":0,"ix":31,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/res/raw/app_start_anim_m.json b/app/src/main/res/raw/app_start_anim_m.json new file mode 100644 index 00000000..6de84a97 --- /dev/null +++ b/app/src/main/res/raw/app_start_anim_m.json @@ -0,0 +1 @@ +{"v":"5.5.10","fr":29.9700012207031,"ip":0,"op":18.000000733155,"w":216,"h":216,"nm":"app_start_animation_fg","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Formebene 1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[0]},{"t":10.0000004073083,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10,"s":[108,108,0],"to":[10.333,12,0],"ti":[-108.333,-121.667,0]},{"t":17.0000006924242,"s":[758,838,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":10,"s":[100,100,100]},{"t":17.0000006924242,"s":[1000,1000,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[23.99,23.99],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-64.055,-71.999],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"app_start_animation_fg Konturen","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":44,"ix":3},"y":{"a":0,"k":41.5,"ix":4}},"a":{"a":0,"k":[44,41.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"app_start_animation_bg Konturen","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[108,108,0],"ix":2},"a":{"a":0,"k":[108,108,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 5","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 6","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 7","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 8","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 9","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 10","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 11","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 12","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 13","np":2,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 14","np":2,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 15","np":2,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 16","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 17","np":2,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 18","np":2,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 19","np":2,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 20","np":2,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 21","np":2,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 22","np":2,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 23","np":2,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 24","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 25","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 26","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 27","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 28","np":2,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 29","np":2,"cix":2,"bm":0,"ix":29,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 30","np":2,"cix":2,"bm":0,"ix":30,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.42,0],[0,0],[0,4.42],[0,0],[-4.421,0],[0,0],[0,-4.421],[0,0]],"o":[[0,0],[-4.421,0],[0,0],[0,-4.421],[0,0],[4.42,0],[0,0],[0,4.42]],"v":[[83.996,92],[-83.996,92],[-92,83.996],[-92,-83.996],[-83.996,-92],[83.996,-92],[92,-83.996],[92,83.996]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[108,108],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 31","np":2,"cix":2,"bm":0,"ix":31,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/res/raw/app_start_anim_slide_bottom.json b/app/src/main/res/raw/app_start_anim_slide_bottom.json new file mode 100644 index 00000000..3c7aa1d6 --- /dev/null +++ b/app/src/main/res/raw/app_start_anim_slide_bottom.json @@ -0,0 +1 @@ +{"v":"5.5.10","fr":29.9700012207031,"ip":0,"op":18.000000733155,"w":216,"h":216,"nm":"app_start_animation_fg","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Formebene 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10,"s":[66.653,315.216,0],"to":[0,-36.5,0],"ti":[0,36.5,0]},{"t":17.0000006924242,"s":[66.653,96.216,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[216,216],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.347,11.784],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"app_start_animation Konturen","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[100]},{"t":17.0000006924242,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10,"s":[108,108,0],"to":[0,-3.167,0],"ti":[0,3.167,0]},{"t":17.0000006924242,"s":[108,89,0]}],"ix":2},"a":{"a":0,"k":[108,108,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 5","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 6","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 7","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 8","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 9","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 10","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 11","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 12","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 13","np":2,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 14","np":2,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 15","np":2,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 16","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 17","np":2,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 18","np":2,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 19","np":2,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 20","np":2,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 21","np":2,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 22","np":2,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 23","np":2,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 24","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 25","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 26","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 27","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 28","np":2,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 29","np":2,"cix":2,"bm":0,"ix":29,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 30","np":2,"cix":2,"bm":0,"ix":30,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 31","np":2,"cix":2,"bm":0,"ix":31,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 32","np":2,"cix":2,"bm":0,"ix":32,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.42,0],[0,0],[0,4.42],[0,0],[-4.421,0],[0,0],[0,-4.421],[0,0]],"o":[[0,0],[-4.421,0],[0,0],[0,-4.421],[0,0],[4.42,0],[0,0],[0,4.42]],"v":[[83.996,92],[-83.996,92],[-92,83.996],[-92,-83.996],[-83.996,-92],[83.996,-92],[92,-83.996],[92,83.996]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[108,108],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 33","np":2,"cix":2,"bm":0,"ix":33,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/res/raw/app_start_anim_splash1.json b/app/src/main/res/raw/app_start_anim_splash1.json new file mode 100644 index 00000000..7e462696 --- /dev/null +++ b/app/src/main/res/raw/app_start_anim_splash1.json @@ -0,0 +1 @@ +{"v":"5.5.10","fr":29.9700012207031,"ip":0,"op":18.000000733155,"w":216,"h":216,"nm":"app_start_animation_fg","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"app_start_animation_fg Konturen","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[100]},{"t":17.0000006924242,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.829],"y":[0.456]},"o":{"x":[0.194],"y":[0.028]},"t":0,"s":[44]},{"t":7.00000028511585,"s":[108]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.03],"y":[0.233]},"t":0,"s":[41.5]},{"t":7.00000028511585,"s":[108]}],"ix":4}},"a":{"a":0,"k":[44,41.5,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":7,"s":[200,200,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":10,"s":[200,200,100]},{"t":17.0000006924242,"s":[2000,2000,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Formebene 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.93],"y":[0.475]},"o":{"x":[0.111],"y":[0.03]},"t":0,"s":[42.158]},{"t":7.00000028511585,"s":[89.576]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.684],"y":[0.983]},"o":{"x":[0.043],"y":[0.297]},"t":0,"s":[34.158]},{"t":7.00000028511585,"s":[89.576]}],"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[0,0,100]},{"t":7.00000028511585,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[254.848,254.848],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Elliptischer Pfad 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[18.424,18.424],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[123.368,123.368],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"app_start_animation_bg Konturen","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":7.00000028511585,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[108,108,0],"ix":2},"a":{"a":0,"k":[108,108,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[100,100,100]},{"t":7.00000028511585,"s":[80,80,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 5","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 6","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 7","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 8","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 9","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 10","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 11","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 12","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 13","np":2,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 14","np":2,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 15","np":2,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 16","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 17","np":2,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 18","np":2,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 19","np":2,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 20","np":2,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 21","np":2,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 22","np":2,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 23","np":2,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 24","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 25","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 26","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 27","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 28","np":2,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 29","np":2,"cix":2,"bm":0,"ix":29,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 30","np":2,"cix":2,"bm":0,"ix":30,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.42,0],[0,0],[0,4.42],[0,0],[-4.421,0],[0,0],[0,-4.421],[0,0]],"o":[[0,0],[-4.421,0],[0,0],[0,-4.421],[0,0],[4.42,0],[0,0],[0,4.42]],"v":[[83.996,92],[-83.996,92],[-92,83.996],[-92,-83.996],[-83.996,-92],[83.996,-92],[92,-83.996],[92,83.996]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[108,108],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 31","np":2,"cix":2,"bm":0,"ix":31,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/res/raw/app_start_anim_splash2.json b/app/src/main/res/raw/app_start_anim_splash2.json new file mode 100644 index 00000000..d9a86c57 --- /dev/null +++ b/app/src/main/res/raw/app_start_anim_splash2.json @@ -0,0 +1 @@ +{"v":"5.5.10","fr":29.9700012207031,"ip":0,"op":18.000000733155,"w":216,"h":216,"nm":"app_start_animation_fg","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"app_start_animation_fg Konturen","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[100]},{"t":17.0000006924242,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.829],"y":[0.456]},"o":{"x":[0.194],"y":[0.028]},"t":0,"s":[44]},{"t":7.00000028511585,"s":[108]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.03],"y":[0.233]},"t":0,"s":[41.5]},{"t":7.00000028511585,"s":[108]}],"ix":4}},"a":{"a":0,"k":[44,41.5,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":7,"s":[200,200,100]},{"t":10.0000004073083,"s":[200,200,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Formebene 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.93],"y":[0.475]},"o":{"x":[0.111],"y":[0.03]},"t":0,"s":[42.158]},{"t":7.00000028511585,"s":[89.576]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.684],"y":[0.983]},"o":{"x":[0.043],"y":[0.297]},"t":0,"s":[34.158]},{"t":7.00000028511585,"s":[89.576]}],"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[0,0,100]},{"t":7.00000028511585,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[254.848,254.848],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Elliptischer Pfad 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[18.424,18.424],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[123.368,123.368],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"app_start_animation_bg Konturen","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":7.00000028511585,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[108,108,0],"ix":2},"a":{"a":0,"k":[108,108,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[100,100,100]},{"t":7.00000028511585,"s":[80,80,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,187.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 5","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 6","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 7","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 8","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 9","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 10","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 11","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,143.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 12","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 13","np":2,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 14","np":2,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 15","np":2,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.628],[-6.627,0]],"o":[[0,6.628],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,123.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 16","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 17","np":2,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 18","np":2,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 19","np":2,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,100.167],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 20","np":2,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 21","np":2,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 22","np":2,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 23","np":2,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[44,79.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 24","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 25","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 26","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16,2.5],[-16,2.5],[-16,-2.5],[16,-2.5]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,56.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 27","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[172,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 28","np":2,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.628,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.628,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[129.333,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 29","np":2,"cix":2,"bm":0,"ix":29,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-6.627],[6.627,0],[0,6.627],[-6.627,0]],"o":[[0,6.627],[-6.627,0],[0,-6.627],[6.627,0]],"v":[[12,0],[0,12],[-12,0],[0,-12]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.118000000598,0.532999973671,0.898000021542,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[86.666,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 30","np":2,"cix":2,"bm":0,"ix":30,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.42,0],[0,0],[0,4.42],[0,0],[-4.421,0],[0,0],[0,-4.421],[0,0]],"o":[[0,0],[-4.421,0],[0,0],[0,-4.421],[0,0],[4.42,0],[0,0],[0,4.42]],"v":[[83.996,92],[-83.996,92],[-92,83.996],[-92,-83.996],[-83.996,-92],[83.996,-92],[92,-83.996],[92,83.996]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[108,108],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 31","np":2,"cix":2,"bm":0,"ix":31,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18.000000733155,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/res/raw/license_apache_2.txt b/app/src/main/res/raw/license_apache_2.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/app/src/main/res/raw/license_apache_2.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/app/src/main/res/raw/license_bsd_2clause.txt b/app/src/main/res/raw/license_bsd_2clause.txt new file mode 100644 index 00000000..a5f54eb3 --- /dev/null +++ b/app/src/main/res/raw/license_bsd_2clause.txt @@ -0,0 +1,7 @@ +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/app/src/main/res/raw/license_bsd_3clause.txt b/app/src/main/res/raw/license_bsd_3clause.txt new file mode 100644 index 00000000..57326dd0 --- /dev/null +++ b/app/src/main/res/raw/license_bsd_3clause.txt @@ -0,0 +1,9 @@ +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/app/src/main/res/raw/license_glide.txt b/app/src/main/res/raw/license_glide.txt new file mode 100644 index 00000000..441c3743 --- /dev/null +++ b/app/src/main/res/raw/license_glide.txt @@ -0,0 +1,94 @@ +License for everything not in third_party and not otherwise marked: + +Copyright 2014 Google, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY GOOGLE, INC. ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Google, Inc. +--------------------------------------------------------------------------------------------- +License for third_party/disklrucache: + +Copyright 2012 Jake Wharton +Copyright 2011 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--------------------------------------------------------------------------------------------- +License for third_party/gif_decoder: + +Copyright (c) 2013 Xcellent Creations, Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------------------------------------------- +License for third_party/gif_encoder/AnimatedGifEncoder.java and +third_party/gif_encoder/LZWEncoder.java: + +No copyright asserted on the source code of this class. May be used for any +purpose, however, refer to the Unisys LZW patent for restrictions on use of +the associated LZWEncoder class. Please forward any corrections to +kweiner@fmsware.com. + +----------------------------------------------------------------------------- +License for third_party/gif_encoder/NeuQuant.java + +Copyright (c) 1994 Anthony Dekker + +NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See +"Kohonen neural networks for optimal colour quantization" in "Network: +Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of +the algorithm. + +Any party obtaining a copy of these files from the author, directly or +indirectly, is granted, free of charge, a full and unrestricted irrevocable, +world-wide, paid up, royalty-free, nonexclusive right and license to deal in +this software and documentation files (the "Software"), including without +limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons who +receive copies from any such party to do so, with the only requirement being +that this copyright notice remain intact. \ No newline at end of file diff --git a/app/src/main/res/raw/license_gpl_3.txt b/app/src/main/res/raw/license_gpl_3.txt new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/app/src/main/res/raw/license_gpl_3.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/app/src/main/res/raw/license_mit.txt b/app/src/main/res/raw/license_mit.txt new file mode 100644 index 00000000..7215f695 --- /dev/null +++ b/app/src/main/res/raw/license_mit.txt @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml new file mode 100644 index 00000000..892bd7f6 --- /dev/null +++ b/app/src/main/res/values/arrays.xml @@ -0,0 +1,107 @@ + + + + 2 + 0 + 1 + 3 + + + @string/preference_theme_system + @string/preference_theme_light + @string/preference_theme_dark + @string/preference_theme_auto + + + @string/preference_icon_shape_circle + @string/preference_icon_shape_squircle + @string/preference_icon_shape_rounded_square + @string/preference_icon_shape_square + @string/preference_icon_shape_hexagon + @string/preference_icon_shape_triangle + + + 0 + 3 + 1 + 2 + 4 + 5 + + + + @string/preference_searchbar_behavior_fade_alpha + @string/preference_searchbar_behavior_always_visible + @string/preference_searchbar_behavior_hide + + + 0 + 1 + 2 + + + @string/websites_protocol_http + @string/websites_protocol_https + + + 0 + 1 + + + + @string/preference_legacy_icon_bg_none + @string/preference_legacy_icon_bg_color + @string/preference_legacy_icon_bg_white + + + 0 + 1 + 2 + + + + @string/provider_metno + @string/provider_openweathermap + @string/provider_here + + + 2 + 0 + 3 + + + 3 + 5 + 8 + 10 + + + + @string/preference_card_background_default + @string/preference_card_background_black + + + 0 + 2 + + + + @color/red + @color/pink + @color/purple + @color/deeppurple + @color/indigo + @color/blue + @color/lightblue + @color/cyan + @color/teal + @color/green + @color/lightgreen + @color/yellow + @color/amber + @color/orange + @color/deeporange + @color/brown + @color/bluegrey + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml new file mode 100644 index 00000000..c8690148 --- /dev/null +++ b/app/src/main/res/values/attrs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/main/res/values/bools.xml b/app/src/main/res/values/bools.xml new file mode 100644 index 00000000..7d18846e --- /dev/null +++ b/app/src/main/res/values/bools.xml @@ -0,0 +1,5 @@ + + + false + false + \ No newline at end of file diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml new file mode 100644 index 00000000..55344e51 --- /dev/null +++ b/app/src/main/res/values/donottranslate.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml new file mode 100644 index 00000000..a9108c88 --- /dev/null +++ b/app/src/main/res/values/integers.xml @@ -0,0 +1,5 @@ + + + 4 + 1 + \ No newline at end of file diff --git a/app/src/main/res/values/licenses.xml b/app/src/main/res/values/licenses.xml new file mode 100644 index 00000000..2ef2eb4c --- /dev/null +++ b/app/src/main/res/values/licenses.xml @@ -0,0 +1,313 @@ + + + + @string/app_name + @string/preference_about_license + @null + @string/gpl3_name + Copyright (c) 2021 MM20 + @raw/license_gpl_3 + https://github.com/MM2-0/Kvaesitso + + + + Kotlin Standard Library + Kotlin Standard Library + + https://kotlinlang.org/assets/images/apple-touch-icon-144x144.png + + @string/apache_license_name + @null + @raw/license_apache_2 + https://kotlinlang.org/ + + + + Android Jetpack + A collection of Android software components to make it easier to develop great Android + apps. + + + https://2.bp.blogspot.com/-2ZMkSo7CnUs/WvMvSK0u9RI/AAAAAAAAFZA/zJOCZ8LUM8ol3hcHYHwVyOpc3iiYaxquACLcBGAs/s1600/Jetpack_logo.png + + @string/apache_license_name + @null + @raw/license_apache_2 + https://developer.android.com/topic/libraries/support-library/index.html + + + + Accompanist + Accompanist is a group of libraries that aim to supplement Jetpack Compose with + features that are commonly required by developers but not yet available. + + https://raw.githubusercontent.com/google/accompanist/main/docs/header.png + @string/apache_license_name + Copyright 2020 The Android Open Source Project + @raw/license_apache_2 + https://google.github.io/accompanist/ + + + + Material Components for Android + + Material Components for Android (MDC-Android) help developers execute Material Design. + + @null + @string/apache_license_name + @null + @raw/license_apache_2 + https://material.io/develop/android/ + + + + Lottie + Lottie is a library for Android, iOS, Web, and Windows that parses Adobe After Effects + animations exported as json with Bodymovin and renders them natively on mobile and on + the web. + + https://airbnb.io/lottie/images/logo.webp + @string/apache_license_name + @null + @raw/license_apache_2 + https://airbnb.io/lottie/#/ + + + + OkHttp + + @null + @string/apache_license_name + Copyright 2019 Square, Inc. + @raw/license_apache_2 + https://square.github.io/okhttp/ + + + + Retrofit + A type-safe HTTP client for Android and Java + @null + @string/apache_license_name + Copyright 2013 Square, Inc. + @raw/license_apache_2 + https://square.github.io/retrofit/ + + + + Gson + Gson is a Java library that can be used to convert Java Objects into their JSON representation. + @null + @string/apache_license_name + Copyright 2008 Google Inc. + @raw/license_apache_2 + https://github.com/google/gson + + + + commons-suncalc + A Java library for calculation of sun and moon positions and phases. + @null + @string/apache_license_name + @null + @raw/license_apache_2 + https://github.com/shred/commons-suncalc + + + + Jsoup + A Java library for working with real-world HTML + @null + @string/mit_license_name + ]]> + @raw/license_mit + https://jsoup.org/ + + + + TextDrawable + A light-weight library providing images with letter/text like the Gmail app + https://github.com/amulyakhare/TextDrawable/raw/master/screens/screen6.png + @string/mit_license_name + Copyright (c) 2014 Amulya Khare + @raw/license_mit + https://github.com/amulyakhare/TextDrawable + + + + Glide + A fast and efficient open source media management and image loading framework for + Android + + https://raw.githubusercontent.com/bumptech/glide/master/static/glide_logo.png + @string/glide_license_name + @null + @raw/license_glide + https://bumptech.github.io/glide/ + + + + Glide Transformations + An Android transformation library providing a variety of image transformations for + Glide + + @null + @string/apache_license_name + Copyright (C) 2020 Wasabeef + @raw/license_apache_2 + https://github.com/wasabeef/glide-transformations + + + + Material Dialogs + Easy to use material design dialogs + + https://raw.githubusercontent.com/afollestad/material-dialogs/master/art/showcase20.jpg + + @string/apache_license_name + @null + @raw/license_apache_2 + https://github.com/afollestad/material-dialogs + + + + Groupie + Groupie is a simple, flexible library for complex RecyclerView layouts. + @null + @string/mit_license_name + @null + @raw/license_mit + https://github.com/lisawray/groupie + + + + DragLinearLayout + An Android LinearLayout that supports draggable and swappable child Views + @null + @string/mit_license_name + Copyright (c) 2014 Justas Medeisis + @raw/license_mit + https://github.com/justasm/DragLinearLayout + + + + ViewPropertyValueAnimator + Wrapper of the ObjectAnimator that can be used similarly to ViewPropertyAnimator. + + @null + @string/apache_license_name + Copyright 2015 Bartosz Lipiński + @raw/license_apache_2 + https://github.com/blipinsk/ViewPropertyObjectAnimator + + + + Nextcloud SingleSignOn + This library allows you to use accounts as well as the network stack provided by the + nextcloud files app. + + @null + @string/gpl3_name + @null + @raw/license_gpl_3 + https://github.com/nextcloud/Android-SingleSignOn + + + + mXparser + A super easy, rich, fast and highly flexible math expression parser library + http://mathparser.org/wp-content/uploads/2017/07/mxparser-logo.png + @string/bsd_2clause_name + Copyright 2010 - 2020 Mariusz Gromada. All rights reserved. + @raw/license_bsd_2clause + https://mathparser.org/ + + + + Google Auth Library + Open source authentication client library for Java. + @null + @string/bsd_3clause_name + + Copyright 2014, Google Inc. All rights reserved. + @raw/license_bsd_3clause + https://github.com/googleapis/google-auth-library-java + + + + Google APIs Client Library for Android + The Google APIs Client Library for Java is a flexible, efficient, and powerful Java client library for accessing any HTTP-based API on the web, not just Google APIs. + @null + @string/apache_license_name + @null + @raw/license_apache_2 + https://github.com/googleapis/google-api-java-client + + + + Microsoft Graph Android SDK + Deprecated API client for Microsoft Graph APIs + @null + @string/mit_license_name + Copyright (c) 2015 Microsoft Corporation + @raw/license_mit + https://github.com/microsoftgraph/msgraph-sdk-android + + + + Microsoft Authentication Library (MSAL) for Android + The MSAL library for Android gives your app the ability to use the Microsoft Cloud by + supporting Microsoft Azure Active Directory and Microsoft accounts in a converged + experience using industry standard OAuth2 and OpenID Connect. The library also supports + Azure AD B2C. + + @null + @string/mit_license_name + Copyright (c) Microsoft Corporation + @raw/license_mit + https://github.com/AzureAD/microsoft-authentication-library-for-android + + + + CrashReporter + CrashReporter is a handy tool to capture app crashes and save them in a file. + + https://github.com/balsikandar/CrashReporter/raw/master/assets/crash_reporter_banner.png + + @string/apache_license_name + " + Copyright (C) 2016 Bal Sikandar + Copyright (C) 2011 Android Open Source Project + " + + @raw/license_apache_2 + https://github.com/MindorksOpenSource/CrashReporter + + + + Material Design Icons + Beautifully crafted, delightful, and easy to use in your web, Android, and iOS + projects + + https://material.io/tools/icons/static/ic_icons_192px_light.svg + @string/apache_license_name + @null + @raw/license_apache_2 + https://material.io/icons/ + + + MIT license + Simplified BSD license + Modified BSD license + Apache license 2.0 + Glide license + GNU General Public License Version 3 + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..30f3f933 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + diff --git a/app/src/main/res/xml/motion_calendar_event_view.xml b/app/src/main/res/xml/motion_calendar_event_view.xml new file mode 100644 index 00000000..d7d33d58 --- /dev/null +++ b/app/src/main/res/xml/motion_calendar_event_view.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_about.xml b/app/src/main/res/xml/preferences_about.xml new file mode 100644 index 00000000..96dace9f --- /dev/null +++ b/app/src/main/res/xml/preferences_about.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_appearance.xml b/app/src/main/res/xml/preferences_appearance.xml new file mode 100644 index 00000000..74c114fd --- /dev/null +++ b/app/src/main/res/xml/preferences_appearance.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_badges.xml b/app/src/main/res/xml/preferences_badges.xml new file mode 100644 index 00000000..8ad0dd71 --- /dev/null +++ b/app/src/main/res/xml/preferences_badges.xml @@ -0,0 +1,23 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_calendar.xml b/app/src/main/res/xml/preferences_calendar.xml new file mode 100644 index 00000000..5e2fd87b --- /dev/null +++ b/app/src/main/res/xml/preferences_calendar.xml @@ -0,0 +1,21 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_cards.xml b/app/src/main/res/xml/preferences_cards.xml new file mode 100644 index 00000000..10d29aeb --- /dev/null +++ b/app/src/main/res/xml/preferences_cards.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_main.xml b/app/src/main/res/xml/preferences_main.xml new file mode 100644 index 00000000..7b1c88a2 --- /dev/null +++ b/app/src/main/res/xml/preferences_main.xml @@ -0,0 +1,38 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_search.xml b/app/src/main/res/xml/preferences_search.xml new file mode 100644 index 00000000..98bc15d2 --- /dev/null +++ b/app/src/main/res/xml/preferences_search.xml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_services.xml b/app/src/main/res/xml/preferences_services.xml new file mode 100644 index 00000000..b05f95cf --- /dev/null +++ b/app/src/main/res/xml/preferences_services.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_weather.xml b/app/src/main/res/xml/preferences_weather.xml new file mode 100644 index 00000000..f9853bea --- /dev/null +++ b/app/src/main/res/xml/preferences_weather.xml @@ -0,0 +1,28 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml new file mode 100644 index 00000000..54f8c691 --- /dev/null +++ b/app/src/main/res/xml/provider_paths.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/release/ic_launcher-playstore.png b/app/src/release/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..d826b6dc7eba1a6eb1a7f915fd3082e1bfbd8e7a GIT binary patch literal 43278 zcmZ^Lc_5VC`}aL&G8BXCyHb)RWM60Gv1H2{MF@o?vX_`aBH1cJ5<<4JWTJ&;$WqxR zOQfU>%zf^2uIsa1uG?6fa_$t_2|*C&QM147AczJ0 zmjz;D1^=u>4sJpa4m$dmp+ltG&-`^PpJ~I|hS6Bm@Yw2?m;Ui;UsNX$W+#;=5VtUh z+k8aa+ZI-@!+Z1Yg``<~#Mh;!hB!y%M=a<}SZRL0_xjRS#gF?H2B#ELOLE_vg~U)| zQus~23^{(k^yA)-N(pq(uYE;%H=jxOth-EyZ^m58%o|vFYjd_=x5uiP#ZLJ0!`T_P zqnq‹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 + + + + \ No newline at end of file diff --git a/app/src/release/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/release/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..4ae7d123 --- /dev/null +++ b/app/src/release/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/release/res/mipmap-hdpi/ic_launcher.png b/app/src/release/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d174951b9014e8e3b689daa254a1ce488429a2d4 GIT binary patch literal 2728 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 igAy-riXdd++(2bMNQ#Irsi>Z+lT)&>DL* zU@#b(;z~ZQsKwiXL?|-!NoOw%hQL$E4nFaL%kKwbhK>PDdf>j>=oB|d($RX~=hyGN zQl>!&%=Pa9D+o^|hT%2CuC~UiLIL|(igG-c{@@&L#ixkFotGhtFF}cE16PoR zim|dt@e7vqId;8v-9m^%zwzh#yo_4Gmgaq<+wU-g1hcBk&whrfCKTD+rG3=x!;zq3qPfIn2Rr3Fv1?&>SUB z0zJ3)>&NslS;I^=JuEd^m zj+bP2=(jW^L|JV2at!_)eW1X}qX=b`oT-ky7noq7NjR*@xLn?^Bk2t9p~Z|zP$q4K z>E(d!z^#|zO)XpPc!Eu&h4e#Z*j7!L8qT;aeJeVExR?KdxQ=k?_6&Vze{f^;(K5Jb z67oAga032us5c{In8&|>2WG0|k#Q)YdY0acnl9N6;pZ>WXyNUPU|;2FYvEtT+F;Tm zIx~~oACD}afg~u&k{V|vC4TX|$RYw8s61$at-1w{!|16OIe1bwGYkXxk_0Hu0qmG6 zc{i}ERII_d3innew=#cGDh?p|zz5B+RsWZ^E+8t!+Tn=M59{U!j{mdOl+mA%bXDPx z8o(*i4jCA^hf|cmDv^F~$p<5ua!04O8jx2zOnQjTukW`ZwOs=HMoQP?3Vy%?1Bb1q zs!qRG-oDLJgdWFKSbeQ<=?w3z{Wn=u#l$?NY%N>wTHQ<*=;lXtzMk|Td2Zv+ilr8# zx~eF{)|#&=h&yZHvnEgOT*=OFa8EB1dwqiAcWvn-1F0{jMwII^roR&+x>Q3=3Uu4cBV y?7}Fwu7u%KN>eg!MslW&u~~PRu4exc%qK7Hj&;FDm&__Y35?=QCG#DFQvU`r+u+;) literal 0 HcmV?d00001 diff --git a/app/src/release/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/release/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..396906002a7b77c60d8b84856d9718fa3963dab5 GIT binary patch literal 4488 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 Wqb*0{tf+_p0000%)B< z3pKSBa)YGQH@sV9!jqHnfOW=0#u<&r#rJJ`sb5x*8GO9eO1qJ~~3Lam@v;-{vPWDiYL_p0>5I ze)Cx~=R(1OzNEi-2mff7nDA^6xOVvedL8?eEnf~gMX1kV>u>FvW;R1;8n>Ws?1se= z%il)EE?l^jQ|ypc^7&U>oVvCf1j724A531jU@5B@=jqmlX?zhWI^nM}G8U7tsAM@5@B48Aqe0L=@#$SMHk7_(!qW`<=nMZ=0vijpg>` zUvqE!v+whx4?n&2=-=swbz&9y=hoJ5sCqxi|JKve#IX7MwzHi!of(?8E^OwxxNFuC zn|JBlxFn*?G&%BZ+_wLs%MVnS_*EqoZ8Vftyy5fxk+bNVr*GEDJc`cV5HmS4H7#I6 z(vgHJak*a!vjrP-IoL$;#XZ&pK}hfu!((%UYG1(|(MD1Y$3rseugc?LQPpg*_m#q7hu;w}sHTn*B22xyn+F SC3?V2$KdJe=d#Wzp$Pz&*_tc> literal 0 HcmV?d00001 diff --git a/app/src/release/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/release/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..2e834b546de968cfce1c4665dd8c884573260bbb GIT binary patch literal 2443 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 JPDHLkV1j1ytDFD; literal 0 HcmV?d00001 diff --git a/app/src/release/res/mipmap-mdpi/ic_launcher_round.png b/app/src/release/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..5f95819c0dc262928b22568f7801d095de66a55f GIT binary patch literal 2854 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 literal 0 HcmV?d00001 diff --git a/app/src/release/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/release/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..4723ce57d7efc4be8227bfa5a3736ef199f28141 GIT binary patch literal 6400 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 O0000y@T`) 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 literal 0 HcmV?d00001 diff --git a/app/src/release/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/release/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..a45b286ce224c2560d0eb543f0d092f769dca001 GIT binary patch literal 10662 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 literal 0 HcmV?d00001 diff --git a/app/src/release/res/mipmap-xxxhdpi/ic_launcher_background.png b/app/src/release/res/mipmap-xxxhdpi/ic_launcher_background.png new file mode 100644 index 0000000000000000000000000000000000000000..b597dbdaa14f92d7ac8333ec9d7d4353b309f24d GIT binary patch literal 6619 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 btI%bo4ofwei`L+!jg-?NSNn=@;P3tgey@YJ literal 0 HcmV?d00001 diff --git a/app/src/release/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/release/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..a528c16d8d2181ad0e9b12cd2f3db18eac4c3aa9 GIT binary patch literal 22562 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 WIi7h{EbRUJ!^QIs=V~l{68;UtI^OXB literal 0 HcmV?d00001 diff --git a/app/src/release/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/release/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..5797243d1d7704d955b2465f64ea8d33db11640c GIT binary patch literal 15424 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 MlTwzf5;qC{KX~Mcg#Z8m literal 0 HcmV?d00001 diff --git a/applications/.gitignore b/applications/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/applications/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/applications/build.gradle.kts b/applications/build.gradle.kts new file mode 100644 index 00000000..bda45315 --- /dev/null +++ b/applications/build.gradle.kts @@ -0,0 +1,54 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } +} + +dependencies { + implementation(libs.bundles.kotlin) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + + implementation(libs.bundles.androidx.lifecycle) + + implementation(project(":search")) + implementation(project(":base")) + implementation(project(":icons")) + implementation(project(":preferences")) + implementation(project(":ktx")) + implementation(project(":badges")) + implementation(project(":hiddenitems")) + implementation(project(":compat")) + +} \ No newline at end of file diff --git a/applications/consumer-rules.pro b/applications/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/applications/proguard-rules.pro b/applications/proguard-rules.pro new file mode 100644 index 00000000..47e28e5f --- /dev/null +++ b/applications/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts.kts.kts.kts.kts.kts.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/applications/src/main/AndroidManifest.xml b/applications/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a1e7322f --- /dev/null +++ b/applications/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + / + \ No newline at end of file diff --git a/applications/src/main/java/de/mm20/launcher2/applications/AppRepository.kt b/applications/src/main/java/de/mm20/launcher2/applications/AppRepository.kt new file mode 100644 index 00000000..de9fffb0 --- /dev/null +++ b/applications/src/main/java/de/mm20/launcher2/applications/AppRepository.kt @@ -0,0 +1,227 @@ +package de.mm20.launcher2.applications + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.pm.LauncherActivityInfo +import android.content.pm.LauncherApps +import android.content.pm.PackageInstaller +import android.content.pm.ShortcutInfo +import android.os.Process +import android.os.UserHandle +import android.util.Log +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.MutableLiveData +import de.mm20.launcher2.badges.Badge +import de.mm20.launcher2.badges.BadgeProvider +import de.mm20.launcher2.hiddenitems.HiddenItemsRepository +import de.mm20.launcher2.icons.IconRepository +import de.mm20.launcher2.preferences.LauncherPreferences +import de.mm20.launcher2.search.BaseSearchableRepository +import de.mm20.launcher2.search.SearchRepository +import de.mm20.launcher2.search.data.AppInstallation +import de.mm20.launcher2.search.data.Application +import de.mm20.launcher2.search.data.LauncherApp +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class AppRepository private constructor(val context: Context) : BaseSearchableRepository() { + + private val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps + + val applications = MediatorLiveData>() + + + private val installedApps = MutableLiveData>(emptyList()) + private val installations = MutableLiveData>(mutableListOf()) + private val hiddenItemKeys = HiddenItemsRepository.getInstance(context).hiddenItemsKeys + + private val installingPackages = mutableMapOf() + + private val profiles: List = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + launcherApps.profiles.takeIf { it.isNotEmpty() } ?: listOf(Process.myUserHandle()) + } else { + listOf(Process.myUserHandle()) + } + + + init { + + applications.addSource(installedApps) { + launch { updateAppsForDisplay() } + } + applications.addSource(installations) { + launch { updateAppsForDisplay() } + } + + applications.addSource(hiddenItemKeys) { + launch { updateAppsForDisplay() } + } + + launcherApps.registerCallback(object : LauncherApps.Callback() { + override fun onPackagesUnavailable(packageNames: Array, user: UserHandle, replacing: Boolean) { + installedApps.value = installedApps.value?.filter { !packageNames.contains(it.`package`) } + } + + override fun onPackageChanged(packageName: String, user: UserHandle) { + val apps = installedApps.value?.toMutableList() ?: return + apps.removeAll { packageName == it.`package` } + apps.addAll(getApplications(packageName)) + installedApps.value = apps + } + + override fun onPackagesAvailable(packageNames: Array, user: UserHandle, replacing: Boolean) { + val apps = installedApps.value?.toMutableList() ?: return + for (packageName in packageNames) { + apps.addAll(getApplications(packageName)) + } + installedApps.value = apps + } + + override fun onPackageAdded(packageName: String, user: UserHandle) { + Log.d("MM20", "App installed: $packageName") + val apps = installedApps.value?.toMutableList() ?: return + apps.addAll(getApplications(packageName)) + installedApps.value = apps + } + + override fun onPackageRemoved(packageName: String, user: UserHandle) { + installedApps.value = installedApps.value?.filter { packageName != (it.`package`) } + } + + override fun onShortcutsChanged(packageName: String, shortcuts: MutableList, user: UserHandle) { + super.onShortcutsChanged(packageName, shortcuts, user) + onPackageChanged(packageName, user) + } + + override fun onPackagesSuspended(packageNames: Array?, user: UserHandle?) { + super.onPackagesSuspended(packageNames, user) + packageNames?.forEach { + BadgeProvider.getInstance(context).setBadge("app://$it", Badge(iconRes = R.drawable.ic_badge_suspended)) + } + } + + override fun onPackagesUnsuspended(packageNames: Array?, user: UserHandle?) { + super.onPackagesUnsuspended(packageNames, user) + packageNames?.forEach { + BadgeProvider.getInstance(context).removeBadge("app://$it") + } + } + + }) + + + val packageInstaller = context.packageManager.packageInstaller + + packageInstaller.registerSessionCallback(object : PackageInstaller.SessionCallback() { + override fun onProgressChanged(sessionId: Int, progress: Float) { + val session = packageInstaller.getSessionInfo(sessionId) ?: return + val pkg = session.appPackageName ?: return + BadgeProvider.getInstance(context).updateBadge("app://$pkg", Badge(progress = progress)) + } + + override fun onActiveChanged(sessionId: Int, active: Boolean) { + if (active) onCreated(sessionId) + else onFinished(sessionId, false) + } + + override fun onFinished(sessionId: Int, success: Boolean) { + val pkg = installingPackages[sessionId] + installingPackages.remove(sessionId) + val key = "app://$pkg" + val badge = BadgeProvider.getInstance(context).getBadge(key)?.apply { progress = null } + ?: Badge() + BadgeProvider.getInstance(context).setBadge(key, badge) + val inst = installations.value ?: return + inst.removeAll { + it.session.sessionId == sessionId + } + installations.postValue(inst) + + } + + override fun onBadgingChanged(sessionId: Int) { + val inst = installations.value ?: mutableListOf() + inst.removeAll { + if (it.session.sessionId == sessionId) { + IconRepository.getInstance(context).removeIconFromCache(it) + true + } else false + } + onCreated(sessionId) + } + + override fun onCreated(sessionId: Int) { + val session = packageInstaller.getSessionInfo(sessionId) ?: return + installingPackages[sessionId] = session.appPackageName ?: return + if (installedApps.value?.any { it.`package` == session.appPackageName } == true) return + if (session.appLabel.isNullOrBlank() || !session.isActive) return + val appInstallation = AppInstallation(session) + val inst = installations.value ?: mutableListOf() + inst.add(appInstallation) + installations.postValue(inst) + } + }) + + + val apps = profiles.map { p -> launcherApps.getActivityList(null, p).mapNotNull { getApplication(it, p) } }.flatten() + installedApps.value = apps + } + + override suspend fun search(query: String) { + updateAppsForDisplay() + } + + private suspend fun updateAppsForDisplay() { + val query = SearchRepository.getInstance().currentQuery.value ?: "" + + val componentName = ComponentName.unflattenFromString(query) + + val apps = withContext(Dispatchers.Default) { + val hiddenItems = hiddenItemKeys.value ?: emptyList() + val installed = installedApps.value ?: emptyList() + val installing = installations.value ?: emptyList() + val results = mutableListOf() + results.addAll(installed) + results.addAll(installing) + if (query.isNotEmpty()) { + results.removeAll { !it.label.contains(query, ignoreCase = true) } + getActivityByComponentName(componentName)?.let { results.add(it) } + } + results.removeAll { hiddenItems.contains(it.key) } + results.sort() + results + } + + applications.value = apps + } + + private fun getActivityByComponentName(componentName: ComponentName?): Application? { + if (!LauncherPreferences.instance.searchActivities) return null + componentName ?: return null + val intent = Intent().setComponent(componentName) + val lai = launcherApps.resolveActivity(intent, Process.myUserHandle()) + return lai?.let { + LauncherApp(context, lai) + } + } + + private fun getApplication(launcherActivityInfo: LauncherActivityInfo, profile: UserHandle): Application? { + if (launcherActivityInfo.applicationInfo.packageName == context.packageName && !context.packageName.endsWith(".debug")) return null + return LauncherApp(context, launcherActivityInfo) + } + + private fun getApplications(packageName: String): List { + if (packageName == context.packageName) return emptyList() + + return profiles.map { p -> launcherApps.getActivityList(packageName, p).mapNotNull { getApplication(it, p) } }.flatten() + } + + companion object { + private lateinit var instance: AppRepository + fun getInstance(context: Context): AppRepository { + if (!::instance.isInitialized) instance = AppRepository(context.applicationContext) + return instance + } + } +} \ No newline at end of file diff --git a/applications/src/main/java/de/mm20/launcher2/applications/AppViewModel.kt b/applications/src/main/java/de/mm20/launcher2/applications/AppViewModel.kt new file mode 100644 index 00000000..2a32b84b --- /dev/null +++ b/applications/src/main/java/de/mm20/launcher2/applications/AppViewModel.kt @@ -0,0 +1,13 @@ +package de.mm20.launcher2.applications + +import android.app.Application as AndroidApp +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import de.mm20.launcher2.applications.AppRepository +import de.mm20.launcher2.search.data.Application + +class AppViewModel(app: AndroidApp): AndroidViewModel(app) { + private val repository = AppRepository.getInstance(app) + val applications: LiveData> = repository.applications +} + diff --git a/applications/src/main/java/de/mm20/launcher2/search/data/AppInstallation.kt b/applications/src/main/java/de/mm20/launcher2/search/data/AppInstallation.kt new file mode 100644 index 00000000..ace6257f --- /dev/null +++ b/applications/src/main/java/de/mm20/launcher2/search/data/AppInstallation.kt @@ -0,0 +1,67 @@ +package de.mm20.launcher2.search.data + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageInstaller +import android.graphics.ColorMatrix +import android.graphics.ColorMatrixColorFilter +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.ColorDrawable +import androidx.core.content.ContextCompat +import de.mm20.launcher2.applications.R +import de.mm20.launcher2.icons.LauncherIcon + +class AppInstallation( + val session: PackageInstaller.SessionInfo +) : Application( + label = session.appLabel?.toString() ?: "", + `package` = session.appPackageName ?: "", + activity = "", + flags = 0, + version = null +) { + + override val key: String + get() = "installer://${session.installerPackageName}:${session.appPackageName}" + + override val badgeKey: String + get() = "app://${session.appPackageName}" + + override fun getLaunchIntent(context: Context): Intent? { + return session.createDetailsIntent() + } + + override fun getPlaceholderIcon(context: Context): LauncherIcon { + return LauncherIcon( + foreground = ContextCompat.getDrawable(context, R.drawable.ic_app_placeholder)!!, + background = ColorDrawable(ContextCompat.getColor(context, R.color.grey)), + foregroundScale = 0.5f) + } + + override suspend fun loadIconAsync(context: Context, size: Int): LauncherIcon? { + val icon = session.appIcon ?: return getPlaceholderIcon(context) + val foreground = BitmapDrawable(context.resources, icon) + foreground.colorFilter = ColorMatrixColorFilter(ColorMatrix().apply { + setSaturation(0f) + }) + return LauncherIcon( + foreground = foreground, + background = ColorDrawable(ContextCompat.getColor(context, R.color.grey)) + ) + } + + override fun getStoreDetails(context: Context): StoreLink? { + return getStoreLinkForInstaller(session.installerPackageName, `package`) + } + + companion object { + fun search(context: Context): List { + val installer = context.packageManager.packageInstaller + val sessions = installer.allSessions + val results = sessions.mapNotNull { + if (it.appLabel != null && it.isActive) AppInstallation(it) else null + } + return results + } + } +} \ No newline at end of file diff --git a/applications/src/main/java/de/mm20/launcher2/search/data/AppShortcut.kt b/applications/src/main/java/de/mm20/launcher2/search/data/AppShortcut.kt new file mode 100644 index 00000000..13b1e40a --- /dev/null +++ b/applications/src/main/java/de/mm20/launcher2/search/data/AppShortcut.kt @@ -0,0 +1,161 @@ +package de.mm20.launcher2.search.data + +import android.content.Context +import android.content.Intent +import android.content.pm.LauncherApps +import android.content.pm.PackageManager +import android.content.pm.ShortcutInfo +import android.graphics.drawable.AdaptiveIconDrawable +import android.graphics.drawable.ColorDrawable +import android.os.* +import androidx.annotation.RequiresApi +import androidx.core.content.ContextCompat +import androidx.core.content.getSystemService +import de.mm20.launcher2.applications.R +import de.mm20.launcher2.badges.Badge +import de.mm20.launcher2.badges.BadgeProvider +import de.mm20.launcher2.graphics.BadgeDrawable +import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.ktx.getSerialNumber +import de.mm20.launcher2.ktx.isAtLeastApiLevel +import de.mm20.launcher2.ktx.jsonObjectOf +import de.mm20.launcher2.preferences.LauncherPreferences +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.json.JSONObject +import java.lang.IllegalStateException + +@RequiresApi(Build.VERSION_CODES.N_MR1) +class AppShortcut( + context: Context, + val launcherShortcut: ShortcutInfo, + val appName: String +) : Searchable() { + + override val label: String + get() = launcherShortcut.shortLabel?.toString() ?: "" + + + + private val userSerialNumber: Long = launcherShortcut.userHandle.getSerialNumber(context) + private val isMainProfile = launcherShortcut.userHandle == Process.myUserHandle() + + override val key: String + get() = if (isMainProfile) { + "shortcut://${launcherShortcut.`package`}/${launcherShortcut.id}" + } else { + "shortcut://${launcherShortcut.`package`}/${launcherShortcut.id}:${userSerialNumber}" + } + + override val badgeKey: String + get() { + return if (LauncherPreferences.instance.shortcutBadges) { + if (isMainProfile) "shortcut://${launcherShortcut.activity?.flattenToShortString()}" else "profile://$userSerialNumber" + } else { + "null" + } + } + + override fun serialize(): String { + return jsonObjectOf( + "packagename" to launcherShortcut.`package`, + "id" to launcherShortcut.id, + "user" to userSerialNumber, + ).toString() + } + + + override fun getLaunchIntent(context: Context): Intent? { + return launcherShortcut.intent + } + + override fun launch(context: Context, options: Bundle?): Boolean { + val launcherApps = context.getSystemService()!! + try { + launcherApps.startShortcut(launcherShortcut, null, options) + } catch (e: IllegalStateException) { + return false + } + return true + } + + override fun getPlaceholderIcon(context: Context): LauncherIcon { + return LauncherIcon( + foreground = ContextCompat.getDrawable(context, R.drawable.ic_app_placeholder)!!, + background = ColorDrawable(ContextCompat.getColor(context, R.color.green)), + foregroundScale = 0.5f) + } + + override suspend fun loadIconAsync(context: Context, size: Int): LauncherIcon? { + val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps + val icon = launcherApps.getShortcutIconDrawable(launcherShortcut, context.resources.displayMetrics.densityDpi) + icon ?: return null + if (isAtLeastApiLevel(Build.VERSION_CODES.O) && icon is AdaptiveIconDrawable) { + return LauncherIcon( + foreground = icon.foreground, + background = icon.background, + foregroundScale = 1.5f, + backgroundScale = 1.5f + ) + } + return LauncherIcon( + foreground = icon, + foregroundScale = 1f, + autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt() + ) + } + + companion object { + fun deserialize(context: Context, serialized: String): AppShortcut? { + val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps + if (!launcherApps.hasShortcutHostPermission()) return null + else { + val json = JSONObject(serialized) + val packageName = json.getString("packagename") + val id = json.getString("id") + val userSerial = json.optLong("user") + val query = LauncherApps.ShortcutQuery() + query.setPackage(packageName) + query.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC or + LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST or + LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED) + query.setShortcutIds(mutableListOf(id)) + val userManager = context.getSystemService()!! + val user = userManager.getUserForSerialNumber(userSerial) ?: Process.myUserHandle() + val shortcuts = try { + launcherApps.getShortcuts(query, user) + } catch (e: IllegalStateException) { + return null + } + val pm = context.packageManager + val appName = try { + pm.getApplicationInfo(packageName, 0).loadLabel(pm).toString() + } catch (e: PackageManager.NameNotFoundException) { + return null + } + if (shortcuts == null || shortcuts.isEmpty()) return null else { + GlobalScope.launch { + val activity = shortcuts[0].activity + withContext(Dispatchers.IO) { + val icon = try { + context.packageManager.getActivityIcon(activity + ?: return@withContext) + } catch (e: PackageManager.NameNotFoundException) { + return@withContext + } + val badge = Badge(icon = BadgeDrawable(context, icon)) + BadgeProvider.getInstance(context).setBadge("shortcut://${activity.flattenToShortString()}", badge) + } + } + return AppShortcut( + context = context, + launcherShortcut = shortcuts[0], + appName = appName + ) + } + } + } + } +} \ No newline at end of file diff --git a/applications/src/main/java/de/mm20/launcher2/search/data/Application.kt b/applications/src/main/java/de/mm20/launcher2/search/data/Application.kt new file mode 100644 index 00000000..8cddd3fe --- /dev/null +++ b/applications/src/main/java/de/mm20/launcher2/search/data/Application.kt @@ -0,0 +1,88 @@ +package de.mm20.launcher2.search.data + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.graphics.drawable.ColorDrawable +import android.util.Log +import androidx.core.content.ContextCompat +import de.mm20.launcher2.applications.R +import de.mm20.launcher2.compat.PackageManagerCompat +import de.mm20.launcher2.icons.LauncherIcon +import org.json.JSONObject + +abstract class Application( + override val label: String, + val `package`: String, + val activity: String, + val flags: Int, + val version: String?, + val shortcuts: List = emptyList() +) : Searchable() { + + override val badgeKey: String + get() = "app://${`package`}" + + override fun serialize(): String { + val json = JSONObject() + json.put("package", `package`) + json.put("activity", activity) + return json.toString() + } + + override fun getLaunchIntent(context: Context): Intent? { + val intent = Intent() + intent.component = ComponentName(`package`, activity) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + return intent + } + + override fun getPlaceholderIcon(context: Context): LauncherIcon { + return LauncherIcon( + foreground = ContextCompat.getDrawable(context, R.drawable.ic_app_placeholder)!!, + background = ColorDrawable(ContextCompat.getColor(context, R.color.lightgreen)), + foregroundScale = 0.5f + ) + } + + open fun getStoreDetails(context: Context): StoreLink? { + val pm = context.packageManager + val installSourceInfo = PackageManagerCompat.getInstallSource(pm, `package`) + return getStoreLinkForInstaller(installSourceInfo.initiatingPackageName, `package`) + } + + override val key: String + get() = "app://$`package`:$activity" + + companion object { + internal fun getStoreLinkForInstaller(installerPackage: String?, packageName: String?): StoreLink? { + if (packageName == null) return null + return when (installerPackage) { + "de.amazon.mShop.android", "com.amazon.venezia" -> { + StoreLink( + "Amazon App Shop", + "http://www.amazon.com/gp/mas/dl/android?p=${packageName}" + ) + } + "com.android.vending" -> { + StoreLink( + "Google Play Store", + "https://play.google.com/store/apps/details?id=${packageName}" + ) + } + "org.fdroid.fdroid", "com.aurora.adroid" -> { + StoreLink( + "F-Droid", + "https://f-droid.org/packages/${packageName}" + ) + } + else -> null + } + } + } +} + +data class StoreLink( + val label: String, + val url: String +) \ No newline at end of file diff --git a/applications/src/main/java/de/mm20/launcher2/search/data/LauncherApp.kt b/applications/src/main/java/de/mm20/launcher2/search/data/LauncherApp.kt new file mode 100644 index 00000000..8087ad39 --- /dev/null +++ b/applications/src/main/java/de/mm20/launcher2/search/data/LauncherApp.kt @@ -0,0 +1,123 @@ +package de.mm20.launcher2.search.data + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.pm.LauncherActivityInfo +import android.content.pm.LauncherApps +import android.content.pm.PackageManager +import android.content.pm.ShortcutInfo +import android.os.* +import androidx.core.content.getSystemService +import de.mm20.launcher2.icons.IconPackManager +import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.ktx.getSerialNumber +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.json.JSONObject + +/** + * An [Application] based on an [android.content.pm.LauncherActivityInfo] + */ +class LauncherApp( + context: Context, + private val launcherActivityInfo: LauncherActivityInfo +) : Application( + label = launcherActivityInfo.label.toString(), + `package` = launcherActivityInfo.applicationInfo.packageName, + activity = launcherActivityInfo.name, + flags = launcherActivityInfo.applicationInfo.flags, + version = getPackageVersionName(context, launcherActivityInfo.applicationInfo.packageName), + shortcuts = run { + val appShortcuts = mutableListOf() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { + val launcherApps = context.getSystemService()!! + if (!launcherApps.hasShortcutHostPermission()) return@run appShortcuts + val query = LauncherApps.ShortcutQuery() + .setPackage(launcherActivityInfo.applicationInfo.packageName) + .setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC or LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST) + val shortcuts = try { + launcherApps.getShortcuts(query, launcherActivityInfo.user) + } catch (e: IllegalStateException) { + emptyList() + } + appShortcuts.addAll(shortcuts?.map { AppShortcut(context, it, launcherActivityInfo.label.toString()) } + ?: emptyList()) + } + appShortcuts + } +) { + + private val userSerialNumber: Long = launcherActivityInfo.user.getSerialNumber(context) + private val isMainProfile = launcherActivityInfo.user == Process.myUserHandle() + + override val badgeKey: String = if (isMainProfile) "app://${`package`}" else "profile://$userSerialNumber" + + override val key: String + get() = if (isMainProfile) "app://$`package`:$activity" else "app://$`package`:$activity:${userSerialNumber}" + + override fun serialize(): String { + val json = JSONObject() + json.put("package", `package`) + json.put("activity", activity) + json.put("user", userSerialNumber) + return json.toString() + } + + fun getUser(): UserHandle? { + return launcherActivityInfo.user + } + + override suspend fun loadIconAsync(context: Context, size: Int): LauncherIcon? { + return withContext(Dispatchers.IO) { + IconPackManager.getInstance(context).getIcon(context, launcherActivityInfo, size) + } + } + + override fun launch(context: Context, options: Bundle?): Boolean { + val launcherApps = context.getSystemService()!! + if (isMainProfile) { + val intent = Intent() + intent.component = ComponentName(`package`, activity) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + context.startActivity(intent, options) + } else { + try { + launcherApps.startMainActivity( + ComponentName(`package`, activity), + launcherActivityInfo.user, + null, + options + ) + } catch (e: SecurityException) { + return false + } + } + return true + } + + companion object { + + fun deserialize(context: Context, serialized: String): LauncherApp? { + val json = JSONObject(serialized) + val launcherApps = context.getSystemService()!! + val userManager = context.getSystemService()!! + val userSerial = json.optLong("user") + val user = userManager.getUserForSerialNumber(userSerial) ?: Process.myUserHandle() + val pkg = json.getString("package") + val intent = Intent().also { + it.component = ComponentName(pkg, json.getString("activity")) + } + val launcherActivityInfo = launcherApps.resolveActivity(intent, user) ?: return null + return LauncherApp(context, launcherActivityInfo) + } + + fun getPackageVersionName(context: Context, packageName: String): String? { + return try { + context.packageManager.getPackageInfo(packageName, 0).versionName + } catch (e: PackageManager.NameNotFoundException) { + null + } + } + } +} \ No newline at end of file diff --git a/applications/src/main/res/drawable/ic_app_placeholder.xml b/applications/src/main/res/drawable/ic_app_placeholder.xml new file mode 100644 index 00000000..8d3fd387 --- /dev/null +++ b/applications/src/main/res/drawable/ic_app_placeholder.xml @@ -0,0 +1,5 @@ + + + diff --git a/appsearch/.gitignore b/appsearch/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/appsearch/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/appsearch/build.gradle.kts b/appsearch/build.gradle.kts new file mode 100644 index 00000000..f211a865 --- /dev/null +++ b/appsearch/build.gradle.kts @@ -0,0 +1,58 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") + id("kotlin-kapt") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } +} + +dependencies { + implementation(libs.bundles.kotlin) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + + implementation(libs.bundles.androidx.lifecycle) + + implementation(libs.bundles.androidx.appsearch) + kapt(libs.androidx.appsearchcompiler) + + implementation(libs.guava) + + implementation(project(":search")) + implementation(project(":base")) + implementation(project(":icons")) + implementation(project(":preferences")) + implementation(project(":ktx")) + implementation(project(":badges")) + implementation(project(":hiddenitems")) +} \ No newline at end of file diff --git a/appsearch/consumer-rules.pro b/appsearch/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/appsearch/proguard-rules.pro b/appsearch/proguard-rules.pro new file mode 100644 index 00000000..ff59496d --- /dev/null +++ b/appsearch/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/appsearch/src/main/AndroidManifest.xml b/appsearch/src/main/AndroidManifest.xml new file mode 100644 index 00000000..fcb7f5f2 --- /dev/null +++ b/appsearch/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/appsearch/src/main/java/de/mm20/launcher2/appsearch/AppSearchRepository.kt b/appsearch/src/main/java/de/mm20/launcher2/appsearch/AppSearchRepository.kt new file mode 100644 index 00000000..287aa1b0 --- /dev/null +++ b/appsearch/src/main/java/de/mm20/launcher2/appsearch/AppSearchRepository.kt @@ -0,0 +1,69 @@ +package de.mm20.launcher2.appsearch + +import android.app.appsearch.AppSearchManager +import android.app.appsearch.GlobalSearchSession +import android.app.appsearch.SearchResult +import android.app.appsearch.SearchSpec +import android.content.Context +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.MutableLiveData +import de.mm20.launcher2.hiddenitems.HiddenItemsRepository +import de.mm20.launcher2.ktx.isAtLeastApiLevel +import de.mm20.launcher2.search.BaseSearchableRepository +import de.mm20.launcher2.search.data.AppSearchResult +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.util.concurrent.Executors +import kotlin.coroutines.suspendCoroutine + +class AppSearchRepository private constructor(val context: Context) : BaseSearchableRepository() { + + private var session: GlobalSearchSession? = null + + val appSearchResults = MediatorLiveData?>() + + private val allAppSearchResults = MutableLiveData?>(emptyList()) + private val hiddenItemKeys = HiddenItemsRepository.getInstance(context).hiddenItemsKeys + + init { + appSearchResults.addSource(hiddenItemKeys) { keys -> + appSearchResults.value = allAppSearchResults.value?.filter { !keys.contains(it.key) } + } + appSearchResults.addSource(allAppSearchResults) { c -> + appSearchResults.value = c?.filter { hiddenItemKeys.value?.contains(it.key) != true } + } + } + + override suspend fun search(query: String) { + val results = emptyList() + if (!isAtLeastApiLevel(31)) return + val executor = Executors.newSingleThreadExecutor() + if (session == null) { + val appSearchManager = context.getSystemService(AppSearchManager::class.java) + session = suspendCoroutine { cont -> + appSearchManager.createGlobalSearchSession(executor) { + cont.resumeWith(Result.success(it.resultValue)) + } + } + } + + withContext(Dispatchers.IO) { + val searchResults = session?.search(query, SearchSpec.Builder().build()) + val page = suspendCoroutine?> { cont -> + searchResults?.getNextPage(executor) { + cont.resumeWith(Result.success(it.resultValue)) + } + } + } + allAppSearchResults.value = results + } + + companion object { + private lateinit var instance: AppSearchRepository + fun getInstance(context: Context): AppSearchRepository { + if (!::instance.isInitialized) instance = AppSearchRepository(context.applicationContext) + return instance + } + } + +} \ No newline at end of file diff --git a/appsearch/src/main/java/de/mm20/launcher2/appsearch/AppSearchViewModel.kt b/appsearch/src/main/java/de/mm20/launcher2/appsearch/AppSearchViewModel.kt new file mode 100644 index 00000000..ddecaf0e --- /dev/null +++ b/appsearch/src/main/java/de/mm20/launcher2/appsearch/AppSearchViewModel.kt @@ -0,0 +1,11 @@ +package de.mm20.launcher2.appsearch + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import de.mm20.launcher2.search.data.AppSearchResult + +class AppSearchViewModel(app: Application) : AndroidViewModel(app) { + private val repository = AppSearchRepository.getInstance(app) + private val appSearch: LiveData?> = repository.appSearchResults +} \ No newline at end of file diff --git a/appsearch/src/main/java/de/mm20/launcher2/search/data/AppSearchResult.kt b/appsearch/src/main/java/de/mm20/launcher2/search/data/AppSearchResult.kt new file mode 100644 index 00000000..28a6fe07 --- /dev/null +++ b/appsearch/src/main/java/de/mm20/launcher2/search/data/AppSearchResult.kt @@ -0,0 +1,15 @@ +package de.mm20.launcher2.search.data + +import android.content.Context +import de.mm20.launcher2.icons.LauncherIcon + +class AppSearchResult: Searchable() { + override val key: String + get() = TODO("Not yet implemented") + override val label: String + get() = TODO("Not yet implemented") + + override fun getPlaceholderIcon(context: Context): LauncherIcon { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/assets/icons/MM20Launcher2-alpha.svg b/assets/icons/MM20Launcher2-alpha.svg new file mode 100644 index 00000000..85c03a6e --- /dev/null +++ b/assets/icons/MM20Launcher2-alpha.svg @@ -0,0 +1,437 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/MM20Launcher2-beta.svg b/assets/icons/MM20Launcher2-beta.svg new file mode 100644 index 00000000..e161133e --- /dev/null +++ b/assets/icons/MM20Launcher2-beta.svg @@ -0,0 +1,437 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/MM20Launcher2-debug.svg b/assets/icons/MM20Launcher2-debug.svg new file mode 100644 index 00000000..3c10e681 --- /dev/null +++ b/assets/icons/MM20Launcher2-debug.svg @@ -0,0 +1,437 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/MM20Launcher2-release.svg b/assets/icons/MM20Launcher2-release.svg new file mode 100644 index 00000000..1b124053 --- /dev/null +++ b/assets/icons/MM20Launcher2-release.svg @@ -0,0 +1,433 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/MM20Launcher2.svg b/assets/icons/MM20Launcher2.svg new file mode 100644 index 00000000..54559163 --- /dev/null +++ b/assets/icons/MM20Launcher2.svg @@ -0,0 +1,414 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/badges/.gitignore b/badges/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/badges/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/badges/build.gradle.kts b/badges/build.gradle.kts new file mode 100644 index 00000000..415569ba --- /dev/null +++ b/badges/build.gradle.kts @@ -0,0 +1,48 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.bundles.kotlin) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + + implementation(libs.bundles.androidx.lifecycle) + + implementation(project(":ktx")) + implementation(project(":preferences")) + +} \ No newline at end of file diff --git a/badges/consumer-rules.pro b/badges/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/badges/proguard-rules.pro b/badges/proguard-rules.pro new file mode 100644 index 00000000..01639a19 --- /dev/null +++ b/badges/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/badges/src/main/AndroidManifest.xml b/badges/src/main/AndroidManifest.xml new file mode 100644 index 00000000..839d84a6 --- /dev/null +++ b/badges/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + / + \ No newline at end of file diff --git a/badges/src/main/java/de/mm20/launcher2/badges/Badge.kt b/badges/src/main/java/de/mm20/launcher2/badges/Badge.kt new file mode 100644 index 00000000..c2cfb6db --- /dev/null +++ b/badges/src/main/java/de/mm20/launcher2/badges/Badge.kt @@ -0,0 +1,10 @@ +package de.mm20.launcher2.badges + +import android.graphics.drawable.Drawable + +data class Badge( + var number: Int? = null, + var progress: Float? = null, + var iconRes: Int? = null, + var icon: Drawable? = null +) \ No newline at end of file diff --git a/badges/src/main/java/de/mm20/launcher2/badges/BadgeProvider.kt b/badges/src/main/java/de/mm20/launcher2/badges/BadgeProvider.kt new file mode 100644 index 00000000..36d2565c --- /dev/null +++ b/badges/src/main/java/de/mm20/launcher2/badges/BadgeProvider.kt @@ -0,0 +1,150 @@ +package de.mm20.launcher2.badges + +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.LauncherApps +import android.os.Build +import android.os.Process +import androidx.annotation.RequiresApi +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import de.mm20.launcher2.ktx.getSerialNumber +import de.mm20.launcher2.ktx.isAtLeastApiLevel +import de.mm20.launcher2.preferences.LauncherPreferences +import kotlinx.coroutines.* + +class BadgeProvider private constructor(val context: Context) { + + private val scope = CoroutineScope(Job() + Dispatchers.Main) + + private val badges = mutableMapOf>() + + init { + if (LauncherPreferences.instance.cloudBadges) { + addCloudBadges() + } + if (LauncherPreferences.instance.suspendBadges) { + addSuspendBadges() + } + if (LauncherPreferences.instance.profileBadges && isAtLeastApiLevel(Build.VERSION_CODES.O)) { + addProfileBadges() + } + } + + + fun setBadge(key: String, badge: Badge) { + if (badges.containsKey(key)) { + badges[key]?.postValue(badge) + return + } + badges[key] = MutableLiveData(badge) + } + + /** + * Updates a badge with all set values of another badge but keeps all other values + */ + fun updateBadge(key: String, + badge: Badge) { + if (badges.containsKey(key)) { + badges[key]?.run { + val updatedBadge = value?.also { + if (badge.number != null) it.number = badge.number + if (badge.progress != null) it.progress = badge.progress + if (badge.iconRes != null) it.iconRes = badge.iconRes + if (badge.icon != null) it.icon = badge.icon + } + postValue(updatedBadge) + } + } else { + badges[key] = MutableLiveData(badge) + } + } + + fun removeBadge(key: String) { + badges[key]?.postValue(Badge()) + } + + fun getBadge(key: String): Badge? { + return badges[key]?.value + } + + fun getLiveBadge(key: String): LiveData { + return badges[key] ?: MutableLiveData(Badge()).also { badges[key] = it } + } + + fun removeNotificationBadges() { + for (k in badges.keys) { + if (k.startsWith("app://")) { + val badge = getBadge(k) ?: continue + badge.number = null + badge.progress = null + updateBadge(k, badge) + } + } + } + + fun removeSuspendBadges() { + for ((k, v) in badges) { + if (k.startsWith("app://") && v.value?.iconRes == R.drawable.ic_badge_suspended) { + val badge = getBadge(k) ?: continue + badge.iconRes = null + updateBadge(k, badge) + } + } + } + + fun addSuspendBadges() { + scope.launch { + withContext(Dispatchers.IO) { + val apps = context.packageManager.getInstalledApplications(0) + for (app in apps) { + if (app.flags and ApplicationInfo.FLAG_SUSPENDED != 0) { + setBadge("app://${app.packageName}", Badge(iconRes = R.drawable.ic_badge_suspended)) + } + } + } + } + } + + fun addCloudBadges() { + setBadge("gdrive://", Badge(iconRes = R.drawable.ic_badge_gdrive)) + setBadge("onedrive://", Badge(iconRes = R.drawable.ic_badge_onedrive)) + setBadge("nextcloud://", Badge(iconRes = R.drawable.ic_badge_nextcloud)) + setBadge("owncloud://", Badge(iconRes = R.drawable.ic_badge_owncloud)) + } + + @RequiresApi(Build.VERSION_CODES.O) + fun addProfileBadges() { + val profiles = (context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps).profiles + for (p in profiles) { + if (p == Process.myUserHandle()) continue + setBadge("profile://${p.getSerialNumber(context)}", Badge( + iconRes = R.drawable.ic_badge_workprofile + )) + } + } + + fun removeCloudBadges() { + removeBadge("gdrive://") + removeBadge("onedrive://") + removeBadge("nextcloud://") + removeBadge("owncloud://") + } + + fun removeShortcutBadges() { + for (k in badges.keys) { + if (k.startsWith("shortcut://")) { + removeBadge(k) + } + } + } + + companion object { + private lateinit var instance: BadgeProvider + + fun getInstance(context: Context): BadgeProvider { + if (!::instance.isInitialized) instance = BadgeProvider(context.applicationContext) + return instance + } + } +} \ No newline at end of file diff --git a/badges/src/main/res/drawable-hdpi/ic_badge_gdrive.webp b/badges/src/main/res/drawable-hdpi/ic_badge_gdrive.webp new file mode 100644 index 0000000000000000000000000000000000000000..a67a50f906699dd8383f7073881934a1e9458be6 GIT binary patch literal 10488 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 literal 0 HcmV?d00001 diff --git a/badges/src/main/res/drawable-xhdpi/ic_badge_gdrive.webp b/badges/src/main/res/drawable-xhdpi/ic_badge_gdrive.webp new file mode 100644 index 0000000000000000000000000000000000000000..747abbaa9ac2c34068ed595fb4c5b50a2da3de06 GIT binary patch literal 9596 zcmeHMc_5VA`#<(2l8dOTOp~&OF~%~JC0iOLjrB(5&dS(EGb2kex4l$1g=n>BE3TB> zR9dtzBuhl8hzOS~S;lvz=RDizInQ~{J1;lo$AZjp290Ndb2$${ZXp8z~y*Em#703Y4Y143SC_*Pg zTAAukra}4~q?fUwgOGlKj8i7*j|lB2q=lfA+3v1(5Sua_NBPa5DKluQKRX!m;2@7C zEjR?~7yV_DrXln;gbof0gT4tb1fd)~#KR5VHDOZ+j(`Z9!8(8f6c7fOAP8`QKD>v( z5e{&L^R_>bpD&bO2c;-biV3Jt!XB_dFdz%%0kQ^=2VP%e%V8K}g+n58wg5=H5(qxQ z^_7YTfFCCijHL+#{B!`sngF;F@>QOd3V`__9FO}trkMhO!Y%+xD!z_Uk^s287XY<} zP%@h=v?B(;MHvhL`m+I$Uk`v%6#%k6lW~J{$UMlX2f!W1YRwY>j-LQvsUOt6;Rm>- zp}?28{YRdu_`%FwV+ohkQ1yny9F11pqrqAM?7LzU3s97DPg(rNp9>Z@c&6V)qXgTe zZ-Z^ur?RANSgC>CWo zw^s_Z0;l}poBNXehZD^yTqaAi4RjN!(VizFr_VgCuiL4c zP%0`oYm⩔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 literal 0 HcmV?d00001 diff --git a/badges/src/main/res/drawable/ic_badge_nextcloud.xml b/badges/src/main/res/drawable/ic_badge_nextcloud.xml new file mode 100644 index 00000000..8124620b --- /dev/null +++ b/badges/src/main/res/drawable/ic_badge_nextcloud.xml @@ -0,0 +1,10 @@ + + + diff --git a/badges/src/main/res/drawable/ic_badge_onedrive.xml b/badges/src/main/res/drawable/ic_badge_onedrive.xml new file mode 100644 index 00000000..1dd014aa --- /dev/null +++ b/badges/src/main/res/drawable/ic_badge_onedrive.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/badges/src/main/res/drawable/ic_badge_owncloud.xml b/badges/src/main/res/drawable/ic_badge_owncloud.xml new file mode 100644 index 00000000..2be8c5f7 --- /dev/null +++ b/badges/src/main/res/drawable/ic_badge_owncloud.xml @@ -0,0 +1,10 @@ + + + diff --git a/badges/src/main/res/drawable/ic_badge_suspended.xml b/badges/src/main/res/drawable/ic_badge_suspended.xml new file mode 100644 index 00000000..d4caaef0 --- /dev/null +++ b/badges/src/main/res/drawable/ic_badge_suspended.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/badges/src/main/res/drawable/ic_badge_workprofile.xml b/badges/src/main/res/drawable/ic_badge_workprofile.xml new file mode 100644 index 00000000..9e6af7f7 --- /dev/null +++ b/badges/src/main/res/drawable/ic_badge_workprofile.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/base/.gitignore b/base/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/base/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/base/build.gradle.kts b/base/build.gradle.kts new file mode 100644 index 00000000..276669c7 --- /dev/null +++ b/base/build.gradle.kts @@ -0,0 +1,51 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.kotlin.stdlib) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + implementation(libs.materialcomponents) + + implementation(libs.androidx.palette) + + implementation(libs.lottie.core) + implementation(libs.bundles.materialdialogs) + + implementation(project(":ktx")) + +} \ No newline at end of file diff --git a/base/consumer-rules.pro b/base/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/base/proguard-rules.pro b/base/proguard-rules.pro new file mode 100644 index 00000000..d99b33c9 --- /dev/null +++ b/base/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/base/src/main/AndroidManifest.xml b/base/src/main/AndroidManifest.xml new file mode 100644 index 00000000..e1598c62 --- /dev/null +++ b/base/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/base/src/main/java/de/mm20/launcher2/graphics/BadgeDrawable.kt b/base/src/main/java/de/mm20/launcher2/graphics/BadgeDrawable.kt new file mode 100644 index 00000000..369b2c65 --- /dev/null +++ b/base/src/main/java/de/mm20/launcher2/graphics/BadgeDrawable.kt @@ -0,0 +1,58 @@ +package de.mm20.launcher2.graphics + +import android.content.Context +import android.graphics.* +import android.graphics.drawable.AdaptiveIconDrawable +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable +import android.os.Build +import androidx.core.graphics.drawable.toBitmap +import de.mm20.launcher2.ktx.dp +import kotlin.math.roundToInt + +class BadgeDrawable(context: Context, drawable: Drawable) : Drawable() { + + private val drawable: BitmapDrawable + + init { + val size = (28.8 * context.dp).roundToInt() + val drw: Drawable = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && drawable is AdaptiveIconDrawable) { + LayerDrawable(arrayOf( + drawable.background, + drawable.foreground + )).apply { + val inset = (-size * 0.25).roundToInt() + setLayerInset(1, inset, inset, inset, inset) + setLayerInset(0, inset, inset, inset, inset) + } + } else { + drawable + } + val bitmap = drw.toBitmap(size, size).run { + if(isMutable) this + else this.copy(config, true) + } + this.drawable = BitmapDrawable(context.resources, bitmap) + } + + override fun setAlpha(alpha: Int) { + } + + override fun getOpacity(): Int { + return PixelFormat.TRANSLUCENT + } + + override fun setColorFilter(colorFilter: ColorFilter?) { + } + + override fun draw(canvas: Canvas) { + canvas.clipPath(Path().apply { addOval( + bounds.left.toFloat(), + bounds.top.toFloat(), + bounds.right.toFloat(), + bounds.bottom.toFloat(), Path.Direction.CW) }) + drawable.setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom) + drawable.draw(canvas) + } +} \ No newline at end of file diff --git a/base/src/main/java/de/mm20/launcher2/helper/NetworkUtils.kt b/base/src/main/java/de/mm20/launcher2/helper/NetworkUtils.kt new file mode 100644 index 00000000..fe32552b --- /dev/null +++ b/base/src/main/java/de/mm20/launcher2/helper/NetworkUtils.kt @@ -0,0 +1,17 @@ +package de.mm20.launcher2.helper + +import android.content.Context +import android.net.ConnectivityManager + +object NetworkUtils { + + fun isOffline(context: Context, allowMobile: Boolean = true): Boolean { + val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + val info = connectivityManager.activeNetworkInfo ?: return true + return when { + info.type == ConnectivityManager.TYPE_WIFI -> false + info.type == ConnectivityManager.TYPE_MOBILE && allowMobile -> false + else -> true + } + } +} \ No newline at end of file diff --git a/base/src/main/java/de/mm20/launcher2/icons/LauncherIcon.kt b/base/src/main/java/de/mm20/launcher2/icons/LauncherIcon.kt new file mode 100644 index 00000000..43e6b344 --- /dev/null +++ b/base/src/main/java/de/mm20/launcher2/icons/LauncherIcon.kt @@ -0,0 +1,84 @@ +package de.mm20.launcher2.icons + +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import androidx.core.graphics.drawable.toBitmap +import androidx.palette.graphics.Palette +import java.lang.ref.WeakReference + +open class LauncherIcon( + foreground: Drawable, + background: Drawable? = null, + foregroundScale: Float = 1f, + backgroundScale: Float = 1f, + var autoGenerateBackgroundMode: Int = BACKGROUND_WHITE +) { + + var foreground = foreground + set(value) { + field = value + updateBackgroundColor() + notifyCallbacks() + } + + private fun updateBackgroundColor() { + if (background == null) { + when (autoGenerateBackgroundMode) { + BACKGROUND_COLOR -> { + val palette = Palette + .from(foreground.toBitmap()) + .generate() + this.background = ColorDrawable(palette.getDominantColor(Color.WHITE)) + badgeColor = palette.getLightVibrantColor(0xFFF0F0F0.toInt()) + } + BACKGROUND_WHITE -> this.background = ColorDrawable(Color.WHITE) + else -> this.foregroundScale = 1f + } + } + } + + var background = background + set(value) { + field = value + notifyCallbacks() + } + + var foregroundScale = foregroundScale + set(value) { + field = value + notifyCallbacks() + } + + var backgroundScale = backgroundScale + set(value) { + field = value + notifyCallbacks() + } + + private val callbacks = mutableListOf Unit>>() + + fun registerCallback(callback: (LauncherIcon) -> Unit) { + callbacks.add(WeakReference(callback)) + } + + protected fun notifyCallbacks() { + val iterator = callbacks.iterator() + while(iterator.hasNext()) { + val callback = iterator.next() + callback.get()?.invoke(this) ?: iterator.remove() + } + } + + var badgeColor: Int = 0xFFF0F0F0.toInt() + + init { + updateBackgroundColor() + } + + companion object { + const val BACKGROUND_NONE = 0 + const val BACKGROUND_COLOR = 1 + const val BACKGROUND_WHITE = 2 + } +} \ No newline at end of file diff --git a/base/src/main/java/de/mm20/launcher2/view/ElevationImageView.kt b/base/src/main/java/de/mm20/launcher2/view/ElevationImageView.kt new file mode 100644 index 00000000..79bec864 --- /dev/null +++ b/base/src/main/java/de/mm20/launcher2/view/ElevationImageView.kt @@ -0,0 +1,135 @@ +package de.mm20.launcher2.view + +import android.content.Context +import android.content.res.ColorStateList +import android.graphics.* +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import android.graphics.drawable.Icon +import android.renderscript.Allocation +import android.renderscript.Element +import android.renderscript.RenderScript +import android.renderscript.ScriptIntrinsicBlur +import android.util.AttributeSet +import android.view.View +import com.airbnb.lottie.LottieDrawable +import com.airbnb.lottie.LottieProperty +import com.airbnb.lottie.model.KeyPath +import kotlin.math.max +import kotlin.math.min + +class ElevationImageView : androidx.appcompat.widget.AppCompatImageView { + + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + constructor(context: Context, attrs: AttributeSet?, defStyleRes: Int) : super(context, attrs, defStyleRes) + + + private val secondCanvas = Canvas() + private var shadowBitmap: Bitmap? = null + private var inAllocation: Allocation? = null + private lateinit var renderScript : RenderScript + private lateinit var blur : ScriptIntrinsicBlur + + val shadowPaint = Paint().also { + it.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OVER) + it.colorFilter = PorterDuffColorFilter(Color.BLACK, PorterDuff.Mode.SRC_ATOP) + it.alpha = 66 + setLayerType(View.LAYER_TYPE_SOFTWARE, null) + } + + val clearPaint = Paint().also { + it.color = Color.TRANSPARENT + it.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + blur.destroy() + renderScript.destroy() + } + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + renderScript = RenderScript.create(context) + blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript)) + } + + override fun onDraw(canvas: Canvas) { + secondCanvas.drawRect(0f, 0f, secondCanvas.width.toFloat(), secondCanvas.height.toFloat(), clearPaint) + + if (drawable == null) { + return // couldn't resolve the URI + } + + val drawableWidth = drawable.intrinsicWidth + val drawableHeight = drawable.intrinsicHeight + + if (drawableWidth == 0 || drawableHeight == 0) { + return // nothing to draw (empty bounds) + } + + if (shadowBitmap?.width != width || shadowBitmap?.height != height) { + shadowBitmap?.recycle() + inAllocation?.destroy() + if (z > 0f && width > 0 && height > 0) { + shadowBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { + secondCanvas.setBitmap(it) + } + inAllocation = shadowBitmap?.let { Allocation.createFromBitmap(renderScript, it) } + } + } + + val saveCount = canvas.saveCount + val saveCount2 = secondCanvas.saveCount + canvas.save() + secondCanvas.save() + + + canvas.translate(paddingLeft.toFloat(), paddingTop.toFloat()) + secondCanvas.translate(paddingLeft.toFloat(), paddingTop.toFloat() + getShadowTranslation()) + + if (!imageMatrix.isIdentity) { + canvas.concat(imageMatrix) + secondCanvas.concat(imageMatrix) + } + + + drawable.draw(canvas) + drawable.draw(secondCanvas) + shadowBitmap = shadowBitmap?.let { blurBitmap(it) } + canvas.restoreToCount(saveCount) + secondCanvas.restoreToCount(saveCount2) + shadowBitmap?.let { canvas.drawBitmap(it, 0f, 0f, shadowPaint) } + } + + private fun blurBitmap(bitmap: Bitmap): Bitmap { + val alloc = inAllocation ?: return bitmap + if (z == 0f) return bitmap + alloc.copyFrom(bitmap) + blur.setRadius(max(0f, min(getShadowBlurRadius(), 25f))) + blur.setInput(alloc) + blur.forEach(alloc) + alloc.copyTo(bitmap) + return bitmap + } + + override fun setColorFilter(cf: ColorFilter?) { + super.setColorFilter(cf) + val drawable = drawable + if (drawable is LottieDrawable) { + drawable.addValueCallback(KeyPath("**"), LottieProperty.COLOR_FILTER) { + cf + } + } + } + + + private fun getShadowTranslation(): Float { + return z * 0.5f + } + + private fun getShadowBlurRadius(): Float { + return z * 0.5f + } +} \ No newline at end of file diff --git a/base/src/main/res/color/chip_background.xml b/base/src/main/res/color/chip_background.xml new file mode 100644 index 00000000..94fbf354 --- /dev/null +++ b/base/src/main/res/color/chip_background.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/base/src/main/res/color/chip_stroke.xml b/base/src/main/res/color/chip_stroke.xml new file mode 100644 index 00000000..63383a75 --- /dev/null +++ b/base/src/main/res/color/chip_stroke.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/base/src/main/res/color/chip_textcolor.xml b/base/src/main/res/color/chip_textcolor.xml new file mode 100644 index 00000000..486c4c2b --- /dev/null +++ b/base/src/main/res/color/chip_textcolor.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/base/src/main/res/color/text_color_primary.xml b/base/src/main/res/color/text_color_primary.xml new file mode 100644 index 00000000..0c9304d6 --- /dev/null +++ b/base/src/main/res/color/text_color_primary.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/base/src/main/res/color/text_color_secondary.xml b/base/src/main/res/color/text_color_secondary.xml new file mode 100644 index 00000000..594414d4 --- /dev/null +++ b/base/src/main/res/color/text_color_secondary.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/base/src/main/res/drawable/ic_permission.xml b/base/src/main/res/drawable/ic_permission.xml new file mode 100644 index 00000000..37b2c790 --- /dev/null +++ b/base/src/main/res/drawable/ic_permission.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_broken_clouds.xml b/base/src/main/res/drawable/ic_weather_broken_clouds.xml new file mode 100644 index 00000000..21ca06e7 --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_broken_clouds.xml @@ -0,0 +1,54 @@ + + + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_broken_clouds_night.xml b/base/src/main/res/drawable/ic_weather_broken_clouds_night.xml new file mode 100644 index 00000000..b6cfbc4a --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_broken_clouds_night.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_clear.xml b/base/src/main/res/drawable/ic_weather_clear.xml new file mode 100644 index 00000000..85150fc5 --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_clear.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/base/src/main/res/drawable/ic_weather_clear_night.xml b/base/src/main/res/drawable/ic_weather_clear_night.xml new file mode 100644 index 00000000..71d0d8cf --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_clear_night.xml @@ -0,0 +1,55 @@ + + + + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_cloudy.xml b/base/src/main/res/drawable/ic_weather_cloudy.xml new file mode 100644 index 00000000..e2c2780f --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_cloudy.xml @@ -0,0 +1,26 @@ + + + + diff --git a/base/src/main/res/drawable/ic_weather_cold.xml b/base/src/main/res/drawable/ic_weather_cold.xml new file mode 100644 index 00000000..04ecb5d7 --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_cold.xml @@ -0,0 +1,37 @@ + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_drizzle.xml b/base/src/main/res/drawable/ic_weather_drizzle.xml new file mode 100644 index 00000000..d0708689 --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_drizzle.xml @@ -0,0 +1,56 @@ + + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_fog.xml b/base/src/main/res/drawable/ic_weather_fog.xml new file mode 100644 index 00000000..bde36fcc --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_fog.xml @@ -0,0 +1,65 @@ + + + + + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_hail.xml b/base/src/main/res/drawable/ic_weather_hail.xml new file mode 100644 index 00000000..4bef7193 --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_hail.xml @@ -0,0 +1,47 @@ + + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_haze.xml b/base/src/main/res/drawable/ic_weather_haze.xml new file mode 100644 index 00000000..b262217a --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_haze.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_haze_night.xml b/base/src/main/res/drawable/ic_weather_haze_night.xml new file mode 100644 index 00000000..675f8513 --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_haze_night.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_hot.xml b/base/src/main/res/drawable/ic_weather_hot.xml new file mode 100644 index 00000000..82a7a06f --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_hot.xml @@ -0,0 +1,37 @@ + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_mostly_cloudy.xml b/base/src/main/res/drawable/ic_weather_mostly_cloudy.xml new file mode 100644 index 00000000..14dff0bf --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_mostly_cloudy.xml @@ -0,0 +1,37 @@ + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_mostly_cloudy_night.xml b/base/src/main/res/drawable/ic_weather_mostly_cloudy_night.xml new file mode 100644 index 00000000..b1556640 --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_mostly_cloudy_night.xml @@ -0,0 +1,44 @@ + + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_partly_cloudy.xml b/base/src/main/res/drawable/ic_weather_partly_cloudy.xml new file mode 100644 index 00000000..906ad6ff --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_partly_cloudy.xml @@ -0,0 +1,44 @@ + + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_partly_cloudy_night.xml b/base/src/main/res/drawable/ic_weather_partly_cloudy_night.xml new file mode 100644 index 00000000..c625e856 --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_partly_cloudy_night.xml @@ -0,0 +1,65 @@ + + + + + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_showers.xml b/base/src/main/res/drawable/ic_weather_showers.xml new file mode 100644 index 00000000..7b41f324 --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_showers.xml @@ -0,0 +1,47 @@ + + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_sleet.xml b/base/src/main/res/drawable/ic_weather_sleet.xml new file mode 100644 index 00000000..3eff5116 --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_sleet.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_snow.xml b/base/src/main/res/drawable/ic_weather_snow.xml new file mode 100644 index 00000000..f2304db1 --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_snow.xml @@ -0,0 +1,47 @@ + + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_thunderstorm.xml b/base/src/main/res/drawable/ic_weather_thunderstorm.xml new file mode 100644 index 00000000..ea8a9c8e --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_thunderstorm.xml @@ -0,0 +1,33 @@ + + + + + diff --git a/base/src/main/res/drawable/ic_weather_thunderstorm_with_rain.xml b/base/src/main/res/drawable/ic_weather_thunderstorm_with_rain.xml new file mode 100644 index 00000000..0c140809 --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_thunderstorm_with_rain.xml @@ -0,0 +1,47 @@ + + + + + + + diff --git a/base/src/main/res/drawable/ic_weather_wind.xml b/base/src/main/res/drawable/ic_weather_wind.xml new file mode 100644 index 00000000..5aaa0792 --- /dev/null +++ b/base/src/main/res/drawable/ic_weather_wind.xml @@ -0,0 +1,13 @@ + + + diff --git a/base/src/main/res/values-night/bools.xml b/base/src/main/res/values-night/bools.xml new file mode 100644 index 00000000..9d20ac83 --- /dev/null +++ b/base/src/main/res/values-night/bools.xml @@ -0,0 +1,4 @@ + + + true + \ No newline at end of file diff --git a/base/src/main/res/values-night/colors.xml b/base/src/main/res/values-night/colors.xml new file mode 100644 index 00000000..367f4d29 --- /dev/null +++ b/base/src/main/res/values-night/colors.xml @@ -0,0 +1,49 @@ + + + #E57373 + #F06292 + #BA68C8 + #9575CD + #7986CB + #64B5F6 + #4FC3F7 + #4DD0E1 + #4DB6AC + #81C784 + #AED581 + #DCE775 + #FFF176 + #FFD54F + #FFB74D + #FF8A65 + #A1887F + #E0E0E0 + #90A4AE + + @color/design_dark_default_color_surface + @android:color/black + #CC424242 + #393939 + + #FFFFFFFF + #B3FFFFFF + #80FFFFFF + #1FFFFFFF + #FFFFFF + #424242 + + #FFFFFF + #DF000000 + + @color/blue + #333 + #222 + @color/cardview_light_background + + #222222 + #282828 + + #DE000000 + #7000 + @color/design_dark_default_color_background + \ No newline at end of file diff --git a/base/src/main/res/values-night/styles.xml b/base/src/main/res/values-night/styles.xml new file mode 100644 index 00000000..e6ff1207 --- /dev/null +++ b/base/src/main/res/values-night/styles.xml @@ -0,0 +1,20 @@ + + + + + + + + + + \ No newline at end of file diff --git a/base/src/main/res/values-notnight-v23/colors.xml b/base/src/main/res/values-notnight-v23/colors.xml new file mode 100644 index 00000000..0d2c94c9 --- /dev/null +++ b/base/src/main/res/values-notnight-v23/colors.xml @@ -0,0 +1,4 @@ + + + #ddd + \ No newline at end of file diff --git a/base/src/main/res/values/attrs.xml b/base/src/main/res/values/attrs.xml new file mode 100644 index 00000000..b17ea6c8 --- /dev/null +++ b/base/src/main/res/values/attrs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/base/src/main/res/values/colors.xml b/base/src/main/res/values/colors.xml new file mode 100644 index 00000000..3f06735d --- /dev/null +++ b/base/src/main/res/values/colors.xml @@ -0,0 +1,53 @@ + + + #8A000000 + + #E53935 + #D81B60 + #9C27B0 + #5E35B1 + #3949AB + #1E88E5 + #039BE5 + #00ACC1 + #00897B + #43A047 + #7CB342 + #C0CA33 + #FDD835 + #FFB300 + #FB8C00 + #F4511E + #6D4C41 + #757575 + #546E7A + + @color/design_default_color_surface + @android:color/white + #CCFFFFFF + #E0E0E0 + + #DE000000 + #8A000000 + #64000000 + #1F000000 + #000000 + #FFFFFF + + #DF000000 + #FFFFFF + + @color/indigo + #fff + #ccc + @color/cardview_dark_background + + #ffffff + #ffffff + + #ffffff + @android:color/transparent + #f5f5f5 + @color/design_default_color_background + + diff --git a/base/src/main/res/values/styles.xml b/base/src/main/res/values/styles.xml new file mode 100644 index 00000000..7f2c22ce --- /dev/null +++ b/base/src/main/res/values/styles.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/src/main/res/values/typography.xml b/base/src/main/res/values/typography.xml new file mode 100644 index 00000000..332f6efb --- /dev/null +++ b/base/src/main/res/values/typography.xml @@ -0,0 +1,28 @@ + + + + + + + + + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..513577fd --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,36 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath("com.android.tools.build:gradle:7.1.0-alpha03") + classpath(libs.kotlin.gradle) + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.30") + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + mavenCentral() + maven(url = "https://jitpack.io") + maven(url = "https://oss.sonatype.org/content/repositories/snapshots") + maven(url = "https://androidx.dev/snapshots/builds/7559387/artifacts/repository/") + maven(url = "https://dl.bintray.com/amulyakhare/maven") { + content { + includeGroup("com.amulyakhare") + } + } + maven(url = "https://pkgs.dev.azure.com/MicrosoftDeviceSDK/DuoSDK-Public/_packaging/Duo-SDK-Feed/maven/v1") + jcenter() // For MS SDK + } +} + +tasks.create("clean") { + delete(rootProject.buildDir) +} \ No newline at end of file diff --git a/calculator/.gitignore b/calculator/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/calculator/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/calculator/build.gradle.kts b/calculator/build.gradle.kts new file mode 100644 index 00000000..d8f01b4f --- /dev/null +++ b/calculator/build.gradle.kts @@ -0,0 +1,50 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.bundles.kotlin) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + + implementation(libs.bundles.androidx.lifecycle) + + implementation(libs.mathparser) + + implementation(project(":preferences")) + implementation(project(":search")) + +} \ No newline at end of file diff --git a/calculator/consumer-rules.pro b/calculator/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/calculator/proguard-rules.pro b/calculator/proguard-rules.pro new file mode 100644 index 00000000..01639a19 --- /dev/null +++ b/calculator/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/calculator/src/main/AndroidManifest.xml b/calculator/src/main/AndroidManifest.xml new file mode 100644 index 00000000..be86c1bd --- /dev/null +++ b/calculator/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + / + \ No newline at end of file diff --git a/calculator/src/main/java/de/mm20/launcher2/calculator/CalculatorRepository.kt b/calculator/src/main/java/de/mm20/launcher2/calculator/CalculatorRepository.kt new file mode 100644 index 00000000..5df784ac --- /dev/null +++ b/calculator/src/main/java/de/mm20/launcher2/calculator/CalculatorRepository.kt @@ -0,0 +1,64 @@ +package de.mm20.launcher2.calculator + +import androidx.lifecycle.MutableLiveData +import de.mm20.launcher2.preferences.LauncherPreferences +import de.mm20.launcher2.search.BaseSearchableRepository +import de.mm20.launcher2.search.data.Calculator +import org.mariuszgromada.math.mxparser.Expression + +class CalculatorRepository private constructor() : BaseSearchableRepository() { + + val calculator = MutableLiveData() + + override suspend fun search(query: String) { + if (query.isBlank()) { + calculator.value = null + return + } + if (!LauncherPreferences.instance.searchCalculator) return + val calc = when { + query.matches(Regex("0x[0-9a-fA-F]+")) -> { + val solution = query.substring(2).toIntOrNull(16) ?: run { + calculator.value = null + return + } + Calculator(term = query, solution = solution.toDouble()) + } + query.matches(Regex("0b[01]+")) -> { + val solution = query.substring(2).toIntOrNull(2) ?: run { + calculator.value = null + return + } + Calculator(term = query, solution = solution.toDouble()) + } + query.matches(Regex("0[0-7]+")) -> { + val solution = query.substring(1).toIntOrNull(8) ?: run { + calculator.value = null + return + } + Calculator(term = query, solution = solution.toDouble()) + } + else -> { + val exp = Expression(query) + if (exp.checkSyntax()) { + Calculator(term = query, solution = exp.calculate()) + } else { + val exp2 = Expression(query.replace(',', '.').replace(';', ',')) + if (exp2.checkSyntax()) { + Calculator(term = query, solution = exp2.calculate()) + } else null + } + } + } + calculator.value = calc + } + + companion object { + private lateinit var instance: CalculatorRepository + + fun getInstance(): CalculatorRepository { + if (!::instance.isInitialized) instance = CalculatorRepository() + return instance + } + } +} \ No newline at end of file diff --git a/calculator/src/main/java/de/mm20/launcher2/calculator/CalculatorViewModel.kt b/calculator/src/main/java/de/mm20/launcher2/calculator/CalculatorViewModel.kt new file mode 100644 index 00000000..7dae9c4a --- /dev/null +++ b/calculator/src/main/java/de/mm20/launcher2/calculator/CalculatorViewModel.kt @@ -0,0 +1,7 @@ +package de.mm20.launcher2.calculator + +import androidx.lifecycle.ViewModel + +class CalculatorViewModel: ViewModel() { + val calculator = CalculatorRepository.getInstance().calculator +} \ No newline at end of file diff --git a/calculator/src/main/java/de/mm20/launcher2/search/data/Calculator.kt b/calculator/src/main/java/de/mm20/launcher2/search/data/Calculator.kt new file mode 100644 index 00000000..ba1570bd --- /dev/null +++ b/calculator/src/main/java/de/mm20/launcher2/search/data/Calculator.kt @@ -0,0 +1,89 @@ +package de.mm20.launcher2.search.data + +import java.text.DecimalFormat +import java.util.* +import kotlin.math.abs +import kotlin.math.roundToInt + +class Calculator( + val term: String, + val solution: Double +) { + + val formattedString: String + val formattedBinaryString: String + val formattedHexString: String + val formattedOctString: String + + init { + if (solution.isNaN()) { + formattedString = "NaN" + formattedOctString = "NaN" + formattedBinaryString = "NaN" + formattedHexString = "NaN" + } else { + val nf = + if ((abs(solution) > 1e12 || abs(solution) < 1e-5) && solution != 0.0) DecimalFormat( + "#.######E0" + ) + else DecimalFormat("#,###.######") + formattedString = nf.format(solution) + var s = StringBuffer(solution.roundToInt().toString(2)) + while (s.length % 4 != 0) { + s = s.insert(0, '0') + } + + for (i in s.length - 4 downTo 4 step 4) { + s.insert(i, ' ') + } + formattedBinaryString = s.toString() + + s = StringBuffer(solution.roundToInt().toString(8)) + while (s.length % 3 != 0) { + s = s.insert(0, '0') + } + + for (i in s.length - 3 downTo 3 step 3) { + s.insert(i, ' ') + } + formattedOctString = s.toString() + + s = StringBuffer(solution.roundToInt().toString(16).toUpperCase()) + while (s.length % 2 != 0) { + s = s.insert(0, '0') + } + + for (i in s.length - 2 downTo 2 step 2) { + s.insert(i, ' ') + } + formattedHexString = s.toString() + } + } + + fun getBeatifiedTerm(): String { + if(term.matches(Regex("0x[0-9a-fA-F]+"))) { + return term.substring(2).toUpperCase(Locale.ROOT) + "₁₆" + } + if(term.matches(Regex("0b[01]+"))) { + return term.substring(2) + "₂" + } + if(term.matches(Regex("0[0-7]+"))) { + return term.substring(1) + "₈" + } + return term.replace(Regex("\\s+"), "") + .replace("pi", " \u03C0 ", ignoreCase = true) + .replace("*", " \u00D7 ") + .replace("-", " \u2212 ") + .replace("/", " \u2215 ") + .replace("+", " + ") + .replace(Regex("&{1,2}"), " \u2227 ") + .replace(Regex("\\|{1,2}"), " \u2228 ") + .replace("!=", " \u2260 ") + .replace("<>", " \u2260 ") + .replace(">=", " \u2265 ") + .replace("<=", " \u2264 ") + .replace("=", " = ") + .replace("<", " < ") + .replace(">", " > ") + } +} \ No newline at end of file diff --git a/calendar/.gitignore b/calendar/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/calendar/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/calendar/build.gradle.kts b/calendar/build.gradle.kts new file mode 100644 index 00000000..f3634891 --- /dev/null +++ b/calendar/build.gradle.kts @@ -0,0 +1,52 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.bundles.kotlin) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + + implementation(libs.textdrawable) + + api(project(":search")) + implementation(project(":preferences")) + implementation(project(":ktx")) + implementation(project(":base")) + implementation(project(":hiddenitems")) + implementation(project(":permissions")) + +} \ No newline at end of file diff --git a/calendar/consumer-rules.pro b/calendar/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/calendar/proguard-rules.pro b/calendar/proguard-rules.pro new file mode 100644 index 00000000..52039aba --- /dev/null +++ b/calendar/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts.kts.kts.kts.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/calendar/src/main/AndroidManifest.xml b/calendar/src/main/AndroidManifest.xml new file mode 100644 index 00000000..40b502ee --- /dev/null +++ b/calendar/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/calendar/src/main/java/de/mm20/launcher2/calendar/CalendarRepository.kt b/calendar/src/main/java/de/mm20/launcher2/calendar/CalendarRepository.kt new file mode 100644 index 00000000..70233402 --- /dev/null +++ b/calendar/src/main/java/de/mm20/launcher2/calendar/CalendarRepository.kt @@ -0,0 +1,81 @@ +package de.mm20.launcher2.calendar + +import android.content.Context +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.MutableLiveData +import de.mm20.launcher2.hiddenitems.HiddenItemsRepository +import de.mm20.launcher2.preferences.LauncherPreferences +import de.mm20.launcher2.search.BaseSearchableRepository +import de.mm20.launcher2.search.data.CalendarEvent +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class CalendarRepository private constructor(val context: Context) : BaseSearchableRepository() { + + val calendarEvents = MediatorLiveData?>() + val upcomingCalendarEvents = MutableLiveData>(emptyList()) + + private val allEvents = MutableLiveData?>(emptyList()) + private val hiddenItemKeys = HiddenItemsRepository.getInstance(context).hiddenItemsKeys + + init { + calendarEvents.addSource(hiddenItemKeys) { keys -> + calendarEvents.value = allEvents.value?.filter { !keys.contains(it.key) } + } + calendarEvents.addSource(allEvents) { e -> + calendarEvents.value = e?.filter { hiddenItemKeys.value?.contains(it.key) != true } + } + hiddenItemKeys.observeForever { + requestCalendarUpdate() + } + + } + + fun requestCalendarUpdate() { + launch { + val unselectedCalendars = LauncherPreferences.instance.unselectedCalendars + val hideAlldayEvents = LauncherPreferences.instance.calendarHideAllday + + val now = System.currentTimeMillis() + val end = now + 14 * 24 * 60 * 60 * 1000L + val events = withContext(Dispatchers.IO) { + CalendarEvent.search( + context = context, + query = "", + intervalStart = now, + intervalEnd = end, + limit = 700, + hideAllDayEvents = hideAlldayEvents, + unselectedCalendars = unselectedCalendars, + hiddenEvents = hiddenItemKeys.value?.mapNotNull { + if (it.startsWith("calendar")) it.substringAfterLast("/").toLong() + else null + } ?: emptyList() + ) + } + upcomingCalendarEvents.value = events + } + } + + override suspend fun search(query: String) { + if (query.isBlank()) { + allEvents.value = null + return + } + val startTime = System.currentTimeMillis() + val endTime = System.currentTimeMillis() + 365L * 24 * 60 * 60 * 1000 + val events = withContext(Dispatchers.IO) { + CalendarEvent.search(context, query, startTime, endTime) + } + allEvents.value = events + } + + + companion object { + private lateinit var instance: CalendarRepository + fun getInstance(context: Context): CalendarRepository { + if (!::instance.isInitialized) instance = CalendarRepository(context.applicationContext) + return instance + } + } +} \ No newline at end of file diff --git a/calendar/src/main/java/de/mm20/launcher2/calendar/CalendarViewModel.kt b/calendar/src/main/java/de/mm20/launcher2/calendar/CalendarViewModel.kt new file mode 100644 index 00000000..f137f669 --- /dev/null +++ b/calendar/src/main/java/de/mm20/launcher2/calendar/CalendarViewModel.kt @@ -0,0 +1,11 @@ +package de.mm20.launcher2.calendar + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import de.mm20.launcher2.search.data.CalendarEvent + +class CalendarViewModel(app:Application): AndroidViewModel(app) { + val calendarEvents: LiveData?> = CalendarRepository.getInstance(app).calendarEvents + val upcomingCalendarEvents: LiveData> = CalendarRepository.getInstance(app).upcomingCalendarEvents +} \ No newline at end of file diff --git a/calendar/src/main/java/de/mm20/launcher2/search/data/CalendarEvent.kt b/calendar/src/main/java/de/mm20/launcher2/search/data/CalendarEvent.kt new file mode 100644 index 00000000..c0ef3681 --- /dev/null +++ b/calendar/src/main/java/de/mm20/launcher2/search/data/CalendarEvent.kt @@ -0,0 +1,321 @@ +package de.mm20.launcher2.search.data + +import android.Manifest +import android.content.ContentUris +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.graphics.Color +import android.graphics.Typeface +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.LayerDrawable +import android.provider.CalendarContract +import androidx.core.content.ContextCompat +import androidx.core.database.getStringOrNull +import androidx.core.graphics.ColorUtils +import androidx.core.graphics.blue +import androidx.core.graphics.green +import androidx.core.graphics.red +import com.amulyakhare.textdrawable.TextDrawable +import de.mm20.launcher2.calendar.R +import de.mm20.launcher2.permissions.PermissionsManager +import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.ktx.checkPermission +import de.mm20.launcher2.ktx.dp +import de.mm20.launcher2.preferences.LauncherPreferences +import org.json.JSONObject +import java.lang.NullPointerException +import java.text.SimpleDateFormat +import java.util.* + +class CalendarEvent( + override val label: String, + val id: Long, + val color: Int, + val startTime: Long, + val endTime: Long, + val allDay: Boolean, + val location: String, + val attendees: List, + val description: String, + val calendar: Long +) : Searchable() { + + override fun serialize(): String { + val json = JSONObject() + json.put("id", id) + return json.toString() + } + + override val key: String + get() = "calendar://$id" + + + override fun getPlaceholderIcon(context: Context): LauncherIcon { + val df = SimpleDateFormat("d") + val day = df.format(startTime) + df.applyPattern("MMM") + val month = df.format(startTime) + val fgLayers = arrayOf( + TextDrawable + .builder() + .beginConfig() + .textColor(Color.WHITE) + .useFont(Typeface.DEFAULT_BOLD) + .fontSize((36 * context.dp).toInt()) + .endConfig() + .buildRect(day, 0), + TextDrawable + .builder() + .beginConfig() + .textColor(Color.WHITE) + .bold() + .fontSize((26 * context.dp).toInt()) + .endConfig() + .buildRect(month, 0) + ) + val foreground = LayerDrawable(fgLayers) + foreground.setLayerInset(0, 0, 0, 0, (26 * context.dp).toInt()) + foreground.setLayerInset(1, 0, (36 * context.dp).toInt(), 0, 0) + val background = ColorDrawable(getDisplayColor(context, color)) + return LauncherIcon( + foreground = foreground, + background = background, + foregroundScale = 0.74f + ) + } + + override fun getLaunchIntent(context: Context): Intent? { + return null + } + + companion object { + fun search(context: Context, + query: String, + intervalStart: Long, + intervalEnd: Long, + limit: Int = 10, + hideAllDayEvents: Boolean = false, + unselectedCalendars: List = emptyList(), + hiddenEvents: List = emptyList() + ): List { + val results = mutableListOf() + if (!query.isEmpty() && query.length < 3) return results + if (!LauncherPreferences.instance.searchCalendars) return listOf() + if (!PermissionsManager.checkPermission(context, PermissionsManager.CALENDAR)) { + return emptyList() + } + val builder = CalendarContract.Instances.CONTENT_URI.buildUpon() + ContentUris.appendId(builder, intervalStart) + ContentUris.appendId(builder, intervalEnd) + val uri = builder.build() + val projection = arrayOf( + CalendarContract.Instances.EVENT_ID, + CalendarContract.Instances.TITLE, + CalendarContract.Instances.BEGIN, + CalendarContract.Instances.END, + CalendarContract.Instances.ALL_DAY, + CalendarContract.Instances.DISPLAY_COLOR, + CalendarContract.Instances.EVENT_LOCATION, + CalendarContract.Instances.CALENDAR_ID, + CalendarContract.Instances.DESCRIPTION + ) + val selection = mutableListOf() + if (query.isNotEmpty()) selection.add("${CalendarContract.Instances.TITLE} LIKE ?") + if (hiddenEvents.isNotEmpty()) selection.add("${CalendarContract.Instances.EVENT_ID} NOT IN (${hiddenEvents.joinToString()})") + if (unselectedCalendars.isNotEmpty()) selection.add("${CalendarContract.Instances.CALENDAR_ID} NOT IN (${unselectedCalendars.joinToString()})") + if (hideAllDayEvents) selection.add("${CalendarContract.Instances.ALL_DAY} = 0") + val selArgs = if (query.isBlank()) null else arrayOf("%$query%") + val sort = "${CalendarContract.Instances.BEGIN} ASC" + if (limit > -1) " LIMIT $limit" else "" + val cursor = context.contentResolver.query(uri, projection, selection.joinToString(separator = " AND "), selArgs, sort) + ?: return mutableListOf() + val proj = arrayOf( + CalendarContract.Attendees.EVENT_ID, + CalendarContract.Attendees.ATTENDEE_NAME, + CalendarContract.Attendees.ATTENDEE_EMAIL + ) + val s = "${CalendarContract.Attendees.ATTENDEE_NAME} COLLATE NOCASE ASC" + while (cursor.moveToNext()) { + val sel = "${CalendarContract.Attendees.EVENT_ID} = ${cursor.getLong(0)}" + val cur = context.contentResolver.query( + CalendarContract.Attendees.CONTENT_URI, + proj, sel, null, s + ) ?: return mutableListOf() + val attendees = mutableListOf() + while (cur.moveToNext()) { + attendees.add(cur.getString(1).takeUnless { it.isNullOrBlank() } + ?: cur.getString(2)) + } + cur.close() + val allday = cursor.getInt(4) > 0 + val begin = cursor.getLong(2) + + val tzOffset = if (allday) { + Calendar.getInstance().timeZone.getOffset(begin) + } else { + 0 + } + val event = CalendarEvent( + label = cursor.getString(1) ?: "", + id = cursor.getLong(0), + color = cursor.getInt(5), + startTime = begin - tzOffset, + endTime = cursor.getLong(3) - tzOffset - if (allday) 1 else 0, + allDay = allday, + location = cursor.getString(6) ?: "", + attendees = attendees, + description = cursor.getStringOrNull(8) + ?: "", + calendar = cursor.getLong(7) + ) + results.add(event) + } + cursor.close() + + return results + } + + fun deserialize(context: Context, serialized: String): CalendarEvent? { + if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED) return null + val json = JSONObject(serialized) + val id = json.getLong("id") + val builder = CalendarContract.Instances.CONTENT_URI.buildUpon() + ContentUris.appendId(builder, System.currentTimeMillis()) + ContentUris.appendId(builder, System.currentTimeMillis() + 63072000000L) + val uri = builder.build() + val projection = arrayOf( + CalendarContract.Instances.EVENT_ID, + CalendarContract.Instances.TITLE, + CalendarContract.Instances.BEGIN, + CalendarContract.Instances.END, + CalendarContract.Instances.ALL_DAY, + CalendarContract.Instances.DISPLAY_COLOR, + CalendarContract.Instances.EVENT_LOCATION, + CalendarContract.Instances.CALENDAR_ID, + CalendarContract.Instances.DESCRIPTION + ) + val selection = CalendarContract.Instances.EVENT_ID + " = ?" + val selArgs = arrayOf(id.toString()) + val cursor = context.contentResolver.query(uri, projection, selection, selArgs, null) + ?: return null + if (cursor.moveToNext()) { + val title = cursor.getString(1) + val begin = cursor.getLong(2) + val end = cursor.getLong(3) + val allday = cursor.getInt(4) != 0 + val color = cursor.getInt(5) + val location = cursor.getString(6) + val calendar = cursor.getLong(7) + val description = cursor.getStringOrNull(8) + ?: "" + cursor.close() + val proj = arrayOf( + CalendarContract.Attendees.EVENT_ID, + CalendarContract.Attendees.ATTENDEE_NAME, + CalendarContract.Attendees.ATTENDEE_EMAIL + ) + val sel = "${CalendarContract.Attendees.EVENT_ID} = $id" + val s = "${CalendarContract.Attendees.ATTENDEE_NAME} COLLATE NOCASE ASC" + val cur = context.contentResolver.query( + CalendarContract.Attendees.CONTENT_URI, + proj, sel, null, s + ) ?: return null + val attendees = mutableListOf() + while (cur.moveToNext()) { + attendees.add(cur.getString(1).takeUnless { it.isNullOrBlank() } + ?: cur.getString(2)) + } + cur.close() + val tzOffset = if (allday) { + Calendar.getInstance().timeZone.getOffset(begin) + } else { + 0 + } + return CalendarEvent( + label = title, + id = id, + color = color, + startTime = begin - tzOffset, + endTime = end - tzOffset - if (allday) 1 else 0, + allDay = allday, + location = location ?: "", + attendees = attendees, + description = description, + calendar = calendar + ) + } + cursor.close() + return null + } + + fun getCalendars(context: Context): List { + val calendars = mutableListOf() + val uri = CalendarContract.Calendars.CONTENT_URI + val proj = arrayOf( + CalendarContract.Calendars._ID, + CalendarContract.Calendars.NAME, + CalendarContract.Calendars.ACCOUNT_NAME, + CalendarContract.Calendars.CALENDAR_COLOR, + CalendarContract.Calendars.VISIBLE, + CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, + ) + if (!context.checkPermission(Manifest.permission.READ_CALENDAR)) return calendars + val cursor = context.contentResolver.query(uri, proj, null, null, null) + ?: return emptyList() + while (cursor.moveToNext()) { + try { + calendars.add(UserCalendar( + id = cursor.getLong(0), + name = cursor.getString(5) ?: cursor.getString(1) ?: "", + owner = cursor.getString(2), + color = cursor.getInt(3) + )) + } catch (e: NullPointerException) { + continue + } + } + cursor.close() + calendars.sortBy { it.owner } + return calendars + } + + fun getDisplayColor(context: Context, color: Int): Int { + val hsl = FloatArray(3).let { + ColorUtils.RGBToHSL(color.red, color.green, color.blue, it) + it + } + return if (context.resources.getBoolean(R.bool.is_dark_theme)) { + if (ColorUtils.calculateContrast(ContextCompat.getColor(context, R.color.calendar_foreground_color), color) < 2.5 || true) { + if (color.red == color.green && color.red == color.blue) { + val level = 0xFF - ((0xFF - color.red) * 0.7f).toInt() + Color.rgb(level, level, level) + } else { + hsl[2] = hsl[2] + (1 - hsl[2]) * 0.2f + hsl[1] = 1 - (1 - hsl[1]) * 0.9f + ColorUtils.HSLToColor(hsl) + } + } else return color + } else { + if (ColorUtils.calculateContrast(ContextCompat.getColor(context, R.color.calendar_foreground_color), color) < 1.8) { + if (color.red == color.green && color.red == color.blue) { + val level = (color.red * 0.7f).toInt() + Color.rgb(level, level, level) + } else { + hsl[2] = (0.5f - hsl[2]) * 0.8f + hsl[2] + hsl[1] = 1 - (1 - hsl[1]) * 0.8f + ColorUtils.HSLToColor(hsl) + } + } else return color + } + } + + + } +} + +data class UserCalendar( + val id: Long, + val name: String, + val owner: String, + val color: Int +) \ No newline at end of file diff --git a/calendar/src/main/res/drawable-hdpi/ic_calendar.webp b/calendar/src/main/res/drawable-hdpi/ic_calendar.webp new file mode 100644 index 0000000000000000000000000000000000000000..ca9bb8a8f4a0e090a00ea797bc4271d72493da4e GIT binary patch literal 730 zcmV<00ww)YNk&G}0ssJ4MM6+kP&iD*0ssInp+G1QhvOi&ZKc|H|727kf;;CPBq{29^snqKoP=kd8CJEe&A8t+d$d%LLMH)akU|nI<__Lw zzN@xv8wQzrcn1K${sq8?0q~mw1m6fC1Ogxd!du{EkaRf#;OGD6PVfe>0XLwelmwup zH*5hZU|~OiOCn&)byZ>Grbxn_0XrZk#Xa{B!iJO1$zP450~)ri-QO2s1L&QY69JNR z04j&!O`FErd2Z@yswJtZwRGZ5t)nDL`XzCHUmf4)`}>xXVkftxSRw7!wquK)`_cW{ z%;sH zF}7z~W>&5rIeL9%W?Ix51f9__iQZ|M7st#pFG6ZSJ7Z0FmJnO(j6TxE53M*7(| zhvWdAG2LW>D7KQI!q9?iy3e84C(y|ZIP^3Fo$R1PkF@oF_R{)4Kw8e` zdpM7f?n6IyGSw&0Np@d$$X!tajZrq(+=GCGEk3isABp@%_WbERsX8t4#7DQDHg!DtvG MMao|5e}6gv0Asag>i_@% literal 0 HcmV?d00001 diff --git a/calendar/src/main/res/drawable-mdpi/ic_calendar.webp b/calendar/src/main/res/drawable-mdpi/ic_calendar.webp new file mode 100644 index 0000000000000000000000000000000000000000..ada1d797feccf5898e5572706038b6347194fb18 GIT binary patch literal 474 zcmV<00VV!YNk&G}0RRA3MM6+kP&iD*0RR9mYrq;1pTZ`RWGnNO;NJye$cgVe8CYP` zMv|mjB+H-0g1P+Sd5cXONs?-jEPoaY=JJc@EjE#DJ305gUwtyj!4Wd4QGf=zoJjyc zpMV4U2MiDp0RaXWAa&^O9e7m(1W5hVZ)Q>zmEA=pB6{`TT>#I_u)6}T-8G<9QJEQl zNd*wlR230{N@h}3T=#O$;eM}E|J@ydZ)RNmpYJQG%HRK=XT|_awk1VMXSMsd3!3AG z!?;i3vj5FEWCnj0(SJs6+eR|m&OYPwJVE(A3an@{l}bh}VCcudzMRTu<13q?AA{&r zZuBkev)b3)jST3LM+f60E;-h|vm~YVTfOCCV zK{l168wV(3+*XlQ_0Axi5S}2j;{fO)(iLP@PYb%J1!PxH2~$LNMaqJjFDHk{;RG+1qEbw_Of( Qg_deME*slamA^s&08D7+A^-pY literal 0 HcmV?d00001 diff --git a/calendar/src/main/res/drawable-xhdpi/ic_calendar.webp b/calendar/src/main/res/drawable-xhdpi/ic_calendar.webp new file mode 100644 index 0000000000000000000000000000000000000000..ed69d698153546deb6edc8cf02b67d95ab41b2d9 GIT binary patch literal 1048 zcmV+z1n2uwNk&Ex1ONb6MM6+kP&iBk1ONap*T6Lp@5DNiWF?b-R#-al_XN4YAp}Q_ zBso&>XSHYO-2K*Xr=v!a94Yv-+B0ZEV}BvhRK0@W`bF8i7tpHUA}$(|~8r zJ!aFkA#2*IZvbFF1OB1;P(TI_`RRb%IzU)}SuYC<5I9s8SKwr^J&%J~0*@JkGsqe& z*EP3}pqN=$SnHUP1tfTZmg`E8Ud!877zs`E!vL04gwp`cPQnM^|%$Q}<|KA7Op673+;rp!*R6UN3^@pDo zRt3IX*B`Xrckg)7GKGX&+Y3qRRJEsi+C8>yWNq8tvAwpfo;81MV|q^ITklrBFA@ER z#72@LDZR|O40qjsoAp0y0!-7>Ahelp=^k(?hsW?`CsBq6b(`rj=mC}86Ur~EtV*KF z@W!@cj zIX%E(_CS5&R-N9&rIa+?kBIdFyP3(f2gn2);*`X0cuIC}2Nb-cWw7DY+A9^k?>B3&sZU6H1KA{ri@nn5)>OhnWaaY@}Y z%j=;L34bn3Ib5DBLn-A86ts^gnLxzkOF$~%GfcC*9$C`m(8b=M9A1Akub`-yN=nO{ zdM_-gm$0gq&ioF}qQ4f`fLvz_1Is&|HyQt2k&YZ=*}d&+U3v!5I$R=>igQ zZ)W$G@8rPkK);AvnKM+gh6A^@Iz$3Sb4Kbpc=bpE?m@0-BL@%3C1hev5uo9C?ICfE_!y519s>|QMRDd z(`fE+`STAeIZ*WeC5FXZM?~EdDQkQ9aSy;VRr^0ZYAuV1JKZExGFVX6($U%715B6f zXsId)dIX+ohbfm4&MhjdAgTm}h$QBQ4VTa}2r

@)|xrVgL|CREF0jMW6DC5$Vza Sj#LC>R$&H?&HA79zn>ZwRss$H literal 0 HcmV?d00001 diff --git a/calendar/src/main/res/drawable-xxhdpi/ic_calendar.webp b/calendar/src/main/res/drawable-xxhdpi/ic_calendar.webp new file mode 100644 index 0000000000000000000000000000000000000000..b3602c94d5df19f59d35b5cd45e447121959ce36 GIT binary patch literal 1748 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 + + + \ No newline at end of file diff --git a/compat/src/main/java/de/mm20/launcher2/compat/PackageManagerCompat.kt b/compat/src/main/java/de/mm20/launcher2/compat/PackageManagerCompat.kt new file mode 100644 index 00000000..3bfc8fa3 --- /dev/null +++ b/compat/src/main/java/de/mm20/launcher2/compat/PackageManagerCompat.kt @@ -0,0 +1,33 @@ +package de.mm20.launcher2.compat + +import android.content.pm.PackageManager +import android.os.Build + +object PackageManagerCompat { + fun getInstallSource( + packageManager: PackageManager, + packageName: String + ): InstallSourceInfoCompat { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + val installSourceInfo = packageManager.getInstallSourceInfo(packageName) + return InstallSourceInfoCompat( + originatingPackageName = installSourceInfo.originatingPackageName, + initiatingPackageName = installSourceInfo.initiatingPackageName, + installingPackageName = installSourceInfo.installingPackageName, + ) + } else { + val installerPackageName = packageManager.getInstallerPackageName(packageName) + return InstallSourceInfoCompat( + originatingPackageName = installerPackageName, + initiatingPackageName = installerPackageName, + installingPackageName = installerPackageName + ) + } + } +} + +data class InstallSourceInfoCompat( + val originatingPackageName: String?, + val initiatingPackageName: String?, + val installingPackageName: String? +) \ No newline at end of file diff --git a/contacts/.gitignore b/contacts/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/contacts/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/contacts/build.gradle.kts b/contacts/build.gradle.kts new file mode 100644 index 00000000..19f51ff6 --- /dev/null +++ b/contacts/build.gradle.kts @@ -0,0 +1,52 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.bundles.kotlin) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + + implementation(libs.textdrawable) + + implementation(project(":search")) + implementation(project(":preferences")) + implementation(project(":ktx")) + implementation(project(":base")) + implementation(project(":hiddenitems")) + implementation(project(":permissions")) + +} \ No newline at end of file diff --git a/contacts/consumer-rules.pro b/contacts/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/contacts/proguard-rules.pro b/contacts/proguard-rules.pro new file mode 100644 index 00000000..52039aba --- /dev/null +++ b/contacts/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts.kts.kts.kts.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/contacts/src/main/AndroidManifest.xml b/contacts/src/main/AndroidManifest.xml new file mode 100644 index 00000000..7842fbc7 --- /dev/null +++ b/contacts/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + / + \ No newline at end of file diff --git a/contacts/src/main/java/de/mm20/launcher2/contacts/ContactRepository.kt b/contacts/src/main/java/de/mm20/launcher2/contacts/ContactRepository.kt new file mode 100644 index 00000000..7ab42335 --- /dev/null +++ b/contacts/src/main/java/de/mm20/launcher2/contacts/ContactRepository.kt @@ -0,0 +1,47 @@ +package de.mm20.launcher2.contacts + +import android.content.Context +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.MutableLiveData +import de.mm20.launcher2.hiddenitems.HiddenItemsRepository +import de.mm20.launcher2.search.BaseSearchableRepository +import de.mm20.launcher2.search.data.Contact +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class ContactRepository private constructor(val context: Context) : BaseSearchableRepository() { + + val contacts = MediatorLiveData?>() + + private val allContacts = MutableLiveData?>(emptyList()) + private val hiddenItemKeys = HiddenItemsRepository.getInstance(context).hiddenItemsKeys + + init { + contacts.addSource(hiddenItemKeys) { keys -> + contacts.value = allContacts.value?.filter { !keys.contains(it.key) } + } + contacts.addSource(allContacts) { c -> + contacts.value = c?.filter { hiddenItemKeys.value?.contains(it.key) != true } + } + } + + override suspend fun search(query: String) { + if (query.isBlank()) { + allContacts.value = null + return + } + val results = withContext(Dispatchers.IO) { + Contact.search(context, query) + } + allContacts.value = results + } + + companion object { + private lateinit var instance: ContactRepository + + fun getInstance(context: Context): ContactRepository { + if (!::instance.isInitialized) instance = ContactRepository(context.applicationContext) + return instance + } + } +} \ No newline at end of file diff --git a/contacts/src/main/java/de/mm20/launcher2/contacts/ContactViewModel.kt b/contacts/src/main/java/de/mm20/launcher2/contacts/ContactViewModel.kt new file mode 100644 index 00000000..72d35fa8 --- /dev/null +++ b/contacts/src/main/java/de/mm20/launcher2/contacts/ContactViewModel.kt @@ -0,0 +1,10 @@ +package de.mm20.launcher2.contacts + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import de.mm20.launcher2.search.data.Contact + +class ContactViewModel(app: Application) : AndroidViewModel(app) { + val contacts: LiveData?> = ContactRepository.getInstance(app).contacts +} \ No newline at end of file diff --git a/contacts/src/main/java/de/mm20/launcher2/search/data/Contact.kt b/contacts/src/main/java/de/mm20/launcher2/search/data/Contact.kt new file mode 100644 index 00000000..d1637fc8 --- /dev/null +++ b/contacts/src/main/java/de/mm20/launcher2/search/data/Contact.kt @@ -0,0 +1,210 @@ +package de.mm20.launcher2.search.data + +import android.Manifest +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.provider.ContactsContract +import androidx.core.content.ContextCompat +import androidx.core.database.getStringOrNull +import androidx.core.graphics.drawable.toDrawable +import com.amulyakhare.textdrawable.TextDrawable +import de.mm20.launcher2.contacts.R +import de.mm20.launcher2.ktx.asBitmap +import de.mm20.launcher2.ktx.jsonObjectOf +import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.permissions.PermissionsManager +import de.mm20.launcher2.preferences.LauncherPreferences +import org.json.JSONObject + +class Contact( + val id: Long, + val firstName: String, + val lastName: String, + val displayName: String, + val lookupKey: String, + val phones: Set, + val emails: Set, + val telegram: Set, + val whatsapp: Set, + val postals: Set +) : Searchable() { + override val key: String + get() = "contact://$id" + override val label: String + get() = "$firstName $lastName" + + val summary: String + get() { + return phones.union(emails).joinToString(separator = ", ") + } + + override fun serialize(): String { + return jsonObjectOf( + "id" to id + ).toString() + } + + override fun getPlaceholderIcon(context: Context): LauncherIcon { + val iconText = if (firstName.isNotEmpty()) firstName[0].toString() else "" + if (lastName.isNotEmpty()) lastName[0].toString() else "" + return LauncherIcon( + foreground = TextDrawable.builder().buildRect(iconText, ContextCompat.getColor(context, R.color.blue)) + ) + } + + override suspend fun loadIconAsync(context: Context, size: Int): LauncherIcon? { + val contentResolver = context.contentResolver + val uri = ContactsContract.Contacts.getLookupUri(id, lookupKey) ?: return null + val bmp = ContactsContract.Contacts.openContactPhotoInputStream(contentResolver, uri, false)?.asBitmap() + ?: return null + return LauncherIcon( + foreground = bmp.toDrawable(context.resources) + ) + } + + override fun getLaunchIntent(context: Context): Intent? { + return null + } + + companion object { + fun search(context: Context, query: String): List { + if (query.length < 3) return mutableListOf() + if (!LauncherPreferences.instance.searchContacts) { + return mutableListOf() + } + if (!PermissionsManager.checkPermission(context, PermissionsManager.CONTACTS)) { + return mutableListOf() + } + val proj = arrayOf( + ContactsContract.RawContacts.CONTACT_ID, + ContactsContract.RawContacts._ID + ) + val sel = "${ContactsContract.RawContacts.DISPLAY_NAME_PRIMARY} LIKE ?" + val selArgs = arrayOf("%$query%") + val cursor = context.contentResolver.query( + ContactsContract.RawContacts.CONTENT_URI, proj, sel, selArgs, null) ?: return mutableListOf() + //Maps raw contact ids to contact ids + val contactMap = mutableMapOf>() + while (cursor.moveToNext()) { + contactMap.getOrPut(cursor.getLong(0)) { mutableSetOf() }.add(cursor.getLong(1)) + } + cursor.close() + val results = mutableListOf() + for ((id, rawIds) in contactMap) { + contactById(context, id, rawIds)?.let { results.add(it) } + } + return results.sortedBy { it } + } + + private fun contactById(context: Context, id: Long, rawIds: Set): Contact? { + val s = "(" + rawIds.joinToString(separator = " OR ", + transform = { "${ContactsContract.Data.RAW_CONTACT_ID} = $it" }) + ")" + + " AND (${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE}\"" + + " OR ${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE}\"" + + " OR ${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE}\"" + + " OR ${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE}\"" + + " OR ${ContactsContract.Data.MIMETYPE} = \"vnd.android.cursor.item/vnd.org.telegram.messenger.android.profile\"" + + " OR ${ContactsContract.Data.MIMETYPE} = \"vnd.android.cursor.item/vnd.com.whatsapp.profile\"" + + ")" + val dataCursor = context.contentResolver.query( + ContactsContract.Data.CONTENT_URI, + null, s, null, null + ) ?: return null + val phones = mutableSetOf() + val emails = mutableSetOf() + val telegram = mutableSetOf() + val whatsapp = mutableSetOf() + val postals = mutableSetOf() + var firstName = "" + var lastName = "" + var displayName = "" + val mimeTypeColumn = dataCursor.getColumnIndex(ContactsContract.Data.MIMETYPE) + val emailAddressColumn = dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS) + val numberColumn = dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER) + val addressColumn = dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS) + val displayNameColumn = dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME) + val givenNameColumn = dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME) + val familyNameColumn = dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME) + val data1Column = dataCursor.getColumnIndex(ContactsContract.Data.DATA1) + val data3Column = dataCursor.getColumnIndex(ContactsContract.Data.DATA3) + val idColumn = dataCursor.getColumnIndex(ContactsContract.Data._ID) + loop@ while (dataCursor.moveToNext()) { + when (dataCursor.getStringOrNull(mimeTypeColumn)) { + ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE -> + dataCursor.getStringOrNull(emailAddressColumn)?.let { emails.add(it) } + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> + dataCursor.getStringOrNull(numberColumn)?.let { + phones.add(it.replace(Regex("[^+0-9]"), "")) + } + ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE -> + dataCursor.getStringOrNull(addressColumn)?.let { postals.add(it) } + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE -> { + firstName = dataCursor.getStringOrNull(givenNameColumn) ?: "" + lastName = dataCursor.getStringOrNull(familyNameColumn) ?: "" + displayName = dataCursor.getStringOrNull(displayNameColumn) ?: "" + } + "vnd.android.cursor.item/vnd.org.telegram.messenger.android.profile" -> { + val data1 = dataCursor.getStringOrNull(data1Column) + ?: continue@loop + val data3 = dataCursor.getStringOrNull(data3Column) + ?: continue@loop + telegram.add("$data1$$data3") + } + "vnd.android.cursor.item/vnd.com.whatsapp.profile" -> { + val data1 = dataCursor.getStringOrNull(data1Column) + ?: continue@loop + val dataId = dataCursor.getLong(idColumn) + whatsapp.add("$dataId$+${data1.substringBefore('@')}") + } + } + } + dataCursor.close() + + val lookupKeyCursor = context.contentResolver.query( + ContactsContract.Contacts.CONTENT_URI, + arrayOf(ContactsContract.Contacts.LOOKUP_KEY), + "${ContactsContract.Contacts._ID} = ?", + arrayOf(id.toString()), + null + ) ?: return null + var lookUpKey = "" + if (lookupKeyCursor.moveToNext()) { + lookUpKey = lookupKeyCursor.getString(0) + } + lookupKeyCursor.close() + + return Contact( + id = id, + emails = emails, + phones = phones, + firstName = firstName, + lastName = lastName, + displayName = displayName, + postals = postals, + telegram = telegram, + whatsapp = whatsapp, + lookupKey = lookUpKey + ) + } + + fun deserialize(context: Context, serialized: String): Contact? { + if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) return null + val id = JSONObject(serialized).getLong("id") + val rawContactsCursor = context.contentResolver.query( + ContactsContract.RawContacts.CONTENT_URI, + arrayOf(ContactsContract.RawContacts._ID), + "${ContactsContract.RawContacts.CONTACT_ID} = ?", + arrayOf(id.toString()), + null + ) ?: return null + val rawContacts = mutableSetOf() + while (rawContactsCursor.moveToNext()) { + rawContacts.add(rawContactsCursor.getLong(0)) + } + rawContactsCursor.close() + if (rawContacts.isEmpty()) return null + + return contactById(context, id, rawContacts) + } + } +} \ No newline at end of file diff --git a/crashreporter/.gitignore b/crashreporter/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/crashreporter/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/crashreporter/build.gradle.kts b/crashreporter/build.gradle.kts new file mode 100644 index 00000000..74c1ae97 --- /dev/null +++ b/crashreporter/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.kotlin.stdlib) + implementation(libs.androidx.appcompat) + implementation(libs.materialcomponents) + implementation(libs.androidx.recyclerview) + + + implementation(project(":base")) +} \ No newline at end of file diff --git a/crashreporter/consumer-rules.pro b/crashreporter/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/crashreporter/proguard-rules.pro b/crashreporter/proguard-rules.pro new file mode 100644 index 00000000..d99b33c9 --- /dev/null +++ b/crashreporter/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/crashreporter/readme.md b/crashreporter/readme.md new file mode 100644 index 00000000..c5b4fec5 --- /dev/null +++ b/crashreporter/readme.md @@ -0,0 +1,26 @@ +# :crashreporter + +The crash reporter that can be found under Settings > About > Crash Reporter. + +## License + +This code is based on this library: +[https://github.com/MindorksOpenSource/CrashReporter](https://github.com/MindorksOpenSource/CrashReporter) +, originally licensed under the Apache 2.0 license. + +``` +Copyright (C) 2016 Bal Sikandar +Copyright (C) 2011 Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` \ No newline at end of file diff --git a/crashreporter/src/main/AndroidManifest.xml b/crashreporter/src/main/AndroidManifest.xml new file mode 100644 index 00000000..05f763f2 --- /dev/null +++ b/crashreporter/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + \ No newline at end of file diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/CrashReporter.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/CrashReporter.java new file mode 100644 index 00000000..5a1e8fdb --- /dev/null +++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/CrashReporter.java @@ -0,0 +1,72 @@ +package com.balsikandar.crashreporter; + +import android.content.Context; +import android.content.Intent; + +import com.balsikandar.crashreporter.ui.CrashReporterActivity; +import com.balsikandar.crashreporter.utils.CrashReporterNotInitializedException; +import com.balsikandar.crashreporter.utils.CrashReporterExceptionHandler; +import com.balsikandar.crashreporter.utils.CrashUtil; + +public class CrashReporter { + + private static Context applicationContext; + + private static String crashReportPath; + + private static boolean isNotificationEnabled = true; + + private CrashReporter() { + // This class in not publicly instantiable + } + + public static void initialize(Context context) { + applicationContext = context; + setUpExceptionHandler(); + } + + public static void initialize(Context context, String crashReportSavePath) { + applicationContext = context; + crashReportPath = crashReportSavePath; + setUpExceptionHandler(); + } + + private static void setUpExceptionHandler() { + if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof CrashReporterExceptionHandler)) { + Thread.setDefaultUncaughtExceptionHandler(new CrashReporterExceptionHandler()); + } + } + + public static Context getContext() { + if (applicationContext == null) { + try { + throw new CrashReporterNotInitializedException("Initialize CrashReporter : call CrashReporter.initialize(context, crashReportPath)"); + } catch (Exception e) { + e.printStackTrace(); + } + } + return applicationContext; + } + + public static String getCrashReportPath() { + return crashReportPath; + } + + public static boolean isNotificationEnabled() { + return isNotificationEnabled; + } + + //LOG Exception APIs + public static void logException(Exception exception) { + CrashUtil.logException(exception); + } + + public static Intent getLaunchIntent() { + return new Intent(applicationContext, CrashReporterActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + + public static void disableNotification() { + isNotificationEnabled = false; + } + +} diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/CrashReporterInitProvider.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/CrashReporterInitProvider.java new file mode 100644 index 00000000..19512fa3 --- /dev/null +++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/CrashReporterInitProvider.java @@ -0,0 +1,68 @@ +package com.balsikandar.crashreporter; + +/** + * Created by bali on 02/08/17. + */ + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.Context; +import android.content.pm.ProviderInfo; +import android.database.Cursor; +import android.net.Uri; + +/** + * Created by amitshekhar on 16/11/16. + */ + +public class CrashReporterInitProvider extends ContentProvider { + + + public CrashReporterInitProvider() { + } + + @Override + public boolean onCreate() { + CrashReporter.initialize(getContext()); + return true; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + return null; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public void attachInfo(Context context, ProviderInfo providerInfo) { + if (providerInfo == null) { + throw new NullPointerException("CrashReporterInitProvider ProviderInfo cannot be null."); + } + // So if the authorities equal the library internal ones, the developer forgot to set his applicationId + if ("com.balsikandar.crashreporter.CrashReporterInitProvider".equals(providerInfo.authority)) { + throw new IllegalStateException("Incorrect provider authority in manifest. Most likely due to a " + + "missing applicationId variable in application\'s build.gradle.kts.kts."); + } + super.attachInfo(context, providerInfo); + } + +} \ No newline at end of file diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/adapter/CrashLogAdapter.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/adapter/CrashLogAdapter.java new file mode 100644 index 00000000..8bf7834a --- /dev/null +++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/adapter/CrashLogAdapter.java @@ -0,0 +1,81 @@ +package com.balsikandar.crashreporter.adapter; + +import android.content.Context; +import android.content.Intent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.recyclerview.widget.RecyclerView; + +import com.balsikandar.crashreporter.ui.LogMessageActivity; +import com.balsikandar.crashreporter.utils.FileUtils; + +import java.io.File; +import java.util.ArrayList; + +import de.mm20.launcher2.crashreporter.R; + +/** + * Created by bali on 10/08/17. + */ + +public class CrashLogAdapter extends RecyclerView.Adapter { + + private Context context; + private ArrayList crashFileList; + + public CrashLogAdapter(Context context, ArrayList allCrashLogs) { + this.context = context; + crashFileList = allCrashLogs; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(context).inflate(R.layout.custom_item, null); + return new CrashLogViewHolder(view); + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + ((CrashLogViewHolder) holder).setUpViewHolder(context, crashFileList.get(position)); + } + + @Override + public int getItemCount() { + return crashFileList.size(); + } + + + public void updateList(ArrayList allCrashLogs) { + crashFileList = allCrashLogs; + notifyDataSetChanged(); + } + + + private class CrashLogViewHolder extends RecyclerView.ViewHolder { + private TextView textViewMsg, messageLogTime; + + CrashLogViewHolder(View itemView) { + super(itemView); + messageLogTime = itemView.findViewById(R.id.messageLogTime); + textViewMsg = itemView.findViewById(R.id.textViewMsg); + } + + void setUpViewHolder(final Context context, final File file) { + final String filePath = file.getAbsolutePath(); + messageLogTime.setText(file.getName().replaceAll("[a-zA-Z_.]", "")); + textViewMsg.setText(FileUtils.readFirstLineFromFile(new File(filePath))); + + textViewMsg.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(context, LogMessageActivity.class); + intent.putExtra("LogMessage", filePath); + context.startActivity(intent); + } + }); + } + } +} diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/adapter/MainPagerAdapter.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/adapter/MainPagerAdapter.java new file mode 100644 index 00000000..bf572d1c --- /dev/null +++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/adapter/MainPagerAdapter.java @@ -0,0 +1,50 @@ +package com.balsikandar.crashreporter.adapter; + +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; + +import com.balsikandar.crashreporter.ui.CrashLogFragment; +import com.balsikandar.crashreporter.ui.ExceptionLogFragment; + +/** + * Created by bali on 11/08/17. + */ + +public class MainPagerAdapter extends FragmentPagerAdapter { + + private CrashLogFragment crashLogFragment; + private ExceptionLogFragment exceptionLogFragment; + private String[] titles; + + public MainPagerAdapter(FragmentManager fm, String[] titles) { + super(fm); + this.titles = titles; + } + + @Override + public Fragment getItem(int position) { + if (position == 0) { + return crashLogFragment = new CrashLogFragment(); + } else if (position == 1) { + return exceptionLogFragment = new ExceptionLogFragment(); + } else { + return new CrashLogFragment(); + } + } + + @Override + public int getCount() { + return 2; + } + + @Override + public CharSequence getPageTitle(int position) { + return titles[position]; + } + + public void clearLogs() { + crashLogFragment.clearLog(); + exceptionLogFragment.clearLog(); + } +} \ No newline at end of file diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/CrashLogFragment.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/CrashLogFragment.java new file mode 100644 index 00000000..9df1a709 --- /dev/null +++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/CrashLogFragment.java @@ -0,0 +1,95 @@ +package com.balsikandar.crashreporter.ui; + +import android.content.Context; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.balsikandar.crashreporter.CrashReporter; +import com.balsikandar.crashreporter.adapter.CrashLogAdapter; +import com.balsikandar.crashreporter.utils.Constants; +import com.balsikandar.crashreporter.utils.CrashUtil; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; + +import de.mm20.launcher2.crashreporter.R; + +/** + * Created by bali on 11/08/17. + */ + +public class CrashLogFragment extends Fragment { + + private CrashLogAdapter logAdapter; + + private RecyclerView crashRecyclerView; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.crash_log, container, false); + crashRecyclerView = (RecyclerView) view.findViewById(R.id.crashRecyclerView); + + return view; + } + + @Override + public void onResume() { + super.onResume(); + loadAdapter(getActivity(), crashRecyclerView); + } + + private void loadAdapter(Context context, RecyclerView crashRecyclerView) { + + logAdapter = new CrashLogAdapter(context, getAllCrashes()); + crashRecyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); + crashRecyclerView.setAdapter(logAdapter); + } + + public void clearLog() { + if (logAdapter != null) { + logAdapter.updateList(getAllCrashes()); + } + } + + + private ArrayList getAllCrashes() { + String directoryPath; + String crashReportPath = CrashReporter.getCrashReportPath(); + + if (TextUtils.isEmpty(crashReportPath)) { + directoryPath = CrashUtil.getDefaultPath(); + } else { + directoryPath = crashReportPath; + } + File directory = new File(directoryPath); + if (!directory.exists() || !directory.isDirectory()) { + throw new RuntimeException("The path provided doesn't exists : " + directoryPath); + } + ArrayList listOfFiles = new ArrayList<>(Arrays.asList(directory.listFiles())); + for (Iterator iterator = listOfFiles.iterator(); iterator.hasNext(); ) { + if (iterator.next().getName().contains(Constants.EXCEPTION_SUFFIX)) { + iterator.remove(); + } + } + Collections.sort(listOfFiles, Collections.reverseOrder()); + return listOfFiles; + } + +} diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/CrashReporterActivity.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/CrashReporterActivity.java new file mode 100644 index 00000000..72658bb7 --- /dev/null +++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/CrashReporterActivity.java @@ -0,0 +1,114 @@ +package com.balsikandar.crashreporter.ui; + +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.Menu; +import android.view.MenuItem; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.viewpager.widget.ViewPager; + +import com.balsikandar.crashreporter.CrashReporter; +import com.balsikandar.crashreporter.adapter.MainPagerAdapter; +import com.balsikandar.crashreporter.utils.Constants; +import com.balsikandar.crashreporter.utils.CrashUtil; +import com.balsikandar.crashreporter.utils.FileUtils; +import com.balsikandar.crashreporter.utils.SimplePageChangeListener; +import com.google.android.material.tabs.TabLayout; + +import java.io.File; + +import de.mm20.launcher2.crashreporter.R; + +public class CrashReporterActivity extends AppCompatActivity { + + private MainPagerAdapter mainPagerAdapter; + private int selectedTabPosition = 0; + + //region activity callbacks + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.log_main_menu, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.delete_crash_logs) { + clearCrashLog(); + return true; + } else { + return super.onOptionsItemSelected(item); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.crash_reporter_activity); + + Toolbar toolbar = findViewById(R.id.toolbar); + toolbar.setTitle(getString(R.string.crash_reporter)); + toolbar.setSubtitle(getApplicationName()); + setSupportActionBar(toolbar); + + ViewPager viewPager = findViewById(R.id.viewpager); + if (viewPager != null) { + setupViewPager(viewPager); + } + + TabLayout tabLayout = findViewById(R.id.tabs); + tabLayout.setupWithViewPager(viewPager); + } + //endregion + + private void clearCrashLog() { + new Thread(new Runnable() { + @Override + public void run() { + String crashReportPath = TextUtils.isEmpty(CrashReporter.getCrashReportPath()) ? + CrashUtil.getDefaultPath() : CrashReporter.getCrashReportPath(); + + File[] logs = new File(crashReportPath).listFiles(); + for (File file : logs) { + FileUtils.delete(file); + } + runOnUiThread(new Runnable() { + @Override + public void run() { + mainPagerAdapter.clearLogs(); + } + }); + } + }).start(); + } + + private void setupViewPager(ViewPager viewPager) { + String[] titles = {getString(R.string.crashes), getString(R.string.exceptions)}; + mainPagerAdapter = new MainPagerAdapter(getSupportFragmentManager(), titles); + viewPager.setAdapter(mainPagerAdapter); + + viewPager.addOnPageChangeListener(new SimplePageChangeListener() { + @Override + public void onPageSelected(int position) { + selectedTabPosition = position; + } + }); + + Intent intent = getIntent(); + if (intent != null && !intent.getBooleanExtra(Constants.LANDING, false)) { + selectedTabPosition = 1; + } + viewPager.setCurrentItem(selectedTabPosition); + } + + private String getApplicationName() { + ApplicationInfo applicationInfo = getApplicationInfo(); + int stringId = applicationInfo.labelRes; + return stringId == 0 ? applicationInfo.nonLocalizedLabel.toString() : getString(stringId); + } + +} diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/ExceptionLogFragment.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/ExceptionLogFragment.java new file mode 100644 index 00000000..6fa4c706 --- /dev/null +++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/ExceptionLogFragment.java @@ -0,0 +1,96 @@ +package com.balsikandar.crashreporter.ui; + +import android.content.Context; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.balsikandar.crashreporter.CrashReporter; +import com.balsikandar.crashreporter.adapter.CrashLogAdapter; +import com.balsikandar.crashreporter.utils.Constants; +import com.balsikandar.crashreporter.utils.CrashUtil; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; + +import de.mm20.launcher2.crashreporter.R; + +/** + * Created by bali on 11/08/17. + */ + +public class ExceptionLogFragment extends Fragment { + + private CrashLogAdapter logAdapter; + + private RecyclerView exceptionRecyclerView; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.exception_log, container, false); + exceptionRecyclerView = (RecyclerView) view.findViewById(R.id.exceptionRecyclerView); + + return view; + } + + @Override + public void onResume() { + super.onResume(); + loadAdapter(getActivity(), exceptionRecyclerView); + } + + private void loadAdapter(Context context, RecyclerView exceptionRecyclerView) { + + logAdapter = new CrashLogAdapter(context, getAllExceptions()); + exceptionRecyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); + exceptionRecyclerView.setAdapter(logAdapter); + } + + public void clearLog() { + if (logAdapter != null) { + logAdapter.updateList(getAllExceptions()); + } + } + + public ArrayList getAllExceptions() { + String directoryPath; + String crashReportPath = CrashReporter.getCrashReportPath(); + + if (TextUtils.isEmpty(crashReportPath)){ + directoryPath = CrashUtil.getDefaultPath(); + } else{ + directoryPath = crashReportPath; + } + + File directory = new File(directoryPath); + if (!directory.exists() || !directory.isDirectory()){ + throw new RuntimeException("The path provided doesn't exists : " + directoryPath); + } + + ArrayList listOfFiles = new ArrayList<>(Arrays.asList(directory.listFiles())); + for (Iterator iterator = listOfFiles.iterator(); iterator.hasNext(); ) { + if (iterator.next().getName().contains(Constants.CRASH_SUFFIX)) { + iterator.remove(); + } + } + Collections.sort(listOfFiles, Collections.reverseOrder()); + return listOfFiles; + } + +} diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/LogMessageActivity.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/LogMessageActivity.java new file mode 100644 index 00000000..d06fca6b --- /dev/null +++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/LogMessageActivity.java @@ -0,0 +1,90 @@ +package com.balsikandar.crashreporter.ui; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.TextView; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.core.content.FileProvider; + +import com.balsikandar.crashreporter.utils.AppUtils; +import com.balsikandar.crashreporter.utils.FileUtils; + +import java.io.File; + +import de.mm20.launcher2.crashreporter.R; + +public class LogMessageActivity extends AppCompatActivity { + + private TextView appInfo; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_log_message); + appInfo = findViewById(R.id.appInfo); + + Intent intent = getIntent(); + if (intent != null) { + String dirPath = intent.getStringExtra("LogMessage"); + File file = new File(dirPath); + String crashLog = FileUtils.readFromFile(file); + TextView textView = findViewById(R.id.logMessage); + textView.setText(crashLog); + } + + Toolbar myToolbar = findViewById(R.id.toolbar); + myToolbar.setTitle(getString(R.string.crash_reporter)); + setSupportActionBar(myToolbar); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + getAppInfo(); + } + + private void getAppInfo() { + appInfo.setText(AppUtils.getDeviceDetails(this)); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.crash_detail_menu, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + Intent intent = getIntent(); + String filePath = null; + if (intent != null) { + filePath = intent.getStringExtra("LogMessage"); + } + + if (item.getItemId() == R.id.delete_log) { + if (FileUtils.delete(filePath)) { + finish(); + } + return true; + } else if (item.getItemId() == R.id.share_crash_log) { + shareCrashReport(filePath); + return true; + } else { + return super.onOptionsItemSelected(item); + } + } + + private void shareCrashReport(String filePath) { + Uri uri = FileProvider.getUriForFile(this, + this.getApplicationContext().getPackageName() + ".fileprovider", + new File(filePath)); + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("*/*"); + intent.putExtra(Intent.EXTRA_TEXT, appInfo.getText().toString()); + intent.putExtra(Intent.EXTRA_STREAM, uri); + startActivity(Intent.createChooser(intent, "Share via")); + } +} diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/AppUtils.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/AppUtils.java new file mode 100644 index 00000000..c304dae1 --- /dev/null +++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/AppUtils.java @@ -0,0 +1,106 @@ +package com.balsikandar.crashreporter.utils; + +import android.Manifest; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Build; +import android.util.Log; + +import androidx.core.app.ActivityCompat; + +import java.util.TimeZone; +import java.util.UUID; + +/** + * Created by bali on 12/08/17. + */ + +public class AppUtils { + private static String getCurrentLauncherApp(Context context) { + String str = ""; + PackageManager localPackageManager = context.getPackageManager(); + Intent intent = new Intent("android.intent.action.MAIN"); + intent.addCategory("android.intent.category.HOME"); + try { + ResolveInfo resolveInfo = localPackageManager.resolveActivity(intent, + PackageManager.MATCH_DEFAULT_ONLY); + if (resolveInfo != null && resolveInfo.activityInfo != null) { + str = resolveInfo.activityInfo.packageName; + } + } catch (Exception e) { + Log.e("AppUtils", "Exception : " + e.getMessage()); + } + return str; + } + + public static String getDeviceDetails(Context context) { + + return "Device Information\n" + + "\nDEVICE.ID : " + getDeviceId(context) + + "\nAPP.VERSION : " + getAppVersion(context) + + "\nLAUNCHER.APP : " + getCurrentLauncherApp(context) + + "\nTIMEZONE : " + timeZone() + + "\nVERSION.RELEASE : " + Build.VERSION.RELEASE + + "\nVERSION.INCREMENTAL : " + Build.VERSION.INCREMENTAL + + "\nVERSION.SDK.NUMBER : " + Build.VERSION.SDK_INT + + "\nBOARD : " + Build.BOARD + + "\nBOOTLOADER : " + Build.BOOTLOADER + + "\nBRAND : " + Build.BRAND + + "\nCPU_ABI : " + Build.CPU_ABI + + "\nCPU_ABI2 : " + Build.CPU_ABI2 + + "\nDISPLAY : " + Build.DISPLAY + + "\nFINGERPRINT : " + Build.FINGERPRINT + + "\nHARDWARE : " + Build.HARDWARE + + "\nHOST : " + Build.HOST + + "\nID : " + Build.ID + + "\nMANUFACTURER : " + Build.MANUFACTURER + + "\nMODEL : " + Build.MODEL + + "\nPRODUCT : " + Build.PRODUCT + + "\nSERIAL : " + Build.SERIAL + + "\nTAGS : " + Build.TAGS + + "\nTIME : " + Build.TIME + + "\nTYPE : " + Build.TYPE + + "\nUNKNOWN : " + Build.UNKNOWN + + "\nUSER : " + Build.USER; + } + + private static String timeZone() { + TimeZone tz = TimeZone.getDefault(); + return tz.getID(); + } + + private static String getDeviceId(Context context) { + String androidDeviceId = getAndroidDeviceId(context); + if (androidDeviceId == null) + androidDeviceId = UUID.randomUUID().toString(); + return androidDeviceId; + + } + + private static String getAndroidDeviceId(Context context) { + final String INVALID_ANDROID_ID = "9774d56d682e549c"; + final String androidId = android.provider.Settings.Secure.getString( + context.getContentResolver(), + android.provider.Settings.Secure.ANDROID_ID); + if (androidId == null + || androidId.toLowerCase().equals(INVALID_ANDROID_ID)) { + return null; + } + return androidId; + } + + private static int getAppVersion(Context context) { + try { + PackageInfo packageInfo = context.getPackageManager() + .getPackageInfo(context.getPackageName(), 0); + return packageInfo.versionCode; + } catch (PackageManager.NameNotFoundException e) { + throw new RuntimeException("Could not get package name: " + e); + } + } +} diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/Constants.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/Constants.java new file mode 100644 index 00000000..14c68fa3 --- /dev/null +++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/Constants.java @@ -0,0 +1,15 @@ +package com.balsikandar.crashreporter.utils; + +/** + * Created by bali on 15/08/17. + */ + +public class Constants { + public static final String EXCEPTION_SUFFIX = "_exception"; + public static final String CRASH_SUFFIX = "_crash"; + public static final String FILE_EXTENSION = ".txt"; + public static final String CRASH_REPORT_DIR = "crashReports"; + public static final int NOTIFICATION_ID = 1; + public static final String CHANNEL_NOTIFICATION_ID = "crashreporter_channel_id"; + public static final String LANDING = "landing"; +} diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterException.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterException.java new file mode 100644 index 00000000..ede6f5fd --- /dev/null +++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterException.java @@ -0,0 +1,64 @@ +package com.balsikandar.crashreporter.utils; + +/** + * Created by bali on 02/08/17. + */ + +/** + * Represents an error condition specific to the Crash Reporter for Android. + */ +public class CrashReporterException extends RuntimeException { + static final long serialVersionUID = 1; + + /** + * Constructs a new CrashReporterException. + */ + public CrashReporterException() { + super(); + } + + /** + * Constructs a new CrashReporterException. + * + * @param message the detail message of this exception + */ + public CrashReporterException(String message) { + super(message); + } + + /** + * Constructs a new CrashReporterException. + * + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args the list of arguments passed to the formatter. + */ + public CrashReporterException(String format, Object... args) { + this(String.format(format, args)); + } + + /** + * Constructs a new CrashReporterException. + * + * @param message the detail message of this exception + * @param throwable the cause of this exception + */ + public CrashReporterException(String message, Throwable throwable) { + super(message, throwable); + } + + /** + * Constructs a new CrashReporterException. + * + * @param throwable the cause of this exception + */ + public CrashReporterException(Throwable throwable) { + super(throwable); + } + + @Override + public String toString() { + // Throwable.toString() returns "CrashReporterException:{message}". Returning just "{message}" + // should be fine here. + return getMessage(); + } +} diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterExceptionHandler.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterExceptionHandler.java new file mode 100644 index 00000000..b2e25391 --- /dev/null +++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterExceptionHandler.java @@ -0,0 +1,18 @@ +package com.balsikandar.crashreporter.utils; + +public class CrashReporterExceptionHandler implements Thread.UncaughtExceptionHandler { + + private Thread.UncaughtExceptionHandler exceptionHandler; + + public CrashReporterExceptionHandler() { + this.exceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); + } + + @Override + public void uncaughtException(Thread thread, Throwable throwable) { + + CrashUtil.saveCrashReport(throwable); + + exceptionHandler.uncaughtException(thread, throwable); + } +} diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterNotInitializedException.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterNotInitializedException.java new file mode 100644 index 00000000..5b6d35a5 --- /dev/null +++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterNotInitializedException.java @@ -0,0 +1,47 @@ +package com.balsikandar.crashreporter.utils; + +/** + * Created by bali on 02/08/17. + */ + +/** + * An Exception indicating that the Crash Reporter has not been correctly initialized. + */ +public class CrashReporterNotInitializedException extends CrashReporterException { + static final long serialVersionUID = 1; + + /** + * Constructs a CrashReporterNotInitializedException with no additional information. + */ + public CrashReporterNotInitializedException() { + super(); + } + + /** + * Constructs a CrashReporterNotInitializedException with a message. + * + * @param message A String to be returned from getMessage. + */ + public CrashReporterNotInitializedException(String message) { + super(message); + } + + /** + * Constructs a CrashReporterNotInitializedException with a message and inner error. + * + * @param message A String to be returned from getMessage. + * @param throwable A Throwable to be returned from getCause. + */ + public CrashReporterNotInitializedException(String message, Throwable throwable) { + super(message, throwable); + } + + /** + * Constructs a CrashReporterNotInitializedException with an inner error. + * + * @param throwable A Throwable to be returned from getCause. + */ + public CrashReporterNotInitializedException(Throwable throwable) { + super(throwable); + } +} \ No newline at end of file diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashUtil.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashUtil.java new file mode 100644 index 00000000..aa6b601f --- /dev/null +++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashUtil.java @@ -0,0 +1,154 @@ +package com.balsikandar.crashreporter.utils; + +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.text.TextUtils; +import android.util.Log; + +import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; + +import com.balsikandar.crashreporter.CrashReporter; +import de.mm20.launcher2.crashreporter.R; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +import static android.content.Context.NOTIFICATION_SERVICE; +import static com.balsikandar.crashreporter.utils.Constants.CHANNEL_NOTIFICATION_ID; + +public class CrashUtil { + + private static final String TAG = CrashUtil.class.getSimpleName(); + + private CrashUtil() { + //this class is not publicly instantiable + } + + private static String getCrashLogTime() { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); + return dateFormat.format(new Date()); + } + + public static void saveCrashReport(final Throwable throwable) { + + String crashReportPath = CrashReporter.getCrashReportPath(); + String filename = getCrashLogTime() + Constants.CRASH_SUFFIX + Constants.FILE_EXTENSION; + writeToFile(crashReportPath, filename, getStackTrace(throwable)); + + showNotification(throwable.getLocalizedMessage(), true); + } + + public static void logException(final Exception exception) { + + new Thread(new Runnable() { + @Override + public void run() { + + String crashReportPath = CrashReporter.getCrashReportPath(); + final String filename = getCrashLogTime() + Constants.EXCEPTION_SUFFIX + Constants.FILE_EXTENSION; + writeToFile(crashReportPath, filename, getStackTrace(exception)); + + //showNotification(exception.getLocalizedMessage(), false); + } + }).start(); + } + + private static void writeToFile(String crashReportPath, String filename, String crashLog) { + + if (TextUtils.isEmpty(crashReportPath)) { + crashReportPath = getDefaultPath(); + } + + File crashDir = new File(crashReportPath); + if (!crashDir.exists() || !crashDir.isDirectory()) { + crashReportPath = getDefaultPath(); + Log.e(TAG, "Path provided doesn't exists : " + crashDir + "\nSaving crash report at : " + getDefaultPath()); + } + + BufferedWriter bufferedWriter; + try { + bufferedWriter = new BufferedWriter(new FileWriter( + crashReportPath + File.separator + filename)); + + bufferedWriter.write(crashLog); + bufferedWriter.flush(); + bufferedWriter.close(); + Log.d(TAG, "crash report saved in : " + crashReportPath); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static void showNotification(String localisedMsg, boolean isCrash) { + + if (CrashReporter.isNotificationEnabled()) { + Context context = CrashReporter.getContext(); + NotificationManager notificationManager = (NotificationManager) context. + getSystemService(NOTIFICATION_SERVICE); + createNotificationChannel(notificationManager, context); + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_NOTIFICATION_ID); + builder.setSmallIcon(R.drawable.ic_warning_black_24dp); + + Intent intent = CrashReporter.getLaunchIntent(); + intent.putExtra(Constants.LANDING, isCrash); + intent.setAction(Long.toString(System.currentTimeMillis())); + + PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); + builder.setContentIntent(pendingIntent); + + builder.setContentTitle(context.getString(R.string.view_crash_report)); + + if (TextUtils.isEmpty(localisedMsg)) { + builder.setContentText(context.getString(R.string.check_your_message_here)); + } else { + builder.setContentText(localisedMsg); + } + + builder.setAutoCancel(true); + builder.setColor(ContextCompat.getColor(context, R.color.colorAccent_CrashReporter)); + + notificationManager.notify(Constants.NOTIFICATION_ID, builder.build()); + } + } + + private static void createNotificationChannel(NotificationManager notificationManager, Context context) { + if (Build.VERSION.SDK_INT >= 26) { + CharSequence name = context.getString(R.string.notification_crash_report_title); + String description = ""; + NotificationChannel channel = new NotificationChannel(CHANNEL_NOTIFICATION_ID, name, NotificationManager.IMPORTANCE_DEFAULT); + channel.setDescription(description); + notificationManager.createNotificationChannel(channel); + } + } + + private static String getStackTrace(Throwable e) { + final Writer result = new StringWriter(); + final PrintWriter printWriter = new PrintWriter(result); + + e.printStackTrace(printWriter); + String crashLog = result.toString(); + printWriter.close(); + return crashLog; + } + + public static String getDefaultPath() { + String defaultPath = CrashReporter.getContext().getExternalFilesDir(null).getAbsolutePath() + + File.separator + Constants.CRASH_REPORT_DIR; + + File file = new File(defaultPath); + file.mkdirs(); + return defaultPath; + } +} diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/FileUtils.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/FileUtils.java new file mode 100644 index 00000000..7a91d8d8 --- /dev/null +++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/FileUtils.java @@ -0,0 +1,119 @@ +package com.balsikandar.crashreporter.utils; + +import android.text.TextUtils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +/** + * Created by bali on 10/08/17. + */ + +public class FileUtils { + + public static final String TAG = FileUtils.class.getSimpleName(); + + private FileUtils() { + //this class is not publicly instantiable + } + + public static boolean delete(String absPath) { + if (TextUtils.isEmpty(absPath)) { + return false; + } + + File file = new File(absPath); + return delete(file); + } + + public static boolean delete(File file) { + if (!exists(file)) { + return true; + } + + if (file.isFile()) { + return file.delete(); + } + + boolean result = true; + File files[] = file.listFiles(); + if (files == null) return false; + for (int index = 0; index < files.length; index++) { + result |= delete(files[index]); + } + result |= file.delete(); + + return result; + } + + public static boolean exists(File file) { + return file != null && file.exists(); + } + + public static String cleanPath(String absPath) { + if (TextUtils.isEmpty(absPath)) { + return absPath; + } + try { + File file = new File(absPath); + absPath = file.getCanonicalPath(); + } catch (Exception e) { + + } + return absPath; + } + + public final static String getParent(File file) { + return file == null ? null : file.getParent(); + } + + public final static String getParent(String absPath) { + if (TextUtils.isEmpty(absPath)) { + return null; + } + absPath = cleanPath(absPath); + File file = new File(absPath); + return getParent(file); + } + + public static boolean deleteFiles(String directoryPath) { + String directoryToDelete; + if (!TextUtils.isEmpty(directoryPath)) { + directoryToDelete = directoryPath; + } else { + directoryToDelete = CrashUtil.getDefaultPath(); + } + + return delete(directoryToDelete); + } + + public static String readFirstLineFromFile(File file) { + String line = ""; + try { + BufferedReader reader = new BufferedReader(new FileReader(file)); + line = reader.readLine(); + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return line; + } + + public static String readFromFile(File file) { + StringBuilder crash = new StringBuilder(); + try { + BufferedReader reader = new BufferedReader(new FileReader(file)); + String line; + while ((line = reader.readLine()) != null) { + crash.append(line); + crash.append('\n'); + } + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return crash.toString(); + } +} diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/SimplePageChangeListener.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/SimplePageChangeListener.java new file mode 100644 index 00000000..e452d920 --- /dev/null +++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/SimplePageChangeListener.java @@ -0,0 +1,17 @@ +package com.balsikandar.crashreporter.utils; + + +import androidx.viewpager.widget.ViewPager; + +/** + * Created by bali on 11/08/17. + */ + +public abstract class SimplePageChangeListener implements ViewPager.OnPageChangeListener { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {} + @Override + public abstract void onPageSelected(int position); + @Override + public void onPageScrollStateChanged(int state) {} +} diff --git a/crashreporter/src/main/java/de/mm20/launcher2/crashreporter/CrashReporter.kt b/crashreporter/src/main/java/de/mm20/launcher2/crashreporter/CrashReporter.kt new file mode 100644 index 00000000..9ccaf905 --- /dev/null +++ b/crashreporter/src/main/java/de/mm20/launcher2/crashreporter/CrashReporter.kt @@ -0,0 +1,14 @@ +package de.mm20.launcher2.crashreporter + +import android.content.Intent +import android.util.Log + +object CrashReporter { + fun logException(e: Exception) { + com.balsikandar.crashreporter.CrashReporter.logException(e) + Log.e("MM20", Log.getStackTraceString(e)) + } + fun getLaunchIntent() : Intent { + return com.balsikandar.crashreporter.CrashReporter.getLaunchIntent() + } +} \ No newline at end of file diff --git a/crashreporter/src/main/res/drawable/ic_menu_delete_white_24dp.xml b/crashreporter/src/main/res/drawable/ic_menu_delete_white_24dp.xml new file mode 100644 index 00000000..21af9a57 --- /dev/null +++ b/crashreporter/src/main/res/drawable/ic_menu_delete_white_24dp.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/crashreporter/src/main/res/drawable/ic_menu_share_white_24dp.xml b/crashreporter/src/main/res/drawable/ic_menu_share_white_24dp.xml new file mode 100644 index 00000000..01726c74 --- /dev/null +++ b/crashreporter/src/main/res/drawable/ic_menu_share_white_24dp.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/crashreporter/src/main/res/drawable/ic_search_white_24dp.xml b/crashreporter/src/main/res/drawable/ic_search_white_24dp.xml new file mode 100644 index 00000000..91972807 --- /dev/null +++ b/crashreporter/src/main/res/drawable/ic_search_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/crashreporter/src/main/res/drawable/ic_warning_black_24dp.xml b/crashreporter/src/main/res/drawable/ic_warning_black_24dp.xml new file mode 100644 index 00000000..7c69ee84 --- /dev/null +++ b/crashreporter/src/main/res/drawable/ic_warning_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/crashreporter/src/main/res/layout/activity_log_message.xml b/crashreporter/src/main/res/layout/activity_log_message.xml new file mode 100644 index 00000000..bd16c5e8 --- /dev/null +++ b/crashreporter/src/main/res/layout/activity_log_message.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + diff --git a/crashreporter/src/main/res/layout/crash_log.xml b/crashreporter/src/main/res/layout/crash_log.xml new file mode 100644 index 00000000..21025eda --- /dev/null +++ b/crashreporter/src/main/res/layout/crash_log.xml @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/crashreporter/src/main/res/layout/crash_reporter_activity.xml b/crashreporter/src/main/res/layout/crash_reporter_activity.xml new file mode 100644 index 00000000..59e46bf7 --- /dev/null +++ b/crashreporter/src/main/res/layout/crash_reporter_activity.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + diff --git a/crashreporter/src/main/res/layout/custom_item.xml b/crashreporter/src/main/res/layout/custom_item.xml new file mode 100644 index 00000000..110486c3 --- /dev/null +++ b/crashreporter/src/main/res/layout/custom_item.xml @@ -0,0 +1,41 @@ + + + + + + + + + \ No newline at end of file diff --git a/crashreporter/src/main/res/layout/exception_log.xml b/crashreporter/src/main/res/layout/exception_log.xml new file mode 100644 index 00000000..86d642fc --- /dev/null +++ b/crashreporter/src/main/res/layout/exception_log.xml @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/crashreporter/src/main/res/menu/crash_detail_menu.xml b/crashreporter/src/main/res/menu/crash_detail_menu.xml new file mode 100644 index 00000000..977da47a --- /dev/null +++ b/crashreporter/src/main/res/menu/crash_detail_menu.xml @@ -0,0 +1,15 @@ + +

+ + + + \ No newline at end of file diff --git a/crashreporter/src/main/res/menu/log_main_menu.xml b/crashreporter/src/main/res/menu/log_main_menu.xml new file mode 100644 index 00000000..5a0b306b --- /dev/null +++ b/crashreporter/src/main/res/menu/log_main_menu.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/crashreporter/src/main/res/values/colors.xml b/crashreporter/src/main/res/values/colors.xml new file mode 100644 index 00000000..451218ab --- /dev/null +++ b/crashreporter/src/main/res/values/colors.xml @@ -0,0 +1,7 @@ + + + #ff4081 + #f50057 + #cf1162 + #000000 + \ No newline at end of file diff --git a/crashreporter/src/main/res/values/strings.xml b/crashreporter/src/main/res/values/strings.xml new file mode 100644 index 00000000..e1bb35f0 --- /dev/null +++ b/crashreporter/src/main/res/values/strings.xml @@ -0,0 +1,11 @@ + + CrashReporter + Crashes + Exceptions + View Crash Report + Crash Reporter notifications + Check your crashes and exceptions here. + Are you sure to delete all the crash logs + CANCEL + OK + diff --git a/crashreporter/src/main/res/values/styles.xml b/crashreporter/src/main/res/values/styles.xml new file mode 100644 index 00000000..d08da4cc --- /dev/null +++ b/crashreporter/src/main/res/values/styles.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/currencies/.gitignore b/currencies/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/currencies/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/currencies/build.gradle.kts b/currencies/build.gradle.kts new file mode 100644 index 00000000..220ef135 --- /dev/null +++ b/currencies/build.gradle.kts @@ -0,0 +1,52 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + + implementation(libs.bundles.kotlin) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + implementation(libs.materialcomponents) + implementation(libs.androidx.work) + + implementation(libs.okhttp) + + implementation(project(":ktx")) + implementation(project(":i18n")) + implementation(project(":database")) + implementation(project(":crashreporter")) +} \ No newline at end of file diff --git a/currencies/consumer-rules.pro b/currencies/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/currencies/proguard-rules.pro b/currencies/proguard-rules.pro new file mode 100644 index 00000000..6cad0a65 --- /dev/null +++ b/currencies/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts.kts.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/currencies/src/main/AndroidManifest.xml b/currencies/src/main/AndroidManifest.xml new file mode 100644 index 00000000..78f9c468 --- /dev/null +++ b/currencies/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/currencies/src/main/java/de/mm20/launcher2/currencies/Currency.kt b/currencies/src/main/java/de/mm20/launcher2/currencies/Currency.kt new file mode 100644 index 00000000..c873faee --- /dev/null +++ b/currencies/src/main/java/de/mm20/launcher2/currencies/Currency.kt @@ -0,0 +1,19 @@ +package de.mm20.launcher2.currencies + +import de.mm20.launcher2.database.entities.CurrencyEntity + +data class Currency( + val symbol: String, + val value: Double, + val lastUpdate: Long +) { + constructor(entity: CurrencyEntity) : this( + symbol = entity.symbol, + value = entity.value, + lastUpdate = entity.lastUpdate + ) + + fun toDatabaseEntity(): CurrencyEntity { + return CurrencyEntity(symbol, value, lastUpdate) + } +} \ No newline at end of file diff --git a/currencies/src/main/java/de/mm20/launcher2/currencies/CurrencyRepository.kt b/currencies/src/main/java/de/mm20/launcher2/currencies/CurrencyRepository.kt new file mode 100644 index 00000000..864ac1d3 --- /dev/null +++ b/currencies/src/main/java/de/mm20/launcher2/currencies/CurrencyRepository.kt @@ -0,0 +1,109 @@ +package de.mm20.launcher2.currencies + +import android.content.Context +import android.util.Log +import androidx.work.* +import de.mm20.launcher2.database.AppDatabase +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.util.concurrent.TimeUnit + +class CurrencyRepository(val context: Context) { + + init { + val currencyWorker = PeriodicWorkRequest.Builder(ExchangeRateWorker::class.java, 60, TimeUnit.MINUTES) + .build() + WorkManager.getInstance().enqueueUniquePeriodicWork("ExchangeRates", + ExistingPeriodicWorkPolicy.REPLACE, currencyWorker) + } + + suspend fun convertCurrency(fromCurrency: String, value: Double, toCurrency: String? = null): List> { + + return withContext>>(Dispatchers.IO) { + val dao = AppDatabase.getInstance(context) + .currencyDao() + + val from = Currency(dao.getCurrency(fromCurrency) ?: return@withContext emptyList()) + + return@withContext if (toCurrency == null) { + dao.getAllCurrencies().mapNotNull { + val to = Currency(it) + if (from.lastUpdate != to.lastUpdate) { + Log.w("MM20", "Exchange rate update dates do not match: $fromCurrency, $it") + return@mapNotNull null + } + if (from.symbol == to.symbol) return@mapNotNull null + to.symbol to value * to.value / from.value + } + } else { + val to = Currency(dao.getCurrency(toCurrency) ?: return@withContext emptyList()) + if (from.lastUpdate != to.lastUpdate) { + Log.w("MM20", "Exchange rate update dates do not match: $fromCurrency, $toCurrency") + return@withContext emptyList() + } + listOf(toCurrency to value * to.value / from.value) + } + } + } + + fun getFlag(currencySymbol: String): String { + return when (currencySymbol) { + "EUR" -> "\uD83C\uDDEA\uD83C\uDDFA" // European Union + "USD" -> "\uD83C\uDDFA\uD83C\uDDF8" // United States + "JPY" -> "\uD83C\uDDEF\uD83C\uDDF5" // Japan + "GBP" -> "\uD83C\uDDEC\uD83C\uDDE7" // United Kingdom + "AUD" -> "\uD83C\uDDE6\uD83C\uDDFA" // Australia + "CAD" -> "\uD83C\uDDE8\uD83C\uDDE6" // Canada + "CHF" -> "\uD83C\uDDE8\uD83C\uDDED" // Switzerland + "CNY" -> "\uD83C\uDDE8\uD83C\uDDF3" // China + "SEK" -> "\uD83C\uDDF8\uD83C\uDDEA" // Sweden + "NZD" -> "\uD83C\uDDF3\uD83C\uDDFF" // New Zealand + + "HKD" -> "\uD83C\uDDED\uD83C\uDDF0" // Hong Kong + "IDR" -> "\uD83C\uDDEE\uD83C\uDDE9" // Indonesia + "ILS" -> "\uD83C\uDDEE\uD83C\uDDF1" // Israel + "DKK" -> "\uD83C\uDDE9\uD83C\uDDF0" // Denmark + "INR" -> "\uD83C\uDDEE\uD83C\uDDF3" // India + "MXN" -> "\uD83C\uDDF2\uD83C\uDDFD" // Mexico + "CZK" -> "\uD83C\uDDE8\uD83C\uDDFF" // Czechia + "SGD" -> "\uD83C\uDDF8\uD83C\uDDEC" // Singapore + "THB" -> "\uD83C\uDDF9\uD83C\uDDED" // Thailand + "HRK" -> "\uD83C\uDDED\uD83C\uDDF7" // Croatia + "MYR" -> "\uD83C\uDDF2\uD83C\uDDFE" // Malaysia + "NOK" -> "\uD83C\uDDF3\uD83C\uDDF4" // Norway + "BGN" -> "\uD83C\uDDE7\uD83C\uDDEC" // Bulgaria + "PHP" -> "\uD83C\uDDF5\uD83C\uDDED" // Philippines + "PLN" -> "\uD83C\uDDF5\uD83C\uDDF1" // Poland + "ZAR" -> "\uD83C\uDDFF\uD83C\uDDE6" // South Africa + "ISK" -> "\uD83C\uDDEE\uD83C\uDDF8" // Iceland + "BRL" -> "\uD83C\uDDE7\uD83C\uDDF7" // Brazil + "RON" -> "\uD83C\uDDF7\uD83C\uDDF4" // Romania + "TRY" -> "\uD83C\uDDF9\uD83C\uDDF7" // Turkey + "RUB" -> "\uD83C\uDDF7\uD83C\uDDFA" // Russia + "KRW" -> "\uD83C\uDDF0\uD83C\uDDF7" // South Korea + "HUF" -> "\uD83C\uDDED\uD83C\uDDFA" // Hungary + + else -> "" + } + } + + suspend fun isValidCurrency(symbol: String): Boolean { + return withContext(Dispatchers.IO) { + AppDatabase.getInstance(context).currencyDao().exists(symbol) + } + } + + suspend fun getLastUpdate(symbol: String): Long { + return withContext(Dispatchers.IO) { + AppDatabase.getInstance(context).currencyDao().getLastUpdate(symbol) + } + } + + companion object { + private lateinit var instance: CurrencyRepository + fun getInstance(context: Context): CurrencyRepository { + if (!::instance.isInitialized) instance = CurrencyRepository(context.applicationContext) + return instance + } + } +} \ No newline at end of file diff --git a/currencies/src/main/java/de/mm20/launcher2/currencies/ExchangeRateWorker.kt b/currencies/src/main/java/de/mm20/launcher2/currencies/ExchangeRateWorker.kt new file mode 100644 index 00000000..65b2bbd6 --- /dev/null +++ b/currencies/src/main/java/de/mm20/launcher2/currencies/ExchangeRateWorker.kt @@ -0,0 +1,56 @@ +package de.mm20.launcher2.currencies + +import android.content.Context +import android.util.Log +import androidx.work.Worker +import androidx.work.WorkerParameters +import de.mm20.launcher2.crashreporter.CrashReporter +import de.mm20.launcher2.database.AppDatabase +import okhttp3.OkHttpClient +import okhttp3.Request +import org.w3c.dom.Element +import java.text.SimpleDateFormat +import javax.xml.parsers.DocumentBuilderFactory + +class ExchangeRateWorker(val context: Context, params: WorkerParameters) : Worker(context, params) { + override fun doWork(): Result { + Log.d("MM20", "Updating currency exchange rates") + val httpClient = OkHttpClient() + val request = Request.Builder() + .url("https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml") + .get() + .build() + try { + val response = httpClient.newCall(request).execute() + val document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(response.body?.byteStream() + ?: return Result.retry()) + val cubes = document.getElementsByTagName("Cube") + val values = mutableListOf>() + var timestamp = System.currentTimeMillis() + values += "EUR" to 1.0 + for (i in 0 until cubes.length) { + val cube = cubes.item(i) as? Element ?: continue + if (cube.hasAttribute("currency")) { + val symbol = cube.getAttribute("currency") + val value = cube.getAttribute("rate").toDoubleOrNull() ?: continue + values += symbol to value + } else if (cube.hasAttribute("time")) { + val date = cube.getAttribute("time") + timestamp = SimpleDateFormat("yyyy-MM-dd").parse(date).time + } + } + val currencies = values.map { + Currency( + symbol = it.first, + value = it.second, + lastUpdate = timestamp + ).toDatabaseEntity() + } + AppDatabase.getInstance(context).currencyDao().insertAll(currencies) + return Result.success() + } catch (e: Exception) { + CrashReporter.logException(e) + return Result.retry() + } + } +} \ No newline at end of file diff --git a/database/.gitignore b/database/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/database/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/database/build.gradle.kts b/database/build.gradle.kts new file mode 100644 index 00000000..ee81a793 --- /dev/null +++ b/database/build.gradle.kts @@ -0,0 +1,57 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") + id("kotlin-kapt") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + + javaCompileOptions { + annotationProcessorOptions { + arguments.put("room.schemaLocation", "$projectDir/schemas") + } + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + + implementation(libs.kotlin.stdlib) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + api(libs.androidx.roomruntime) + kapt(libs.androidx.roomcompiler) + implementation(libs.androidx.room) + + implementation(project(":i18n")) + implementation(project(":ktx")) + +} \ No newline at end of file diff --git a/database/consumer-rules.pro b/database/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/database/proguard-rules.pro b/database/proguard-rules.pro new file mode 100644 index 00000000..01639a19 --- /dev/null +++ b/database/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/database/readme.md b/database/readme.md new file mode 100644 index 00000000..8a61e7d4 --- /dev/null +++ b/database/readme.md @@ -0,0 +1,4 @@ +# :database + +The central database module for everything that needs to be stored in an SQLite database. Uses +AndroidX Room. \ No newline at end of file diff --git a/database/schemas/de.mm20.launcher2.database.AppDatabase/0.json b/database/schemas/de.mm20.launcher2.database.AppDatabase/0.json new file mode 100644 index 00000000..6a0172ee --- /dev/null +++ b/database/schemas/de.mm20.launcher2.database.AppDatabase/0.json @@ -0,0 +1,218 @@ +{ + "formatVersion": 1, + "database": { + "version": 0, + "identityHash": "d7a9ad5e5af58e6f63ac1a9a90f2b6b1", + "entities": [ + { + "tableName": "forecasts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, PRIMARY KEY(`timestamp`))", + "fields": [ + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "minTemp", + "columnName": "minTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "maxTemp", + "columnName": "maxTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pressure", + "columnName": "pressure", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "clouds", + "columnName": "clouds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "rain", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snow", + "columnName": "snow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "night", + "columnName": "night", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "providerUrl", + "columnName": "providerUrl", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "timestamp" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Searchable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `searchable` TEXT NOT NULL, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, `inAllApps` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "searchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "launchCount", + "columnName": "launchCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pinned", + "columnName": "pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "inAllApps", + "columnName": "inAllApps", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Websearch", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlTemplate` TEXT NOT NULL, `label` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, PRIMARY KEY(`urlTemplate`))", + "fields": [ + { + "fieldPath": "urlTemplate", + "columnName": "urlTemplate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "urlTemplate" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"d7a9ad5e5af58e6f63ac1a9a90f2b6b1\")" + ] + } +} \ No newline at end of file diff --git a/database/schemas/de.mm20.launcher2.database.AppDatabase/1.json b/database/schemas/de.mm20.launcher2.database.AppDatabase/1.json new file mode 100644 index 00000000..f0bbc8ab --- /dev/null +++ b/database/schemas/de.mm20.launcher2.database.AppDatabase/1.json @@ -0,0 +1,224 @@ +{ + "formatVersion": 1, + "database": { + "version": 1, + "identityHash": "a28f601f4a9dee37e9a41f895f3ac512", + "entities": [ + { + "tableName": "forecasts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, PRIMARY KEY(`timestamp`))", + "fields": [ + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "minTemp", + "columnName": "minTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "maxTemp", + "columnName": "maxTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pressure", + "columnName": "pressure", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "clouds", + "columnName": "clouds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "rain", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snow", + "columnName": "snow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "night", + "columnName": "night", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "providerUrl", + "columnName": "providerUrl", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "timestamp" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Searchable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `searchable` TEXT NOT NULL, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, `inAllApps` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "searchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "launchCount", + "columnName": "launchCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pinned", + "columnName": "pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "inAllApps", + "columnName": "inAllApps", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Websearch", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlTemplate` TEXT NOT NULL, `label` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "urlTemplate", + "columnName": "urlTemplate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"a28f601f4a9dee37e9a41f895f3ac512\")" + ] + } +} \ No newline at end of file diff --git a/database/schemas/de.mm20.launcher2.database.AppDatabase/10.json b/database/schemas/de.mm20.launcher2.database.AppDatabase/10.json new file mode 100644 index 00000000..33fda801 --- /dev/null +++ b/database/schemas/de.mm20.launcher2.database.AppDatabase/10.json @@ -0,0 +1,380 @@ +{ + "formatVersion": 1, + "database": { + "version": 10, + "identityHash": "05dab1b38f9c52d852630743e48eb4c9", + "entities": [ + { + "tableName": "forecasts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, `rainProbability` INTEGER NOT NULL, `snowProbability` INTEGER NOT NULL, `updateTime` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))", + "fields": [ + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "minTemp", + "columnName": "minTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "maxTemp", + "columnName": "maxTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pressure", + "columnName": "pressure", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "clouds", + "columnName": "clouds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "rain", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snow", + "columnName": "snow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "night", + "columnName": "night", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "providerUrl", + "columnName": "providerUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rainProbability", + "columnName": "rainProbability", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "snowProbability", + "columnName": "snowProbability", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updateTime", + "columnName": "updateTime", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "timestamp" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Searchable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `searchable` TEXT NOT NULL, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, `inAllApps` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serializedSearchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "launchCount", + "columnName": "launchCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pinPosition", + "columnName": "pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "inAllApps", + "columnName": "inAllApps", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Websearch", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlTemplate` TEXT NOT NULL, `label` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "urlTemplate", + "columnName": "urlTemplate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Icons", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `componentName` TEXT, `drawable` TEXT, `iconPack` TEXT NOT NULL, `scale` REAL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "componentName", + "columnName": "componentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "drawable", + "columnName": "drawable", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconPack", + "columnName": "iconPack", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "IconPack", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `version` TEXT NOT NULL, `scale` REAL NOT NULL, PRIMARY KEY(`packageName`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "packageName" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Widget", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `data` TEXT NOT NULL, `height` INTEGER NOT NULL, `position` INTEGER NOT NULL, `label` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "height", + "columnName": "height", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '05dab1b38f9c52d852630743e48eb4c9')" + ] + } +} \ No newline at end of file diff --git a/database/schemas/de.mm20.launcher2.database.AppDatabase/11.json b/database/schemas/de.mm20.launcher2.database.AppDatabase/11.json new file mode 100644 index 00000000..7c353bfe --- /dev/null +++ b/database/schemas/de.mm20.launcher2.database.AppDatabase/11.json @@ -0,0 +1,374 @@ +{ + "formatVersion": 1, + "database": { + "version": 11, + "identityHash": "c9a2ea19df17751b9f0d986ad51beb2a", + "entities": [ + { + "tableName": "forecasts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, `rainProbability` INTEGER NOT NULL, `snowProbability` INTEGER NOT NULL, `updateTime` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))", + "fields": [ + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "minTemp", + "columnName": "minTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "maxTemp", + "columnName": "maxTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pressure", + "columnName": "pressure", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "clouds", + "columnName": "clouds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "rain", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snow", + "columnName": "snow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "night", + "columnName": "night", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "providerUrl", + "columnName": "providerUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rainProbability", + "columnName": "rainProbability", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "snowProbability", + "columnName": "snowProbability", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updateTime", + "columnName": "updateTime", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "timestamp" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Searchable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `searchable` TEXT NOT NULL, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serializedSearchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "launchCount", + "columnName": "launchCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pinPosition", + "columnName": "pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Websearch", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlTemplate` TEXT NOT NULL, `label` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "urlTemplate", + "columnName": "urlTemplate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Icons", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `componentName` TEXT, `drawable` TEXT, `iconPack` TEXT NOT NULL, `scale` REAL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "componentName", + "columnName": "componentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "drawable", + "columnName": "drawable", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconPack", + "columnName": "iconPack", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "IconPack", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `version` TEXT NOT NULL, `scale` REAL NOT NULL, PRIMARY KEY(`packageName`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "packageName" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Widget", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `data` TEXT NOT NULL, `height` INTEGER NOT NULL, `position` INTEGER NOT NULL, `label` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "height", + "columnName": "height", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c9a2ea19df17751b9f0d986ad51beb2a')" + ] + } +} \ No newline at end of file diff --git a/database/schemas/de.mm20.launcher2.database.AppDatabase/12.json b/database/schemas/de.mm20.launcher2.database.AppDatabase/12.json new file mode 100644 index 00000000..a2a7c5e8 --- /dev/null +++ b/database/schemas/de.mm20.launcher2.database.AppDatabase/12.json @@ -0,0 +1,406 @@ +{ + "formatVersion": 1, + "database": { + "version": 12, + "identityHash": "7e492b4e6f9b150fd7a36e3e028650e8", + "entities": [ + { + "tableName": "forecasts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, `rainProbability` INTEGER NOT NULL, `snowProbability` INTEGER NOT NULL, `updateTime` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))", + "fields": [ + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "minTemp", + "columnName": "minTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "maxTemp", + "columnName": "maxTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pressure", + "columnName": "pressure", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "clouds", + "columnName": "clouds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "rain", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snow", + "columnName": "snow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "night", + "columnName": "night", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "providerUrl", + "columnName": "providerUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rainProbability", + "columnName": "rainProbability", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "snowProbability", + "columnName": "snowProbability", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updateTime", + "columnName": "updateTime", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "timestamp" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Searchable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `searchable` TEXT NOT NULL, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serializedSearchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "launchCount", + "columnName": "launchCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pinPosition", + "columnName": "pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Websearch", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlTemplate` TEXT NOT NULL, `label` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "urlTemplate", + "columnName": "urlTemplate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Currency", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`symbol` TEXT NOT NULL, `value` REAL NOT NULL, `lastUpdate` INTEGER NOT NULL, PRIMARY KEY(`symbol`))", + "fields": [ + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "lastUpdate", + "columnName": "lastUpdate", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "symbol" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Icons", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `componentName` TEXT, `drawable` TEXT, `iconPack` TEXT NOT NULL, `scale` REAL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "componentName", + "columnName": "componentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "drawable", + "columnName": "drawable", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconPack", + "columnName": "iconPack", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "IconPack", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `version` TEXT NOT NULL, `scale` REAL NOT NULL, PRIMARY KEY(`packageName`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "packageName" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Widget", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `data` TEXT NOT NULL, `height` INTEGER NOT NULL, `position` INTEGER NOT NULL, `label` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "height", + "columnName": "height", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7e492b4e6f9b150fd7a36e3e028650e8')" + ] + } +} \ No newline at end of file diff --git a/database/schemas/de.mm20.launcher2.database.AppDatabase/13.json b/database/schemas/de.mm20.launcher2.database.AppDatabase/13.json new file mode 100644 index 00000000..531d76df --- /dev/null +++ b/database/schemas/de.mm20.launcher2.database.AppDatabase/13.json @@ -0,0 +1,439 @@ +{ + "formatVersion": 1, + "database": { + "version": 13, + "identityHash": "89c214d548f80f5c25612f5e6ebf9725", + "entities": [ + { + "tableName": "forecasts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, `rainProbability` INTEGER NOT NULL, `snowProbability` INTEGER NOT NULL, `updateTime` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))", + "fields": [ + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "minTemp", + "columnName": "minTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "maxTemp", + "columnName": "maxTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pressure", + "columnName": "pressure", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "clouds", + "columnName": "clouds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "precipitation", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snow", + "columnName": "snow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "night", + "columnName": "night", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "providerUrl", + "columnName": "providerUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "precipProbability", + "columnName": "rainProbability", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "snowProbability", + "columnName": "snowProbability", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updateTime", + "columnName": "updateTime", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "timestamp" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Searchable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `searchable` TEXT NOT NULL, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serializedSearchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "launchCount", + "columnName": "launchCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pinPosition", + "columnName": "pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Websearch", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlTemplate` TEXT NOT NULL, `label` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "urlTemplate", + "columnName": "urlTemplate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Currency", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`symbol` TEXT NOT NULL, `value` REAL NOT NULL, `lastUpdate` INTEGER NOT NULL, PRIMARY KEY(`symbol`))", + "fields": [ + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "lastUpdate", + "columnName": "lastUpdate", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "symbol" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Icons", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `componentName` TEXT, `drawable` TEXT, `iconPack` TEXT NOT NULL, `scale` REAL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "componentName", + "columnName": "componentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "drawable", + "columnName": "drawable", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconPack", + "columnName": "iconPack", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "IconPack", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `version` TEXT NOT NULL, `scale` REAL NOT NULL, PRIMARY KEY(`packageName`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "packageName" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Plugin", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`packageName` TEXT NOT NULL, `data` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`packageName`, `data`))", + "fields": [ + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "packageName", + "data" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Widget", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `data` TEXT NOT NULL, `height` INTEGER NOT NULL, `position` INTEGER NOT NULL, `label` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "height", + "columnName": "height", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '89c214d548f80f5c25612f5e6ebf9725')" + ] + } +} \ No newline at end of file diff --git a/database/schemas/de.mm20.launcher2.database.AppDatabase/14.json b/database/schemas/de.mm20.launcher2.database.AppDatabase/14.json new file mode 100644 index 00000000..111bb63d --- /dev/null +++ b/database/schemas/de.mm20.launcher2.database.AppDatabase/14.json @@ -0,0 +1,439 @@ +{ + "formatVersion": 1, + "database": { + "version": 14, + "identityHash": "89c214d548f80f5c25612f5e6ebf9725", + "entities": [ + { + "tableName": "forecasts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, `rainProbability` INTEGER NOT NULL, `snowProbability` INTEGER NOT NULL, `updateTime` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))", + "fields": [ + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "minTemp", + "columnName": "minTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "maxTemp", + "columnName": "maxTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pressure", + "columnName": "pressure", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "clouds", + "columnName": "clouds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "precipitation", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snow", + "columnName": "snow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "night", + "columnName": "night", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "providerUrl", + "columnName": "providerUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "precipProbability", + "columnName": "rainProbability", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "snowProbability", + "columnName": "snowProbability", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updateTime", + "columnName": "updateTime", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "timestamp" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Searchable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `searchable` TEXT NOT NULL, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serializedSearchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "launchCount", + "columnName": "launchCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pinPosition", + "columnName": "pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Websearch", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlTemplate` TEXT NOT NULL, `label` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "urlTemplate", + "columnName": "urlTemplate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Currency", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`symbol` TEXT NOT NULL, `value` REAL NOT NULL, `lastUpdate` INTEGER NOT NULL, PRIMARY KEY(`symbol`))", + "fields": [ + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "lastUpdate", + "columnName": "lastUpdate", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "symbol" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Icons", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `componentName` TEXT, `drawable` TEXT, `iconPack` TEXT NOT NULL, `scale` REAL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "componentName", + "columnName": "componentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "drawable", + "columnName": "drawable", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconPack", + "columnName": "iconPack", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "IconPack", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `version` TEXT NOT NULL, `scale` REAL NOT NULL, PRIMARY KEY(`packageName`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "packageName" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Plugin", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`packageName` TEXT NOT NULL, `data` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`packageName`, `data`))", + "fields": [ + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "packageName", + "data" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Widget", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `data` TEXT NOT NULL, `height` INTEGER NOT NULL, `position` INTEGER NOT NULL, `label` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "height", + "columnName": "height", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '89c214d548f80f5c25612f5e6ebf9725')" + ] + } +} \ No newline at end of file diff --git a/database/schemas/de.mm20.launcher2.database.AppDatabase/2.json b/database/schemas/de.mm20.launcher2.database.AppDatabase/2.json new file mode 100644 index 00000000..c136d87a --- /dev/null +++ b/database/schemas/de.mm20.launcher2.database.AppDatabase/2.json @@ -0,0 +1,224 @@ +{ + "formatVersion": 1, + "database": { + "version": 2, + "identityHash": "a28f601f4a9dee37e9a41f895f3ac512", + "entities": [ + { + "tableName": "forecasts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, PRIMARY KEY(`timestamp`))", + "fields": [ + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "minTemp", + "columnName": "minTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "maxTemp", + "columnName": "maxTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pressure", + "columnName": "pressure", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "clouds", + "columnName": "clouds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "rain", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snow", + "columnName": "snow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "night", + "columnName": "night", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "providerUrl", + "columnName": "providerUrl", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "timestamp" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Searchable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `searchable` TEXT NOT NULL, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, `inAllApps` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "searchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "launchCount", + "columnName": "launchCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pinned", + "columnName": "pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "inAllApps", + "columnName": "inAllApps", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Websearch", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlTemplate` TEXT NOT NULL, `label` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "urlTemplate", + "columnName": "urlTemplate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"a28f601f4a9dee37e9a41f895f3ac512\")" + ] + } +} \ No newline at end of file diff --git a/database/schemas/de.mm20.launcher2.database.AppDatabase/3.json b/database/schemas/de.mm20.launcher2.database.AppDatabase/3.json new file mode 100644 index 00000000..38c81314 --- /dev/null +++ b/database/schemas/de.mm20.launcher2.database.AppDatabase/3.json @@ -0,0 +1,224 @@ +{ + "formatVersion": 1, + "database": { + "version": 3, + "identityHash": "a28f601f4a9dee37e9a41f895f3ac512", + "entities": [ + { + "tableName": "forecasts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, PRIMARY KEY(`timestamp`))", + "fields": [ + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "minTemp", + "columnName": "minTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "maxTemp", + "columnName": "maxTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pressure", + "columnName": "pressure", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "clouds", + "columnName": "clouds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "rain", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snow", + "columnName": "snow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "night", + "columnName": "night", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "providerUrl", + "columnName": "providerUrl", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "timestamp" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Searchable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `searchable` TEXT NOT NULL, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, `inAllApps` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "searchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "launchCount", + "columnName": "launchCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pinned", + "columnName": "pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "inAllApps", + "columnName": "inAllApps", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Websearch", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlTemplate` TEXT NOT NULL, `label` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "urlTemplate", + "columnName": "urlTemplate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"a28f601f4a9dee37e9a41f895f3ac512\")" + ] + } +} \ No newline at end of file diff --git a/database/schemas/de.mm20.launcher2.database.AppDatabase/4.json b/database/schemas/de.mm20.launcher2.database.AppDatabase/4.json new file mode 100644 index 00000000..727168ed --- /dev/null +++ b/database/schemas/de.mm20.launcher2.database.AppDatabase/4.json @@ -0,0 +1,312 @@ +{ + "formatVersion": 1, + "database": { + "version": 4, + "identityHash": "d557636d2e0f727ae8a980c1eb0395a5", + "entities": [ + { + "tableName": "forecasts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, PRIMARY KEY(`timestamp`))", + "fields": [ + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "minTemp", + "columnName": "minTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "maxTemp", + "columnName": "maxTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pressure", + "columnName": "pressure", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "clouds", + "columnName": "clouds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "rain", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snow", + "columnName": "snow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "night", + "columnName": "night", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "providerUrl", + "columnName": "providerUrl", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "timestamp" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Searchable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `searchable` TEXT NOT NULL, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, `inAllApps` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "searchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "launchCount", + "columnName": "launchCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pinned", + "columnName": "pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "inAllApps", + "columnName": "inAllApps", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Websearch", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlTemplate` TEXT NOT NULL, `label` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "urlTemplate", + "columnName": "urlTemplate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Icons", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `componentName` TEXT, `drawable` TEXT, `iconPack` TEXT NOT NULL, `scale` REAL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "componentName", + "columnName": "componentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "drawable", + "columnName": "drawable", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconPack", + "columnName": "iconPack", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "IconPack", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `version` TEXT NOT NULL, `scale` REAL NOT NULL, PRIMARY KEY(`packageName`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "packageName" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"d557636d2e0f727ae8a980c1eb0395a5\")" + ] + } +} \ No newline at end of file diff --git a/database/schemas/de.mm20.launcher2.database.AppDatabase/5.json b/database/schemas/de.mm20.launcher2.database.AppDatabase/5.json new file mode 100644 index 00000000..3bd89cf2 --- /dev/null +++ b/database/schemas/de.mm20.launcher2.database.AppDatabase/5.json @@ -0,0 +1,312 @@ +{ + "formatVersion": 1, + "database": { + "version": 5, + "identityHash": "d557636d2e0f727ae8a980c1eb0395a5", + "entities": [ + { + "tableName": "forecasts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, PRIMARY KEY(`timestamp`))", + "fields": [ + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "minTemp", + "columnName": "minTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "maxTemp", + "columnName": "maxTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pressure", + "columnName": "pressure", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "clouds", + "columnName": "clouds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "rain", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snow", + "columnName": "snow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "night", + "columnName": "night", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "providerUrl", + "columnName": "providerUrl", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "timestamp" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Searchable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `searchable` TEXT NOT NULL, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, `inAllApps` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "searchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "launchCount", + "columnName": "launchCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pinned", + "columnName": "pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "inAllApps", + "columnName": "inAllApps", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Websearch", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlTemplate` TEXT NOT NULL, `label` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "urlTemplate", + "columnName": "urlTemplate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Icons", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `componentName` TEXT, `drawable` TEXT, `iconPack` TEXT NOT NULL, `scale` REAL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "componentName", + "columnName": "componentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "drawable", + "columnName": "drawable", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconPack", + "columnName": "iconPack", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "IconPack", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `version` TEXT NOT NULL, `scale` REAL NOT NULL, PRIMARY KEY(`packageName`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "packageName" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"d557636d2e0f727ae8a980c1eb0395a5\")" + ] + } +} \ No newline at end of file diff --git a/database/schemas/de.mm20.launcher2.database.AppDatabase/6.json b/database/schemas/de.mm20.launcher2.database.AppDatabase/6.json new file mode 100644 index 00000000..192ed783 --- /dev/null +++ b/database/schemas/de.mm20.launcher2.database.AppDatabase/6.json @@ -0,0 +1,362 @@ +{ + "formatVersion": 1, + "database": { + "version": 6, + "identityHash": "b70a2f4ee8900ba1492884e061351de0", + "entities": [ + { + "tableName": "forecasts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, PRIMARY KEY(`timestamp`))", + "fields": [ + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "minTemp", + "columnName": "minTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "maxTemp", + "columnName": "maxTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pressure", + "columnName": "pressure", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "clouds", + "columnName": "clouds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "rain", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snow", + "columnName": "snow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "night", + "columnName": "night", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "providerUrl", + "columnName": "providerUrl", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "timestamp" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Searchable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `searchable` TEXT NOT NULL, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, `inAllApps` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "searchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "launchCount", + "columnName": "launchCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pinned", + "columnName": "pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "inAllApps", + "columnName": "inAllApps", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Websearch", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlTemplate` TEXT NOT NULL, `label` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "urlTemplate", + "columnName": "urlTemplate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Icons", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `componentName` TEXT, `drawable` TEXT, `iconPack` TEXT NOT NULL, `scale` REAL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "componentName", + "columnName": "componentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "drawable", + "columnName": "drawable", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconPack", + "columnName": "iconPack", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "IconPack", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `version` TEXT NOT NULL, `scale` REAL NOT NULL, PRIMARY KEY(`packageName`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "packageName" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Widget", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `data` TEXT NOT NULL, `height` INTEGER NOT NULL, `position` INTEGER NOT NULL, `label` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "height", + "columnName": "height", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"b70a2f4ee8900ba1492884e061351de0\")" + ] + } +} \ No newline at end of file diff --git a/database/schemas/de.mm20.launcher2.database.AppDatabase/7.json b/database/schemas/de.mm20.launcher2.database.AppDatabase/7.json new file mode 100644 index 00000000..b4047e9b --- /dev/null +++ b/database/schemas/de.mm20.launcher2.database.AppDatabase/7.json @@ -0,0 +1,362 @@ +{ + "formatVersion": 1, + "database": { + "version": 7, + "identityHash": "e5bc7c00f579b2da9ad18dec65a0aae7", + "entities": [ + { + "tableName": "forecasts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, PRIMARY KEY(`timestamp`))", + "fields": [ + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "minTemp", + "columnName": "minTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "maxTemp", + "columnName": "maxTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pressure", + "columnName": "pressure", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "clouds", + "columnName": "clouds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "rain", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snow", + "columnName": "snow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "night", + "columnName": "night", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "providerUrl", + "columnName": "providerUrl", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "timestamp" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Searchable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `searchable` TEXT, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, `inAllApps` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "searchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "launchCount", + "columnName": "launchCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pinned", + "columnName": "pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "inAllApps", + "columnName": "inAllApps", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Websearch", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlTemplate` TEXT NOT NULL, `label` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "urlTemplate", + "columnName": "urlTemplate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Icons", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `componentName` TEXT, `drawable` TEXT, `iconPack` TEXT NOT NULL, `scale` REAL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "componentName", + "columnName": "componentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "drawable", + "columnName": "drawable", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconPack", + "columnName": "iconPack", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "IconPack", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `version` TEXT NOT NULL, `scale` REAL NOT NULL, PRIMARY KEY(`packageName`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "packageName" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Widget", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `data` TEXT NOT NULL, `height` INTEGER NOT NULL, `position` INTEGER NOT NULL, `label` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "height", + "columnName": "height", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"e5bc7c00f579b2da9ad18dec65a0aae7\")" + ] + } +} \ No newline at end of file diff --git a/database/schemas/de.mm20.launcher2.database.AppDatabase/8.json b/database/schemas/de.mm20.launcher2.database.AppDatabase/8.json new file mode 100644 index 00000000..beff3eac --- /dev/null +++ b/database/schemas/de.mm20.launcher2.database.AppDatabase/8.json @@ -0,0 +1,374 @@ +{ + "formatVersion": 1, + "database": { + "version": 8, + "identityHash": "b6cb2687ae974dfa5ab2e437d5d58184", + "entities": [ + { + "tableName": "forecasts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, `rainPropability` INTEGER NOT NULL, `snowProbability` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))", + "fields": [ + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "minTemp", + "columnName": "minTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "maxTemp", + "columnName": "maxTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pressure", + "columnName": "pressure", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "clouds", + "columnName": "clouds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "rain", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snow", + "columnName": "snow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "night", + "columnName": "night", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "providerUrl", + "columnName": "providerUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rainPropability", + "columnName": "rainPropability", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "snowProbability", + "columnName": "snowProbability", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "timestamp" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Searchable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `searchable` TEXT, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, `inAllApps` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "searchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "launchCount", + "columnName": "launchCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pinned", + "columnName": "pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "inAllApps", + "columnName": "inAllApps", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Websearch", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlTemplate` TEXT NOT NULL, `label` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "urlTemplate", + "columnName": "urlTemplate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Icons", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `componentName` TEXT, `drawable` TEXT, `iconPack` TEXT NOT NULL, `scale` REAL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "componentName", + "columnName": "componentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "drawable", + "columnName": "drawable", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconPack", + "columnName": "iconPack", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "IconPack", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `version` TEXT NOT NULL, `scale` REAL NOT NULL, PRIMARY KEY(`packageName`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "packageName" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Widget", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `data` TEXT NOT NULL, `height` INTEGER NOT NULL, `position` INTEGER NOT NULL, `label` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "height", + "columnName": "height", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b6cb2687ae974dfa5ab2e437d5d58184')" + ] + } +} \ No newline at end of file diff --git a/database/schemas/de.mm20.launcher2.database.AppDatabase/9.json b/database/schemas/de.mm20.launcher2.database.AppDatabase/9.json new file mode 100644 index 00000000..37ba3b61 --- /dev/null +++ b/database/schemas/de.mm20.launcher2.database.AppDatabase/9.json @@ -0,0 +1,380 @@ +{ + "formatVersion": 1, + "database": { + "version": 9, + "identityHash": "e6c6e581b9cfb88daecbe977dfdb0d05", + "entities": [ + { + "tableName": "forecasts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, `rainProbability` INTEGER NOT NULL, `snowProbability` INTEGER NOT NULL, `updateTime` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))", + "fields": [ + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "minTemp", + "columnName": "minTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "maxTemp", + "columnName": "maxTemp", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pressure", + "columnName": "pressure", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "clouds", + "columnName": "clouds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "rain", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snow", + "columnName": "snow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "night", + "columnName": "night", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "providerUrl", + "columnName": "providerUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rainProbability", + "columnName": "rainProbability", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "snowProbability", + "columnName": "snowProbability", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updateTime", + "columnName": "updateTime", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "timestamp" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Searchable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `searchable` TEXT, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, `inAllApps` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "searchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "launchCount", + "columnName": "launchCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pinned", + "columnName": "pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "inAllApps", + "columnName": "inAllApps", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Websearch", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlTemplate` TEXT NOT NULL, `label` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "urlTemplate", + "columnName": "urlTemplate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Icons", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `componentName` TEXT, `drawable` TEXT, `iconPack` TEXT NOT NULL, `scale` REAL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "componentName", + "columnName": "componentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "drawable", + "columnName": "drawable", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconPack", + "columnName": "iconPack", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "IconPack", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `version` TEXT NOT NULL, `scale` REAL NOT NULL, PRIMARY KEY(`packageName`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "packageName" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Widget", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `data` TEXT NOT NULL, `height` INTEGER NOT NULL, `position` INTEGER NOT NULL, `label` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "height", + "columnName": "height", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e6c6e581b9cfb88daecbe977dfdb0d05')" + ] + } +} \ No newline at end of file diff --git a/database/src/main/AndroidManifest.xml b/database/src/main/AndroidManifest.xml new file mode 100644 index 00000000..d8973773 --- /dev/null +++ b/database/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + / + \ No newline at end of file diff --git a/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt b/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt new file mode 100644 index 00000000..67cc6f7e --- /dev/null +++ b/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt @@ -0,0 +1,147 @@ +package de.mm20.launcher2.database + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import de.mm20.launcher2.database.entities.* + +@Database(entities = [ForecastEntity::class, + FavoritesItemEntity::class, + WebsearchEntity::class, + CurrencyEntity::class, + IconEntity::class, + IconPackEntity::class, + PluginEntity::class, + WidgetEntity::class], version = 14, exportSchema = true) +@TypeConverters(ComponentNameConverter::class, StringListConverter::class) +abstract class AppDatabase : RoomDatabase() { + + abstract fun weatherDao(): WeatherDao + abstract fun searchDao(): SearchDao + abstract fun iconDao(): IconDao + abstract fun widgetDao(): WidgetDao + abstract fun currencyDao(): CurrencyDao + + companion object { + private var _instance: AppDatabase? = null + fun getInstance(context: Context): AppDatabase { + val instance = _instance + ?: Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "room") + //.fallbackToDestructiveMigration() + .addCallback(object : Callback() { + override fun onCreate(db: SupportSQLiteDatabase) { + super.onCreate(db) + db.execSQL("INSERT INTO Websearch (urlTemplate, label, color, icon) VALUES " + + "('${context.getString(R.string.websearch_google_url)}', '${context.getString(R.string.websearch_google)}', 0xFF4285F4, NULL )," + + "('${context.getString(R.string.websearch_youtube_url)}', '${context.getString(R.string.websearch_youtube)}', 0xFFFF0000, NULL )," + + "('${context.getString(R.string.websearch_playstore_url)}', '${context.getString(R.string.websearch_playstore)}', 0xFF00D3FF, NULL );") + + db.execSQL("INSERT INTO Widget (type, data, height, position, label) VALUES " + + "('internal', 'weather', -1, 0, '${context.getString(R.string.widget_name_weather)}')," + + "('internal', 'music', -1, 1, '${context.getString(R.string.widget_name_music)}')," + + "('internal', 'calendar', -1, 2, '${context.getString(R.string.widget_name_calendar)}');") + } + }) + .addMigrations( + Migration_6_7(), + Migration_7_8(), + Migration_8_9(), + Migration_9_10(), + Migration_10_11(), + Migration_11_12(), + Migration_12_13(), + Migration_13_14() + ).build() + if (_instance == null) _instance = instance + return instance + } + } +} + +class Migration_6_7 : Migration(6, 7) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("CREATE TABLE Searchable2 (`key` TEXT NOT NULL, `searchable` TEXT, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, `inAllApps` INTEGER NOT NULL, PRIMARY KEY(`key`))") + database.execSQL("INSERT INTO Searchable2 SELECT * FROM Searchable") + database.execSQL("DROP TABLE Searchable") + database.execSQL("ALTER TABLE Searchable2 RENAME TO Searchable") + } + +} + +class Migration_7_8 : Migration(7, 8) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("CREATE TABLE IF NOT EXISTS `${ForecastEntity.TABLE_NAME}2` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, `rainPropability` INTEGER NOT NULL, `snowProbability` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))") + database.execSQL("INSERT INTO ${ForecastEntity.TABLE_NAME}2 SELECT *, -1 as rainPropability, -1 as snowPropability FROM ${ForecastEntity.TABLE_NAME}") + database.execSQL("DROP TABLE ${ForecastEntity.TABLE_NAME}") + database.execSQL("ALTER TABLE ${ForecastEntity.TABLE_NAME}2 RENAME TO ${ForecastEntity.TABLE_NAME}") + } +} + +class Migration_8_9 : Migration(8, 9) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("CREATE TABLE IF NOT EXISTS `${ForecastEntity.TABLE_NAME}2` (" + + "`timestamp` INTEGER NOT NULL, " + + "`temperature` REAL NOT NULL, " + + "`minTemp` REAL NOT NULL, " + + "`maxTemp` REAL NOT NULL, " + + "`pressure` REAL NOT NULL, " + + "`humidity` REAL NOT NULL, " + + "`icon` INTEGER NOT NULL, " + + "`condition` TEXT NOT NULL, " + + "`clouds` INTEGER NOT NULL, " + + "`windSpeed` REAL NOT NULL, " + + "`windDirection` REAL NOT NULL, " + + "`rain` REAL NOT NULL, " + + "`snow` REAL NOT NULL, " + + "`night` INTEGER NOT NULL, " + + "`location` TEXT NOT NULL, " + + "`provider` TEXT NOT NULL, " + + "`providerUrl` TEXT NOT NULL, " + + "`rainProbability` INTEGER NOT NULL, " + + "`snowProbability` INTEGER NOT NULL, " + + "`updateTime` INTEGER NOT NULL, " + + "PRIMARY KEY(`timestamp`))") + database.execSQL("INSERT INTO ${ForecastEntity.TABLE_NAME}2 SELECT timestamp, temperature, minTemp, maxTemp, pressure, humidity, icon, condition, clouds, windSpeed, windDirection, rain, snow, night, location, provider, providerUrl, rainPropability as rainProbability, snowProbability, 0 as updateTime FROM ${ForecastEntity.TABLE_NAME}") + database.execSQL("DROP TABLE ${ForecastEntity.TABLE_NAME}") + database.execSQL("ALTER TABLE ${ForecastEntity.TABLE_NAME}2 RENAME TO ${ForecastEntity.TABLE_NAME}") + } + +} + +class Migration_9_10 : Migration(9, 10) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("CREATE TABLE IF NOT EXISTS `Plugins` (`packageName` TEXT NOT NULL, `label` TEXT NOT NULL, `description` TEXT NOT NULL, `pluginClassName` TEXT NOT NULL, `enabled` INTEGER NOT NULL, PRIMARY KEY(`packageName`) );") + } + +} + +class Migration_10_11 : Migration(10, 11) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("CREATE TABLE IF NOT EXISTS `temp` (`key` TEXT NOT NULL, `searchable` TEXT NOT NULL, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, PRIMARY KEY(`key`))") + database.execSQL("INSERT INTO `temp` SELECT `key`, `searchable`, `launchCount`, `pinned`, `hidden` FROM `Searchable`") + database.execSQL("DROP TABLE `Searchable`") + database.execSQL("ALTER TABLE `temp` RENAME TO `Searchable`") + } +} + +class Migration_11_12 : Migration(11, 12) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("CREATE TABLE IF NOT EXISTS `Currency` (`symbol` TEXT NOT NULL, `value` REAL NOT NULL, `lastUpdate` INTEGER NOT NULL, PRIMARY KEY(`symbol`))") + } +} + +class Migration_12_13 : Migration(12, 13) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("CREATE TABLE IF NOT EXISTS `Plugin` (`packageName` TEXT NOT NULL, `data` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`packageName`, `data`))") + } +} +class Migration_13_14 : Migration(13, 14) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS `Plugins`;") + } + +} \ No newline at end of file diff --git a/database/src/main/java/de/mm20/launcher2/database/Converters.kt b/database/src/main/java/de/mm20/launcher2/database/Converters.kt new file mode 100644 index 00000000..4df57adb --- /dev/null +++ b/database/src/main/java/de/mm20/launcher2/database/Converters.kt @@ -0,0 +1,34 @@ +package de.mm20.launcher2.database + +import android.content.ComponentName +import androidx.room.TypeConverter +import org.json.JSONArray + +class ComponentNameConverter { + @TypeConverter + fun toString(componentName: ComponentName?): String? { + return componentName?.flattenToString() + } + + @TypeConverter + fun toComponentName(string: String?) : ComponentName? { + string ?: return null + return ComponentName.unflattenFromString(string) + } + +} + +class StringListConverter { + @TypeConverter + fun toString(list: List): String { + val json = JSONArray() + list.forEach { json.put(it) } + return json.toString() + } + + @TypeConverter + fun toStringList(string: String): List { + val json = JSONArray(string) + return (0..json.length()).map { json.getString(it) } + } +} diff --git a/database/src/main/java/de/mm20/launcher2/database/CurrencyDao.kt b/database/src/main/java/de/mm20/launcher2/database/CurrencyDao.kt new file mode 100644 index 00000000..c5e6e8c0 --- /dev/null +++ b/database/src/main/java/de/mm20/launcher2/database/CurrencyDao.kt @@ -0,0 +1,34 @@ +package de.mm20.launcher2.database + +import androidx.room.* +import de.mm20.launcher2.database.entities.CurrencyEntity + +@Dao +interface CurrencyDao { + + @Query("SELECT value FROM Currency WHERE symbol = :symbol") + fun getExchangeRate(symbol: String) : Double? + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insert(currency: CurrencyEntity) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertAll(currencies: List) + + @Query("SELECT * FROM Currency WHERE symbol = :symbol") + fun getCurrency(symbol: String) : CurrencyEntity? + + @Query("SELECT * FROM Currency WHERE symbol IN (:symbols)") + fun getCurrencies(symbols: List) : List + + @Query("SELECT * FROM Currency") + fun getAllCurrencies() : List + + @Transaction + fun exists(symbol: String): Boolean { + return getCurrency(symbol) != null + } + + @Query("SELECT lastUpdate FROM Currency WHERE symbol = :symbol") + fun getLastUpdate(symbol: String) : Long +} \ No newline at end of file diff --git a/database/src/main/java/de/mm20/launcher2/database/IconDao.kt b/database/src/main/java/de/mm20/launcher2/database/IconDao.kt new file mode 100644 index 00000000..444c8ca3 --- /dev/null +++ b/database/src/main/java/de/mm20/launcher2/database/IconDao.kt @@ -0,0 +1,73 @@ +package de.mm20.launcher2.database + +import androidx.lifecycle.LiveData +import androidx.room.* +import de.mm20.launcher2.database.entities.IconEntity +import de.mm20.launcher2.database.entities.IconPackEntity + +@Dao +interface IconDao { + @Insert + fun insertAll(icons: List) + + @Query("SELECT drawable FROM Icons WHERE componentName = :componentName AND iconPack = :iconPack") + fun getIconName(componentName: String, iconPack: String): String? + + @Query("SELECT * FROM Icons WHERE componentName = :componentName AND iconPack = :iconPack") + fun getIcon(componentName: String, iconPack: String): IconEntity? + + @Query("DELETE FROM Icons WHERE iconPack = :iconPack") + fun deleteIcons(iconPack: String) + + @Transaction + fun installIconPack(iconPack: IconPackEntity, icons: List) { + deleteIconPack(iconPack) + deleteIcons(iconPack.packageName) + insertAll(icons) + installIconPack(iconPack) + } + + @Insert + fun installIconPack(iconPack: IconPackEntity) + + @Query("SELECT * FROM IconPack") + fun getInstalledIconPacks(): List + + @Query("SELECT * FROM IconPack") + fun getInstalledIconPacksLiveData(): LiveData> + + @Delete + fun deleteIconPack(iconPack: IconPackEntity) + + @Query("SELECT * FROM IconPack WHERE packageName = :packageName AND version = :version") + fun getPacks(packageName: String, version: String): List + + @Transaction + fun isInstalled(iconPack: IconPackEntity): Boolean { + return getPacks(iconPack.packageName, iconPack.version).isNotEmpty() + } + + @Query("DELETE FROM Icons WHERE iconPack NOT IN (:packs)") + fun deleteAllIconsExcept(packs: List) + + @Query("DELETE FROM IconPack WHERE packageName NOT IN (:packs)") + fun deleteAllPacksExcept(packs: List) + + @Transaction + fun uninstallIconPacksExcept(packs: List) { + deleteAllIconsExcept(packs) + deleteAllPacksExcept(packs) + } + + @Query("SELECT drawable FROM Icons WHERE iconPack = :pack AND type = 'iconback'") + fun getIconBacks(pack: String): List + + @Query("SELECT drawable FROM Icons WHERE iconPack = :pack AND type = 'iconupon'") + fun getIconUpons(pack: String): List + + @Query("SELECT drawable FROM Icons WHERE iconPack = :pack AND type = 'iconmask'") + fun getIconMasks(pack: String): List + + @Query("SELECT scale FROM IconPack WHERE packageName = :pack") + fun getScale(pack: String): Float? +} \ No newline at end of file diff --git a/database/src/main/java/de/mm20/launcher2/database/SearchDao.kt b/database/src/main/java/de/mm20/launcher2/database/SearchDao.kt new file mode 100644 index 00000000..d66fa64f --- /dev/null +++ b/database/src/main/java/de/mm20/launcher2/database/SearchDao.kt @@ -0,0 +1,120 @@ +package de.mm20.launcher2.database + +import androidx.lifecycle.LiveData +import androidx.room.* +import de.mm20.launcher2.database.entities.FavoritesItemEntity +import de.mm20.launcher2.database.entities.WebsearchEntity + +@Dao +interface SearchDao { + + @Insert() + fun insertAll(items: List) + + @Insert(onConflict = OnConflictStrategy.IGNORE) + fun insertAllSkipExisting(items: List) + + @Insert(onConflict = OnConflictStrategy.IGNORE) + fun insertSkipExisting(items: FavoritesItemEntity) + + @Query("SELECT * FROM Searchable WHERE pinned > 0 ORDER BY pinned DESC, launchCount DESC") + fun getFavorites() : LiveData> + + + @Query("SELECT COUNT(key) as count FROM Searchable WHERE pinned = 1;") + fun getPinCount(): Int + + @Query("SELECT * FROM Searchable WHERE pinned = 0 AND launchCount > 0 AND hidden = 0 ORDER BY launchCount DESC LIMIT :count") + fun getAutoFavorites(count: Int): List + + @Query("DELETE FROM Searchable WHERE `key` IN (:keys)") + fun deleteAll(keys: List) + + + @Query("UPDATE Searchable SET pinned = 1, hidden = 0 WHERE `key` = :key") + fun pinExistingItem(key: String) + + @Transaction + fun pinToFavorites(item: FavoritesItemEntity) { + pinExistingItem(item.key) + insertSkipExisting(item) + } + + @Query("UPDATE Searchable SET pinned = 0 WHERE `key` = :key") + fun unpinFavorite(key: String) + + @Query("DELETE FROM Searchable WHERE `key` = :key") + fun deleteByKey(key: String) + + @Query("UPDATE Searchable SET pinned = 0 WHERE `key` = :key") + fun unpinApp(key: String) + + + @Query("SELECT pinned FROM Searchable WHERE `key` = :key UNION SELECT 0 as pinned ORDER BY pinned DESC LIMIT 1") + fun isPinned(key: String): LiveData + + + @Query("UPDATE Searchable SET hidden = 1, pinned = 0 WHERE `key` = :key") + fun hideExistingItem(key: String) + + @Transaction + fun hideItem(item: FavoritesItemEntity) { + hideExistingItem(item.key) + insertSkipExisting(item) + } + + @Query("UPDATE Searchable SET hidden = 0 WHERE `key` = :key") + fun unhideItem(key: String) + + @Query("SELECT hidden FROM Searchable WHERE `key` = :key UNION SELECT 0 as hidden ORDER BY hidden DESC LIMIT 1") + fun isHidden(key: String): LiveData + + @Query("SELECT `key` FROM SEARCHABLE WHERE hidden = 1") + fun getHiddenItemKeys(): LiveData> + + @Query("SELECT * FROM SEARCHABLE WHERE hidden = 1") + fun getHiddenItems(): LiveData> + + @Query("SELECT * FROM Websearch ORDER BY label ASC") + fun getWebSearches(): List + + @Query("SELECT * FROM Websearch ORDER BY label ASC") + fun getWebSearchesLiveData(): LiveData> + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertWebsearch(websearch: WebsearchEntity) + + @Delete + fun deleteWebsearch(websearch: WebsearchEntity) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertAllWebsearches(websearches: List) + + @Query("UPDATE Searchable SET launchCount = launchCount + 1 WHERE `key` = :key") + fun incrementExistingLaunchCount(key: String) + + @Transaction + fun incrementLaunchCount(item: FavoritesItemEntity) { + incrementExistingLaunchCount(item.key) + insertSkipExisting(item) + } + + @Query("SELECT * FROM Searchable WHERE `key` = :key") + fun getFavorite(key: String): FavoritesItemEntity? + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertReplaceExisting(toDatabaseEntity: FavoritesItemEntity) + + @Query("SELECT * FROM Searchable WHERE (pinned > 0 OR launchCount > 0) AND hidden = 0 ORDER BY pinned DESC, launchCount DESC") + fun getAllFavoriteItems(): List + + @Transaction + fun saveFavorites(favorites: List) { + deleteAllFavorites() + insertAll(favorites) + } + + @Query("DELETE FROM Searchable WHERE hidden = 0") + fun deleteAllFavorites() + +} diff --git a/database/src/main/java/de/mm20/launcher2/database/WeatherDao.kt b/database/src/main/java/de/mm20/launcher2/database/WeatherDao.kt new file mode 100644 index 00000000..9bd7bfc7 --- /dev/null +++ b/database/src/main/java/de/mm20/launcher2/database/WeatherDao.kt @@ -0,0 +1,27 @@ +package de.mm20.launcher2.database + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy.REPLACE +import androidx.room.Query +import androidx.room.Transaction +import de.mm20.launcher2.database.entities.ForecastEntity + +@Dao +interface WeatherDao { + @Query("SELECT * FROM ${ForecastEntity.TABLE_NAME} ORDER BY timestamp ASC") + fun getForecasts(): LiveData> + + @Insert(onConflict = REPLACE) + fun insertAll(forecasts: List) + + @Query("DELETE FROM ${ForecastEntity.TABLE_NAME}") + fun deleteAll() + + @Transaction + fun replaceAll(forecasts: List) { + deleteAll() + insertAll(forecasts) + } +} \ No newline at end of file diff --git a/database/src/main/java/de/mm20/launcher2/database/WidgetDao.kt b/database/src/main/java/de/mm20/launcher2/database/WidgetDao.kt new file mode 100644 index 00000000..ef317713 --- /dev/null +++ b/database/src/main/java/de/mm20/launcher2/database/WidgetDao.kt @@ -0,0 +1,23 @@ +package de.mm20.launcher2.database + +import androidx.lifecycle.LiveData +import androidx.room.* +import de.mm20.launcher2.database.entities.WidgetEntity + +@Dao +interface WidgetDao { + @Query("SELECT * FROM Widget ORDER BY position ASC") + fun getWidgets(): List + + @Transaction + fun updateWidgets(widgets: List) { + deleteAll() + insertAll(widgets) + } + + @Insert + fun insertAll(widgets: List) + + @Query("DELETE FROM Widget") + fun deleteAll() +} \ No newline at end of file diff --git a/database/src/main/java/de/mm20/launcher2/database/entities/CurrencyEntity.kt b/database/src/main/java/de/mm20/launcher2/database/entities/CurrencyEntity.kt new file mode 100644 index 00000000..be95bb83 --- /dev/null +++ b/database/src/main/java/de/mm20/launcher2/database/entities/CurrencyEntity.kt @@ -0,0 +1,11 @@ +package de.mm20.launcher2.database.entities + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "Currency") +data class CurrencyEntity( + @PrimaryKey val symbol: String, + val value: Double, + val lastUpdate: Long +) \ No newline at end of file diff --git a/database/src/main/java/de/mm20/launcher2/database/entities/FavoritesItemEntity.kt b/database/src/main/java/de/mm20/launcher2/database/entities/FavoritesItemEntity.kt new file mode 100644 index 00000000..03a54ca7 --- /dev/null +++ b/database/src/main/java/de/mm20/launcher2/database/entities/FavoritesItemEntity.kt @@ -0,0 +1,14 @@ +package de.mm20.launcher2.database.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "Searchable") +data class FavoritesItemEntity( + @PrimaryKey val key: String, + @ColumnInfo(name = "searchable") val serializedSearchable: String, + var launchCount: Int, + @ColumnInfo(name = "pinned") var pinPosition: Int, + var hidden: Boolean +) diff --git a/database/src/main/java/de/mm20/launcher2/database/entities/ForecastEntity.kt b/database/src/main/java/de/mm20/launcher2/database/entities/ForecastEntity.kt new file mode 100644 index 00000000..7304ec83 --- /dev/null +++ b/database/src/main/java/de/mm20/launcher2/database/entities/ForecastEntity.kt @@ -0,0 +1,33 @@ +package de.mm20.launcher2.database.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = ForecastEntity.TABLE_NAME) +data class ForecastEntity( + @PrimaryKey val timestamp: Long, + val temperature: Double, + val minTemp: Double = -1.0, + val maxTemp: Double = -1.0, + val pressure: Double = -1.0, + val humidity: Double = -1.0, + val icon: Int, + val condition: String, + val clouds: Int = -1, + val windSpeed: Double = -1.0, + val windDirection: Double = -1.0, + @ColumnInfo(name = "rain") val precipitation: Double = -1.0, + val snow: Double = -1.0, + val night: Boolean = false, + val location: String, + val provider: String, + val providerUrl: String = "", + @ColumnInfo(name = "rainProbability") val precipProbability: Int = -1, + val snowProbability: Int = -1, + val updateTime: Long +) { + companion object { + const val TABLE_NAME = "forecasts" + } +} \ No newline at end of file diff --git a/database/src/main/java/de/mm20/launcher2/database/entities/IconEntity.kt b/database/src/main/java/de/mm20/launcher2/database/entities/IconEntity.kt new file mode 100644 index 00000000..f5564e88 --- /dev/null +++ b/database/src/main/java/de/mm20/launcher2/database/entities/IconEntity.kt @@ -0,0 +1,15 @@ +package de.mm20.launcher2.database.entities + +import android.content.ComponentName +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "Icons") +data class IconEntity( + val type: String, + val componentName: ComponentName?, + val drawable: String?, + val iconPack: String, + val scale : Float? = null, + @PrimaryKey(autoGenerate = true) val id : Long? = null +) \ No newline at end of file diff --git a/database/src/main/java/de/mm20/launcher2/database/entities/IconPackEntity.kt b/database/src/main/java/de/mm20/launcher2/database/entities/IconPackEntity.kt new file mode 100644 index 00000000..0c54c17a --- /dev/null +++ b/database/src/main/java/de/mm20/launcher2/database/entities/IconPackEntity.kt @@ -0,0 +1,12 @@ +package de.mm20.launcher2.database.entities + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "IconPack") +data class IconPackEntity( + val name: String, + @PrimaryKey val packageName: String, + val version: String, + var scale: Float = 1f +) \ No newline at end of file diff --git a/database/src/main/java/de/mm20/launcher2/database/entities/PluginEntity.kt b/database/src/main/java/de/mm20/launcher2/database/entities/PluginEntity.kt new file mode 100644 index 00000000..f43e7188 --- /dev/null +++ b/database/src/main/java/de/mm20/launcher2/database/entities/PluginEntity.kt @@ -0,0 +1,10 @@ +package de.mm20.launcher2.database.entities + +import androidx.room.Entity + +@Entity(tableName = "Plugin", primaryKeys = ["packageName", "data"]) +data class PluginEntity( + val packageName: String, + val data: String, + val type: String +) \ No newline at end of file diff --git a/database/src/main/java/de/mm20/launcher2/database/entities/WebsearchEntity.kt b/database/src/main/java/de/mm20/launcher2/database/entities/WebsearchEntity.kt new file mode 100644 index 00000000..5a14a97a --- /dev/null +++ b/database/src/main/java/de/mm20/launcher2/database/entities/WebsearchEntity.kt @@ -0,0 +1,13 @@ +package de.mm20.launcher2.database.entities + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "Websearch") +data class WebsearchEntity( + var urlTemplate: String, + var label: String, + var color: Int, + var icon: String?, + @PrimaryKey(autoGenerate = true) val id: Long? +) \ No newline at end of file diff --git a/database/src/main/java/de/mm20/launcher2/database/entities/WidgetEntity.kt b/database/src/main/java/de/mm20/launcher2/database/entities/WidgetEntity.kt new file mode 100644 index 00000000..97bbe8cf --- /dev/null +++ b/database/src/main/java/de/mm20/launcher2/database/entities/WidgetEntity.kt @@ -0,0 +1,15 @@ +package de.mm20.launcher2.database.entities + +import androidx.room.Entity +import androidx.room.PrimaryKey + + +@Entity(tableName = "Widget") +data class WidgetEntity( + val type: String, + var data: String, + var height: Int, + var position: Int, + val label: String = "", + @PrimaryKey(autoGenerate = true) val id: Int? = null +) \ No newline at end of file diff --git a/favorites/.gitignore b/favorites/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/favorites/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/favorites/build.gradle.kts b/favorites/build.gradle.kts new file mode 100644 index 00000000..88c08a8c --- /dev/null +++ b/favorites/build.gradle.kts @@ -0,0 +1,54 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.bundles.kotlin) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + + implementation(project(":search")) + implementation(project(":calendar")) + implementation(project(":database")) + implementation(project(":preferences")) + implementation(project(":applications")) + implementation(project(":contacts")) + implementation(project(":ktx")) + implementation(project(":files")) + implementation(project(":websites")) + implementation(project(":wikipedia")) + +} \ No newline at end of file diff --git a/favorites/consumer-rules.pro b/favorites/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/favorites/proguard-rules.pro b/favorites/proguard-rules.pro new file mode 100644 index 00000000..6f610ec0 --- /dev/null +++ b/favorites/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/favorites/src/main/AndroidManifest.xml b/favorites/src/main/AndroidManifest.xml new file mode 100644 index 00000000..ec0d4793 --- /dev/null +++ b/favorites/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesItem.kt b/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesItem.kt new file mode 100644 index 00000000..fa5914b4 --- /dev/null +++ b/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesItem.kt @@ -0,0 +1,35 @@ +package de.mm20.launcher2.favorites + +import android.content.Context +import de.mm20.launcher2.database.entities.FavoritesItemEntity +import de.mm20.launcher2.search.data.Searchable + +data class FavoritesItem( + val key: String, + /** + * null if searchable could not be deserialized (i.e. the app has been uninstalled) + */ + val searchable: Searchable?, + var launchCount: Int, + var pinPosition: Int, + var hidden: Boolean +){ + constructor(context: Context, entity: FavoritesItemEntity) : this( + key = entity.key, + searchable = SearchableDeserializer(context).deserialize(entity.serializedSearchable), + launchCount = entity.launchCount, + pinPosition = entity.pinPosition, + hidden = entity.hidden + ) + + + fun toDatabaseEntity(): FavoritesItemEntity { + return FavoritesItemEntity( + key = key, + serializedSearchable = searchable?.let { "${SearchableDeserializer.getTypePrefix(it)}#${it.serialize()}" } ?: "", + hidden = hidden, + pinPosition = pinPosition, + launchCount = launchCount + ) + } +} \ No newline at end of file diff --git a/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesRepository.kt b/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesRepository.kt new file mode 100644 index 00000000..0bd8861c --- /dev/null +++ b/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesRepository.kt @@ -0,0 +1,218 @@ +package de.mm20.launcher2.favorites + +import android.content.Context +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.MutableLiveData +import de.mm20.launcher2.database.AppDatabase +import de.mm20.launcher2.database.entities.FavoritesItemEntity +import de.mm20.launcher2.ktx.ceilToInt +import de.mm20.launcher2.preferences.LauncherPreferences +import de.mm20.launcher2.search.BaseSearchableRepository +import de.mm20.launcher2.search.data.CalendarEvent +import de.mm20.launcher2.search.data.Searchable +import kotlinx.coroutines.* +import kotlin.math.max +import kotlin.math.min + +class FavoritesRepository private constructor(private val context: Context) : BaseSearchableRepository() { + + private val scope = CoroutineScope(Job() + Dispatchers.Main) + + private val favorites = MediatorLiveData>() + private val favoriteItems: LiveData> = MutableLiveData() + + val hiddenItems = MediatorLiveData>() + + private val pinnedFavorites = AppDatabase.getInstance(context).searchDao().getFavorites() + + + val pinnedCalendarEvents = MediatorLiveData>() + + private val reloadFavorites: (String) -> Unit = { + scope.launch { + if(!LauncherPreferences.instance.searchShowFavorites) { + favorites.value = emptyList() + return@launch + } + val favs = mutableListOf() + withContext(Dispatchers.IO) { + val dao = AppDatabase.getInstance(context).searchDao() + val favItems = pinnedFavorites.value ?: emptyList() + favs.addAll(favItems.mapNotNull { + val item = FavoritesItem(context, it) + if (item.searchable == null) { + dao.deleteByKey(item.key) + } + if (item.searchable is CalendarEvent) return@mapNotNull null + item.searchable + }) + var favCount = (favs.size.toDouble() / columns).ceilToInt() * columns + if(favItems.size < columns) favCount += columns + val autoFavs = dao.getAutoFavorites(favCount - favs.size) + favs.addAll(autoFavs.mapNotNull { + val item = FavoritesItem(context, it) + if (item.searchable == null) { + dao.deleteByKey(item.key) + } + item.searchable + }) + } + favorites.value = favs + } + } + + private var columns = 1 + + init { + val hidden = AppDatabase.getInstance(context).searchDao().getHiddenItems() + hiddenItems.addSource(hidden) { h -> + hiddenItems.value = h.mapNotNull { FavoritesItem(context, it).searchable } + } + favorites.addSource(pinnedFavorites) { + reloadFavorites("") + } + pinnedCalendarEvents.addSource(pinnedFavorites) { + scope.launch { + val dao = AppDatabase.getInstance(context).searchDao() + pinnedCalendarEvents.value = it.filter { it.key.startsWith("calendar://") }.mapNotNull { + val item = FavoritesItem(context, it) + if (item.searchable == null) { + withContext(Dispatchers.IO) { dao.deleteByKey(item.key) } + } + item.searchable as? CalendarEvent + } + } + } + LauncherPreferences.instance.doOnPreferenceChange( + "search_show_favorites", + "search_auto_add_favorites", + action = reloadFavorites + ) + } + + + fun isHidden(searchable: Searchable): LiveData { + return AppDatabase.getInstance(context).searchDao().isHidden(searchable.key) + } + + fun getFavorites(columns: Int): LiveData> { + if (columns != this.columns) { + this.columns = columns + reloadFavorites("") + } + return favorites + } + + override suspend fun search(query: String) { + if (query.isEmpty()) { + reloadFavorites("") + } else { + favorites.value = emptyList() + } + } + + fun isPinned(searchable: Searchable): LiveData { + return AppDatabase.getInstance(context).searchDao().isPinned(searchable.key) + } + + fun pinItem(searchable: Searchable) { + scope.launch { + withContext(Dispatchers.IO) { + val dao = AppDatabase.getInstance(context).searchDao() + val databaseItem = dao.getFavorite(searchable.key) + val favoritesItem = FavoritesItem( + key = searchable.key, + searchable = searchable, + launchCount = databaseItem?.launchCount ?: 0, + pinPosition = 1, + hidden = false + ) + dao.insertReplaceExisting(favoritesItem.toDatabaseEntity()) + } + } + } + + fun unpinItem(searchable: Searchable) { + scope.launch { + withContext(Dispatchers.IO) { + AppDatabase.getInstance(context).searchDao().unpinFavorite(searchable.key) + } + } + } + + fun hideItem(searchable: Searchable) { + scope.launch { + withContext(Dispatchers.IO) { + val dao = AppDatabase.getInstance(context).searchDao() + val databaseItem = dao.getFavorite(searchable.key) + val favoritesItem = FavoritesItem( + key = searchable.key, + searchable = searchable, + launchCount = databaseItem?.launchCount ?: 0, + pinPosition = 0, + hidden = true + ) + dao.insertReplaceExisting(favoritesItem.toDatabaseEntity()) + } + } + } + + fun unhideItem(searchable: Searchable) { + scope.launch { + withContext(Dispatchers.IO) { + AppDatabase.getInstance(context).searchDao().unhideItem(searchable.key) + } + } + } + + fun deleteItem(key: String) { + scope.launch { + withContext(Dispatchers.IO) { + AppDatabase.getInstance(context).searchDao().deleteByKey(key) + } + } + } + + fun incrementLaunchCount(searchable: Searchable) { + scope.launch { + withContext(Dispatchers.IO) { + val item = FavoritesItem(searchable.key, searchable, 0, 0, false) + AppDatabase.getInstance(context).searchDao().incrementLaunchCount(item.toDatabaseEntity()) + } + } + } + + suspend fun getAllFavoriteItems(): List { + return withContext(Dispatchers.IO) { + AppDatabase.getInstance(context).searchDao().getAllFavoriteItems().mapNotNull { + FavoritesItem(context, it).takeIf { it.searchable != null } + } + } + } + + fun saveFavorites(favorites: MutableList) { + scope.launch { + withContext(Dispatchers.IO) { + AppDatabase.getInstance(context).searchDao().saveFavorites(favorites.map { it.toDatabaseEntity() }) + } + } + } + + fun getTopFavorites(count: Int): LiveData> { + val favs = MediatorLiveData>() + favs.addSource(favorites) { + favs.value = it.subList(0, min(count, it.size)) + } + return favs + } + + companion object { + private lateinit var instance: FavoritesRepository + fun getInstance(context: Context): FavoritesRepository { + if (!::instance.isInitialized) instance = FavoritesRepository(context.applicationContext) + return instance + } + } +} \ No newline at end of file diff --git a/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesViewModel.kt b/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesViewModel.kt new file mode 100644 index 00000000..18adeb51 --- /dev/null +++ b/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesViewModel.kt @@ -0,0 +1,58 @@ +package de.mm20.launcher2.favorites + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData +import de.mm20.launcher2.search.data.CalendarEvent +import de.mm20.launcher2.search.data.Searchable +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class FavoritesViewModel(app: Application) : AndroidViewModel(app) { + + val repository = FavoritesRepository.getInstance(app) + + fun getTopFavorites(count: Int): LiveData> { + return repository.getTopFavorites(count) + } + + fun getFavorites(columns: Int): LiveData> { + return repository.getFavorites(columns) + } + + fun pinItem(searchable: Searchable) { + repository.pinItem(searchable) + } + + fun unpinItem(searchable: Searchable) { + repository.unpinItem(searchable) + } + + fun isPinned(searchable: Searchable): LiveData { + return repository.isPinned(searchable) + } + + fun isHidden(searchable: Searchable): LiveData { + return repository.isHidden(searchable) + } + + fun hideItem(searchable: Searchable) { + repository.hideItem(searchable) + } + + fun unhideItem(searchable: Searchable) { + repository.unhideItem(searchable) + } + + suspend fun getAllFavoriteItems(): List { + return repository.getAllFavoriteItems() + } + + fun saveFavorites(favorites: MutableList) { + repository.saveFavorites(favorites) + } + + val hiddenItems: LiveData> = repository.hiddenItems + val pinnedCalendarEvents: LiveData> = repository.pinnedCalendarEvents +} \ No newline at end of file diff --git a/favorites/src/main/java/de/mm20/launcher2/favorites/SearchableDeserializer.kt b/favorites/src/main/java/de/mm20/launcher2/favorites/SearchableDeserializer.kt new file mode 100644 index 00000000..1363003c --- /dev/null +++ b/favorites/src/main/java/de/mm20/launcher2/favorites/SearchableDeserializer.kt @@ -0,0 +1,47 @@ +package de.mm20.launcher2.favorites + +import android.content.Context +import android.util.Log +import de.mm20.launcher2.search.data.* + +class SearchableDeserializer(val context: Context) { + fun deserialize(serialized: String?): Searchable? { + val type = serialized?.substringBefore("#") ?: return null + val data = serialized.substringAfter("#") + return when (type) { + "app" -> LauncherApp.deserialize(context, data) + "shortcut" -> AppShortcut.deserialize(context, data) + "calculator" -> null + "calendar" -> CalendarEvent.deserialize(context, data) + "contact" -> Contact.deserialize(context, data) + "gdrive" -> GDriveFile.deserialize(data) + "owncloud" -> OwncloudFile.deserialize(data) + "nextcloud" -> NextcloudFile.deserialize(data) + "file" -> File.deserialize(context, data) + "onedrive" -> OneDriveFile.deserialize(data) + "websearch" -> null + "website" -> Website.deserialize(data) + "wikipedia" -> Wikipedia.deserialize(data) + else -> null + } + } + + companion object { + fun getTypePrefix(searchable: Searchable): String { + return when(searchable) { + is Application -> "app" + is AppShortcut -> "shortcut" + is CalendarEvent -> "calendar" + is Contact -> "contact" + is GDriveFile -> "gdrive" + is OneDriveFile -> "onedrive" + is NextcloudFile -> "nextcloud" + is OwncloudFile -> "owncloud" + is File -> "file" + is Website -> "website" + is Wikipedia -> "wikipedia" + else -> "" + } + } + } +} diff --git a/files/.gitignore b/files/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/files/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/files/build.gradle.kts b/files/build.gradle.kts new file mode 100644 index 00000000..f0e66d51 --- /dev/null +++ b/files/build.gradle.kts @@ -0,0 +1,57 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.bundles.kotlin) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.exifinterface) + + implementation(libs.bundles.androidx.lifecycle) + + implementation(project(":search")) + implementation(project(":hiddenitems")) + implementation(project(":preferences")) + implementation(project(":base")) + implementation(project(":ktx")) + implementation(project(":ms-services")) + implementation(project(":g-services")) + implementation(project(":nextcloud")) + implementation(project(":owncloud")) + implementation(project(":i18n")) + implementation(project(":permissions")) +} \ No newline at end of file diff --git a/files/consumer-rules.pro b/files/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/files/proguard-rules.pro b/files/proguard-rules.pro new file mode 100644 index 00000000..35280e32 --- /dev/null +++ b/files/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/files/src/main/AndroidManifest.xml b/files/src/main/AndroidManifest.xml new file mode 100644 index 00000000..b5b954c3 --- /dev/null +++ b/files/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + / + \ No newline at end of file diff --git a/files/src/main/java/de/mm20/launcher2/files/FilesRepository.kt b/files/src/main/java/de/mm20/launcher2/files/FilesRepository.kt new file mode 100644 index 00000000..56bc12c8 --- /dev/null +++ b/files/src/main/java/de/mm20/launcher2/files/FilesRepository.kt @@ -0,0 +1,70 @@ +package de.mm20.launcher2.files + +import android.content.Context +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.MutableLiveData +import de.mm20.launcher2.hiddenitems.HiddenItemsRepository +import de.mm20.launcher2.nextcloud.NextcloudApiHelper +import de.mm20.launcher2.owncloud.OwncloudClient +import de.mm20.launcher2.search.BaseSearchableRepository +import de.mm20.launcher2.search.data.* +import kotlinx.coroutines.* + +class FilesRepository private constructor(val context: Context) : BaseSearchableRepository() { + + val files = MediatorLiveData?>() + + private val allFiles = MutableLiveData?>(emptyList()) + private val hiddenItemKeys = HiddenItemsRepository.getInstance(context).hiddenItemsKeys + + private val nextcloudClient by lazy { + NextcloudApiHelper(context) + } + private val owncloudClient by lazy { + OwncloudClient(context) + } + + init { + files.addSource(hiddenItemKeys) { keys -> + files.value = allFiles.value?.filter { !keys.contains(it.key) } + } + files.addSource(allFiles) { f -> + files.value = f?.filter { hiddenItemKeys.value?.contains(it.key) != true } + } + } + + override suspend fun search(query: String) { + if (query.isBlank()) { + allFiles.value = null + return + } + val localFiles = withContext(Dispatchers.IO) { + File.search(context, query).sorted().toMutableList() + } + allFiles.value = localFiles + + val cloudFiles = withContext(Dispatchers.IO) { + delay(300) + listOf( + async { OneDriveFile.search(context, query) }, + async { GDriveFile.search(context, query) }, + async { NextcloudFile.search(context, query, nextcloudClient) }, + async { OwncloudFile.search(context, query, owncloudClient) } + ).awaitAll().flatten() + } + yield() + allFiles.value = localFiles + cloudFiles + } + + fun removeFile(file: File) { + allFiles.value = allFiles.value?.filter { it != file } + } + + companion object { + private lateinit var instance: FilesRepository + fun getInstance(context: Context): FilesRepository { + if (!::instance.isInitialized) instance = FilesRepository(context.applicationContext) + return instance + } + } +} \ No newline at end of file diff --git a/files/src/main/java/de/mm20/launcher2/files/FilesViewModel.kt b/files/src/main/java/de/mm20/launcher2/files/FilesViewModel.kt new file mode 100644 index 00000000..ef3e91b9 --- /dev/null +++ b/files/src/main/java/de/mm20/launcher2/files/FilesViewModel.kt @@ -0,0 +1,16 @@ +package de.mm20.launcher2.files + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import de.mm20.launcher2.search.data.File + +class FilesViewModel(app: Application): AndroidViewModel(app) { + + + private val repository = FilesRepository.getInstance(app) + val files = repository.files + + fun removeFile(file: File) { + repository.removeFile(file) + } +} \ No newline at end of file diff --git a/files/src/main/java/de/mm20/launcher2/media/ThumbnailUtilsCompat.kt b/files/src/main/java/de/mm20/launcher2/media/ThumbnailUtilsCompat.kt new file mode 100644 index 00000000..7d955347 --- /dev/null +++ b/files/src/main/java/de/mm20/launcher2/media/ThumbnailUtilsCompat.kt @@ -0,0 +1,26 @@ +package de.mm20.launcher2.media + +import android.graphics.Bitmap +import android.media.ThumbnailUtils +import android.os.Build +import android.os.CancellationSignal +import android.provider.MediaStore +import android.util.Size +import androidx.core.content.ContentResolverCompat +import java.io.File +import java.io.IOException + +object ThumbnailUtilsCompat { + fun createVideoThumbnail(file: File, size: Size, signal: CancellationSignal? = null): Bitmap? { + return try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + ThumbnailUtils.createVideoThumbnail(file, size, signal) + } else { + ThumbnailUtils.createVideoThumbnail(file.absolutePath, + MediaStore.Video.Thumbnails.MICRO_KIND) + } + } catch (e: IOException) { + null + } + } +} \ No newline at end of file diff --git a/files/src/main/java/de/mm20/launcher2/search/data/File.kt b/files/src/main/java/de/mm20/launcher2/search/data/File.kt new file mode 100644 index 00000000..bb548a68 --- /dev/null +++ b/files/src/main/java/de/mm20/launcher2/search/data/File.kt @@ -0,0 +1,403 @@ +package de.mm20.launcher2.search.data + +import android.Manifest +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.database.sqlite.SQLiteDatabase +import android.database.sqlite.SQLiteQueryBuilder +import android.graphics.BitmapFactory +import android.graphics.drawable.AdaptiveIconDrawable +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.ColorDrawable +import android.location.Geocoder +import android.media.MediaMetadataRetriever +import android.media.ThumbnailUtils +import android.net.Uri +import android.os.Build +import android.provider.MediaStore +import android.text.format.DateUtils +import android.util.Size +import androidx.core.content.ContentResolverCompat +import androidx.core.content.ContextCompat +import androidx.core.content.FileProvider +import androidx.core.database.getStringOrNull +import androidx.exifinterface.media.ExifInterface +import de.mm20.launcher2.files.R +import de.mm20.launcher2.ktx.checkPermission +import de.mm20.launcher2.ktx.formatToString +import de.mm20.launcher2.ktx.jsonObjectOf +import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.media.ThumbnailUtilsCompat +import de.mm20.launcher2.permissions.PermissionsManager +import de.mm20.launcher2.preferences.LauncherPreferences +import org.json.JSONObject +import java.io.IOException +import java.util.* +import java.io.File as JavaIOFile + +open class File( + val id: Long, + val path: String, + val mimeType: String, + val size: Long, + val isDirectory: Boolean, + val metaData: List> +) : Searchable() { + + override val label = path.substringAfterLast('/') + + override val key = "file://$path" + + open val isStoredInCloud = false + + override suspend fun loadIconAsync(context: Context, size: Int): LauncherIcon? { + if (!JavaIOFile(path).exists()) return null + when { + mimeType.startsWith("image/") -> { + val thumbnail = ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(path), + size, size) ?: return null + return LauncherIcon( + foreground = BitmapDrawable(context.resources, thumbnail), + autoGenerateBackgroundMode = LauncherIcon.BACKGROUND_COLOR + ) + } + mimeType.startsWith("video/") -> { + val thumbnail = ThumbnailUtilsCompat.createVideoThumbnail(JavaIOFile(path), + Size(size, size)) ?: return null + return LauncherIcon( + foreground = BitmapDrawable(context.resources, thumbnail), + autoGenerateBackgroundMode = LauncherIcon.BACKGROUND_COLOR + ) + } + mimeType.startsWith("audio/") -> { + val mediaMetadataRetriever = MediaMetadataRetriever() + try { + mediaMetadataRetriever.setDataSource(path) + val thumbData = mediaMetadataRetriever.embeddedPicture + if (thumbData != null) { + val thumbnail = ThumbnailUtils.extractThumbnail( + BitmapFactory.decodeByteArray(thumbData, 0, thumbData.size), size, size) + mediaMetadataRetriever.release() + thumbnail ?: return null + return LauncherIcon( + foreground = BitmapDrawable(context.resources, thumbnail), + autoGenerateBackgroundMode = LauncherIcon.BACKGROUND_COLOR + ) + } + } catch (e: RuntimeException) { + mediaMetadataRetriever.release() + return null + } + } + mimeType == "application/vnd.android.package-archive" -> { + val pkgInfo = context.packageManager.getPackageArchiveInfo(path, 0) + val icon = pkgInfo?.applicationInfo?.loadIcon(context.packageManager) ?: return null + when { + Build.VERSION.SDK_INT > Build.VERSION_CODES.O && icon is AdaptiveIconDrawable -> { + return LauncherIcon( + foreground = icon.foreground, + background = icon.background, + foregroundScale = 1.5f, + backgroundScale = 1.5f + ) + } + else -> { + return LauncherIcon( + foreground = icon, + foregroundScale = 0.7f, + autoGenerateBackgroundMode = LauncherIcon.BACKGROUND_COLOR + ) + } + } + } + } + return null + } + + override fun getPlaceholderIcon(context: Context): LauncherIcon { + val (resId, bgColor) = when { + isDirectory -> R.drawable.ic_file_folder to R.color.lightblue + mimeType.startsWith("image/") -> R.drawable.ic_file_picture to R.color.teal + mimeType.startsWith("audio/") -> R.drawable.ic_file_music to R.color.orange + mimeType.startsWith("video/") -> R.drawable.ic_file_video to R.color.purple + else -> when (mimeType) { + "application/zip", "application/x-gtar", "application/x-tar", + "application/java-archive", "application/x-7z-compressed", + "application/x-compressed-tar", "application/x-gzip", "application/x-bzip2" -> R.drawable.ic_file_archive to R.color.brown + "application/pdf" -> R.drawable.ic_file_pdf to R.color.red + "application/vnd.oasis.opendocument.text", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/msword", "text/plain", "application/vnd.google-apps.document" -> R.drawable.ic_file_document to R.color.blue + "application/vnd.oasis.opendocument.spreadsheet", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.ms-excel", "application/vnd.google-apps.spreadsheet" -> R.drawable.ic_file_spreadsheet to R.color.green + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/vnd.ms-powerpoint", "application/vnd.google-apps.presentation" -> R.drawable.ic_file_presentation to R.color.amber + "text/x-asm", "text/x-c", "text/x-java-source", "text/x-script.phyton", "text/x-pascal", + "text/x-script.perl", "text/javascript", "application/json" -> R.drawable.ic_file_code to R.color.pink + "text/xml", "text/html" -> R.drawable.ic_file_markup to R.color.deeporange + "application/vnd.android.package-archive" -> R.drawable.ic_file_android to R.color.lightgreen + "application/vnd.google-apps.form" -> R.drawable.ic_file_form to R.color.deeppurple + "application/vnd.google-apps.drawing" -> R.drawable.ic_file_picture to R.color.teal + else -> R.drawable.ic_file_generic to R.color.bluegrey + } + } + return LauncherIcon( + foreground = context.getDrawable(resId)!!, + background = ColorDrawable(ContextCompat.getColor(context, bgColor)), + foregroundScale = 0.5f + ) + } + + override fun getLaunchIntent(context: Context): Intent? { + val uri = FileProvider.getUriForFile(context, + context.applicationContext.packageName + ".fileprovider", JavaIOFile(path)) + return Intent(Intent.ACTION_VIEW) + .setDataAndType(uri, mimeType) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + + override fun serialize(): String { + return jsonObjectOf( + "id" to id + ).toString() + } + + fun getFileType(context: Context): String { + if (isDirectory) return context.getString(R.string.file_type_directory) + val resource = when (mimeType) { + "application/zip", + "application/x-zip-compressed", + "application/x-gtar", + "application/x-tar", + "application/java-archive", + "application/x-7z-compressed" -> R.string.file_type_archive + "application/x-gzip", + "application/x-bzip2" -> R.string.file_type_compressed + "application/vnd.android.package-archive" -> R.string.file_type_android + "text/x-asm", + "text/x-c", + "text/x-java-source", + "text/x-script.phyton", + "text/x-pascal", + "text/x-script.perl", + "text/javascript", + "application/json" -> R.string.file_type_source_code + "application/vnd.oasis.opendocument.text", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/msword", + "application/x-iwork-pages-sffpages", + "application/vnd.apple.pages", + "application/vnd.google-apps.document" -> R.string.file_type_document + "application/vnd.oasis.opendocument.spreadsheet", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.ms-excel", + "application/x-iwork-numbers-sffnumbers", + "application/vnd.apple.numbers", + "application/vnd.google-apps.spreadsheet" -> R.string.file_type_spreadsheet + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/vnd.ms-powerpoint", + "application/x-iwork-keynote-sffkey", + "application/vnd.apple.keynote", + "application/vnd.google-apps.presentation" -> R.string.file_type_presentation + "text/plain" -> R.string.file_type_text + "application/vnd.google-apps.drawing" -> R.string.file_type_drawing + "application/vnd.google-apps.form" -> R.string.file_type_form + "application/epub+zip" -> R.string.file_type_ebook + else -> when { + mimeType.startsWith("image/") -> R.string.file_type_image + mimeType.startsWith("video/") -> R.string.file_type_video + mimeType.startsWith("audio/") -> R.string.file_type_music + else -> R.string.file_type_none + } + } + if (resource == R.string.file_type_none && label.matches(Regex(".+\\..+"))) { + val extension = label.substringAfterLast(".").toUpperCase(Locale.getDefault()) + return context.getString(R.string.file_type_generic, extension) + } + return context.getString(resource) + } + + companion object { + fun search(context: Context, query: String): List { + val results = mutableListOf() + if (!LauncherPreferences.instance.searchFiles) return results + if (query.isBlank()) return results + if (!PermissionsManager.checkPermission(context, PermissionsManager.EXTERNAL_STORAGE)) return results + val uri = MediaStore.Files.getContentUri("external").buildUpon().appendQueryParameter("limit", "10").build() + val projection = arrayOf( + MediaStore.Files.FileColumns.DISPLAY_NAME, + MediaStore.Files.FileColumns._ID, + MediaStore.Files.FileColumns.SIZE, + MediaStore.Files.FileColumns.DATA, + MediaStore.Files.FileColumns.MIME_TYPE) + val selection = if (query.length > 3) "${MediaStore.Files.FileColumns.TITLE} LIKE ?" else "${MediaStore.Files.FileColumns.TITLE} = ?" + val selArgs = if (query.length > 3) arrayOf("%$query%") else arrayOf(query) + val sort = "${MediaStore.Files.FileColumns.DISPLAY_NAME} COLLATE NOCASE ASC" + + + val cursor = context.contentResolver.query(uri, projection, selection, selArgs, sort) + ?: return results + while (cursor.moveToNext()) { + if (results.size >= 10) { + break + } + val path = cursor.getString(3) + if (!JavaIOFile(path).exists()) continue + val directory = JavaIOFile(path).isDirectory + val mimeType = (cursor.getStringOrNull(4) + ?: if (directory) "inode/directory" else getMimetypeByFileExtension(path.substringAfterLast('.'))) + val file = File( + path = path, + mimeType = mimeType, + size = cursor.getLong(2), + isDirectory = directory, + id = cursor.getLong(1), + metaData = getMetaData(context, mimeType, path)) + results.add(file) + } + cursor.close() + return results.sortedBy { it } + } + + private fun getMimetypeByFileExtension(extension: String): String { + return when (extension) { + "apk" -> "application/vnd.android.package-archive" + "zip" -> "application/zip" + "jar" -> "application/java-archive" + "txt" -> "text/plain" + "js" -> "text/javascript" + "html", "htm" -> "text/html" + "css" -> "text/css" + "gif" -> "image/gif" + "png" -> "image/png" + "jpg", "jpeg" -> "image/jpeg" + "bmp" -> "image/bmp" + "webp" -> "image/webp" + "ico" -> "image/x-icon" + "midi" -> "audio/midi" + "mp3" -> "audio/mpeg3" + "webm" -> "audio/webm" + "ogg" -> "audio/ogg" + "wav" -> "audio/wav" + "mp4" -> "video/mp4" + else -> "application/octet-stream" + } + } + + + private fun getMetaData(context: Context, mimeType: String, path: String): List> { + val metaData = mutableListOf>() + when { + mimeType.startsWith("audio/") -> { + val retriever = MediaMetadataRetriever() + try { + retriever.setDataSource(path) + arrayOf( + R.string.file_meta_title to MediaMetadataRetriever.METADATA_KEY_TITLE, + R.string.file_meta_artist to MediaMetadataRetriever.METADATA_KEY_ARTIST, + R.string.file_meta_album to MediaMetadataRetriever.METADATA_KEY_ALBUM, + R.string.file_meta_year to MediaMetadataRetriever.METADATA_KEY_YEAR + ).forEach { + retriever.extractMetadata(it.second)?.let { m -> metaData.add(it.first to m) } + } + val duration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0 + val d = DateUtils.formatElapsedTime((duration) / 1000) + metaData.add(3, R.string.file_meta_duration to d) + retriever.release() + } catch (e: RuntimeException) { + retriever.release() + } + } + mimeType.startsWith("video/") -> { + val retriever = MediaMetadataRetriever() + try { + retriever.setDataSource(path) + val width = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)?.toLong() ?: 0 + val height = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)?.toLong() ?: 0 + metaData.add(R.string.file_meta_dimensions to "${width}x$height") + val duration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0 + val d = DateUtils.formatElapsedTime(duration / 1000) + metaData.add(R.string.file_meta_duration to d) + val loc = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION) + if (Geocoder.isPresent() && loc != null) { + val lon = loc.substring(0, loc.lastIndexOfAny(charArrayOf('+', '-'))).toDouble() + val lat = loc.substring(loc.lastIndexOfAny(charArrayOf('+', '-')), loc.indexOf('/')).toDouble() + val list = Geocoder(context).getFromLocation(lon, lat, 1) + if (list.size > 0) { + metaData.add(R.string.file_meta_location to list[0].formatToString()) + } + } + retriever.release() + } catch (e: RuntimeException) { + retriever.release() + } + } + mimeType.startsWith("image/") -> { + val options = BitmapFactory.Options() + options.inJustDecodeBounds = true + BitmapFactory.decodeFile(path, options) + val width = options.outWidth + val height = options.outHeight + metaData.add(R.string.file_meta_dimensions to "${width}x$height") + try { + val exif = ExifInterface(path) + val loc = exif.latLong + if (loc != null && Geocoder.isPresent()) { + val list = Geocoder(context).getFromLocation(loc[0], loc[1], 1) + if (list.size > 0) { + metaData.add(R.string.file_meta_location to list[0].formatToString()) + } + } + } catch (_: IOException) { + + } + } + mimeType == "application/vnd.android.package-archive" -> { + val pkgInfo = context.packageManager.getPackageArchiveInfo(path, 0) + ?: return metaData + metaData.add(R.string.file_meta_app_name to pkgInfo.applicationInfo.loadLabel(context.packageManager).toString()) + metaData.add(R.string.file_meta_app_pkgname to pkgInfo.packageName) + metaData.add(R.string.file_meta_app_version to pkgInfo.versionName) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + metaData.add(R.string.file_meta_app_min_sdk to pkgInfo.applicationInfo.minSdkVersion.toString()) + } + } + } + return metaData + } + + fun deserialize(context: Context, serialized: String): File? { + if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) return null + val json = JSONObject(serialized) + val uri = MediaStore.Files.getContentUri("external") + val proj = arrayOf(MediaStore.Files.FileColumns._ID, + MediaStore.Files.FileColumns.SIZE, + MediaStore.Files.FileColumns.DATA, + MediaStore.Files.FileColumns.MIME_TYPE) + val sel = "${MediaStore.Files.FileColumns._ID} = ?" + val selArgs = arrayOf(json.getLong("id").toString()) + val cursor = context.contentResolver.query(uri, proj, sel, selArgs, null) ?: return null + if (cursor.moveToNext()) { + val path = cursor.getString(2) + if (!JavaIOFile(path).exists()) return null + val directory = JavaIOFile(path).isDirectory + val id = cursor.getLong(0) + val mimeType = cursor.getStringOrNull(3) + ?: if (directory) "inode/directory" else getMimetypeByFileExtension(path.substringAfterLast('.')) + val size = cursor.getLong(1) + cursor.close() + return File( + path = path, + mimeType = mimeType, + size = size, + isDirectory = directory, + id = id, + metaData = getMetaData(context, mimeType, path)) + } + cursor.close() + return null + } + } +} \ No newline at end of file diff --git a/files/src/main/java/de/mm20/launcher2/search/data/GDriveFile.kt b/files/src/main/java/de/mm20/launcher2/search/data/GDriveFile.kt new file mode 100644 index 00000000..c3b079af --- /dev/null +++ b/files/src/main/java/de/mm20/launcher2/search/data/GDriveFile.kt @@ -0,0 +1,126 @@ +package de.mm20.launcher2.search.data + +import android.content.Context +import android.content.Intent +import android.net.Uri +import de.mm20.launcher2.files.R +import de.mm20.launcher2.gservices.DriveFileMeta +import de.mm20.launcher2.gservices.GoogleApiHelper +import de.mm20.launcher2.helper.NetworkUtils +import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.ktx.jsonObjectOf +import de.mm20.launcher2.preferences.LauncherPreferences +import org.json.JSONObject + +class GDriveFile( + val fileId: String, + override val label: String, + path: String, + mimeType: String, + size: Long, + isDirectory: Boolean, + metaData: List>, + val directoryColor: String?, + val viewUri: String +) : File(0, path, mimeType, size, isDirectory, metaData) { + + override val key: String = "gdrive://$fileId" + + override val badgeKey: String + get() = "gdrive://" + + override fun serialize(): String { + return jsonObjectOf( + "id" to fileId, + "label" to label, + "path" to path, + "mimeType" to mimeType, + "size" to size, + "directory" to isDirectory, + "color" to directoryColor, + "uri" to viewUri + ).apply { + for ((k, v) in metaData) { + put(when (k) { + R.string.file_meta_owner -> "owner" + R.string.file_meta_dimensions -> "dimensions" + else -> "other" + }, v) + } + }.toString() + } + + override val isStoredInCloud = true + + override fun getLaunchIntent(context: Context): Intent? { + return Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse(viewUri) + flags = Intent.FLAG_ACTIVITY_NEW_TASK + } + } + + override suspend fun loadIconAsync(context: Context, size: Int): LauncherIcon? { + return null + } + + companion object { + suspend fun search(context: Context, query: String): List { + if (query.length < 4) return emptyList() + val prefs = LauncherPreferences.instance + if (!prefs.searchGDrive) return emptyList() + if (NetworkUtils.isOffline(context, prefs.searchGDriveMobileData)) return emptyList() + val driveFiles = GoogleApiHelper.getInstance(context).queryGDriveFiles(query) + return driveFiles.map { + GDriveFile( + fileId = it.fileId, + label = it.label, + size = it.size, + mimeType = it.mimeType, + isDirectory = it.isDirectory, + path = "", + directoryColor = it.directoryColor, + viewUri = it.viewUri, + metaData = getMetadata(it.metadata) + ) + }.sorted() + } + + private fun getMetadata(file: DriveFileMeta): List> { + val metaData = mutableListOf>() + val owners = file.owners + metaData.add(R.string.file_meta_owner to owners.joinToString(separator = ", ")) + val width = file.width ?: file.width + val height = file.height ?: file.height + if (width != null && height != null) metaData.add(R.string.file_meta_dimensions to "${width}x$height") + return metaData + } + + fun deserialize(serialized: String): GDriveFile? { + val json = JSONObject(serialized) + val id = json.getString("id") + val label = json.getString("label") + val path = json.getString("path") + val mimeType = json.getString("mimeType") + val size = json.getLong("size") + val directory = json.getBoolean("directory") + val color = json.optString("color") + val uri = json.getString("uri") + val owner = json.optString("owner") + val dimensions = json.optString("dimensions") + val metaData = mutableListOf>() + owner.takeIf { it.isNotEmpty() }?.let { metaData.add(R.string.file_meta_owner to it) } + dimensions.takeIf { it.isNotEmpty() }?.let { metaData.add(R.string.file_meta_dimensions to it) } + return GDriveFile( + fileId = id, + label = label, + path = path, + mimeType = mimeType, + size = size, + directoryColor = color, + isDirectory = directory, + viewUri = uri, + metaData = metaData + ) + } + } +} \ No newline at end of file diff --git a/files/src/main/java/de/mm20/launcher2/search/data/NextcloudFile.kt b/files/src/main/java/de/mm20/launcher2/search/data/NextcloudFile.kt new file mode 100644 index 00000000..a42789b8 --- /dev/null +++ b/files/src/main/java/de/mm20/launcher2/search/data/NextcloudFile.kt @@ -0,0 +1,101 @@ +package de.mm20.launcher2.search.data + +import android.content.Context +import android.content.Intent +import android.net.Uri +import de.mm20.launcher2.files.R +import de.mm20.launcher2.helper.NetworkUtils +import de.mm20.launcher2.ktx.jsonObjectOf +import de.mm20.launcher2.nextcloud.NextcloudApiHelper +import de.mm20.launcher2.preferences.LauncherPreferences +import org.json.JSONObject + +class NextcloudFile( + fileId: Long, + override val label: String, + path: String, + mimeType: String, + size: Long, + isDirectory: Boolean, + val server: String, + metaData: List> +) : File(fileId, path, mimeType, size, isDirectory, metaData) { + override val badgeKey: String = "nextcloud://" + + override val key: String = "nextcloud://$server/$fileId" + + override val isStoredInCloud: Boolean + get() = true + + override fun getLaunchIntent(context: Context): Intent? { + return Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse("$server/f/$id") + flags = Intent.FLAG_ACTIVITY_NEW_TASK + } + } + + override fun serialize(): String { + return jsonObjectOf( + "id" to id, + "label" to label, + "path" to path, + "mimeType" to mimeType, + "size" to size, + "isDirectory" to isDirectory, + "server" to server + ).apply { + for ((k, v) in metaData) { + put(when (k) { + R.string.file_meta_owner -> "owner" + else -> "other" + }, v) + } + }.toString() + } + + companion object { + suspend fun search(context: Context, query: String, nextcloudClient: NextcloudApiHelper) : List { + if (!LauncherPreferences.instance.searchNextcloud) return emptyList() + if (query.length < 4) return emptyList() + val server = nextcloudClient.getServer() ?: return emptyList() + if (NetworkUtils.isOffline(context, LauncherPreferences.instance.searchGDriveMobileData)) return emptyList() + return nextcloudClient.files.search(query).map { + NextcloudFile( + fileId = it.id, + label = it.name, + path = server + it.url, + mimeType = it.mimeType, + size = it.size, + isDirectory = it.isDirectory, + server = server, + metaData = it.owner?.let { listOf(R.string.file_meta_owner to it) } ?: emptyList() + ) + } + } + + fun deserialize(serialized: String): NextcloudFile? { + val json = JSONObject(serialized) + val id = json.getLong("id") + val label = json.getString("label") + val path = json.getString("path") + val mimeType = json.getString("mimeType") + val size = json.getLong("size") + val isDirectory = json.getBoolean("isDirectory") + val server = json.getString("server") + val owner = json.optString("owner").takeIf { it.isNotEmpty() } + + return NextcloudFile( + fileId = id, + label = label, + path = path, + mimeType = mimeType, + size = size, + isDirectory = isDirectory, + server = server, + metaData = owner?.let { listOf(R.string.file_meta_owner to it) } ?: emptyList() + + ) + } + + } +} \ No newline at end of file diff --git a/files/src/main/java/de/mm20/launcher2/search/data/OneDriveFile.kt b/files/src/main/java/de/mm20/launcher2/search/data/OneDriveFile.kt new file mode 100644 index 00000000..4405c98c --- /dev/null +++ b/files/src/main/java/de/mm20/launcher2/search/data/OneDriveFile.kt @@ -0,0 +1,123 @@ +package de.mm20.launcher2.search.data + +import android.content.Context +import android.content.Intent +import android.net.Uri +import de.mm20.launcher2.msservices.DriveItem +import de.mm20.launcher2.files.R +import de.mm20.launcher2.msservices.MicrosoftGraphApiHelper +import de.mm20.launcher2.ktx.jsonObjectOf +import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.preferences.LauncherPreferences +import org.json.JSONObject + +class OneDriveFile( + val fileId: String, + override val label: String, + path: String, + mimeType: String, + size: Long, + isDirectory: Boolean, + metaData: List>, + val webUrl: String +) : File(0, path, mimeType, size, isDirectory, metaData) { + + override val badgeKey: String = "onedrive://" + + override val key: String = "onedrive://$fileId" + + override val isStoredInCloud = true + + override suspend fun loadIconAsync(context: Context, size: Int): LauncherIcon? { + return null + } + + override fun getLaunchIntent(context: Context): Intent? { + return Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse(webUrl) + flags = Intent.FLAG_ACTIVITY_NEW_TASK + } + } + + override fun serialize(): String { + return jsonObjectOf( + "id" to fileId, + "label" to label, + "mimeType" to mimeType, + "size" to size, + "directory" to isDirectory, + "webUrl" to webUrl + ).apply { + for ((k, v) in metaData) { + put(when (k) { + R.string.file_meta_owner -> "owner" + R.string.file_meta_dimensions -> "dimensions" + else -> "other" + }, v) + } + }.toString() + } + + companion object { + suspend fun search(context: Context, query: String): List { + if (query.length < 4) return emptyList() + if (!LauncherPreferences.instance.searchOneDrive) return emptyList() + val driveItems = MicrosoftGraphApiHelper.getInstance(context).queryOneDriveFiles(query) ?: return emptyList() + val files = mutableListOf() + for (driveItem in driveItems) { + files += OneDriveFile( + fileId = driveItem.id, + label = driveItem.label, + path = "", + mimeType = driveItem.mimeType, + size = driveItem.size, + isDirectory = driveItem.isDirectory, + metaData = getMetaData(driveItem), + webUrl = driveItem.webUrl + ) + } + return files.sorted() + } + + fun deserialize(serialized: String): OneDriveFile? { + val json = JSONObject(serialized) + val fileId = json.getString("id") + val label = json.getString("label") + val mimeType = json.getString("mimeType") + val size = json.getLong("size") + val isDirectory = json.getBoolean("directory") + val webUrl = json.getString("webUrl") + val owner = json.optString("owner") + val dimensions = json.optString("dimensions") + val metaData = mutableListOf>() + owner.takeIf { it.isNotEmpty() }?.let { metaData.add(R.string.file_meta_owner to it) } + dimensions.takeIf { it.isNotEmpty() }?.let { metaData.add(R.string.file_meta_dimensions to it) } + return OneDriveFile( + fileId = fileId, + label = label, + path = "", + mimeType = mimeType, + size = size, + isDirectory = isDirectory, + metaData = metaData, + webUrl = webUrl + ) + } + + private fun getMetaData(driveItem: DriveItem): List> { + val metaData = mutableListOf>() + driveItem.meta.owner?.let { + metaData.add(R.string.file_meta_owner to it) + } ?: driveItem.meta.createdBy?.let { + metaData.add(R.string.file_meta_owner to it) + } + val width = driveItem.meta.width + val height = driveItem.meta.height + + if (width != null && height != null) { + metaData.add(R.string.file_meta_dimensions to "${width}x${height}") + } + return metaData + } + } +} \ No newline at end of file diff --git a/files/src/main/java/de/mm20/launcher2/search/data/OwncloudFile.kt b/files/src/main/java/de/mm20/launcher2/search/data/OwncloudFile.kt new file mode 100644 index 00000000..a9c96111 --- /dev/null +++ b/files/src/main/java/de/mm20/launcher2/search/data/OwncloudFile.kt @@ -0,0 +1,101 @@ +package de.mm20.launcher2.search.data + +import android.content.Context +import android.content.Intent +import android.net.Uri +import de.mm20.launcher2.files.R +import de.mm20.launcher2.helper.NetworkUtils +import de.mm20.launcher2.ktx.jsonObjectOf +import de.mm20.launcher2.owncloud.OwncloudClient +import de.mm20.launcher2.preferences.LauncherPreferences +import org.json.JSONObject + +class OwncloudFile( + fileId: Long, + override val label: String, + path: String, + mimeType: String, + size: Long, + isDirectory: Boolean, + val server: String, + metaData: List> +) : File(fileId, path, mimeType, size, isDirectory, metaData) { + override val badgeKey: String = "owncloud://" + + override val key: String = "owncloud://$server/$fileId" + + override val isStoredInCloud: Boolean + get() = true + + override fun getLaunchIntent(context: Context): Intent? { + return Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse("$server/f/$id") + flags = Intent.FLAG_ACTIVITY_NEW_TASK + } + } + + override fun serialize(): String { + return jsonObjectOf( + "id" to id, + "label" to label, + "path" to path, + "mimeType" to mimeType, + "size" to size, + "isDirectory" to isDirectory, + "server" to server + ).apply { + for ((k, v) in metaData) { + put(when (k) { + R.string.file_meta_owner -> "owner" + else -> "other" + }, v) + } + }.toString() + } + + companion object { + suspend fun search(context: Context, query: String, owncloudClient: OwncloudClient) : List { + if (!LauncherPreferences.instance.searchOwncloud) return emptyList() + if (query.length < 4) return emptyList() + val server = owncloudClient.getServer() ?: return emptyList() + if (NetworkUtils.isOffline(context, LauncherPreferences.instance.searchGDriveMobileData)) return emptyList() + return owncloudClient.files.query(query).map { + OwncloudFile( + fileId = it.id, + label = it.name, + path = server + it.url, + mimeType = it.mimeType, + size = it.size, + isDirectory = it.isDirectory, + server = server, + metaData = it.owner?.let { listOf(R.string.file_meta_owner to it) } ?: emptyList() + ) + } + } + + fun deserialize(serialized: String): OwncloudFile? { + val json = JSONObject(serialized) + val id = json.getLong("id") + val label = json.getString("label") + val path = json.getString("path") + val mimeType = json.getString("mimeType") + val size = json.getLong("size") + val isDirectory = json.getBoolean("isDirectory") + val server = json.getString("server") + val owner = json.optString("owner").takeIf { it.isNotEmpty() } + + return OwncloudFile( + fileId = id, + label = label, + path = path, + mimeType = mimeType, + size = size, + isDirectory = isDirectory, + server = server, + metaData = owner?.let { listOf(R.string.file_meta_owner to it) } ?: emptyList() + + ) + } + + } +} \ No newline at end of file diff --git a/files/src/main/res/drawable/ic_file_android.xml b/files/src/main/res/drawable/ic_file_android.xml new file mode 100644 index 00000000..8d3fd387 --- /dev/null +++ b/files/src/main/res/drawable/ic_file_android.xml @@ -0,0 +1,5 @@ + + + diff --git a/files/src/main/res/drawable/ic_file_archive.xml b/files/src/main/res/drawable/ic_file_archive.xml new file mode 100644 index 00000000..f38c06fe --- /dev/null +++ b/files/src/main/res/drawable/ic_file_archive.xml @@ -0,0 +1,5 @@ + + + diff --git a/files/src/main/res/drawable/ic_file_code.xml b/files/src/main/res/drawable/ic_file_code.xml new file mode 100644 index 00000000..f7cc253e --- /dev/null +++ b/files/src/main/res/drawable/ic_file_code.xml @@ -0,0 +1,5 @@ + + + diff --git a/files/src/main/res/drawable/ic_file_document.xml b/files/src/main/res/drawable/ic_file_document.xml new file mode 100644 index 00000000..f55b73f2 --- /dev/null +++ b/files/src/main/res/drawable/ic_file_document.xml @@ -0,0 +1,5 @@ + + + diff --git a/files/src/main/res/drawable/ic_file_folder.xml b/files/src/main/res/drawable/ic_file_folder.xml new file mode 100644 index 00000000..82347288 --- /dev/null +++ b/files/src/main/res/drawable/ic_file_folder.xml @@ -0,0 +1,5 @@ + + + diff --git a/files/src/main/res/drawable/ic_file_form.xml b/files/src/main/res/drawable/ic_file_form.xml new file mode 100644 index 00000000..84cf107e --- /dev/null +++ b/files/src/main/res/drawable/ic_file_form.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/files/src/main/res/drawable/ic_file_generic.xml b/files/src/main/res/drawable/ic_file_generic.xml new file mode 100644 index 00000000..a431a5e9 --- /dev/null +++ b/files/src/main/res/drawable/ic_file_generic.xml @@ -0,0 +1,5 @@ + + + diff --git a/files/src/main/res/drawable/ic_file_markup.xml b/files/src/main/res/drawable/ic_file_markup.xml new file mode 100644 index 00000000..6720df48 --- /dev/null +++ b/files/src/main/res/drawable/ic_file_markup.xml @@ -0,0 +1,9 @@ + + + diff --git a/files/src/main/res/drawable/ic_file_music.xml b/files/src/main/res/drawable/ic_file_music.xml new file mode 100644 index 00000000..69f0a3a4 --- /dev/null +++ b/files/src/main/res/drawable/ic_file_music.xml @@ -0,0 +1,5 @@ + + + diff --git a/files/src/main/res/drawable/ic_file_pdf.xml b/files/src/main/res/drawable/ic_file_pdf.xml new file mode 100644 index 00000000..a73638b0 --- /dev/null +++ b/files/src/main/res/drawable/ic_file_pdf.xml @@ -0,0 +1,5 @@ + + + diff --git a/files/src/main/res/drawable/ic_file_picture.xml b/files/src/main/res/drawable/ic_file_picture.xml new file mode 100644 index 00000000..0d8d5030 --- /dev/null +++ b/files/src/main/res/drawable/ic_file_picture.xml @@ -0,0 +1,5 @@ + + + diff --git a/files/src/main/res/drawable/ic_file_presentation.xml b/files/src/main/res/drawable/ic_file_presentation.xml new file mode 100644 index 00000000..4d799702 --- /dev/null +++ b/files/src/main/res/drawable/ic_file_presentation.xml @@ -0,0 +1,5 @@ + + + diff --git a/files/src/main/res/drawable/ic_file_spreadsheet.xml b/files/src/main/res/drawable/ic_file_spreadsheet.xml new file mode 100644 index 00000000..da1dbcc4 --- /dev/null +++ b/files/src/main/res/drawable/ic_file_spreadsheet.xml @@ -0,0 +1,5 @@ + + + diff --git a/files/src/main/res/drawable/ic_file_video.xml b/files/src/main/res/drawable/ic_file_video.xml new file mode 100644 index 00000000..349fbf31 --- /dev/null +++ b/files/src/main/res/drawable/ic_file_video.xml @@ -0,0 +1,5 @@ + + + diff --git a/g-services/.gitignore b/g-services/.gitignore new file mode 100644 index 00000000..bcb56ceb --- /dev/null +++ b/g-services/.gitignore @@ -0,0 +1,2 @@ +/build +*/**/g_services.json diff --git a/g-services/build.gradle.kts b/g-services/build.gradle.kts new file mode 100644 index 00000000..b5a00fb0 --- /dev/null +++ b/g-services/build.gradle.kts @@ -0,0 +1,55 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.bundles.kotlin) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.browser) + implementation(libs.bundles.androidx.lifecycle) + + implementation(libs.google.auth) + implementation(libs.google.apiclient) + implementation(libs.google.drive) + implementation(libs.google.oauth2) + + + implementation(libs.bundles.materialdialogs) + + implementation(project(":i18n")) + implementation(project(":crashreporter")) +} \ No newline at end of file diff --git a/g-services/consumer-rules.pro b/g-services/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/g-services/proguard-rules.pro b/g-services/proguard-rules.pro new file mode 100644 index 00000000..ff59496d --- /dev/null +++ b/g-services/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/g-services/readme.md b/g-services/readme.md new file mode 100644 index 00000000..4c1333c9 --- /dev/null +++ b/g-services/readme.md @@ -0,0 +1,30 @@ +# :g-services + +⚠️ Depends on non-free external services. + +This module manages API calls to Google APIs and connected Google accounts. + +## Configuration + +This module requires additional configuration in order to work properly. You can skip this step but +then Google API related features (e.g. Google Drive search) won't be available. + +In order to use Google APIs, you need to setup a new project in the Google Cloud Console first. + +1. Open the [Google Cloud Console](https://console.cloud.google.com) +1. Create a new project. +1. Enable the Drive API: + 1. Go to APIs & Services > Library and search for the Google Drive API. + 1. Enable this API for your project. +1. Create a new Oauth 2.0 client (you need to do this twice, for debug builds and for release builds) + 1. Go to APIs & Services > Credentials + 1. Click on Create Credentials > OAuth client ID + 1. Choose application type Android + 1. Enter the package name (de.mm20.launcher2.debug for debug builds or de.mm20.launcher2.release for release builds) + 1. Enter the SHA-1 certificate fingerprint of your APK signing key + 1. Click create +1. Download the client config file (repeat this step for both the debug and the release client) + 1. On the APIs & Services > Credentials page, find your OAuth client in the list under OAuth 2.0 Client IDs. + 1. Click the download icon to download a client_config.json + 1. Place this file under src/debug/res/raw/g_services.json or src/release/res/raw/g_services.json + diff --git a/g-services/src/debug/res/raw/g_services_example.json b/g-services/src/debug/res/raw/g_services_example.json new file mode 100644 index 00000000..71718eec --- /dev/null +++ b/g-services/src/debug/res/raw/g_services_example.json @@ -0,0 +1 @@ +{"installed":{"client_id":"xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com","project_id":"xxxxx-xxxxxxxxxxxxx","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}} diff --git a/g-services/src/main/AndroidManifest.xml b/g-services/src/main/AndroidManifest.xml new file mode 100644 index 00000000..7a13544e --- /dev/null +++ b/g-services/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/g-services/src/main/java/de/mm20/launcher2/gservices/DriveFile.kt b/g-services/src/main/java/de/mm20/launcher2/gservices/DriveFile.kt new file mode 100644 index 00000000..6f6bcf9f --- /dev/null +++ b/g-services/src/main/java/de/mm20/launcher2/gservices/DriveFile.kt @@ -0,0 +1,40 @@ +package de.mm20.launcher2.gservices + +import com.google.api.services.drive.model.File +import java.util.* + +data class DriveFile( + val fileId : String, + val label: String, + val size: Long, + val mimeType : String, + val isDirectory : Boolean, + val directoryColor: String?, + val viewUri: String, + val metadata: DriveFileMeta +) { + companion object { + fun fromApiDriveFile(file: File): DriveFile { + return DriveFile( + fileId = file.id, + label = file.name, + size = file.getSize() ?: 0, + isDirectory = file.mimeType == "application/vnd.google-apps.folder", + mimeType = file.mimeType, + metadata = DriveFileMeta( + owners = file.owners?.map { it.displayName ?: it.emailAddress ?: "" } ?: emptyList(), + width = file.imageMediaMetadata?.width ?: file.videoMediaMetadata?.width, + height = file.imageMediaMetadata?.height ?: file.videoMediaMetadata?.height + ), + directoryColor = file.folderColorRgb?.toLowerCase(Locale.ROOT), + viewUri = file.webViewLink ?: "" + ) + } + } +} + +data class DriveFileMeta( + val owners : List, + val width: Int?, + val height: Int? +) \ No newline at end of file diff --git a/g-services/src/main/java/de/mm20/launcher2/gservices/GoogleAccount.kt b/g-services/src/main/java/de/mm20/launcher2/gservices/GoogleAccount.kt new file mode 100644 index 00000000..e6cdc69f --- /dev/null +++ b/g-services/src/main/java/de/mm20/launcher2/gservices/GoogleAccount.kt @@ -0,0 +1,5 @@ +package de.mm20.launcher2.gservices + +data class GoogleAccount( + val name: String +) \ No newline at end of file diff --git a/g-services/src/main/java/de/mm20/launcher2/gservices/GoogleApiHelper.kt b/g-services/src/main/java/de/mm20/launcher2/gservices/GoogleApiHelper.kt new file mode 100644 index 00000000..cec25f6f --- /dev/null +++ b/g-services/src/main/java/de/mm20/launcher2/gservices/GoogleApiHelper.kt @@ -0,0 +1,222 @@ +package de.mm20.launcher2.gservices + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.net.Uri +import androidx.browser.customtabs.* +import androidx.core.content.edit +import com.google.api.client.auth.oauth2.Credential +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow +import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets +import com.google.api.client.http.HttpRequestInitializer +import com.google.api.client.http.javanet.NetHttpTransport +import com.google.api.client.json.gson.GsonFactory +import com.google.api.client.util.store.FileDataStoreFactory +import com.google.api.services.drive.Drive +import com.google.api.services.oauth2.Oauth2 +import de.mm20.launcher2.crashreporter.CrashReporter +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.IOException + +class GoogleApiHelper private constructor(private val context: Context) { + + val transport by lazy { + NetHttpTransport() + } + + suspend fun queryGDriveFiles(query: String): List { + val requestInitializer = getRequestInitializer() ?: return emptyList() + val jsonFactory = GsonFactory.getDefaultInstance() + return withContext(Dispatchers.IO) { + try { + val drive = + Drive.Builder(transport, jsonFactory, requestInitializer).build() + val request = drive.files().list() + request.q = "name contains '${query.replace("'", "")}'" + request.pageSize = 10 + request.fields = + "files(id, webViewLink, size, name, mimeType, owners, imageMediaMetadata, videoMediaMetadata, folderColorRgb)" + request.corpora = "user" + val response = request.execute() + val files = response.files ?: return@withContext emptyList() + files.map { DriveFile.fromApiDriveFile(it) } + + } catch (e: IOException) { + emptyList() + } catch (e: Error) { + emptyList() + } + } + } + + private suspend fun getCredential(): Credential? { + val authFlow = getAuthFlow() ?: return null + return withContext(Dispatchers.IO) { + val credential: Credential? = authFlow.loadCredential(USER_ID) + if ((credential?.expiresInSeconds ?: 0) < 5 * 60) { + try { + if (credential?.refreshToken() == false) return@withContext null + } catch (e: IOException) { + CrashReporter.logException(e) + } + } + return@withContext credential + } + } + + private suspend fun getRequestInitializer(): HttpRequestInitializer? { + val credential = getCredential() ?: return null + + return HttpRequestInitializer { request -> + credential.initialize(request) + request?.connectTimeout = 5000 + request?.readTimeout = 10000 + } + } + + suspend fun getAccount(): GoogleAccount? { + + val name = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).getString( + PREF_ACCOUNT_NAME, + null + ) ?: loadAccountName() + + + return name?.let { + GoogleAccount(name = it) + } + } + + fun isAvailable(): Boolean { + return getConfigResId() != 0 + } + + private fun getConfigResId(): Int { + return context.resources.getIdentifier("g_services", "raw", context.packageName) + } + + + private fun getAuthFlow(): GoogleAuthorizationCodeFlow? { + val configResId = getConfigResId() + if (configResId == 0) return null + val jsonFactory = GsonFactory.getDefaultInstance() + return GoogleAuthorizationCodeFlow.Builder( + NetHttpTransport(), + jsonFactory, + GoogleClientSecrets.load( + jsonFactory, + context.resources.openRawResource(configResId).reader() + ), + SCOPES + ) + .setCredentialDataStore( + FileDataStoreFactory(context.filesDir).getDataStore( + "google_signin" + ) + ) + .build() + } + + fun login(activity: Activity) { + val authFlow = getAuthFlow() ?: return + + val url = authFlow + .newAuthorizationUrl() + .setRedirectUri(getRedirectUri()) + .toString() + val themeColor = 0xFF4285f4.toInt() + + val customTabsIntent = CustomTabsIntent + .Builder() + .setDefaultColorSchemeParams( + CustomTabColorSchemeParams.Builder() + .setToolbarColor(themeColor) + .setNavigationBarColor(themeColor) + .build() + ) + .build() + + callingActivity = activity.javaClass + + customTabsIntent.intent.flags = Intent.FLAG_ACTIVITY_NO_HISTORY + customTabsIntent.launchUrl(activity, Uri.parse(url)) + } + + suspend fun finishAuthFlow(activity: Activity, code: String) { + val authFlow = getAuthFlow() ?: return + withContext(Dispatchers.IO) { + val tokenResponse = try { + authFlow.newTokenRequest(code).setRedirectUri(getRedirectUri()).execute() + } catch (e: IOException) { + CrashReporter.logException(e) + return@withContext + } + authFlow.createAndStoreCredential(tokenResponse, USER_ID) + } + loadAccountName() + returnToPreviousActivity(activity) + } + + fun cancelAuthFlow(activity: Activity) { + returnToPreviousActivity(activity) + } + + private fun returnToPreviousActivity(activity: Activity) { + val intent = Intent(activity, callingActivity) + callingActivity = null + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) + activity.startActivity(intent) + } + + private suspend fun loadAccountName(): String? { + val requestInitializer = getRequestInitializer() ?: return null + val jsonFactory = GsonFactory.getDefaultInstance() + val oauth2 = Oauth2.Builder(transport, jsonFactory, requestInitializer).build() + try { + val meResponse = withContext(Dispatchers.IO) { + oauth2.userinfo().v2().me().get().execute() + } + if (meResponse != null) { + val name = meResponse.name + context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit { + putString(PREF_ACCOUNT_NAME, name) + } + return name + } + } catch (e: IOException) { + CrashReporter.logException(e) + } + return null + } + + + fun logout() { + val authFlow = getAuthFlow() ?: return + authFlow.credentialDataStore.clear() + context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit { + putString(PREF_ACCOUNT_NAME, null) + } + } + + private fun getRedirectUri(): String { + return "${context.packageName}:/google-auth-redirect" + } + + companion object { + private lateinit var instance: GoogleApiHelper + + fun getInstance(context: Context): GoogleApiHelper { + if (!::instance.isInitialized) instance = GoogleApiHelper(context.applicationContext) + return instance + } + + val SCOPES = setOf("https://www.googleapis.com/auth/drive", "profile") + const val USER_ID = "google-user" + const val PREFS = "google-account" + const val PREF_ACCOUNT_NAME = "name" + + private var callingActivity: Class? = null + } +} \ No newline at end of file diff --git a/g-services/src/main/java/de/mm20/launcher2/gservices/GoogleAuthRedirectActivity.kt b/g-services/src/main/java/de/mm20/launcher2/gservices/GoogleAuthRedirectActivity.kt new file mode 100644 index 00000000..0c60de11 --- /dev/null +++ b/g-services/src/main/java/de/mm20/launcher2/gservices/GoogleAuthRedirectActivity.kt @@ -0,0 +1,24 @@ +package de.mm20.launcher2.gservices + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.launch + +class GoogleAuthRedirectActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val gServiceHelper = GoogleApiHelper.getInstance(this) + val code = intent.data?.getQueryParameter("code") + if (code == null) { + gServiceHelper.cancelAuthFlow(this) + finish() + } + else { + lifecycleScope.launch { + gServiceHelper.finishAuthFlow(this@GoogleAuthRedirectActivity, code) + finish() + } + } + } +} \ No newline at end of file diff --git a/g-services/src/main/res/values/styles.xml b/g-services/src/main/res/values/styles.xml new file mode 100644 index 00000000..b8c8d4eb --- /dev/null +++ b/g-services/src/main/res/values/styles.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/g-services/src/release/res/raw/g_services_example.json b/g-services/src/release/res/raw/g_services_example.json new file mode 100644 index 00000000..71718eec --- /dev/null +++ b/g-services/src/release/res/raw/g_services_example.json @@ -0,0 +1 @@ +{"installed":{"client_id":"xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com","project_id":"xxxxx-xxxxxxxxxxxxx","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..c872b9d0 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +org.gradle.java.home=/usr/lib/jvm/default + +android.useAndroidX=true +android.enableJetifier=true +android.injected.testOnly=false + +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.caching=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f6b961fd5a86aa5fbfe90f707c3138408be7c718 GIT binary patch literal 54329 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

<5G2M1WZP5 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 F{|7XC?P&l2 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..7531f5ae --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Aug 16 10:19:49 CEST 2021 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-rc-2-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..cccdd3d5 --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..e95643d6 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/hiddenitems/.gitignore b/hiddenitems/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/hiddenitems/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/hiddenitems/build.gradle.kts b/hiddenitems/build.gradle.kts new file mode 100644 index 00000000..2d9849de --- /dev/null +++ b/hiddenitems/build.gradle.kts @@ -0,0 +1,45 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.kotlin.stdlib) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + + implementation(project(":database")) + implementation(project(":search")) +} \ No newline at end of file diff --git a/hiddenitems/consumer-rules.pro b/hiddenitems/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/hiddenitems/proguard-rules.pro b/hiddenitems/proguard-rules.pro new file mode 100644 index 00000000..01639a19 --- /dev/null +++ b/hiddenitems/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/hiddenitems/src/main/AndroidManifest.xml b/hiddenitems/src/main/AndroidManifest.xml new file mode 100644 index 00000000..0692c026 --- /dev/null +++ b/hiddenitems/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + / + \ No newline at end of file diff --git a/hiddenitems/src/main/java/de/mm20/launcher2/hiddenitems/HiddenItemsRepository.kt b/hiddenitems/src/main/java/de/mm20/launcher2/hiddenitems/HiddenItemsRepository.kt new file mode 100644 index 00000000..3fe5ef6e --- /dev/null +++ b/hiddenitems/src/main/java/de/mm20/launcher2/hiddenitems/HiddenItemsRepository.kt @@ -0,0 +1,29 @@ +package de.mm20.launcher2.hiddenitems + +import android.content.Context +import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData +import de.mm20.launcher2.database.AppDatabase +import de.mm20.launcher2.search.data.Searchable + +/** + * A low level repository for hidden items. This can only be used to retrieve keys and to check + * whether an item is hidden. To retrieve actual Searchable objects, use FavoritesRepository. + */ +class HiddenItemsRepository private constructor(val context: Context) { + + val hiddenItemsKeys : LiveData> = AppDatabase.getInstance(context).searchDao().getHiddenItemKeys() + + fun isHidden(item: Searchable): LiveData { + return AppDatabase.getInstance(context).searchDao().isHidden(item.key) + } + + companion object { + private lateinit var instance: HiddenItemsRepository + + fun getInstance(context: Context): HiddenItemsRepository { + if(!Companion::instance.isInitialized) instance = HiddenItemsRepository(context.applicationContext) + return instance + } + } +} \ No newline at end of file diff --git a/hiddenitems/src/main/java/de/mm20/launcher2/hiddenitems/HiddenItemsViewModel.kt b/hiddenitems/src/main/java/de/mm20/launcher2/hiddenitems/HiddenItemsViewModel.kt new file mode 100644 index 00000000..e0b4c6cc --- /dev/null +++ b/hiddenitems/src/main/java/de/mm20/launcher2/hiddenitems/HiddenItemsViewModel.kt @@ -0,0 +1,9 @@ +package de.mm20.launcher2.hiddenitems + +import android.app.Application +import androidx.lifecycle.AndroidViewModel + +class HiddenItemsViewModel(app: Application): AndroidViewModel(app) { + val hiddenItemsKeys = HiddenItemsRepository.getInstance(app).hiddenItemsKeys + +} \ No newline at end of file diff --git a/i18n/.gitignore b/i18n/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/i18n/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/i18n/build.gradle.kts b/i18n/build.gradle.kts new file mode 100644 index 00000000..c8a0e186 --- /dev/null +++ b/i18n/build.gradle.kts @@ -0,0 +1,43 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.kotlin.stdlib) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + +} \ No newline at end of file diff --git a/i18n/consumer-rules.pro b/i18n/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/i18n/proguard-rules.pro b/i18n/proguard-rules.pro new file mode 100644 index 00000000..ff59496d --- /dev/null +++ b/i18n/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/i18n/readme.md b/i18n/readme.md new file mode 100644 index 00000000..533e5123 --- /dev/null +++ b/i18n/readme.md @@ -0,0 +1,12 @@ +# :i18n + +This module contains all data required for internationalization and localization. This includes +strings, icons (if they require localization), and config and default values. **All resources that +might need localization must go to this module.** + +## Contribute + +Currently looking into web translation services (e.g. Crowdin or something similar). For now just +manually translate the XML files or use Android Studio's Translations Editor and submit a pull +request with your changes. + diff --git a/i18n/src/main/AndroidManifest.xml b/i18n/src/main/AndroidManifest.xml new file mode 100644 index 00000000..fab88aca --- /dev/null +++ b/i18n/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/i18n/src/main/res/values-de/defaults.xml b/i18n/src/main/res/values-de/defaults.xml new file mode 100644 index 00000000..9ad73496 --- /dev/null +++ b/i18n/src/main/res/values-de/defaults.xml @@ -0,0 +1,5 @@ + + + false + EUR + \ No newline at end of file diff --git a/i18n/src/main/res/values-de/strings.xml b/i18n/src/main/res/values-de/strings.xml new file mode 100644 index 00000000..bc1b6759 --- /dev/null +++ b/i18n/src/main/res/values-de/strings.xml @@ -0,0 +1,409 @@ + + + Kvæsitso + Suchen + Wird vorbereitet… + %1$s-Link + Paketdatei + Deinstallieren + Teilen + Version %1$s\n%2$s + Einstellungen + Hintergrundbild + Einstellungen + ganztägig + Nord + Nordost + Nordnordost + Ostnordost + Ost + Ostsüdost + Südost + Südsüdost + Süd + Südsüdwest + Südwest + Westsüdwest + West + Westnordwest + Nordwest + Nordnordwest + Kein Niederschlag + Erscheinungsbild + Design + Hell + Dunkel + Über + Version + Entwickler + Links + Open-Source-Lizenzen + Webseite öffnen + Version, Entwickler, Open-Source-Lizenzen + Design, Symbole, Suchleiste, Systemleisten + Provider, Einheiten, Standort + Wetter + Standort + Automatischer Standort + GPS und Ortungsdienste verwenden, um Standort automatisch zu ermitteln + Nicht gesetzt + Standort + Grad Fahrenheit und Meilen pro Stunde verwenden + Imperiale Einheiten + °F + °C + %1$s mph + %1$s km/h + %1$s mm + Durchscheinende Karten + Debug + Debug-Informationen exportieren + Exportiere nach %1$s + https://de.wikipedia.org + Aus Wikipedia + Symbole + Form + Quadrat + Abgerundetes Quadrat + Squircle + Kreis + = %1$s + Suchleiste + Auf Startbildschirm + Hintergrund ausblenden + Nichts tun + Ausblenden + Verzeichnis + Archiv-Datei + Android-Paket-Datei + Quellcode-Datei + Dokument + Kalkulationstabelle + Video + Musik-Datei + Bild + Datei + %1$s-Datei + Auto (nach Tageszeit) + Systemeinstellung verwenden + Keine passende Anwendung installiert! + Präsentation + Komprimierte Datei + Text-Datei + Das Verzeichnis %1$s wird samt Inhalt unwideruflich gelöscht. Fortfahren? + Die Datei %1$s wird unwideruflich gelöscht. Fortfahren? + Titel + Künstler + Album + Länge + Jahr + %1$s: %2$s + Größe + Pfad + Typ + Abmessungen + App-Name + Version + Paketname + Min-SDK-Version + Dienste + Suche + Suchkategorien + Favoriten + Favoriten anzeigen + Favoritenliste über allen Apps anzeigen + Häufig genutze Elemente + Häufig genutzte Elemente automatisch in den Favoriten anzeigen + Dateien + Dateisuche + Ordner, Dokumente, Fotos und andere Dateitypen auf diesem Gerät durchsuchen + Wikipedia + Wikipedia durchsuchen + Mobile Daten verwenden + Zusätzliche Kosten können anfallen + Webseiten + Nach Webseiten suchen + Probieren Sie „wikipedia.org“ + Standard Web-Protokoll + Standard-Protokoll, um Webseiten zu suchen, wenn nicht angegeben. Stellen Sie https:// oder http:// vor Ihre Suchanfrage, um das Protokoll explizit festzulegen + HTTP (unverschlüsselt) + HTTPS (verschlüsselt) + Taschenrechner + Probieren Sie „4*2+9“ + Taschenrechner aktivieren + Probieren Sie „%1$s“ + Websuche + Shortcuts zu verschiedenen Websuch-Engines anzeigen + Websuchen-Shortcuts + Websuchen bearbeiten + Ort + Unschärfeeffekt + Hintergrund hinter Karten unscharf darstellen + Nicht mit Live-Hintergründen kompatibel + Google + YouTube + Google Play + https://google.de/search?q=${1} + https://www.youtube.com/results?search_query=${1} + https://play.google.com/store/search?q=${1} + Websuche hinzufügen… + Löschen + Name + URL + „${1}“ wird durch den eigentlichen Suchbegriff ersetzt. + In dieser URL fehlt der Platzhalter „${1}“ + Wetterdienst + OpenWeatherMap + Von diesem Wetterdienst nicht unterstützt + EE, d. MMMM + %1$s • %2$s + Keine Symbolpakete installiert + Symbolpaket + Dynamischer Hintergrund + Hintergrundfarbe an Symbol anpassen + Widget + Kalender + Kalender, Widgets + Kalender + Berechtigung fehlt + Ganztägige Termine ausblenden + + %1$d Kalender ausgewählt + %1$d Kalender ausgewählt + + %1$s\n%2$s + Widgets bearbeiten + Wetter + Kalender + Widget hinzufügen + Weitere + Systemleisten + Dunkle Statusleisten-Symbole + Dunkle Navigationsleisten-Symbole + Bitte aktivieren Sie den Benachrichtigungszugriff für diese App (wird benötigt um die Musik-Wiedergabe zu steuern) + Musik + In Kontakte-App anzeigen + An Favoriten anheften + Aus Favoriten entfernen + Zurück + Anruf + SMS + Telegram + WhatsApp + E-Mail + %1$d Telefonnummern + %1$d E-Mail-Adressen + Ort + %1$d Postadressen + Kalender + Kalendersuche aktivieren + Kontakte + Kontakte durchsuchen + App-Info + Verbundene Accounts und Dienste verwalten + Google + Angemeldet als %1$s + Abmelden + Eigentümer + Google Drive + Sie sind im Moment nicht angemeldet + %1$ss Dateien auf Google Drive durchsuchen + Zeichnung + E-Book + Formular + Schwarz + Ausblenden + Ausgeblendete Elemente + Löschen + Nicht ausblenden + In Kalender-App anzeigen + BIN:\nOCT:\nHEX: + Bilder anzeigen + Erhöht den Datenverbrauch signifikant + Sechseck + Reuleaux-Dreieck + Teilnehmer + Beschreibung + Ort + Zeit + Über Google anmelden + Anmelden, um Google Drive durchsuchen zu können + Geben Sie den Auth-Code hier ein: + Google-Login fehlgeschlagen! + Wird installiert… (%1$d%%) + Von %1$s + %1$s konnte nicht geöffnet werden + Anzahl der Termine + MET Norway + + Schneeregenschauer + Starker Schneeregen + Leichte Regenschauer und Gewitter + Starkregen + Leichter Schneefall und Gewitter + Leichter Regen + Leichte Regenschauer + Leichter Schnee + Starke Schneeregenschauer und Gewitter + Leichte Schneeschauer + Leichte Schneeregenschauer und Gewitter + Schneefall und Gewitter + Starke Schneeregenschauer + Starker Schneefall + Bedeckt + Leichter Regen und Gewitter + Schneefall + Starke Schneeschauer + Starke Regenschauer + Regenschauer und Gewitter + Klarer Himmel + Schneeregen + Regen + Schneeregen und Gewitter + Leichte Schneeschauer und Gewitter + Starke Regenschauer und Gewitter + Heiter + Nebel + Schneeregenschauer und Gewitter + Regen und Gewitter + Leichter Schneeregen + Starker Schneeregen und Gewitter + Teilweise bewölkt + Starker Schneefall und Gewitter + Regenschauer + Light sleet and thunder + Starke Schneeschauer und Gewitter + Light sleet showers + Schneeschauer und Gewitter + Schneeschauer + Starkregen und Gewitter + Unbekannt + + Von diesem Wetterdienst nicht unterstützt + Einheitenrechner + Probieren Sie „23 kg“ oder„5 cm >> in“ + Einheitenrechner aktivieren + Symbol-Hintergrund + Keiner + Dynamisch + Weiß + Hier gibt es keine Easter Eggs, es sei denn Ihr hättet sie mitgebracht. + Bitte, hör auf, du verschwendest deine Zeit + Ich werde es nicht noch einmal sagen: hier sind definitiv keine Easter Eggs versteckt. + Tja, da sind Sie. Herzlichen Glückwunsch. War es das wert? + Easter Egg deaktiviert! + Easter Egg aktiviert! + Kalender-Berechtigung gewähren, um Ihre nächsten Termine hier anzuzeigen. + Kalender-Berechtigung gewähren, um Termine zu durchsuchen. + Kontakt-Berechtigung gewähren, um Kontakte zu durchsuchen. + Speicher-Berechtigung gewähren, um Fotos, Medien und Dokumente auf diesem Gerät zu durchsuchen. + Schließen + Fortfahren + Eigenes Symbol + Symbolfarbe wählen + Höhe anpassen + Entfernen + Einstellungen + HERE + Fünfeck + Hintergrund + Hintergrundbild setzen + Hintergrund dimmen + Hintergrundbild bei Verwendung von dunklen Desings abdunkeln + Plaketten + Symbolplaketten konfigurieren + Benachrichtigungsplaketten + Plaketten für Anwendungen mit ungelesenen Benachrichtigungen anzeigen + Pausierte Apps + Plaketten für pausierte Apps anzeigen + Cloud-Plaketten + Shortcut-Plaketten + Eine Plakette für Dateien, die in einer Cloud gespeichert sind anzeigen + Für Shortcuts anzeigen, zu welcher App diese gehören + Systemstandard + Datenbanken exportieren + Die exportierten Datenbanken beinhalten persönliche Daten und sollten nicht Dritten zugänglich gemacht werden. + Animationen + App-Start-Animation + Splashscreen 1 + Splashscreen 2 + Von unten gleiten + Blenden + Aus Symbol erweitern + Standard + Microsoft + Bei Microsoft anmelden + Anmelden, um OneDrive durchsuchen zu können + Bei Nextcloud anmelden + Anmelden, um Ihren Nextcloud-Server durchsuchen zu können + Status wird abgerufen… + OneDrive durchsuchen + %1$ss Dateien auf OneDrive durchsuchen + Nextcloud durchsuchen + %1$ss Dateien durchsuchen + Google Drive + OneDrive + Telegram-Gruppe + F-Droid-Repository + Crash-Reporter + Plug-Ins + Plug-Ins aktivieren oder deaktivieren + Installierte Plug-Ins + Einstellungen für %1$s + Favoriten bearbeiten + Nicht angeheftet – häufig genutzt + Angeheftet – automatisch sortiert + Angeheftet – manuell sortiert + Weiter + Nextcloud-Server-URL + Server-URL darf nicht leer sein. + Nextcloud + Diese URL verweist auf keine gültige Nextcloud-Installation + Angemeldet als %1$s. + Anmelden + Anmeldung fehlgeschlagen: Nutzername oder Passwort ungültig + + Owncloud-Server-URL + Owncloud + Diese URL verweist auf keine gültige Owncloud-Installation + Passwort + Benutzername + Wenn Sie Zwei-Faktor-Authentifizierung aktiviert haben, müssen Sie hier ein App-Passwort verwenden. + Anmeldung fehlgeschlagen: Passwort oder Benutzername ist ungültig. + Bei Owncloud anmelden + Anmelden, um Ihren Owncloud-Server durchsuchen zu können + Nutzername darf nicht leer sein + Passwort darf nicht leer sein + Owncloud durchsuchen + Erscheinungsbild von Karten anpassen + Karten + Eckradius + Rahmenstärke + Deckkraft + Haftungsausschluss + Wechselkurse so wie sie einmal täglich von der Europäischen Zentralbank herausgegeben werden. Alle Angaben sind ohne Gewähr. Es wird keine Haftung für die hier dargestellten Informationen übernommen. + Alle anzeigen + Hintergrund + Standard (weiß/dunkelgrau) + Weiß/schwarz + Farbig (aus Hintergrundbild) + + Heute + Morgen + Demnächst + Heute keine Termine + Kalender-App öffnen + Termin erstellen + Details anzeigen + Details ausblenden + Luftfeuchte: + Niederschlag: + Lizenz + Diese App ist freie Software. + Lizenziert unter der GNU General Public License 3.0 + Diese Funktion ist in dieser Version von %1$s nicht verfügbar. + + +%1$d laufender Termin aus vergangenen Tagen + +%1$d laufende Termine aus vergangenen Tagen + + \ No newline at end of file diff --git a/i18n/src/main/res/values-de/units.xml b/i18n/src/main/res/values-de/units.xml new file mode 100644 index 00000000..6d065fd8 --- /dev/null +++ b/i18n/src/main/res/values-de/units.xml @@ -0,0 +1,263 @@ + + + m + + Meter + Meter + + km + + Kilometer + Kilometer + + cm + + Zentimeter + Zentimeter + + mm + + Millimeter + Millimeter + + in + + Zoll + Zoll + + ft + + Fuß + Fuß + + yd + + Yard + Yard + + mi + + Meile + Meilen + + sm + + Seemeile + Seemeilen + + + + + + Quadratmeter + Quadratmeter + + km² + + Quadratkilometer + Quadratkilometer + + cm² + + Quadratzentimeter + Quadratzentimeter + + mm² + + Quadratmillimeter + Quadratmillimeter + + sqin + + Quadratzoll + Quadratzoll + + sqft + + Quadratfuß + Quadratfuß + + sqyd + + Quadratyard + Quadratyard + + ha + + Hektar + Hektar + + ac + + Acre + Acre + + + + s + + Sekunde + Sekunden + + ms + + Millisekunde + Millisekunden + + min + + Minute + Minuten + + h + + Stunde + Stunden + + d + + Tag + Tage + + a + + Jahr + Jahre + + + + B + + Byte + Byte + + kB + + Kilobyte + Kilobyte + + MB + + Megabyte + Megabyte + + GB + + Gigabyte + Gigabyte + + TB + + Terabyte + Terabyte + + kiB + + Kibibyte + Kibibyte + + MiB + + Mebibyte + Mebibyte + + GiB + + Gibiyte + Gibibyte + + TiB + + Tebiyte + Tebiyte + + bit + + Bit + Bit + + kbit + + Kilobit + Kilobit + + Mbit + + Megabit + Megabit + + Gbit + + Gigabit + Gigabit + + Tbit + + Terabit + Terabit + + + + m/s + + Meter pro Sekunde + Meter pro Sekunde + + km/h + + Kilometer pro Stunde + Kilometer pro Stunde + + mph + + Meile pro Stunde + Meilen pro Stunde + + kn + + Knoten + Knoten + + + + kg + + Kilogramm + Kilogramm + + g + + Gramm + Gramm + + t + + Tonne + Tonnen + + tn.l. + + Long Ton + Long Tons + + st. + + Stone + Stones + + lb. + + Pfund + Pfund + + oz. + + Unze + Unzen + + tn.sh. + + Short Ton + Short Tons + + \ No newline at end of file diff --git a/i18n/src/main/res/values-en-rUS/defaults.xml b/i18n/src/main/res/values-en-rUS/defaults.xml new file mode 100644 index 00000000..f679a1ba --- /dev/null +++ b/i18n/src/main/res/values-en-rUS/defaults.xml @@ -0,0 +1,5 @@ + + + true + USD + \ No newline at end of file diff --git a/i18n/src/main/res/values/defaults.xml b/i18n/src/main/res/values/defaults.xml new file mode 100644 index 00000000..e1d5e5d3 --- /dev/null +++ b/i18n/src/main/res/values/defaults.xml @@ -0,0 +1,5 @@ + + + false + USD + \ No newline at end of file diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml new file mode 100644 index 00000000..5abb34be --- /dev/null +++ b/i18n/src/main/res/values/strings.xml @@ -0,0 +1,443 @@ + + + Kvæsitso + + Search + + %1$s,\n%2$s + + Preparing… + + %1$s link + + Package file + + Uninstall + + Share + + Version %1$s\n%2$s + + Settings + + Wallpaper + + Settings + + all-day + + North + + North east + + North north east + + East north east + + East + + East south east + + South east + + South south east + + South + + South south west + + South west + + West south west + + West + + West north west + + North west + + North north west + + No precipitation + + Appearance + Theme + Light + Dark + Black + About + Version + Developer + Links + Open source licenses + + Open website + Version, Developer, Open source licenses + Theme, Icons, Search bar, System bars + Provider, Units, location + Weather + Location + Automatic location + Use GPS and location services to determine location automatically + Not set + Location + Use degrees Fahrenheit and miles per hour + Imperial units + °F + °C + %1$s mph + %1$s km/h + %1$s mm + Translucent cards + Debug + Export debug information + Exporting to %1$s + + https://en.wikipedia.org + From Wikipedia + Icons + Shape + Square + Rounded square + Squircle + Reuleaux triangle + Circle + = %1$s + Search bar + On home screen + Hide background + Do nothing + Hide + Directory + Archive file + Android package file + Source code file + Document + Spreadsheet + Music file + Video + Picture + File + %1$s file + No suitable app installed. + Presentation + Compressed file + Text file + The directory %1$s and all its content will be deleted permanently. Proceed? + The file %1$s will be deleted permanently. Proceed? + Title + Artist + Album + Duration + Year + %1$s: %2$s + Size + Path + Type + Dimensions + App name + Version + Package name + Min SDK version + Services + Search + Search categories + Auto (by time of day) + Follow system + Favorites + Show favorites + Show favorites above the app list + Frequently used items + Automatically add frequently used items to favorites + Files + File search + Search folders, documents, photos and other kinds of file on this device + Wikipedia + Search Wikipedia + Allow mobile data usage + Additional fees may apply + Websites + Search for websites + Try \'wikipedia.org\' + Default web protocol + Default protocol for websites, if not given. You can explicate protocol by adding https:// or http:// in front of your search term + HTTP (unencrypted) + HTTPS (encrypted) + Enable calculator + Try \'4*2+9\' + Unit converter + Try \'23 kg\' or \'5 cm >> in\' + Enable unit converter + Calculator + Try \'%1$s\' + Web search + Show shortcuts to several web search engines + Web search shortcuts + Edit web searches + Location + Blur effect + Blur wallpaper behind translucent cards + Not compatible with live wallpapers + Google + YouTube + Google Play + https://google.com/search?q=${1} + https://www.youtube.com/results?search_query=${1} + https://play.google.com/store/search?q=${1} + Add web search… + Delete + Name + URL + \'${1}\' will be replaced by the actual search term. + The placeholder \'${1}\' is missing in this URL + Provider + MET Norway + OpenWeatherMap + HERE + Not supported by this provider + EE, MMMM d + %1$s • %2$s + Icon pack + No icon packs installed + Dynamic background + Adjust background color to icon + Widget + Calendar + Calendars, Widget settings + Calendars + Permission denied + Hide all-day events + Edit widgets + Weather + Calendar + Add widget + More + System bars + Dark status bar icons + Dark navigation bar icons + Please grant notification listener permission for this app (required to control music playback) + Music + Open in contacts app + Pin to favorites + Unpin + Back + Call + Message + Telegram + WhatsApp + Email + %1$d phone numbers + %1$d email addresses + Location + %1$d postal addresses + Calendar + Search calendar events + Contacts + Search contacts + App info + Manage connected accounts and services + Google + Signed in as %1$s + Log out + Owner + Google Drive + You are currently not logged in + Search %1$s\'s files on Google Drive + E-book + Drawing + Form + + %1$d calendar selected + %1$d calendars selected + + Hide + Don\'t hide + Hidden items + Delete + Open in calendar app + BIN:\nOCT:\nHEX: + Show pictures + Significantly increases data usage + Sign in with Google + Sign in to search Google Drive + Enter the auth code here: + Google sing-in failed! + Hexagon + Time + Description + Location + Attendees + By %1$s + Installation in progress… (%1$d%%) + Couldn\'t open %1$s + Number of events + Not supported by this provider + Icon background + None + Dynamic + White + + There are no easter eggs here, unless you brought them with you. + Please, stop it, you are wasting your time + I won\'t say it again: there are absolutely no easter eggs hidden here + Well, you found me. Congratulations. Was it worth it? + Easter egg enabled! + Easter egg disabled! + Grant calendar permission to display your upcomping events here. + Grant calendar permission to search your calendar. + Grant storage permission to search photos, media and document on this device. + Grant contact permission to search your contact. + Close + Continue + Custom icon + Choose icon color + Adjust height + Remove + Settings + Pentagon + Wallpaper + Dim wallpaper + In dark themes, darken wallpaper + Choose a wallpaper + Badges + Configure icon badges + Notification badges + Show a badge for applications with unread notifications + Show a badge for suspended applicatoins + Suspended apps + Cloud badges + Show a badge for files that are stored in a cloud + Shortcut badges + Show a badge which indicates to which app a shortcut belongs + System default + Export databases + The exported databases contain personal data and are not intended to be shared. + Animations + App start animation + Splash screen 1 + Splash screen 2 + Slide from bottom + Fade + Expand from icon + Default + Microsoft + Sign in with Microsoft + Sign in to search OneDrive + Sign in to Nextcloud + Sign in to search your Nextcloud server + Checking status… + Search OneDrive + Search %1$s\'s files on OneDrive + Search Nextcloud + Search %1$s\'s files + Google Drive + OneDrive + Telegram group + F-Droid repository + Crash reporter + Plugins + Enable or disable plugins + Installed plugins + Settings for %1$s + Edit favorites + Not pinned – frequently used + Pinned – automatically sorted + Pinned – manually sorted + Next + Nextcloud server URL + Server URL must not be empty + Nextcloud + This URL does not point to a valid Nextcloud installation + Logged in as %1$s. + Login failed: incorrect username or password. + + Owncloud server URL + Owncloud + This URL does not point to a valid Owncloud installation + Password + User name + If you have two factor authentication enabled, you must use an app password here. + Log in + Login failed: incorrect username or password. + Sign in to Owncloud + Sign in to search your Owncloud server + User name must not be empty + Password must not be empty + Search Owncloud + Customize card appearance + Cards + Corner radius + Stroke width + Opacity + Disclaimer + "Exchange rates as published once per day by the European Central Bank. All information is provided \"as is\" without any kind of guarantee. No liability is assumed for these information." + Show all + Background + Default (white/dark gray) + White/black + Colored (from wallpaper) + + Today + Tomorrow + Upcoming + No events today + Open calendar app + New event + + +%1$d running event from past days + +%1$d running events from past days + + + + Sleet showers + Heavy sleet + Light rain showers and thunder + Heavy rain + Light snow and thunder + Light rain + Light rain showers + Light snow + Heavy sleet showers and thunder + Light snow showers + Lights sleet showers and thunder + Snow and thunder + Heavy sleet showers + Heavy snow + Cloudy + Light rain and thunder + Snow + Heavy snow showers + Heavy rain showers + Rain showers and thunder + Clear sky + Sleet + Rain + Sleet and thunder + Lights snow showers and thunder + Heavy rain showers and thunder + Fair + Fog + Sleet showers and thunder + Rain and thunder + Light sleet + Heavy sleet and thunder + Partly cloudy + Heavy snow and thunder + Rain showers + Light sleet and thunder + Heavy snow showers and thunder + Light sleet showers + Snow showers and thunder + Snow showers + Heavy rain and thunder + Unknown + Show details + Hide details + Humidity: + Wind: + Precipitation: + License + This app is free software. + Licensed under the GNU General Public License 3.0 + This feature is not available in this version of %1$s + diff --git a/i18n/src/main/res/values/units.xml b/i18n/src/main/res/values/units.xml new file mode 100644 index 00000000..009b6487 --- /dev/null +++ b/i18n/src/main/res/values/units.xml @@ -0,0 +1,267 @@ + + + + + m + + meter + meters + + km + + kilometer + kilometers + + cm + + centimeter + centimeters + + mm + + millimeter + millimeters + + in + + inch + inches + + ft + + foot + feet + + yd + + yard + yards + + mi + + mile + miles + + NM + + nautic mile + nautic miles + + + + + + square meter + square meters + + km² + + square kilometer + square kilometers + + cm² + + square centimeter + square centimeters + + mm² + + square millimeter + square millimeters + + sqin + + square inch + square inches + + sqft + + square foot + square feet + + sqyd + + square yard + square yards + + ha + + hectare + hectares + + ac + + acre + acres + + + + s + + second + seconds + + ms + + millisecond + milliseconds + + min + + minute + minutes + + h + + hour + hours + + d + + day + days + + a + + year + years + + + + B + + byte + bytes + + kB + + kilobyte + kilobytes + + MB + + megabytes + megabytes + + GB + + gigabyte + gigabytes + + TB + + terabyte + terabytes + + kiB + + kibibyte + kibibytes + + MiB + + mebibytes + mebibytes + + GiB + + gibiyte + gibibytes + + TiB + + tebiyte + tebiytes + + bit + + bit + bits + + kbit + + kilobit + kilobits + + Mbit + + megabit + megabits + + Gbit + + gigabit + gigabits + + Tbit + + terabit + terabits + + + + m/s + + meter per second + meters per second + + km/h + + kilometer per hour + kilometers per hour + + mph + + mile per hour + miles per hour + + kn + + knot + knots + + + + kg + + kilogram + kilograms + + g + + gram + grams + + t + + metric ton + metric tons + + tn.l. + + long ton + long tons + + st. + + stone + stones + + lb. + + pound + pounds + + oz. + + ounce + ounces + + tn.sh. + + short ton + short tons + + \ No newline at end of file diff --git a/icons/.gitignore b/icons/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/icons/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/icons/build.gradle.kts b/icons/build.gradle.kts new file mode 100644 index 00000000..7578f80d --- /dev/null +++ b/icons/build.gradle.kts @@ -0,0 +1,53 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.bundles.kotlin) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.palette) + + implementation(libs.bundles.androidx.lifecycle) + + implementation(project(":database")) + implementation(project(":preferences")) + implementation(project(":ktx")) + implementation(project(":base")) + implementation(project(":search")) + implementation(project(":crashreporter")) + +} \ No newline at end of file diff --git a/icons/consumer-rules.pro b/icons/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/icons/proguard-rules.pro b/icons/proguard-rules.pro new file mode 100644 index 00000000..52039aba --- /dev/null +++ b/icons/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts.kts.kts.kts.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/icons/src/main/AndroidManifest.xml b/icons/src/main/AndroidManifest.xml new file mode 100644 index 00000000..6e78bb99 --- /dev/null +++ b/icons/src/main/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/icons/src/main/java/de/mm20/launcher2/icons/CalendarDynamicLauncherIcon.kt b/icons/src/main/java/de/mm20/launcher2/icons/CalendarDynamicLauncherIcon.kt new file mode 100644 index 00000000..b538f392 --- /dev/null +++ b/icons/src/main/java/de/mm20/launcher2/icons/CalendarDynamicLauncherIcon.kt @@ -0,0 +1,65 @@ +package de.mm20.launcher2.icons + +import android.content.Context +import android.content.pm.PackageManager +import android.graphics.drawable.AdaptiveIconDrawable +import android.graphics.drawable.Drawable +import android.os.Build +import de.mm20.launcher2.ktx.getDrawableOrNull +import java.util.* +import java.util.concurrent.Executors + +class CalendarDynamicLauncherIcon( + context: Context, + foreground: Drawable, + background: Drawable?, + foregroundScale: Float, + backgroundScale: Float, + badgeNumber: Float = 0f, + val packageName: String, + val drawableIds: IntArray, + autoGenerateBackgroundMode: Int +) : DynamicLauncherIcon( + foreground, + background, + foregroundScale, + backgroundScale, + /** Not needed, we already have a background **/ + autoGenerateBackgroundMode, + badgeNumber, + null +) { + + init { + DynamicIconController.getInstance(context).registerIcon(this) + update(context) + } + + var currentDay = 0 + override fun update(context: Context) { + val calendar = Calendar.getInstance() + val day = calendar[Calendar.DAY_OF_MONTH] + if (day == currentDay || drawableIds.size < currentDay) return + val resources = try { + context.packageManager.getResourcesForApplication(packageName) + } catch (e: PackageManager.NameNotFoundException) { + return + } + Executors.newSingleThreadExecutor().execute { + val currentDayDrawable = resources.getDrawableOrNull(drawableIds[day - 1]) + ?: return@execute + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && currentDayDrawable is AdaptiveIconDrawable) { + foreground = currentDayDrawable.foreground + background = currentDayDrawable.background + foregroundScale = 1.5f + backgroundScale = 1.5f + } else { + foregroundScale = 1f + backgroundScale = 1f + background = null + foreground = currentDayDrawable + } + } + currentDay = day + } +} \ No newline at end of file diff --git a/icons/src/main/java/de/mm20/launcher2/icons/ClockDynamicLauncherIcon.kt b/icons/src/main/java/de/mm20/launcher2/icons/ClockDynamicLauncherIcon.kt new file mode 100644 index 00000000..9aaec4c5 --- /dev/null +++ b/icons/src/main/java/de/mm20/launcher2/icons/ClockDynamicLauncherIcon.kt @@ -0,0 +1,57 @@ +package de.mm20.launcher2.icons + +import android.content.Context +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable +import android.graphics.drawable.RotateDrawable +import android.os.Build +import androidx.annotation.RequiresApi +import java.util.* +import kotlin.math.roundToInt + +@RequiresApi(Build.VERSION_CODES.O) +class ClockDynamicLauncherIcon( + context: Context, + foreground: LayerDrawable, + background: Drawable?, + foregroundScale: Float, + backgroundScale: Float, + badgeNumber: Float, + val hourLayer: Int, + val minuteLayer: Int, + val secondLayer: Int +) : DynamicLauncherIcon( + foreground, + background, + foregroundScale, + backgroundScale, + /** Not needed, we already have a background **/ + LauncherIcon.BACKGROUND_WHITE, + badgeNumber, + null +) { + + init { + foreground.also { + it.setDrawable(secondLayer, ColorDrawable(0)) + (it.getDrawable(hourLayer) as? RotateDrawable)?.fromDegrees = 0f + (it.getDrawable(hourLayer) as? RotateDrawable)?.toDegrees = 360f + (it.getDrawable(minuteLayer) as? RotateDrawable)?.fromDegrees = 0f + (it.getDrawable(minuteLayer) as? RotateDrawable)?.toDegrees = 360f + } + DynamicIconController.getInstance(context).registerIcon(this) + update(context) + } + + override fun update(context: Context) { + val calendar = Calendar.getInstance() + val hourDegrees = calendar[Calendar.HOUR] / 12f * 10000 + calendar[Calendar.MINUTE] / 60f * 10000f / 12 + val minuteDegrees = calendar[Calendar.MINUTE] / 60f * 10000 + (foreground as LayerDrawable).also { + (it.getDrawable(hourLayer) as? RotateDrawable)?.level = hourDegrees.roundToInt() + (it.getDrawable(minuteLayer) as? RotateDrawable)?.level = minuteDegrees.roundToInt() + } + notifyCallbacks() + } +} \ No newline at end of file diff --git a/icons/src/main/java/de/mm20/launcher2/icons/DynamicIconController.kt b/icons/src/main/java/de/mm20/launcher2/icons/DynamicIconController.kt new file mode 100644 index 00000000..31d23cb1 --- /dev/null +++ b/icons/src/main/java/de/mm20/launcher2/icons/DynamicIconController.kt @@ -0,0 +1,66 @@ +package de.mm20.launcher2.icons + +import android.app.Activity +import android.app.Application +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.util.Log +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleObserver +import androidx.lifecycle.OnLifecycleEvent +import java.lang.ref.WeakReference + +class DynamicIconController(val context: Context): LifecycleObserver { + + private var timeReceiver: BroadcastReceiver? = null + private val registeredIcons = mutableListOf>() + + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) + fun resume() { + timeReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent?) { + updateAllIcons(context) + } + } + val filter = IntentFilter(Intent.ACTION_TIME_TICK).also { + it.addAction(Intent.ACTION_TIME_CHANGED) + it.addAction(Intent.ACTION_TIMEZONE_CHANGED) + } + context.registerReceiver(timeReceiver, filter) + updateAllIcons(context) + } + + private fun updateAllIcons(context: Context) { + val iterator = registeredIcons.iterator() + while (iterator.hasNext()) { + val iconRef = iterator.next() + if (iconRef.get() != null) iconRef.get()?.update(context) + else iterator.remove() + } + } + + @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) + fun pause() { + try { + context.unregisterReceiver(timeReceiver) + } catch (e: IllegalArgumentException) { + + } + timeReceiver = null + } + + fun registerIcon(icon: DynamicLauncherIcon) { + registeredIcons.add(WeakReference(icon)) + } + + companion object { + private lateinit var instance: DynamicIconController + + fun getInstance(context: Context): DynamicIconController { + if(!::instance.isInitialized) instance = DynamicIconController(context.applicationContext) + return instance + } + } +} \ No newline at end of file diff --git a/icons/src/main/java/de/mm20/launcher2/icons/DynamicLauncherIcon.kt b/icons/src/main/java/de/mm20/launcher2/icons/DynamicLauncherIcon.kt new file mode 100644 index 00000000..bfb0db60 --- /dev/null +++ b/icons/src/main/java/de/mm20/launcher2/icons/DynamicLauncherIcon.kt @@ -0,0 +1,22 @@ +package de.mm20.launcher2.icons + +import android.content.Context +import android.graphics.drawable.Drawable + +abstract class DynamicLauncherIcon( + foreground: Drawable, + background: Drawable?, + foregroundScale: Float, + backgroundScale: Float, + autoGenerateBackgroundMode: Int, + badgeNumber: Float, + badgeDrawable: Drawable?) + : LauncherIcon( + foreground, + background, + foregroundScale, + backgroundScale, + autoGenerateBackgroundMode +) { + abstract fun update(context: Context) +} \ No newline at end of file diff --git a/icons/src/main/java/de/mm20/launcher2/icons/Icon.kt b/icons/src/main/java/de/mm20/launcher2/icons/Icon.kt new file mode 100644 index 00000000..b0e226c2 --- /dev/null +++ b/icons/src/main/java/de/mm20/launcher2/icons/Icon.kt @@ -0,0 +1,30 @@ +package de.mm20.launcher2.icons + +import android.content.ComponentName +import de.mm20.launcher2.database.entities.IconEntity + +data class Icon( + val type: String, + val componentName: ComponentName?, + val drawable: String?, + val iconPack: String, + val scale: Float? = null +) { + constructor(entity: IconEntity) : this( + type = entity.type, + componentName = entity.componentName, + drawable = entity.drawable, + iconPack = entity.iconPack, + scale = entity.scale + ) + + fun toDatabaseEntity(): IconEntity { + return IconEntity( + type = type, + componentName = componentName, + drawable = drawable, + iconPack = iconPack, + scale = scale + ) + } +} \ No newline at end of file diff --git a/icons/src/main/java/de/mm20/launcher2/icons/IconPack.kt b/icons/src/main/java/de/mm20/launcher2/icons/IconPack.kt new file mode 100644 index 00000000..ed388d4a --- /dev/null +++ b/icons/src/main/java/de/mm20/launcher2/icons/IconPack.kt @@ -0,0 +1,26 @@ +package de.mm20.launcher2.icons + +import de.mm20.launcher2.database.entities.IconPackEntity + +data class IconPack( + val name: String, + val packageName: String, + val version: String, + var scale: Float = 1f +) { + constructor(entity: IconPackEntity) : this( + name = entity.name, + packageName = entity.packageName, + version = entity.packageName, + scale = entity.scale + ) + + fun toDatabaseEntity(): IconPackEntity { + return IconPackEntity( + name = name, + scale = scale, + version = version, + packageName = packageName + ) + } +} \ No newline at end of file diff --git a/icons/src/main/java/de/mm20/launcher2/icons/IconPackManager.kt b/icons/src/main/java/de/mm20/launcher2/icons/IconPackManager.kt new file mode 100644 index 00000000..71eadcb1 --- /dev/null +++ b/icons/src/main/java/de/mm20/launcher2/icons/IconPackManager.kt @@ -0,0 +1,517 @@ +package de.mm20.launcher2.icons + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.pm.LauncherActivityInfo +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import android.content.res.Resources +import android.content.res.XmlResourceParser +import android.graphics.* +import android.graphics.drawable.AdaptiveIconDrawable +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.LayerDrawable +import android.os.Build +import android.os.UserHandle +import android.util.DisplayMetrics +import android.util.Log +import androidx.core.content.res.ResourcesCompat +import androidx.core.graphics.drawable.toBitmap +import de.mm20.launcher2.crashreporter.CrashReporter +import de.mm20.launcher2.database.AppDatabase +import de.mm20.launcher2.ktx.dp +import de.mm20.launcher2.ktx.obtainTypedArrayOrNull +import de.mm20.launcher2.ktx.randomElementOrNull +import de.mm20.launcher2.preferences.IconShape +import de.mm20.launcher2.preferences.LauncherPreferences +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import org.xmlpull.v1.XmlPullParserFactory +import java.io.InputStreamReader +import kotlin.math.roundToInt + + +class IconPackManager private constructor(val context: Context) { + var selectedIconPack: String + get() { + return context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) + .getString(KEY_ICON_PACK, "")!! + } + set(value) { + Log.d("MM20", "Selected icon pack: $value") + context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) + .edit() + .putString(KEY_ICON_PACK, value) + .apply() + } + + + fun selectIconPack(iconPack: String) { + selectedIconPack = iconPack + } + + fun getIcon(context: Context, activity: LauncherActivityInfo, size: Int): LauncherIcon? { + if (selectedIconPack.isEmpty()) return getDefaultIcon(context, activity) + return getFromPack(context, activity, size) ?: generateIcon(context, activity, size) + } + + private fun getFromPack(context: Context, activity: LauncherActivityInfo, size: Int): LauncherIcon? { + val res = try { + context.packageManager.getResourcesForApplication(selectedIconPack) + } catch (e: PackageManager.NameNotFoundException) { + Log.e("MM20", "Icon pack package $selectedIconPack not found!") + return getDefaultIcon(context, activity) + } + val iconDao = AppDatabase.getInstance(context).iconDao() + val component = ComponentName(activity.applicationInfo.packageName, activity.name) + val icon = iconDao.getIcon(component.flattenToString(), selectedIconPack) + ?: return generateIcon(context, activity, size) + + if (icon.type == "calendar") { + return getIconPackCalendarIcon(context, icon.iconPack, icon.drawable ?: return null) + } + val drawableName = icon.drawable + val resId = res.getIdentifier(drawableName, "drawable", selectedIconPack).takeIf { it != 0 } + ?: return generateIcon(context, activity, size) + val drawable = ResourcesCompat.getDrawable(res, resId, context.theme) ?: return null + return when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && drawable is AdaptiveIconDrawable -> { + LauncherIcon( + foreground = drawable.foreground, + background = drawable.background, + foregroundScale = 1.5f, + backgroundScale = 1.5f + ) + } + else -> { + LauncherIcon( + foreground = drawable, + foregroundScale = getScale(), + autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt() + ) + } + } + } + + + private fun generateIcon(context: Context, activity: LauncherActivityInfo, size: Int): LauncherIcon? { + val back = getIconBack() + val upon = getIconUpon() + val mask = getIconMask() + val scale = getPackScale() + + if (back == null && upon == null && mask == null) { + return getDefaultIcon(context, activity) + } + + val drawable = activity.getIcon(context.resources.displayMetrics.densityDpi) + val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888) + + val canvas = Canvas(bitmap) + val paint = Paint() + paint.isAntiAlias = true + paint.isFilterBitmap = true + paint.isDither = true + + + var inBounds: Rect + var outBounds: Rect + + val icon = drawable.toBitmap(width = size, height = size) + + inBounds = Rect(0, 0, icon.width, icon.height) + outBounds = Rect((bitmap.width * (1 - scale) * 0.5).roundToInt(), + (bitmap.height * (1 - scale) * 0.5).roundToInt(), + (bitmap.width - bitmap.width * (1 - scale) * 0.5).roundToInt(), + (bitmap.height - bitmap.height * (1 - scale) * 0.5).roundToInt()) + canvas.drawBitmap(icon, inBounds, outBounds, paint) + + val pack = selectedIconPack + val pm = context.packageManager + val res = try { + pm.getResourcesForApplication(pack) + } catch (e: Resources.NotFoundException) { + return getDefaultIcon(context, activity) + } + + if (mask != null) { + res.getIdentifier(mask, "drawable", pack).takeIf { it != 0 }?.let { + paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OUT) + val maskDrawable = ResourcesCompat.getDrawable(res, it, null) ?: return null + val maskBmp = maskDrawable.toBitmap(size, size) + inBounds = Rect(0, 0, maskBmp.width, maskBmp.height) + outBounds = Rect(0, 0, bitmap.width, bitmap.height) + canvas.drawBitmap(maskBmp, inBounds, outBounds, paint) + } + } + if (upon != null) { + res.getIdentifier(upon, "drawable", pack).takeIf { it != 0 }?.let { + paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OVER); + val maskDrawable = ResourcesCompat.getDrawable(res, it, null) ?: return null + val maskBmp = maskDrawable.toBitmap(size, size) + inBounds = Rect(0, 0, maskBmp.width, maskBmp.height) + outBounds = Rect(0, 0, bitmap.width, bitmap.height) + canvas.drawBitmap(maskBmp, inBounds, outBounds, paint) + } + } + if (back != null) { + res.getIdentifier(back, "drawable", pack).takeIf { it != 0 }?.let { + paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OVER); + val maskDrawable = ResourcesCompat.getDrawable(res, it, null) ?: return null + val maskBmp = maskDrawable.toBitmap(size, size) + inBounds = Rect(0, 0, maskBmp.width, maskBmp.height) + outBounds = Rect(0, 0, bitmap.width, bitmap.height) + canvas.drawBitmap(maskBmp, inBounds, outBounds, paint) + } + } + + return LauncherIcon( + foreground = BitmapDrawable(context.resources, bitmap), + foregroundScale = getScale(), + autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt() + ) + } + + private fun getDefaultIcon(context: Context, activity: LauncherActivityInfo): LauncherIcon? { + if (activity.applicationInfo.packageName == GOOGLE_DESK_CLOCK_PACKAGE_NAME) { + getGoogleDeskClockIcon(context)?.let { return it } + } + getCalendarIcon(context, activity)?.let { return it } + try { + val icon = activity.getIcon(context.resources.displayMetrics.densityDpi) ?: return null + when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && icon is AdaptiveIconDrawable -> { + return LauncherIcon( + foreground = icon.foreground ?: return null, + background = icon.background, + foregroundScale = 1.5f, + backgroundScale = 1.5f + ) + } + else -> { + return LauncherIcon( + foreground = icon, + foregroundScale = getScale(), + autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt() + ) + } + } + } catch (e: PackageManager.NameNotFoundException) { + return null + } + } + + private fun getIconPackCalendarIcon(context: Context, iconPack: String, baseIconName: String): CalendarDynamicLauncherIcon? { + val resources = try { + context.packageManager.getResourcesForApplication(iconPack) + } catch (e: PackageManager.NameNotFoundException) { + return null + } + val drawableIds = (1..31).map { + val drawableName = baseIconName + it + val id = resources.getIdentifier(drawableName, "drawable", iconPack) + if (id == 0) return null + id + }.toIntArray() + return CalendarDynamicLauncherIcon( + context = context, + background = ColorDrawable(0), + foreground = ColorDrawable(0), + foregroundScale = 1.5f, + backgroundScale = 1.5f, + packageName = iconPack, + drawableIds = drawableIds, + autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt() + ) + } + + private fun getCalendarIcon(context: Context, activity: LauncherActivityInfo): CalendarDynamicLauncherIcon? { + val component = ComponentName(activity.applicationInfo.packageName, activity.name) + val pm = context.packageManager + val ai = try { + pm.getActivityInfo(component, PackageManager.GET_META_DATA) + } catch (e: PackageManager.NameNotFoundException) { + return null + } + val resources = pm.getResourcesForActivity(component) + var arrayId = ai.metaData?.getInt("com.teslacoilsw.launcher.calendarIconArray") ?: 0 + if (arrayId == 0) arrayId = ai.metaData?.getInt("com.google.android.calendar.dynamic_icons") + ?: return null + if (arrayId == 0) return null + val typedArray = resources.obtainTypedArrayOrNull(arrayId) ?: return null + if (typedArray.length() != 31) { + typedArray.recycle() + return null + } + val drawableIds = IntArray(31) + for (i in 0 until 31) { + drawableIds[i] = typedArray.getResourceId(i, 0) + } + typedArray.recycle() + return CalendarDynamicLauncherIcon( + context = context, + background = ColorDrawable(0), + foreground = ColorDrawable(0), + foregroundScale = 1.5f, + backgroundScale = 1.5f, + packageName = component.packageName, + drawableIds = drawableIds, + autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt() + ) + } + + private fun getGoogleDeskClockIcon(context: Context): ClockDynamicLauncherIcon? { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return null + val pm = context.packageManager + val appInfo = pm.getApplicationInfo(GOOGLE_DESK_CLOCK_PACKAGE_NAME, PackageManager.GET_META_DATA) + ?: return null + val drawable = appInfo.metaData.getInt("com.google.android.apps.nexuslauncher.LEVEL_PER_TICK_ICON_ROUND") + val resources = pm.getResourcesForApplication(appInfo) + val baseIcon = try { + ResourcesCompat.getDrawable(resources, drawable, null) as? AdaptiveIconDrawable ?: return null + } catch (e: Resources.NotFoundException) { + return null + } + val foreground = baseIcon.foreground as? LayerDrawable ?: return null + val hourLayer = appInfo.metaData.getInt("com.google.android.apps.nexuslauncher.HOUR_LAYER_INDEX") + val minuteLayer = appInfo.metaData.getInt("com.google.android.apps.nexuslauncher.MINUTE_LAYER_INDEX") + val secondLayer = appInfo.metaData.getInt("com.google.android.apps.nexuslauncher.SECOND_LAYER_INDEX") + return ClockDynamicLauncherIcon( + context = context, + background = baseIcon.background, + backgroundScale = 1.5f, + foreground = foreground, + foregroundScale = 1.5f, + badgeNumber = 0f, + hourLayer = hourLayer, + minuteLayer = minuteLayer, + secondLayer = secondLayer + ) + } + + private fun getScale(): Float { + return when (LauncherPreferences.instance.iconShape) { + IconShape.CIRCLE, IconShape.PLATFORM_DEFAULT -> 0.7f + else -> 0.8f + + } + } + + private fun getIconBack(): String? { + val iconDao = AppDatabase.getInstance(context).iconDao() + val iconbacks = iconDao.getIconBacks(selectedIconPack) + return iconbacks.randomElementOrNull() + } + + private fun getIconUpon(): String? { + val iconDao = AppDatabase.getInstance(context).iconDao() + val iconupons = iconDao.getIconUpons(selectedIconPack) + return iconupons.randomElementOrNull() + } + + private fun getIconMask(): String? { + val iconDao = AppDatabase.getInstance(context).iconDao() + val iconmasks = iconDao.getIconMasks(selectedIconPack) + return iconmasks.randomElementOrNull() + } + + private fun getPackScale(): Float { + val iconDao = AppDatabase.getInstance(context).iconDao() + return iconDao.getScale(selectedIconPack) ?: 1f + } + + suspend fun getInstalledIconPacks(): List { + return withContext(Dispatchers.IO) { + AppDatabase.getInstance(context).iconDao().getInstalledIconPacks().map { + IconPack(it) + } + } + } + + companion object { + const val GOOGLE_DESK_CLOCK_PACKAGE_NAME = "com.google.android.deskclock" + const val GOOGLE_CALENDAR_PACKAGE_NAME = "com.google.android.calendar" + + private lateinit var instance: IconPackManager + fun getInstance(context: Context): IconPackManager { + if (!::instance.isInitialized) instance = IconPackManager(context.applicationContext) + return instance + } + } + + @Synchronized + suspend fun updateIconPacks() { + withContext(Dispatchers.IO) { + UpdateIconPacksWorker(context).doWork() + } + } +} + + +class UpdateIconPacksWorker(val context: Context) { + + fun doWork() { + val packs = loadInstalledPacks(context).map { it.activityInfo.packageName } + val iconDao = AppDatabase.getInstance(context).iconDao() + iconDao.uninstallIconPacksExcept(packs) + + for (pack in packs) { + try { + val packInfo = context.packageManager.getPackageInfo(pack, 0) + val iconPack = IconPack( + name = packInfo.applicationInfo.loadLabel(context.packageManager).toString(), + packageName = pack, + version = packInfo.versionName + ) + //if (iconDao.isInstalled(iconPack)) continue + installIconPack(iconPack) + } catch (e: PackageManager.NameNotFoundException) { + continue + } + } + } + + private fun loadInstalledPacks(context: Context): List { + val packs = mutableListOf() + val pm = context.packageManager + var intent = Intent("org.adw.ActivityStarter.THEMES") + val adwPacks = pm.queryIntentActivities(intent, 0) + packs.addAll(adwPacks) + intent = Intent("com.novalauncher.THEME") + val novaPacks = pm.queryIntentActivities(intent, 0) + novaPacks.forEach { + if (packs.none { p -> p.activityInfo.packageName == it.activityInfo.packageName }) packs.add(it) + } + packs.sortWith(ResolveInfo.DisplayNameComparator(pm)) + return packs + } + + private fun installIconPack(iconPack: IconPack) { + val pkgName = iconPack.packageName + try { + val res = context.packageManager.getResourcesForApplication(pkgName) + val parser: XmlPullParser + var inStream: InputStreamReader? = null + val xmlId = res.getIdentifier("appfilter", "xml", pkgName) + if (xmlId != 0) parser = res.getXml(xmlId) + else { + val rawId = res.getIdentifier("appfilter", "raw", pkgName) + if (rawId == 0) { + Log.e("MM20", "Icon pack $pkgName has no appfilter.xml, neither in xml nor in raw") + return + } + parser = XmlPullParserFactory.newInstance().newPullParser() + inStream = res.openRawResource(rawId).reader() + parser.setInput(inStream) + } + + val icons = mutableListOf() + val iconDao = AppDatabase.getInstance(context).iconDao() + + loop@ while (parser.next() != XmlPullParser.END_DOCUMENT) { + if (parser.eventType != XmlPullParser.START_TAG) continue + when (parser.name) { + "item" -> { + val component = parser.getAttributeValue(null, "component") + ?: continue@loop + val drawable = parser.getAttributeValue(null, "drawable") + ?: continue@loop + if (component.length <= 14) continue@loop + val componentName = ComponentName.unflattenFromString(component.substring(14, component.lastIndex)) + ?: continue@loop + val icon = Icon( + componentName = componentName, + drawable = drawable, + iconPack = pkgName, + type = "app" + ) + icons.add(icon) + } + "calendar" -> { + val component = parser.getAttributeValue(null, "component") + ?: continue@loop + val drawable = parser.getAttributeValue(null, "prefix") ?: continue@loop + if (component.length < 14) continue@loop + val componentName = ComponentName.unflattenFromString(component.substring(14, component.lastIndex)) + ?: continue@loop + + val icon = Icon( + componentName = componentName, + drawable = drawable, + iconPack = pkgName, + type = "calendar" + ) + icons.add(icon) + } + "iconback" -> { + for (i in 0 until parser.attributeCount) { + if (parser.getAttributeName(i).startsWith("img")) { + val drawable = parser.getAttributeValue(i) + val icon = Icon( + componentName = null, + drawable = drawable, + iconPack = pkgName, + type = "iconback" + ) + icons.add(icon) + } + } + } + "iconupon" -> { + for (i in 0 until parser.attributeCount) { + if (parser.getAttributeName(i).startsWith("img")) { + val drawable = parser.getAttributeValue(i) + val icon = Icon( + componentName = null, + drawable = drawable, + iconPack = pkgName, + type = "iconupon" + ) + icons.add(icon) + } + } + } + "iconmask" -> { + for (i in 0 until parser.attributeCount) { + if (parser.getAttributeName(i).startsWith("img")) { + val drawable = parser.getAttributeValue(i) + val icon = Icon( + componentName = null, + drawable = drawable, + iconPack = pkgName, + type = "iconmask" + ) + icons.add(icon) + } + } + } + "scale" -> { + val scale = parser.getAttributeValue(null, "factor")?.toFloatOrNull() + ?: continue@loop + iconPack.scale = scale + } + } + } + + iconDao.installIconPack(iconPack.toDatabaseEntity(), icons.map { it.toDatabaseEntity() }) + + (parser as? XmlResourceParser)?.close() + inStream?.close() + + Log.d("MM20", "Icon pack has been installed successfully") + } catch (e: PackageManager.NameNotFoundException) { + Log.e("MM20", "Could not install icon pack $pkgName: package not found.") + } catch (e: XmlPullParserException) { + CrashReporter.logException(e) + } + } +} + +private const val PREFERENCE_NAME = "icon_pack" +private const val KEY_ICON_PACK = "icon_pack" +private const val KEY_VERSION = "version" +private const val KEY_ICONSCALE = "iconscale" \ No newline at end of file diff --git a/icons/src/main/java/de/mm20/launcher2/icons/IconRepository.kt b/icons/src/main/java/de/mm20/launcher2/icons/IconRepository.kt new file mode 100644 index 00000000..90c2fcf1 --- /dev/null +++ b/icons/src/main/java/de/mm20/launcher2/icons/IconRepository.kt @@ -0,0 +1,67 @@ +package de.mm20.launcher2.icons + +import android.content.Context +import android.util.Log +import android.util.LruCache +import de.mm20.launcher2.search.data.Searchable +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow + +class IconRepository private constructor(val context: Context) { + + private val scope = CoroutineScope(Job() + Dispatchers.Main) + + private val cache = LruCache(200) + + fun getIcon(searchable: Searchable, size: Int): Flow = flow { + var icon = cache.get(searchable.key) + if (icon != null) { + emit(icon) + return@flow + } + val placeholderIcon = withContext(Dispatchers.IO) { + searchable.getPlaceholderIcon(context) + } + emit(placeholderIcon) + icon = withContext(Dispatchers.IO) { + searchable.loadIconAsync(context, size) + } + if (icon != null) { + cache.put(searchable.key, icon) + emit(icon) + } + } + + /** + * Returns the icon for the given Searchable if it was requested earlier and is still in cache. + * Returns `null` otherwise. + */ + fun getIconIfCached(searchable: Searchable): LauncherIcon? { + return cache[searchable.key] + } + + fun requestIconPackListUpdate() { + scope.launch { + IconPackManager.getInstance(context).updateIconPacks() + } + } + + fun removeIconFromCache(searchable: Searchable) { + cache.remove(searchable.key) + } + + fun clearCache() { + cache.evictAll() + } + + + companion object { + private lateinit var instance: IconRepository + + fun getInstance(context: Context): IconRepository { + if (!::instance.isInitialized) instance = IconRepository(context.applicationContext) + return instance + } + } +} \ No newline at end of file diff --git a/ktx/.gitignore b/ktx/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/ktx/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/ktx/build.gradle.kts b/ktx/build.gradle.kts new file mode 100644 index 00000000..f158626b --- /dev/null +++ b/ktx/build.gradle.kts @@ -0,0 +1,45 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.bundles.kotlin) + + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + implementation(libs.bundles.androidx.lifecycle) + +} \ No newline at end of file diff --git a/ktx/consumer-rules.pro b/ktx/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/ktx/proguard-rules.pro b/ktx/proguard-rules.pro new file mode 100644 index 00000000..ff59496d --- /dev/null +++ b/ktx/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/ktx/readme.md b/ktx/readme.md new file mode 100644 index 00000000..89f348a4 --- /dev/null +++ b/ktx/readme.md @@ -0,0 +1,3 @@ +# :ktx + +A collection of useful Kotlin extension functions. \ No newline at end of file diff --git a/ktx/src/main/AndroidManifest.xml b/ktx/src/main/AndroidManifest.xml new file mode 100644 index 00000000..3b1edbcf --- /dev/null +++ b/ktx/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + / + \ No newline at end of file diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/Address.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/Address.kt new file mode 100644 index 00000000..9d4d6dfc --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/Address.kt @@ -0,0 +1,11 @@ +package de.mm20.launcher2.ktx + +import android.location.Address + +fun Address.formatToString( +): String { + val sb = StringBuilder() + if (locality != null) sb.append(locality).append(", ") + if (countryCode != null) sb.append(countryCode) + return sb.toString() +} \ No newline at end of file diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/Context.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/Context.kt new file mode 100644 index 00000000..7f614574 --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/Context.kt @@ -0,0 +1,29 @@ +package de.mm20.launcher2.ktx + +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Bundle +import androidx.core.content.ContextCompat + +val Context.dp: Float + get() = resources.displayMetrics.density + + +val Context.sp: Float + get() = resources.displayMetrics.scaledDensity + +fun Context.checkPermission(permission: String): Boolean { + return ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED +} + + +fun Context.tryStartActivity(intent: Intent, bundle: Bundle? = null): Boolean { + return try { + startActivity(intent, bundle) + true + } catch (e: ActivityNotFoundException) { + false + } +} \ No newline at end of file diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/Double.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/Double.kt new file mode 100644 index 00000000..ed7fec8f --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/Double.kt @@ -0,0 +1,7 @@ +package de.mm20.launcher2.ktx + +import kotlin.math.ceil + +fun Double.ceilToInt(): Int { + return ceil(this).toInt() +} diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/Drawable.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/Drawable.kt new file mode 100644 index 00000000..53ab699e --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/Drawable.kt @@ -0,0 +1,16 @@ +package de.mm20.launcher2.ktx + +import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import androidx.annotation.Px +import androidx.core.graphics.drawable.toBitmap + +fun Drawable.toBitmapOrNull( + @Px width: Int = intrinsicWidth, + @Px height: Int = intrinsicHeight, + config: Bitmap.Config? = null +): Bitmap? { + if (this is BitmapDrawable && bitmap == null) return null + return toBitmap(width, height, config) +} \ No newline at end of file diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/Extensions.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/Extensions.kt new file mode 100644 index 00000000..b63d617f --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/Extensions.kt @@ -0,0 +1,36 @@ +package de.mm20.launcher2.ktx + +import android.os.Build +import androidx.annotation.ChecksSdkIntAtLeast +import org.json.JSONObject + +fun jsonObjectOf(vararg pairs: Pair): JSONObject { + val json = JSONObject() + for ((k, v) in pairs) { + when (v) { + is Float -> json.put(k, v.toDouble()) + is Double -> json.put(k, v) + is Int -> json.put(k, v) + is Long -> json.put(k, v) + is Boolean -> json.put(k, v) + is String -> json.put(k, v) + else -> json.put(k, v) + } + } + return json +} + +@ChecksSdkIntAtLeast(parameter = 0) +fun isAtLeastApiLevel(apiLevel: Int): Boolean { + return Build.VERSION.SDK_INT >= apiLevel +} + +inline fun Any?.castTo(): T { + @Suppress("UNCHECKED_CAST") + return this as T +} + +inline fun Any?.castToOrNull(): T? { + @Suppress("UNCHECKED_CAST") + return this as? T +} \ No newline at end of file diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/Float.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/Float.kt new file mode 100644 index 00000000..2149c1a7 --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/Float.kt @@ -0,0 +1,7 @@ +package de.mm20.launcher2.ktx + +import kotlin.math.ceil + +fun Float.ceilToInt(): Int { + return ceil(this).toInt() +} diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/Fragment.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/Fragment.kt new file mode 100644 index 00000000..7faa1fbc --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/Fragment.kt @@ -0,0 +1,11 @@ +package de.mm20.launcher2.ktx + +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment + +val Fragment.dp: Float + get() = context?.dp ?: 0f + +val Fragment.sp: Float + get() = context?.sp ?: 0f diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/InputStream.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/InputStream.kt new file mode 100644 index 00000000..06d55a4f --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/InputStream.kt @@ -0,0 +1,9 @@ +package de.mm20.launcher2.ktx + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import java.io.InputStream + +fun InputStream.asBitmap(options : BitmapFactory.Options? = null): Bitmap? { + return BitmapFactory.decodeStream(this, null, options) +} \ No newline at end of file diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/Int.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/Int.kt new file mode 100644 index 00000000..c985623b --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/Int.kt @@ -0,0 +1,19 @@ +package de.mm20.launcher2.ktx + +import androidx.core.graphics.ColorUtils +import androidx.core.graphics.blue +import androidx.core.graphics.green +import androidx.core.graphics.red + +fun Int.isBrightColor(): Boolean { + val darkness = 1 - (0.299 * red + 0.587 * green + 0.114 * blue) / 255 + return darkness < 0.5 +} + +val Int.sat : Float + get() { + FloatArray(3).also { + ColorUtils.RGBToHSL(red, green, blue, it) + return it[1] + } + } \ No newline at end of file diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/List.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/List.kt new file mode 100644 index 00000000..9b8e0080 --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/List.kt @@ -0,0 +1,13 @@ +package de.mm20.launcher2.ktx + +import java.util.* + +fun List.randomElement(): T { + if (isEmpty()) throw IndexOutOfBoundsException("List is empty") + return get(Random().nextInt(size)) +} + +fun List.randomElementOrNull(): T? { + if (isEmpty()) return null + return get(Random().nextInt(size)) +} \ No newline at end of file diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/Notification.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/Notification.kt new file mode 100644 index 00000000..b5745dbd --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/Notification.kt @@ -0,0 +1,9 @@ +package de.mm20.launcher2.ktx + +import android.app.Notification +import android.content.Context +import android.graphics.drawable.Drawable + +fun Notification.getBadgeIcon(context: Context, packageName: String): Drawable? { + return smallIcon.loadDrawable(context) +} \ No newline at end of file diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/Rect.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/Rect.kt new file mode 100644 index 00000000..2eb1873a --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/Rect.kt @@ -0,0 +1,19 @@ +package de.mm20.launcher2.ktx + +import android.graphics.Rect +import android.graphics.RectF + +fun Rect.translate(x: Int, y: Int): Rect { + top += y + bottom += y + left += x + right += x + return this +} + +fun Rect.toRectF(other: RectF) { + other.left = left.toFloat() + other.bottom = bottom.toFloat() + other.right = right.toFloat() + other.top = top.toFloat() +} diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/RectF.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/RectF.kt new file mode 100644 index 00000000..78318b37 --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/RectF.kt @@ -0,0 +1,25 @@ +package de.mm20.launcher2.ktx + +import android.graphics.RectF + +fun RectF.scale(factor: Float) { + val newWidth = width() * factor + val newHeight = height() * factor + bottom += newHeight - height() + right += newWidth - width() +} + +fun RectF.translate(x: Float, y: Float): RectF { + top += y + bottom += y + left += x + right += x + return this +} + +infix fun RectF.copyTo(other: RectF) { + other.top = top + other.left = left + other.right = right + other.bottom = bottom +} \ No newline at end of file diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/Resources.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/Resources.kt new file mode 100644 index 00000000..a5425f51 --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/Resources.kt @@ -0,0 +1,29 @@ +package de.mm20.launcher2.ktx + +import android.content.res.Resources +import android.content.res.TypedArray +import android.graphics.drawable.Drawable + +fun Resources.getIntArrayOrNull(id: Int): IntArray? { + return try { + getIntArray(id) + } catch (e: Resources.NotFoundException) { + null + } +} + +fun Resources.getDrawableOrNull(id: Int, theme: Resources.Theme? = null): Drawable? { + return try { + getDrawable(id, theme) + } catch (e: Resources.NotFoundException) { + null + } +} + +fun Resources.obtainTypedArrayOrNull(id: Int): TypedArray? { + return try { + obtainTypedArray(id) + } catch (e: Resources.NotFoundException) { + null + } +} \ No newline at end of file diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/SharedPreferences.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/SharedPreferences.kt new file mode 100644 index 00000000..0b4a7acb --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/SharedPreferences.kt @@ -0,0 +1,17 @@ +package de.mm20.launcher2.ktx + +import android.content.SharedPreferences + +fun SharedPreferences.Editor.putDouble(key: String, value: Double) { + putLong(key, value.toBits()) +} + +fun SharedPreferences.getDouble(key: String): Double? { + if (contains(key)) return Double.fromBits(getLong(key, 0)) + return null +} + +fun SharedPreferences.getDouble(key: String, defValue: Double): Double { + if (contains(key)) return Double.fromBits(getLong(key, 0)) + return defValue +} \ No newline at end of file diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/String.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/String.kt new file mode 100644 index 00000000..d1fd1d1f --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/String.kt @@ -0,0 +1,7 @@ +package de.mm20.launcher2.ktx + +import java.net.URLDecoder + +fun String.decodeUrl(charset: String): String? { + return URLDecoder.decode(this, charset) +} \ No newline at end of file diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/TextView.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/TextView.kt new file mode 100644 index 00000000..97d82795 --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/TextView.kt @@ -0,0 +1,13 @@ +package de.mm20.launcher2.ktx + +import android.graphics.drawable.Drawable +import android.widget.TextView +import androidx.annotation.DrawableRes + +fun TextView.setStartCompoundDrawable(drawable: Drawable?) { + setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null) +} + +fun TextView.setStartCompoundDrawable(@DrawableRes drawableRes: Int) { + setCompoundDrawablesRelativeWithIntrinsicBounds(drawableRes, 0, 0, 0) +} \ No newline at end of file diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/UserHandle.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/UserHandle.kt new file mode 100644 index 00000000..f07b70ad --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/UserHandle.kt @@ -0,0 +1,12 @@ +package de.mm20.launcher2.ktx + +import android.content.Context +import android.os.Process +import android.os.UserHandle +import android.os.UserManager + +fun UserHandle.getSerialNumber(context: Context): Long { + if (this == Process.myUserHandle()) return 0L + val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager + return userManager.getSerialNumberForUser(this) +} \ No newline at end of file diff --git a/ktx/src/main/java/de/mm20/launcher2/ktx/View.kt b/ktx/src/main/java/de/mm20/launcher2/ktx/View.kt new file mode 100644 index 00000000..a1336b0d --- /dev/null +++ b/ktx/src/main/java/de/mm20/launcher2/ktx/View.kt @@ -0,0 +1,24 @@ +package de.mm20.launcher2.ktx + +import android.view.View +import android.view.ViewGroup +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import kotlin.coroutines.CoroutineContext + +val View.dp: Float + get() = context.dp + +val View.sp: Float + get() = context.sp + +fun View.asViewGroup(): ViewGroup? { + return this as? ViewGroup +} + +val View.lifecycleScope +get() = (context as LifecycleOwner).lifecycleScope + +fun View.setPadding(vertical: Int, horizontal: Int) { + setPadding(vertical, horizontal, vertical, horizontal) +} \ No newline at end of file diff --git a/ms-services/.gitignore b/ms-services/.gitignore new file mode 100644 index 00000000..e14e350a --- /dev/null +++ b/ms-services/.gitignore @@ -0,0 +1,2 @@ +/build +*/**/msal_auth_config.json \ No newline at end of file diff --git a/ms-services/build.gradle.kts b/ms-services/build.gradle.kts new file mode 100644 index 00000000..e84968e7 --- /dev/null +++ b/ms-services/build.gradle.kts @@ -0,0 +1,49 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.bundles.kotlin) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + + implementation(libs.microsoft.identity) + implementation(libs.microsoft.graph) + implementation(libs.guava) + + implementation(project(":crashreporter")) + implementation(project(":preferences")) +} \ No newline at end of file diff --git a/ms-services/consumer-rules.pro b/ms-services/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/ms-services/proguard-rules.pro b/ms-services/proguard-rules.pro new file mode 100644 index 00000000..01639a19 --- /dev/null +++ b/ms-services/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/ms-services/readme.md b/ms-services/readme.md new file mode 100644 index 00000000..39a828a2 --- /dev/null +++ b/ms-services/readme.md @@ -0,0 +1,41 @@ +# :ms-services + +⚠️ Depends on non-free external services. + +This module manages API calls to Microsoft APIs and connected Microsoft accounts. + +## Configuration + +This module requires additional configuration in order to work properly. You can skip this step but +then Microsoft API related features (e.g. OneDrive search) won't be available. + +In order to use Microsoft Graph APIs, you need to setup a new project in the Microsoft Azure Portal first. + +1. Open the [Microsoft Azure Portal](https://portal.azure.com) +1. Create a new project. + 1. Search for Azure Active Directory + 1. On the left side, select App registrations + 1. Add a new registration + 1. Supported account types: Personal Microsoft Accounts only +1. Add an authentication platform + 1. Go to Authentication + 1. Add a platform > Android + 1. Enter the debug package name (de.mm20.launcher2.debug) and the signature hash of your debug key + 1. You can use the following command to generate the signature hash: + `keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64` + 1. Click Configure > Done + 1. In the newly created Android section, click on Add URI + 1. Add package name (de.mm20.launcher2.release) and signature hash of your release key +1. Download the client details + 1. In the debug client row, click on View + 1. Copy the JSON below MSAL Configuration to ./src/debug/res/raw/msal_auth_config.json +1. Repeat the previous step for the release config +1. Add the required scopes + 1. Go to API permissions + 1. Add a permission + 1. Select Microsoft Graph > Delegated permissions + 1. Tick the following scopes: + - Files.Read.All + - User.Read + 1. Click Add permissions + diff --git a/ms-services/src/debug/res/raw/msal_auth_config_example.json b/ms-services/src/debug/res/raw/msal_auth_config_example.json new file mode 100644 index 00000000..7305e89e --- /dev/null +++ b/ms-services/src/debug/res/raw/msal_auth_config_example.json @@ -0,0 +1,14 @@ +{ + "client_id" : "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "authorization_user_agent" : "DEFAULT", + "account_mode": "SINGLE", + "redirect_uri" : "msauth://de.mm20.launcher2.debug/xxxxxxxxxxxxxxxxxxxxxxxxxxx", + "authorities" : [ + { + "type": "AAD", + "audience": { + "type": "PersonalMicrosoftAccount" + } + } + ] +} diff --git a/ms-services/src/main/AndroidManifest.xml b/ms-services/src/main/AndroidManifest.xml new file mode 100644 index 00000000..1dbe0cd5 --- /dev/null +++ b/ms-services/src/main/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ms-services/src/main/java/de/mm20/launcher2/msservices/DriveItem.kt b/ms-services/src/main/java/de/mm20/launcher2/msservices/DriveItem.kt new file mode 100644 index 00000000..cff2ae1b --- /dev/null +++ b/ms-services/src/main/java/de/mm20/launcher2/msservices/DriveItem.kt @@ -0,0 +1,39 @@ +package de.mm20.launcher2.msservices + +import com.microsoft.graph.extensions.DriveItem as MSDriveItem + +data class DriveItem( + val id : String, + val label : String, + val mimeType : String, + val size: Long, + val isDirectory : Boolean, + val webUrl: String, + val meta: DriveItemMeta +) { + companion object { + fun fromApiDriveItem(driveItem: MSDriveItem) : DriveItem? { + return DriveItem( + id = driveItem.id ?: return null, + label = driveItem.name ?: return null, + mimeType = driveItem.file?.mimeType ?: "inode/directory", + size = driveItem.size ?: 0, + isDirectory = driveItem.file == null, + webUrl = driveItem.webUrl ?: return null, + meta = DriveItemMeta( + owner = driveItem.shared?.owner?.user?.displayName, + createdBy = driveItem.createdBy?.user?.displayName, + width = driveItem.image?.width ?: driveItem.video?.width, + height = driveItem.image?.height ?: driveItem.video?.height + ) + ) + } + } +} + +data class DriveItemMeta( + val owner: String?, + val createdBy: String?, + val width: Int?, + val height: Int? +) \ No newline at end of file diff --git a/ms-services/src/main/java/de/mm20/launcher2/msservices/MicrosoftGraphApiHelper.kt b/ms-services/src/main/java/de/mm20/launcher2/msservices/MicrosoftGraphApiHelper.kt new file mode 100644 index 00000000..d698a503 --- /dev/null +++ b/ms-services/src/main/java/de/mm20/launcher2/msservices/MicrosoftGraphApiHelper.kt @@ -0,0 +1,195 @@ +package de.mm20.launcher2.msservices + +import android.app.Activity +import android.content.Context +import android.util.Log +import androidx.core.content.edit +import com.microsoft.graph.core.ClientException +import com.microsoft.graph.core.DefaultClientConfig +import com.microsoft.graph.extensions.GraphServiceClient +import com.microsoft.graph.extensions.IGraphServiceClient +import com.microsoft.graph.http.GraphServiceException +import com.microsoft.identity.client.AuthenticationCallback +import com.microsoft.identity.client.IAuthenticationResult +import com.microsoft.identity.client.ISingleAccountPublicClientApplication +import com.microsoft.identity.client.PublicClientApplication +import com.microsoft.identity.client.exception.MsalException +import de.mm20.launcher2.crashreporter.CrashReporter +import de.mm20.launcher2.preferences.LauncherPreferences +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.net.URLEncoder +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine + +class MicrosoftGraphApiHelper(val context: Context) { + + private var accessToken: String? = null + private val client: IGraphServiceClient + private var clientApplication: ISingleAccountPublicClientApplication? = null + + init { + client = GraphServiceClient + .Builder() + .fromConfig(DefaultClientConfig.createWithAuthenticationProvider { + it.addHeader("Authorization", "Bearer $accessToken") + }) + .buildClient() + } + + private suspend fun getClientApplication(): ISingleAccountPublicClientApplication? { + val resId = getConfigResId() + if (resId == 0) return null + if (clientApplication == null) { + clientApplication = withContext(Dispatchers.IO) { + PublicClientApplication.createSingleAccountPublicClientApplication( + context.applicationContext, + resId + ) + } + } + return clientApplication!! + } + + private suspend fun acquireAccessToken(): Boolean { + val result = withContext(Dispatchers.IO) { + try { + val application = getClientApplication() ?: return@withContext null + val authority = application.configuration.defaultAuthority.authorityURL.toString() + application.acquireTokenSilent(SCOPES, authority) + } catch (e: MsalException) { + CrashReporter.logException(e) + logout() + null + } catch (e: ClientException) { + CrashReporter.logException(e) + null + } + } + accessToken = result?.accessToken + return result != null + } + + suspend fun login(context: Activity) { + val clientApplication = getClientApplication() ?: return + suspendCoroutine { + clientApplication.signIn(context, "", SCOPES, object : AuthenticationCallback { + override fun onSuccess(authenticationResult: IAuthenticationResult?) { + accessToken = authenticationResult?.accessToken + LauncherPreferences.instance.searchOneDrive = true + it.resume(authenticationResult) + } + + override fun onCancel() { + it.resume(null) + } + + override fun onError(exception: MsalException?) { + if (exception != null) Log.e("MM20", exception.stackTraceToString()) + it.resume(null) + } + + }) + } + loadAccountName() + } + + suspend fun logout() { + accessToken = null + LauncherPreferences.instance.searchOneDrive = false + context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit { + putString(PREF_ACCOUNT_NAME, null) + } + withContext(Dispatchers.IO) { getClientApplication()?.signOut() } + } + + suspend fun getUser(): MsUser? { + val name = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).getString( + PREF_ACCOUNT_NAME, + null + ) ?: loadAccountName() + + + return name?.let { + MsUser(name = it) + } + } + + private suspend fun loadAccountName(): String? { + if (!isLoggedIn()) return null + if (!acquireAccessToken()) return null + return withContext(Dispatchers.IO) { + try { + val user = client.me.buildRequest().get() ?: return@withContext null + val name = user.displayName ?: user.mail ?: "Microsoft User" + context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit { + putString(PREF_ACCOUNT_NAME, name) + } + return@withContext name + } catch (e: GraphServiceException) { + CrashReporter.logException(e) + logout() + } catch (e: ClientException) { + CrashReporter.logException(e) + } + null + } + } + + suspend fun isLoggedIn(): Boolean { + return withContext(Dispatchers.IO) { + getClientApplication()?.currentAccount?.currentAccount != null + } + } + + + suspend fun queryOneDriveFiles(query: String): List? { + if (!acquireAccessToken()) return null + return try { + withContext(Dispatchers.IO) { + client.me.drive.getSearch( + URLEncoder.encode(query.replace("'", "''"), "utf8") + ) + .buildRequest() + .select("id,name,file,size,video,image,webUrl,shared,createdBy") + .top(10) + .get() + ?.currentPage + ?.mapNotNull { DriveItem.fromApiDriveItem(it) } + } + } catch (e: GraphServiceException) { + CrashReporter.logException(e) + null + } catch (e: ClientException) { + CrashReporter.logException(e) + null + } + } + + fun isAvailable(): Boolean { + return getConfigResId() != 0 + } + + private fun getConfigResId(): Int { + return context.resources.getIdentifier("msal_auth_config", "raw", context.packageName) + } + + companion object { + private lateinit var instance: MicrosoftGraphApiHelper + + fun getInstance(context: Context): MicrosoftGraphApiHelper { + if (!Companion::instance.isInitialized) instance = + MicrosoftGraphApiHelper(context.applicationContext) + return instance + } + + private val SCOPES = arrayOf( + "User.Read", + "Files.Read.All" + ) + + const val PREFS = "ms-account" + const val PREF_ACCOUNT_NAME = "name" + } + +} \ No newline at end of file diff --git a/ms-services/src/main/java/de/mm20/launcher2/msservices/MsUser.kt b/ms-services/src/main/java/de/mm20/launcher2/msservices/MsUser.kt new file mode 100644 index 00000000..dc17af39 --- /dev/null +++ b/ms-services/src/main/java/de/mm20/launcher2/msservices/MsUser.kt @@ -0,0 +1,5 @@ +package de.mm20.launcher2.msservices + +data class MsUser( + val name: String +) \ No newline at end of file diff --git a/ms-services/src/release/res/raw/msal_auth_config_example.json b/ms-services/src/release/res/raw/msal_auth_config_example.json new file mode 100644 index 00000000..2be2262c --- /dev/null +++ b/ms-services/src/release/res/raw/msal_auth_config_example.json @@ -0,0 +1,14 @@ +{ + "client_id" : "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "authorization_user_agent" : "DEFAULT", + "account_mode": "SINGLE", + "redirect_uri" : "msauth://de.mm20.launcher2.release/xxxxxxxxxxxxxxxxxx", + "authorities" : [ + { + "type": "AAD", + "audience": { + "type": "PersonalMicrosoftAccount" + } + } + ] +} diff --git a/music/.gitignore b/music/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/music/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/music/build.gradle.kts b/music/build.gradle.kts new file mode 100644 index 00000000..4b1e5bc1 --- /dev/null +++ b/music/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.bundles.kotlin) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.media2) + + implementation(project(":ktx")) + +} \ No newline at end of file diff --git a/music/consumer-rules.pro b/music/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/music/proguard-rules.pro b/music/proguard-rules.pro new file mode 100644 index 00000000..d99b33c9 --- /dev/null +++ b/music/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/music/src/main/AndroidManifest.xml b/music/src/main/AndroidManifest.xml new file mode 100644 index 00000000..8766846e --- /dev/null +++ b/music/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + / + \ No newline at end of file diff --git a/music/src/main/java/de/mm20/launcher2/music/MusicRepository.kt b/music/src/main/java/de/mm20/launcher2/music/MusicRepository.kt new file mode 100644 index 00000000..615acd66 --- /dev/null +++ b/music/src/main/java/de/mm20/launcher2/music/MusicRepository.kt @@ -0,0 +1,268 @@ +package de.mm20.launcher2.music + +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.media.AudioManager +import android.os.Bundle +import android.support.v4.media.session.MediaSessionCompat +import android.util.Log +import android.view.KeyEvent +import androidx.core.content.edit +import androidx.core.graphics.scale +import androidx.lifecycle.MutableLiveData +import androidx.media2.common.MediaItem +import androidx.media2.common.MediaMetadata +import androidx.media2.common.SessionPlayer +import androidx.media2.session.MediaController +import androidx.media2.session.SessionCommand +import androidx.media2.session.SessionCommandGroup +import androidx.media2.session.SessionResult +import kotlinx.coroutines.* +import java.io.File +import java.util.concurrent.Executors + +class MusicRepository private constructor(val context: Context) { + + private val scope = CoroutineScope(Job() + Dispatchers.Main) + + val playbackState = MutableLiveData() + val title = MutableLiveData() + val artist = MutableLiveData() + val album = MutableLiveData() + val albumArt = MutableLiveData() + + private var lastPlayer: String? = null + set(value) { + context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit { + putString(PREFS_KEY_LAST_PLAYER, value) + } + field = value + } + + private var lastToken: String? = null + + fun setMediaSession(token: MediaSessionCompat.Token) { + if (token.toString() == lastToken.toString()) return + mediaController?.close() + mediaController = MediaController.Builder(context) + .setSessionCompatToken(token) + .setControllerCallback(Executors.newSingleThreadExecutor(), mediaSessionCallback) + .build() + lastToken = token.toString() + } + + private var mediaController: MediaController? = null + set(value) { + if (value == null) { + playbackState.postValue(PlaybackState.Stopped) + } + field = value + } + + private val mediaSessionCallback = object : MediaController.ControllerCallback() { + override fun onConnected(controller: MediaController, allowedCommands: SessionCommandGroup) { + super.onConnected(controller, allowedCommands) + updateMetadata() + updateState() + } + + override fun onCurrentMediaItemChanged(controller: MediaController, item: MediaItem?) { + super.onCurrentMediaItemChanged(controller, item) + updateMetadata() + } + + override fun onPlayerStateChanged(controller: MediaController, state: Int) { + super.onPlayerStateChanged(controller, state) + updateState() + } + + override fun onPlaybackInfoChanged(controller: MediaController, info: MediaController.PlaybackInfo) { + super.onPlaybackInfoChanged(controller, info) + Log.d("MM20", "CurrentPosition" + controller.currentPosition.toString()) + } + + override fun onDisconnected(controller: MediaController) { + super.onDisconnected(controller) + mediaController = null + } + + /*override fun onMetadataChanged(metadata: MediaController?) { + super.onMetadataChanged(metadata) + updateState() + } + + override fun onSessionDestroyed() { + super.onSessionDestroyed() + mediaController = null + hasActiveSession.value = false + playbackState.value = PlaybackState.Stopped + }*/ + } + + private fun updateMetadata() { + val metadata = mediaController?.currentMediaItem?.metadata ?: return + val title = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE) + ?: metadata.getString(MediaMetadata.METADATA_KEY_TITLE) + val artist = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST) + ?: metadata.getString(MediaMetadata.METADATA_KEY_COMPOSER) + ?: metadata.getString(MediaMetadata.METADATA_KEY_AUTHOR) + ?: metadata.getString(MediaMetadata.METADATA_KEY_WRITER) + val album = metadata.getString(MediaMetadata.METADATA_KEY_ALBUM) + + lastPlayer = mediaController?.connectedToken?.packageName ?: lastPlayer + this@MusicRepository.title.postValue(title) + this@MusicRepository.artist.postValue(artist) + this@MusicRepository.album.postValue(album) + + scope.launch { + withContext(Dispatchers.IO) { + val albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART) + context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit { + putString(PREFS_KEY_ALBUM_ART, if (albumArt == null) "null" else "notnull") + } + if (albumArt == null) { + this@MusicRepository.albumArt.postValue(null) + return@withContext + } + val size = context.resources.getDimensionPixelSize(R.dimen.album_art_size) + val scaledBitmap = albumArt.scale(size, size) + val file = File(context.cacheDir, "album_art") + val outStream = file.outputStream() + scaledBitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream) + outStream.close() + this@MusicRepository.albumArt.postValue(scaledBitmap) + } + } + + context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit { + putString(PREFS_KEY_TITLE, title) + putString(PREFS_KEY_ARTIST, artist) + putString(PREFS_KEY_ALBUM, album) + } + } + + private fun updateState() { + val playbackState = when (mediaController?.playerState) { + SessionPlayer.PLAYER_STATE_PLAYING -> PlaybackState.Playing + SessionPlayer.PLAYER_STATE_PAUSED -> PlaybackState.Paused + else -> PlaybackState.Stopped + } + this@MusicRepository.playbackState.postValue(playbackState) + } + + val hasActiveSession: Boolean = mediaController?.isConnected != null + + init { + loadLastPlaybackMetadata() + } + + + private fun loadLastPlaybackMetadata() { + val prefs = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE) + lastPlayer = prefs.getString(PREFS_KEY_LAST_PLAYER, null) + title.value = prefs.getString(PREFS_KEY_TITLE, null) + artist.value = prefs.getString(PREFS_KEY_ARTIST, null) + album.value = prefs.getString(PREFS_KEY_ALBUM, null) + if (prefs.getString(PREFS_KEY_ALBUM_ART, "null") == "null") { + albumArt.value = null + } else scope.launch { + val albumArt = withContext(Dispatchers.IO) { + BitmapFactory.decodeFile(File(context.cacheDir, "album_art").absolutePath) + } + this@MusicRepository.albumArt.value = albumArt + } + playbackState.value = PlaybackState.Stopped + } + + fun previous() { + if (mediaController?.skipToPreviousPlaylistItem()?.get() == null) { + val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + val downEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PREVIOUS) + audioManager.dispatchMediaKeyEvent(downEvent) + val upEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PREVIOUS) + audioManager.dispatchMediaKeyEvent(upEvent) + } + } + + fun next() { + if (mediaController?.skipToNextPlaylistItem()?.get() == null) { + val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + val downEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT) + audioManager.dispatchMediaKeyEvent(downEvent) + val upEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT) + audioManager.dispatchMediaKeyEvent(upEvent) + } + } + + fun play() { + if (mediaController?.play()?.get() == null) { + val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + val downEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY) + audioManager.dispatchMediaKeyEvent(downEvent) + val upEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY) + audioManager.dispatchMediaKeyEvent(upEvent) + } + } + + fun pause() { + if (mediaController?.pause()?.get() == null) { + val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + val downEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PAUSE) + audioManager.dispatchMediaKeyEvent(downEvent) + val upEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PAUSE) + audioManager.dispatchMediaKeyEvent(upEvent) + } + } + + fun togglePause() { + if (playbackState.value != PlaybackState.Playing) play() else pause() + } + + fun getLaunchIntent(context: Context): PendingIntent { + mediaController?.sessionActivity?.let { + return it + } + val intent = Intent(Intent.ACTION_MAIN) + .setPackage(lastPlayer) + .addCategory(Intent.CATEGORY_APP_MUSIC) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + + return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE) + } + + fun openPlayerChooser(context: Context) { + context.startActivity(Intent.createChooser( + Intent(Intent.ACTION_MAIN) + .apply { + addCategory(Intent.CATEGORY_APP_MUSIC) + flags = Intent.FLAG_ACTIVITY_NEW_TASK + }, + null) + ) + } + + companion object { + private lateinit var instance: MusicRepository + + fun getInstance(context: Context): MusicRepository { + if (!::instance.isInitialized) instance = MusicRepository(context.applicationContext) + return instance + } + + private const val PREFS = "music" + private const val PREFS_KEY_TITLE = "title" + private const val PREFS_KEY_ARTIST = "artist" + private const val PREFS_KEY_ALBUM = "album" + private const val PREFS_KEY_ALBUM_ART = "album_art" + private const val PREFS_KEY_LAST_PLAYER = "last_player" + } +} + +enum class PlaybackState { + Paused, + Playing, + Stopped +} \ No newline at end of file diff --git a/music/src/main/java/de/mm20/launcher2/music/MusicViewModel.kt b/music/src/main/java/de/mm20/launcher2/music/MusicViewModel.kt new file mode 100644 index 00000000..cc3be95b --- /dev/null +++ b/music/src/main/java/de/mm20/launcher2/music/MusicViewModel.kt @@ -0,0 +1,50 @@ +package de.mm20.launcher2.music + +import android.app.Application +import android.app.PendingIntent +import android.content.Context +import android.graphics.Bitmap +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData + +class MusicViewModel(app: Application) : AndroidViewModel(app) { + + val musicRepository = MusicRepository.getInstance(app) + + val title: LiveData = musicRepository.title + val artist: LiveData = musicRepository.artist + val album: LiveData = musicRepository.album + val albumArt: LiveData = musicRepository.albumArt + val playbackState: LiveData = musicRepository.playbackState + + val hasActiveSession : Boolean = musicRepository.hasActiveSession + + fun previous() { + musicRepository.previous() + } + + fun next() { + musicRepository.next() + } + + fun play() { + musicRepository.play() + } + + fun pause() { + musicRepository.pause() + } + + fun togglePause() { + musicRepository.togglePause() + } + + fun getLaunchIntent(context: Context): PendingIntent { + return musicRepository.getLaunchIntent(context) + } + + fun openPlayerChooser(context: Context) { + musicRepository.openPlayerChooser(context) + } +} \ No newline at end of file diff --git a/music/src/main/res/values/dimens.xml b/music/src/main/res/values/dimens.xml new file mode 100644 index 00000000..c8637f62 --- /dev/null +++ b/music/src/main/res/values/dimens.xml @@ -0,0 +1,3 @@ + + 144dp + diff --git a/nextcloud/.gitignore b/nextcloud/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/nextcloud/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/nextcloud/build.gradle.kts b/nextcloud/build.gradle.kts new file mode 100644 index 00000000..f33d4826 --- /dev/null +++ b/nextcloud/build.gradle.kts @@ -0,0 +1,55 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +android { + compileSdk = sdk.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = sdk.versions.minSdk.get().toInt() + targetSdk = sdk.versions.targetSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.bundles.kotlin) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + implementation(libs.materialcomponents) + implementation(libs.androidx.browser) + implementation(libs.androidx.constraintlayout) + implementation(libs.androidx.securitycrypto) + + implementation(libs.bundles.androidx.lifecycle) + + implementation(libs.okhttp) + + api(project(":webdav")) + implementation(project(":base")) + implementation(project(":i18n")) + +} \ No newline at end of file diff --git a/nextcloud/consumer-rules.pro b/nextcloud/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/nextcloud/proguard-rules.pro b/nextcloud/proguard-rules.pro new file mode 100644 index 00000000..975872b4 --- /dev/null +++ b/nextcloud/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/nextcloud/src/main/AndroidManifest.xml b/nextcloud/src/main/AndroidManifest.xml new file mode 100644 index 00000000..929b68b3 --- /dev/null +++ b/nextcloud/src/main/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/nextcloud/src/main/java/de/mm20/launcher2/nextcloud/LoginActivity.kt b/nextcloud/src/main/java/de/mm20/launcher2/nextcloud/LoginActivity.kt new file mode 100644 index 00000000..87b2f7de --- /dev/null +++ b/nextcloud/src/main/java/de/mm20/launcher2/nextcloud/LoginActivity.kt @@ -0,0 +1,81 @@ +package de.mm20.launcher2.nextcloud + +import android.app.Activity +import android.os.Bundle +import android.webkit.WebResourceRequest +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope +import kotlinx.android.synthetic.main.activity_nextcloud_login.* +import kotlinx.coroutines.* + +class LoginActivity : AppCompatActivity() { + + private val nextcloudClient = NextcloudApiHelper(this) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_nextcloud_login) + nextButton.setOnClickListener { + serverUrlInputLayout.error = null + lifecycleScope.launch { + var url = serverUrlInput.text.toString() + if (!(url.startsWith("http://") || url.startsWith("https://"))) { + url = "https://$url" + } + if (url.isBlank()) { + serverUrlInputLayout.error = getString(R.string.next_cloud_server_url_empty) + return@launch + } + if (nextcloudClient.checkNextcloudInstallation(url)) { + openLoginPage(url) + } else { + serverUrlInputLayout.error = getString(R.string.next_cloud_server_invalid_url) + } + } + } + } + + private fun openLoginPage(url: String) { + val webView = WebView(this) + webView.settings.userAgentString = getString(R.string.app_name) + webView.webViewClient = object : WebViewClient() { + override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean { + if (request?.url?.scheme == "nc") { + val path = request.url?.path?.trim('/') ?: run { + setResult(0) + finish() + return false + } + val segments = path.split('&') + var username: String? = null + var token: String? = null + var server: String? = null + for (segment in segments) { + when { + segment.startsWith("server") -> server = segment.substringAfter(":") + segment.startsWith("user") -> username = segment.substringAfter(":") + segment.startsWith("password") -> token = segment.substringAfter(":") + } + } + if (username != null && server != null && token != null) { + nextcloudClient.setServer(server, username, token) + } + setResult(Activity.RESULT_OK) + finish() + return true + } + webView.loadUrl(request?.url?.toString() ?: "") + return false + } + } + webView.settings.javaScriptEnabled = true + setContentView(webView) + val headers = mapOf( + "OCS-APIREQUEST" to "true" + ) + webView.loadUrl("$url/index.php/login/flow", headers) + + } +} \ No newline at end of file diff --git a/nextcloud/src/main/java/de/mm20/launcher2/nextcloud/NcUser.kt b/nextcloud/src/main/java/de/mm20/launcher2/nextcloud/NcUser.kt new file mode 100644 index 00000000..67ada948 --- /dev/null +++ b/nextcloud/src/main/java/de/mm20/launcher2/nextcloud/NcUser.kt @@ -0,0 +1,6 @@ +package de.mm20.launcher2.nextcloud + +data class NcUser( + val displayName: String, + val username: String +) diff --git a/nextcloud/src/main/java/de/mm20/launcher2/nextcloud/NextcloudApiHelper.kt b/nextcloud/src/main/java/de/mm20/launcher2/nextcloud/NextcloudApiHelper.kt new file mode 100644 index 00000000..ae7e0911 --- /dev/null +++ b/nextcloud/src/main/java/de/mm20/launcher2/nextcloud/NextcloudApiHelper.kt @@ -0,0 +1,217 @@ +package de.mm20.launcher2.nextcloud + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import androidx.core.content.edit +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey +import de.mm20.launcher2.webdav.WebDavApi +import de.mm20.launcher2.webdav.WebDavFile +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import okhttp3.* +import org.json.JSONObject +import java.io.File +import java.io.IOException + +class NextcloudApiHelper(val context: Context) { + + + private val httpClient by lazy { + OkHttpClient.Builder() + .authenticator(object : Authenticator { + override fun authenticate(route: Route?, response: Response): Request? { + if (response.priorResponse?.priorResponse != null) return null + return response.request + .newBuilder() + .addHeader("Authorization", getAuthorization() ?: return null) + .build() + } + + }) + .build() + } + + private val preferences by lazy { + createPreferences() + } + + private fun createPreferences(catchErrors: Boolean = true): SharedPreferences { + try { + val masterKey = + MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build() + return EncryptedSharedPreferences.create( + context, + "nextcloud", + masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ).also { + if (!it.getBoolean("encrypted", false)) { + val legacyPrefs = + context.getSharedPreferences("nextcloud", Context.MODE_PRIVATE) + val keys = arrayOf("server", "username", "token", "displayname") + it.edit { + for (k in keys) { + putString(k, legacyPrefs.getString(k, null)) + } + putBoolean("encrypted", true) + } + legacyPrefs.edit { + for (k in keys) { + putString(k, null) + } + } + } + } + } catch (e: IOException) { + if (!catchErrors) throw e + File(context.filesDir, "../shared_prefs/nextcloud.xml").delete() + return createPreferences(false) + } + } + + fun login(activity: Activity) { + activity.startActivity(Intent(context, LoginActivity::class.java)) + } + + + suspend fun checkNextcloudInstallation(url: String): Boolean { + var url = url + if (!url.startsWith("http://") && !url.startsWith("https://")) { + url = "https://$url" + } + val request = Request.Builder() + .url("$url/remote.php/dav") + .build() + val response = runCatching { + withContext(Dispatchers.IO) { + httpClient.newCall(request).execute() + } + }.getOrNull() ?: return false + return response.code == 200 || response.code == 401 + } + + suspend fun getLoggedInUser(): NcUser? { + val server = getServer() + val username = getUserName() + val token = getToken() + + if (server == null || username == null || token == null) { + return null + } + + val displayName = getDisplayName() ?: return null + + return NcUser( + displayName, + username + ) + } + + /** + * Returns the user's display name or user name if the user is logged in and their token has + * not been revoked, + * returns null if they are not logged in. + */ + private suspend fun getDisplayName(): String? { + + val displayname = preferences.getString("displayname", null) + if (displayname != null) { + return displayname + } + + val server = getServer() ?: return null + + val request = Request.Builder() + .addHeader("OCS-APIRequest", "true") + .url("$server/ocs/v1.php/cloud/user?format=json") + .build() + + val response = runCatching { + withContext(Dispatchers.IO) { + httpClient.newCall(request).execute() + } + }.getOrNull() ?: return getUserName() + + if (response.code != 200) { + logout() + return null + } + val body = response.body ?: return getUserName() + + return withContext(Dispatchers.IO) { + val json = JSONObject(body.string()) + val name = json.optJSONObject("ocs") + ?.optJSONObject("data") + ?.optString("display-name") + + preferences.edit { + putString("displayname", name) + } + + return@withContext name + ?: getUserName() + } + } + + private fun getAuthorization(): String? { + return Credentials.basic(getUserName() ?: return null, getToken() ?: return null) + } + + fun getServer(): String? { + return preferences.getString("server", null) + } + + fun getUserName(): String? { + return preferences.getString("username", null) + } + + private fun getToken(): String? { + return preferences.getString("token", null) + } + + internal fun setServer(server: String, username: String, token: String) { + preferences.edit { + putString("server", server) + putString("username", username) + putString("token", token) + } + } + + suspend fun logout() { + val server = getServer() + val username = getUserName() + val token = getToken() + if (server == null || username == null || token == null) return + val request = Request.Builder() + .addHeader("OCS-APIREQUEST", "true") + .delete() + .url("$server/ocs/v2.php/core/apppassword") + .build() + withContext(Dispatchers.IO) { + val response = httpClient.newCall(request).execute() + response + } + preferences.edit { + putString("server", null) + putString("username", null) + putString("token", null) + putString("displayname", null) + } + } + + val files by lazy { + FilesApi() + } + + inner class FilesApi internal constructor() { + suspend fun search(query: String): List { + val server = getServer() ?: return emptyList() + val username = getUserName() ?: return emptyList() + return WebDavApi.search("$server/remote.php/dav/", username, query, httpClient) + } + } +} \ No newline at end of file diff --git a/nextcloud/src/main/res/drawable/ic_nextcloud_logo.xml b/nextcloud/src/main/res/drawable/ic_nextcloud_logo.xml new file mode 100644 index 00000000..4f2494b6 --- /dev/null +++ b/nextcloud/src/main/res/drawable/ic_nextcloud_logo.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/nextcloud/src/main/res/layout/activity_nextcloud_login.xml b/nextcloud/src/main/res/layout/activity_nextcloud_login.xml new file mode 100644 index 00000000..1842077d --- /dev/null +++ b/nextcloud/src/main/res/layout/activity_nextcloud_login.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + +