Showing preview only (881K chars total). Download the full file or copy to clipboard to get everything.
Repository: prof18/MoneyFlow
Branch: main
Commit: fd967b051efc
Files: 490
Total size: 743.0 KB
Directory structure:
gitextract_z7ii1zwp/
├── .claude/
│ └── settings.local.json
├── .github/
│ ├── FUNDING.yml
│ ├── actions/
│ │ └── setup-gradle/
│ │ └── action.yml
│ └── workflows/
│ ├── android-release.yml
│ ├── checks.yml
│ ├── ios-release.yml
│ ├── release.yml
│ └── roborazzi.yml
├── .gitignore
├── AGENTS.md
├── README.md
├── androidApp/
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src/
│ ├── debug/
│ │ └── res/
│ │ └── mipmap-anydpi-v26/
│ │ └── ic_launcher.xml
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── kotlin/
│ │ └── com/
│ │ └── prof18/
│ │ └── moneyflow/
│ │ ├── MainActivity.kt
│ │ └── MoneyFlowApp.kt
│ └── release/
│ └── res/
│ └── mipmap-anydpi-v26/
│ └── ic_launcher.xml
├── build-logic/
│ ├── convention/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── main/
│ │ └── kotlin/
│ │ └── DetektConventionPlugin.kt
│ └── settings.gradle.kts
├── build.gradle.kts
├── config/
│ └── detekt/
│ └── detekt.yml
├── gradle/
│ ├── libs.versions.toml
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── iosApp/
│ ├── .scripts/
│ │ └── version.sh
│ ├── Assets/
│ │ ├── DebugIcon.icon/
│ │ │ └── icon.json
│ │ ├── Icon.icon/
│ │ │ └── icon.json
│ │ └── Info.plist
│ ├── Configuration/
│ │ └── Config.xcconfig
│ ├── MoneyFlow.entitlements
│ ├── MoneyFlow.xcodeproj/
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace/
│ │ │ └── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── MoneyFlow.xcscheme
│ └── Source/
│ ├── ContentView.swift
│ ├── DI/
│ │ └── Koin.swift
│ └── MoneyFlowApp.swift
├── renovate.json
├── settings.gradle.kts
├── setup.sh
├── shared/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── androidMain/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── prof18/
│ │ │ └── moneyflow/
│ │ │ ├── AndroidBiometricAuthenticator.kt
│ │ │ ├── AndroidBiometricAvailabilityChecker.kt
│ │ │ ├── database/
│ │ │ │ └── DatabaseDriverFactory.kt
│ │ │ ├── di/
│ │ │ │ └── KoinAndroid.kt
│ │ │ └── utils/
│ │ │ ├── LocalAppLocale.android.kt
│ │ │ └── LocalAppTheme.android.kt
│ │ └── res/
│ │ ├── values/
│ │ │ └── themes.xml
│ │ └── values-night/
│ │ └── themes.xml
│ ├── androidUnitTest/
│ │ ├── AndroidManifest.xml
│ │ └── kotlin/
│ │ └── com/
│ │ └── prof18/
│ │ └── moneyflow/
│ │ ├── AddTransactionRoborazziTest.kt
│ │ ├── AllTransactionsRoborazziTest.kt
│ │ ├── AuthRoborazziTest.kt
│ │ ├── BudgetAndRecapRoborazziTest.kt
│ │ ├── CategoriesRoborazziTest.kt
│ │ ├── ComponentsRoborazziTest.kt
│ │ ├── HomeRoborazziTest.kt
│ │ ├── MoneyFlowLockedRoborazziTest.kt
│ │ ├── MoneyFlowNavHostRoborazziTest.kt
│ │ ├── RoborazziRule.kt
│ │ ├── RoborazziTestBase.kt
│ │ ├── SettingsRoborazziTest.kt
│ │ └── utilities/
│ │ └── TestUtilsAndroid.kt
│ ├── commonMain/
│ │ ├── composeResources/
│ │ │ ├── drawable/
│ │ │ │ ├── ic_address_book.xml
│ │ │ │ ├── ic_address_card.xml
│ │ │ │ ├── ic_adjust_solid.xml
│ │ │ │ ├── ic_air_freshener_solid.xml
│ │ │ │ ├── ic_algolia.xml
│ │ │ │ ├── ic_allergies_solid.xml
│ │ │ │ ├── ic_ambulance_solid.xml
│ │ │ │ ├── ic_anchor_solid.xml
│ │ │ │ ├── ic_android.xml
│ │ │ │ ├── ic_angle_down_solid.xml
│ │ │ │ ├── ic_angle_left_solid.xml
│ │ │ │ ├── ic_angle_right_solid.xml
│ │ │ │ ├── ic_angle_up_solid.xml
│ │ │ │ ├── ic_apple.xml
│ │ │ │ ├── ic_apple_alt_solid.xml
│ │ │ │ ├── ic_archive_solid.xml
│ │ │ │ ├── ic_archway_solid.xml
│ │ │ │ ├── ic_arrow_down_rotate.xml
│ │ │ │ ├── ic_arrow_down_solid.xml
│ │ │ │ ├── ic_arrow_left_solid.xml
│ │ │ │ ├── ic_arrow_right_solid.xml
│ │ │ │ ├── ic_arrow_up_rotate.xml
│ │ │ │ ├── ic_arrow_up_solid.xml
│ │ │ │ ├── ic_asterisk_solid.xml
│ │ │ │ ├── ic_at_solid.xml
│ │ │ │ ├── ic_atlas_solid.xml
│ │ │ │ ├── ic_atom_solid.xml
│ │ │ │ ├── ic_award_solid.xml
│ │ │ │ ├── ic_baby_carriage_solid.xml
│ │ │ │ ├── ic_bacon_solid.xml
│ │ │ │ ├── ic_balance_scale_left_solid.xml
│ │ │ │ ├── ic_band_aid_solid.xml
│ │ │ │ ├── ic_baseball_ball_solid.xml
│ │ │ │ ├── ic_basketball_ball_solid.xml
│ │ │ │ ├── ic_bath_solid.xml
│ │ │ │ ├── ic_battery_three_quarters_solid.xml
│ │ │ │ ├── ic_bed_solid.xml
│ │ │ │ ├── ic_beer_solid.xml
│ │ │ │ ├── ic_bell.xml
│ │ │ │ ├── ic_bell_slash.xml
│ │ │ │ ├── ic_bicycle_solid.xml
│ │ │ │ ├── ic_biking_solid.xml
│ │ │ │ ├── ic_binoculars_solid.xml
│ │ │ │ ├── ic_birthday_cake_solid.xml
│ │ │ │ ├── ic_bitcoin.xml
│ │ │ │ ├── ic_black_tie.xml
│ │ │ │ ├── ic_blender_solid.xml
│ │ │ │ ├── ic_blind_solid.xml
│ │ │ │ ├── ic_bolt_solid.xml
│ │ │ │ ├── ic_bomb_solid.xml
│ │ │ │ ├── ic_bone_solid.xml
│ │ │ │ ├── ic_bong_solid.xml
│ │ │ │ ├── ic_book_open_solid.xml
│ │ │ │ ├── ic_book_solid.xml
│ │ │ │ ├── ic_bookmark.xml
│ │ │ │ ├── ic_bowling_ball_solid.xml
│ │ │ │ ├── ic_box_solid.xml
│ │ │ │ ├── ic_brain_solid.xml
│ │ │ │ ├── ic_bread_slice_solid.xml
│ │ │ │ ├── ic_briefcase_medical_solid.xml
│ │ │ │ ├── ic_briefcase_solid.xml
│ │ │ │ ├── ic_broadcast_tower_solid.xml
│ │ │ │ ├── ic_broom_solid.xml
│ │ │ │ ├── ic_brush_solid.xml
│ │ │ │ ├── ic_bug_solid.xml
│ │ │ │ ├── ic_building.xml
│ │ │ │ ├── ic_bullhorn_solid.xml
│ │ │ │ ├── ic_bullseye_solid.xml
│ │ │ │ ├── ic_burn_solid.xml
│ │ │ │ ├── ic_bus_solid.xml
│ │ │ │ ├── ic_calculator_solid.xml
│ │ │ │ ├── ic_calendar.xml
│ │ │ │ ├── ic_camera_solid.xml
│ │ │ │ ├── ic_campground_solid.xml
│ │ │ │ ├── ic_candy_cane_solid.xml
│ │ │ │ ├── ic_capsules_solid.xml
│ │ │ │ ├── ic_car_alt_solid.xml
│ │ │ │ ├── ic_car_side_solid.xml
│ │ │ │ ├── ic_caret_down_solid.xml
│ │ │ │ ├── ic_caret_left_solid.xml
│ │ │ │ ├── ic_caret_right_solid.xml
│ │ │ │ ├── ic_caret_up_solid.xml
│ │ │ │ ├── ic_carrot_solid.xml
│ │ │ │ ├── ic_cart_arrow_down_solid.xml
│ │ │ │ ├── ic_cash_register_solid.xml
│ │ │ │ ├── ic_cat_solid.xml
│ │ │ │ ├── ic_certificate_solid.xml
│ │ │ │ ├── ic_chair_solid.xml
│ │ │ │ ├── ic_chalkboard_solid.xml
│ │ │ │ ├── ic_chalkboard_teacher_solid.xml
│ │ │ │ ├── ic_charging_station_solid.xml
│ │ │ │ ├── ic_chart_area_solid.xml
│ │ │ │ ├── ic_chart_bar.xml
│ │ │ │ ├── ic_chart_line_solid.xml
│ │ │ │ ├── ic_chart_pie_solid.xml
│ │ │ │ ├── ic_check_circle.xml
│ │ │ │ ├── ic_cheese_solid.xml
│ │ │ │ ├── ic_church_solid.xml
│ │ │ │ ├── ic_city_solid.xml
│ │ │ │ ├── ic_clinic_medical_solid.xml
│ │ │ │ ├── ic_clipboard.xml
│ │ │ │ ├── ic_clock.xml
│ │ │ │ ├── ic_cloud_download_alt_solid.xml
│ │ │ │ ├── ic_cloud_solid.xml
│ │ │ │ ├── ic_cloud_upload_alt_solid.xml
│ │ │ │ ├── ic_cocktail_solid.xml
│ │ │ │ ├── ic_code_branch_solid.xml
│ │ │ │ ├── ic_code_solid.xml
│ │ │ │ ├── ic_coffee_solid.xml
│ │ │ │ ├── ic_cog_solid.xml
│ │ │ │ ├── ic_coins_solid.xml
│ │ │ │ ├── ic_comment_alt.xml
│ │ │ │ ├── ic_compact_disc_solid.xml
│ │ │ │ ├── ic_compass.xml
│ │ │ │ ├── ic_concierge_bell_solid.xml
│ │ │ │ ├── ic_cookie_bite_solid.xml
│ │ │ │ ├── ic_couch_solid.xml
│ │ │ │ ├── ic_credit_card.xml
│ │ │ │ ├── ic_crown_solid.xml
│ │ │ │ ├── ic_cubes_solid.xml
│ │ │ │ ├── ic_cut_solid.xml
│ │ │ │ ├── ic_desktop_solid.xml
│ │ │ │ ├── ic_diaspora.xml
│ │ │ │ ├── ic_dice_d6_solid.xml
│ │ │ │ ├── ic_dna_solid.xml
│ │ │ │ ├── ic_dog_solid.xml
│ │ │ │ ├── ic_dollar_sign.xml
│ │ │ │ ├── ic_dollar_sign_solid.xml
│ │ │ │ ├── ic_dolly_flatbed_solid.xml
│ │ │ │ ├── ic_dolly_solid.xml
│ │ │ │ ├── ic_donate_solid.xml
│ │ │ │ ├── ic_drafting_compass_solid.xml
│ │ │ │ ├── ic_drum_solid.xml
│ │ │ │ ├── ic_drumstick_bite_solid.xml
│ │ │ │ ├── ic_dumbbell_solid.xml
│ │ │ │ ├── ic_dumpster_solid.xml
│ │ │ │ ├── ic_edit.xml
│ │ │ │ ├── ic_egg_solid.xml
│ │ │ │ ├── ic_envelope.xml
│ │ │ │ ├── ic_envelope_open.xml
│ │ │ │ ├── ic_eraser_solid.xml
│ │ │ │ ├── ic_euro_sign.xml
│ │ │ │ ├── ic_euro_sign_solid.xml
│ │ │ │ ├── ic_exchange_alt_solid.xml
│ │ │ │ ├── ic_exclamation_circle_solid.xml
│ │ │ │ ├── ic_exclamation_triangle_solid.xml
│ │ │ │ ├── ic_expeditedssl.xml
│ │ │ │ ├── ic_external_link_alt_solid.xml
│ │ │ │ ├── ic_eye_dropper_solid.xml
│ │ │ │ ├── ic_fan_solid.xml
│ │ │ │ ├── ic_fax_solid.xml
│ │ │ │ ├── ic_feather_alt_solid.xml
│ │ │ │ ├── ic_female_solid.xml
│ │ │ │ ├── ic_fighter_jet_solid.xml
│ │ │ │ ├── ic_file.xml
│ │ │ │ ├── ic_file_alt.xml
│ │ │ │ ├── ic_file_audio.xml
│ │ │ │ ├── ic_file_code.xml
│ │ │ │ ├── ic_file_csv_solid.xml
│ │ │ │ ├── ic_file_export_solid.xml
│ │ │ │ ├── ic_file_import_solid.xml
│ │ │ │ ├── ic_file_invoice_dollar_solid.xml
│ │ │ │ ├── ic_file_invoice_solid.xml
│ │ │ │ ├── ic_file_pdf.xml
│ │ │ │ ├── ic_fill_solid.xml
│ │ │ │ ├── ic_film_solid.xml
│ │ │ │ ├── ic_fire_alt_solid.xml
│ │ │ │ ├── ic_fire_extinguisher_solid.xml
│ │ │ │ ├── ic_first_aid_solid.xml
│ │ │ │ ├── ic_fish_solid.xml
│ │ │ │ ├── ic_flag.xml
│ │ │ │ ├── ic_flag_checkered_solid.xml
│ │ │ │ ├── ic_flask_solid.xml
│ │ │ │ ├── ic_fly.xml
│ │ │ │ ├── ic_folder.xml
│ │ │ │ ├── ic_football_ball_solid.xml
│ │ │ │ ├── ic_fort_awesome.xml
│ │ │ │ ├── ic_frown.xml
│ │ │ │ ├── ic_futbol.xml
│ │ │ │ ├── ic_gamepad_solid.xml
│ │ │ │ ├── ic_gas_pump_solid.xml
│ │ │ │ ├── ic_gavel_solid.xml
│ │ │ │ ├── ic_gift_solid.xml
│ │ │ │ ├── ic_glass_cheers_solid.xml
│ │ │ │ ├── ic_glass_martini_alt_solid.xml
│ │ │ │ ├── ic_globe_solid.xml
│ │ │ │ ├── ic_golf_ball_solid.xml
│ │ │ │ ├── ic_gopuram_solid.xml
│ │ │ │ ├── ic_graduation_cap_solid.xml
│ │ │ │ ├── ic_guitar_solid.xml
│ │ │ │ ├── ic_hamburger_solid.xml
│ │ │ │ ├── ic_hammer_solid.xml
│ │ │ │ ├── ic_hat_cowboy_solid.xml
│ │ │ │ ├── ic_hdd.xml
│ │ │ │ ├── ic_headphones_solid.xml
│ │ │ │ ├── ic_helicopter_solid.xml
│ │ │ │ ├── ic_highlighter_solid.xml
│ │ │ │ ├── ic_hiking_solid.xml
│ │ │ │ ├── ic_home_solid.xml
│ │ │ │ ├── ic_horse_head_solid.xml
│ │ │ │ ├── ic_hospital.xml
│ │ │ │ ├── ic_hotdog_solid.xml
│ │ │ │ ├── ic_hourglass_half_solid.xml
│ │ │ │ ├── ic_ice_cream_solid.xml
│ │ │ │ ├── ic_id_card.xml
│ │ │ │ ├── ic_image.xml
│ │ │ │ ├── ic_inbox_solid.xml
│ │ │ │ ├── ic_industry_solid.xml
│ │ │ │ ├── ic_itunes_note.xml
│ │ │ │ ├── ic_key_solid.xml
│ │ │ │ ├── ic_keyboard.xml
│ │ │ │ ├── ic_landmark_solid.xml
│ │ │ │ ├── ic_laptop_solid.xml
│ │ │ │ ├── ic_lightbulb.xml
│ │ │ │ ├── ic_list_ul_solid.xml
│ │ │ │ ├── ic_luggage_cart_solid.xml
│ │ │ │ ├── ic_mail_bulk_solid.xml
│ │ │ │ ├── ic_male_solid.xml
│ │ │ │ ├── ic_map_marked_alt_solid.xml
│ │ │ │ ├── ic_marker_solid.xml
│ │ │ │ ├── ic_mars_solid.xml
│ │ │ │ ├── ic_mask_solid.xml
│ │ │ │ ├── ic_medal_solid.xml
│ │ │ │ ├── ic_medapps.xml
│ │ │ │ ├── ic_medkit_solid.xml
│ │ │ │ ├── ic_mercury_solid.xml
│ │ │ │ ├── ic_microchip_solid.xml
│ │ │ │ ├── ic_microphone_alt_solid.xml
│ │ │ │ ├── ic_microscope_solid.xml
│ │ │ │ ├── ic_mobile_solid.xml
│ │ │ │ ├── ic_money_bill_wave.xml
│ │ │ │ ├── ic_money_check_alt_solid.xml
│ │ │ │ ├── ic_mortar_pestle_solid.xml
│ │ │ │ ├── ic_motorcycle_solid.xml
│ │ │ │ ├── ic_mountain_solid.xml
│ │ │ │ ├── ic_mug_hot_solid.xml
│ │ │ │ ├── ic_oil_can_solid.xml
│ │ │ │ ├── ic_pager_solid.xml
│ │ │ │ ├── ic_paint_roller_solid.xml
│ │ │ │ ├── ic_paperclip_solid.xml
│ │ │ │ ├── ic_parachute_box_solid.xml
│ │ │ │ ├── ic_parking_solid.xml
│ │ │ │ ├── ic_passport_solid.xml
│ │ │ │ ├── ic_paw_solid.xml
│ │ │ │ ├── ic_pen_alt_solid.xml
│ │ │ │ ├── ic_pen_solid.xml
│ │ │ │ ├── ic_phone_solid.xml
│ │ │ │ ├── ic_photo_video_solid.xml
│ │ │ │ ├── ic_piggy_bank_solid.xml
│ │ │ │ ├── ic_pills_solid.xml
│ │ │ │ ├── ic_pizza_slice_solid.xml
│ │ │ │ ├── ic_plane_solid.xml
│ │ │ │ ├── ic_plug_solid.xml
│ │ │ │ ├── ic_pound_sign.xml
│ │ │ │ ├── ic_pound_sign_solid.xml
│ │ │ │ ├── ic_prescription_bottle_solid.xml
│ │ │ │ ├── ic_print_solid.xml
│ │ │ │ ├── ic_question_circle.xml
│ │ │ │ ├── ic_readme.xml
│ │ │ │ ├── ic_recycle_solid.xml
│ │ │ │ ├── ic_restroom_solid.xml
│ │ │ │ ├── ic_road_solid.xml
│ │ │ │ ├── ic_robot_solid.xml
│ │ │ │ ├── ic_rocket_solid.xml
│ │ │ │ ├── ic_running_solid.xml
│ │ │ │ ├── ic_screwdriver_solid.xml
│ │ │ │ ├── ic_scroll_solid.xml
│ │ │ │ ├── ic_seedling_solid.xml
│ │ │ │ ├── ic_server_solid.xml
│ │ │ │ ├── ic_shield_alt_solid.xml
│ │ │ │ ├── ic_ship_solid.xml
│ │ │ │ ├── ic_shipping_fast_solid.xml
│ │ │ │ ├── ic_shopping_bag_solid.xml
│ │ │ │ ├── ic_shopping_cart_solid.xml
│ │ │ │ ├── ic_shuttle_van_solid.xml
│ │ │ │ ├── ic_signal_solid.xml
│ │ │ │ ├── ic_sim_card_solid.xml
│ │ │ │ ├── ic_skating_solid.xml
│ │ │ │ ├── ic_skiing_nordic_solid.xml
│ │ │ │ ├── ic_skiing_solid.xml
│ │ │ │ ├── ic_smoking_solid.xml
│ │ │ │ ├── ic_sms_solid.xml
│ │ │ │ ├── ic_snowboarding_solid.xml
│ │ │ │ ├── ic_snowflake.xml
│ │ │ │ ├── ic_socks_solid.xml
│ │ │ │ ├── ic_spider_solid.xml
│ │ │ │ ├── ic_spray_can_solid.xml
│ │ │ │ ├── ic_stamp_solid.xml
│ │ │ │ ├── ic_star_of_life_solid.xml
│ │ │ │ ├── ic_stethoscope_solid.xml
│ │ │ │ ├── ic_sticky_note.xml
│ │ │ │ ├── ic_stopwatch_solid.xml
│ │ │ │ ├── ic_store_alt_solid.xml
│ │ │ │ ├── ic_subway_solid.xml
│ │ │ │ ├── ic_suitcase_solid.xml
│ │ │ │ ├── ic_swimmer_solid.xml
│ │ │ │ ├── ic_syringe_solid.xml
│ │ │ │ ├── ic_table_tennis_solid.xml
│ │ │ │ ├── ic_tablet_solid.xml
│ │ │ │ ├── ic_tachometer_alt_solid.xml
│ │ │ │ ├── ic_tag_solid.xml
│ │ │ │ ├── ic_taxi_solid.xml
│ │ │ │ ├── ic_temperature_high_solid.xml
│ │ │ │ ├── ic_terminal_solid.xml
│ │ │ │ ├── ic_theater_masks_solid.xml
│ │ │ │ ├── ic_thermometer_full_solid.xml
│ │ │ │ ├── ic_ticket_alt_solid.xml
│ │ │ │ ├── ic_tint_solid.xml
│ │ │ │ ├── ic_toilet_paper_solid.xml
│ │ │ │ ├── ic_toolbox_solid.xml
│ │ │ │ ├── ic_tools_solid.xml
│ │ │ │ ├── ic_tooth_solid.xml
│ │ │ │ ├── ic_tractor_solid.xml
│ │ │ │ ├── ic_train_solid.xml
│ │ │ │ ├── ic_trash_alt.xml
│ │ │ │ ├── ic_tree_solid.xml
│ │ │ │ ├── ic_trophy_solid.xml
│ │ │ │ ├── ic_truck_loading_solid.xml
│ │ │ │ ├── ic_truck_moving_solid.xml
│ │ │ │ ├── ic_truck_pickup_solid.xml
│ │ │ │ ├── ic_tshirt_solid.xml
│ │ │ │ ├── ic_tv_solid.xml
│ │ │ │ ├── ic_university_solid.xml
│ │ │ │ ├── ic_user.xml
│ │ │ │ ├── ic_user_friends_solid.xml
│ │ │ │ ├── ic_utensils_solid.xml
│ │ │ │ ├── ic_venus_solid.xml
│ │ │ │ ├── ic_vial_solid.xml
│ │ │ │ ├── ic_video_solid.xml
│ │ │ │ ├── ic_volleyball_ball_solid.xml
│ │ │ │ ├── ic_volume_up_solid.xml
│ │ │ │ ├── ic_walking_solid.xml
│ │ │ │ ├── ic_wallet_solid.xml
│ │ │ │ ├── ic_wine_glass_solid.xml
│ │ │ │ ├── ic_wrench_solid.xml
│ │ │ │ └── ic_yen_sign_solid.xml
│ │ │ └── values/
│ │ │ └── strings.xml
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── prof18/
│ │ │ └── moneyflow/
│ │ │ ├── MainViewModel.kt
│ │ │ ├── data/
│ │ │ │ ├── MoneyRepository.kt
│ │ │ │ ├── SettingsRepository.kt
│ │ │ │ └── settings/
│ │ │ │ └── SettingsSource.kt
│ │ │ ├── database/
│ │ │ │ ├── DatabaseHelper.kt
│ │ │ │ ├── default/
│ │ │ │ │ └── DefaultValues.kt
│ │ │ │ └── model/
│ │ │ │ └── TransactionType.kt
│ │ │ ├── di/
│ │ │ │ └── Koin.kt
│ │ │ ├── domain/
│ │ │ │ └── entities/
│ │ │ │ ├── BalanceRecap.kt
│ │ │ │ ├── Category.kt
│ │ │ │ ├── CurrencyConfig.kt
│ │ │ │ ├── DBImportExportException.kt
│ │ │ │ ├── MoneyFlowError.kt
│ │ │ │ ├── MoneyFlowResult.kt
│ │ │ │ ├── MoneySummary.kt
│ │ │ │ ├── MoneyTransaction.kt
│ │ │ │ └── TransactionTypeUI.kt
│ │ │ ├── features/
│ │ │ │ ├── addtransaction/
│ │ │ │ │ └── AddTransactionViewModel.kt
│ │ │ │ ├── alltransactions/
│ │ │ │ │ └── AllTransactionsViewModel.kt
│ │ │ │ ├── authentication/
│ │ │ │ │ └── BiometricAuthenticator.kt
│ │ │ │ ├── categories/
│ │ │ │ │ └── CategoriesViewModel.kt
│ │ │ │ ├── home/
│ │ │ │ │ └── HomeViewModel.kt
│ │ │ │ └── settings/
│ │ │ │ ├── BiometricAvailabilityChecker.kt
│ │ │ │ └── SettingsViewModel.kt
│ │ │ ├── navigation/
│ │ │ │ ├── AppRoute.kt
│ │ │ │ └── MoneyFlowNavHost.kt
│ │ │ ├── presentation/
│ │ │ │ ├── MoneyFlowApp.kt
│ │ │ │ ├── MoneyFlowErrorMapper.kt
│ │ │ │ ├── addtransaction/
│ │ │ │ │ ├── AddTransactionAction.kt
│ │ │ │ │ ├── AddTransactionScreen.kt
│ │ │ │ │ ├── TransactionToSave.kt
│ │ │ │ │ └── components/
│ │ │ │ │ ├── IconTextClicableRow.kt
│ │ │ │ │ ├── MFTextField.kt
│ │ │ │ │ └── TransactionTypeTabBar.kt
│ │ │ │ ├── alltransactions/
│ │ │ │ │ └── AllTransactionsScreen.kt
│ │ │ │ ├── auth/
│ │ │ │ │ ├── AuthInProgressScreen.kt
│ │ │ │ │ └── AuthState.kt
│ │ │ │ ├── budget/
│ │ │ │ │ └── BudgetScreen.kt
│ │ │ │ ├── categories/
│ │ │ │ │ ├── CategoriesScreen.kt
│ │ │ │ │ ├── CategoryModel.kt
│ │ │ │ │ ├── IconCategoryMapper.kt
│ │ │ │ │ ├── components/
│ │ │ │ │ │ └── CategoryCard.kt
│ │ │ │ │ └── data/
│ │ │ │ │ └── CategoryUIData.kt
│ │ │ │ ├── home/
│ │ │ │ │ ├── HomeModel.kt
│ │ │ │ │ ├── HomeScreen.kt
│ │ │ │ │ └── components/
│ │ │ │ │ ├── HeaderNavigator.kt
│ │ │ │ │ └── HomeRecap.kt
│ │ │ │ ├── model/
│ │ │ │ │ ├── CategoryIcon.kt
│ │ │ │ │ ├── UIErrorMessage.kt
│ │ │ │ │ └── UIErrorMessageFactory.kt
│ │ │ │ ├── recap/
│ │ │ │ │ └── RecapScreen.kt
│ │ │ │ └── settings/
│ │ │ │ └── SettingsScreen.kt
│ │ │ ├── ui/
│ │ │ │ ├── components/
│ │ │ │ │ ├── ArrowCircleIcon.kt
│ │ │ │ │ ├── ErrorView.kt
│ │ │ │ │ ├── HideableTextField.kt
│ │ │ │ │ ├── Loader.kt
│ │ │ │ │ ├── MFTopBar.kt
│ │ │ │ │ ├── SwitchWithText.kt
│ │ │ │ │ └── TransactionCard.kt
│ │ │ │ └── style/
│ │ │ │ ├── Color.kt
│ │ │ │ ├── Margins.kt
│ │ │ │ ├── Shape.kt
│ │ │ │ ├── Theme.kt
│ │ │ │ └── Typography.kt
│ │ │ └── utils/
│ │ │ ├── CurrencyFormatter.kt
│ │ │ ├── DispatcherProvider.kt
│ │ │ ├── LocalAppDensity.kt
│ │ │ ├── LocalAppLocale.kt
│ │ │ ├── LocalAppTheme.kt
│ │ │ └── Utils.kt
│ │ └── sqldelight/
│ │ └── com/
│ │ └── prof18/
│ │ └── moneyflow/
│ │ └── db/
│ │ ├── AccountTable.sq
│ │ ├── CategoryTable.sq
│ │ └── TransactionTable.sq
│ ├── commonTest/
│ │ └── kotlin/
│ │ └── com/
│ │ └── prof18/
│ │ └── moneyflow/
│ │ └── utilities/
│ │ └── TestDatabaseHelper.kt
│ ├── iosMain/
│ │ └── kotlin/
│ │ └── com/
│ │ └── prof18/
│ │ └── moneyflow/
│ │ ├── IosBiometricAuthenticator.kt
│ │ ├── IosBiometricAvailabilityChecker.kt
│ │ ├── MainViewController.kt
│ │ ├── database/
│ │ │ └── DatabaseDriverFactory.kt
│ │ ├── di/
│ │ │ └── KoinIos.kt
│ │ └── utils/
│ │ ├── LocalAppLocale.ios.kt
│ │ └── LocalAppTheme.ios.kt
│ └── iosTest/
│ └── kotlin/
│ └── com/
│ └── prof18/
│ └── moneyflow/
│ └── utilities/
│ ├── DatabaseHelperIosTest.kt
│ └── TestUtilsIos.kt
├── version.properties
└── versioning.gradle.kts
================================================
FILE CONTENTS
================================================
================================================
FILE: .claude/settings.local.json
================================================
{
"permissions": {
"allow": [
"Bash(lipo -info:*)",
"Bash(./gradlew:*)",
"Bash(find:*)",
"Bash(wc:*)",
"Bash(cat:*)"
],
"deny": [],
"ask": []
}
}
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
custom: https://www.paypal.me/MarcoGomiero
================================================
FILE: .github/actions/setup-gradle/action.yml
================================================
name: Setup Environment
description: Setup the environment (Java and Gradle)
inputs:
gradle-cache-encryption-key:
description: 'The encryption key to use for the Gradle cache'
required: true
runs:
using: 'composite'
steps:
- name: set up JDK
uses: actions/setup-java@v5
with:
distribution: 'zulu'
java-version: 21
- uses: gradle/actions/setup-gradle@v5
with:
gradle-home-cache-cleanup: true
cache-encryption-key: ${{ inputs.gradle-cache-encryption-key }}
- name: Setup Gradle Properties
shell: bash
run: |
echo "org.gradle.jvmargs=-Xms10g -Xmx10g -XX:MaxMetaspaceSize=1g -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC" >> ~/.gradle/gradle.properties
echo "kotlin.daemon.jvmargs=-Xms4g -Xmx4g -XX:+UseParallelGC" >> ~/.gradle/gradle.properties
echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties
echo "org.gradle.workers.max=2" >> ~/.gradle/gradle.properties
echo "org.gradle.vfs.watch=false" >> ~/.gradle/gradle.properties
- name: Cache KMP tooling
uses: actions/cache@v4
with:
path: |
~/.konan
key: ${{ runner.os }}-v1-${{ hashFiles('**/libs.versions.toml') }}
================================================
FILE: .github/workflows/android-release.yml
================================================
name: Android Release
on:
workflow_call:
secrets:
GRADLE_CACHE_ENCRYPTION_KEY:
required: true
KEYSTORE_FILE:
required: true
KEYSTORE_PASSPHRASE:
required: true
KEYSTORE_KEY_ALIAS:
required: true
KEYSTORE_KEY_PASSWORD:
required: true
KEYSTORE_STORE_PASSWORD:
required: true
PLAY_CONFIG:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Setup environment
uses: ./.github/actions/setup-gradle
with:
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
- name: Configure Keystore
run: |
echo '${{ secrets.KEYSTORE_FILE }}' > release.keystore.asc
gpg -d --passphrase '${{ secrets.KEYSTORE_PASSPHRASE }}' --batch release.keystore.asc > androidApp/release.keystore
echo "storeFile=release.keystore" >> keystore.properties
echo "keyAlias=$KEYSTORE_KEY_ALIAS" >> keystore.properties
echo "storePassword=$KEYSTORE_STORE_PASSWORD" >> keystore.properties
echo "keyPassword=$KEYSTORE_KEY_PASSWORD" >> keystore.properties
env:
KEYSTORE_KEY_ALIAS: ${{ secrets.KEYSTORE_KEY_ALIAS }}
KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }}
KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }}
- name: Create Google Play Config file
run: |
echo "$PLAY_CONFIG_JSON" > play_config.json.b64
base64 -d -i play_config.json.b64 > play_config.json
env:
PLAY_CONFIG_JSON: ${{ secrets.PLAY_CONFIG }}
- name: Distribute app to Alpha track
run: ./gradlew :androidApp:bundleRelease :androidApp:publishReleaseBundle
================================================
FILE: .github/workflows/checks.yml
================================================
name: Code Checks
on:
push:
branches:
- '*'
pull_request:
branches:
- '*'
jobs:
checks:
runs-on: macos-15
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
- name: Setup environment
uses: ./.github/actions/setup-gradle
with:
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
- name: Run Checks
run: ./gradlew check
- name: Upload reports
if: failure()
uses: actions/upload-artifact@v5
with:
name: build-reports
path: |
**/build/reports/*
build-android-app:
name: Build Android App
runs-on: ubuntu-latest
needs: [ checks ]
steps:
- uses: actions/checkout@v6
- name: Setup environment
uses: ./.github/actions/setup-gradle
with:
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
- name: Build Android Sample
run: ./gradlew :androidApp:assembleDebug
build-ios-app:
name: Build iOS App
runs-on: macos-15
needs: [ checks ]
steps:
- uses: actions/checkout@v6
- name: Xcode version
run: |
/usr/bin/xcodebuild -version
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
- name: Xcode version
run: |
/usr/bin/xcodebuild -version
- name: Setup environment
uses: ./.github/actions/setup-gradle
with:
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
- name: Build iOS Sample
run: |
cd iosApp
xcodebuild -configuration Debug -scheme MoneyFlow -sdk iphoneos -destination name='iPhone 17 Pro' build | xcbeautify --renderer github-actions
================================================
FILE: .github/workflows/ios-release.yml
================================================
name: iOS Release
on:
workflow_call:
secrets:
GRADLE_CACHE_ENCRYPTION_KEY:
required: true
CERTIFICATES_P12:
required: true
CERTIFICATES_PASSWORD:
required: true
BUNDLE_ID:
required: true
APPSTORE_ISSUER_ID:
required: true
APPSTORE_KEY_ID:
required: true
APPSTORE_PRIVATE_KEY:
required: true
APPSTORE_TEAM_ID:
required: true
jobs:
deploy:
runs-on: macos-15
timeout-minutes: 90
steps:
- uses: actions/checkout@v6
- name: Xcode version
run: |
/usr/bin/xcodebuild -version
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
- name: Xcode version
run: |
/usr/bin/xcodebuild -version
- name: Setup Gradle
uses: ./.github/actions/setup-gradle
with:
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
- name: import certs
uses: apple-actions/import-codesign-certs@v5
with:
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
p12-password: ${{ secrets.CERTIFICATES_PASSWORD }}
- name: download provisioning profiles
uses: apple-actions/download-provisioning-profiles@v4
with:
bundle-id: ${{ secrets.BUNDLE_ID }}
issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }}
api-key-id: ${{ secrets.APPSTORE_KEY_ID }}
api-private-key: ${{ secrets.APPSTORE_PRIVATE_KEY }}
- name: build archive
run: |
cd iosApp
xcrun xcodebuild \
-scheme "MoneyFlow" \
-configuration "Release" \
-sdk "iphoneos" \
-showBuildTimingSummary \
-disableAutomaticPackageResolution \
-derivedDataPath "${RUNNER_TEMP}/Build/DerivedData" \
-archivePath "${RUNNER_TEMP}/Build/Archives/MoneyFlow.xcarchive" \
-resultBundlePath "${RUNNER_TEMP}/Build/Artifacts/MoneyFlow.xcresult" \
-destination "generic/platform=iOS" \
DEVELOPMENT_TEAM="${{ secrets.APPSTORE_TEAM_ID }}" \
CODE_SIGN_STYLE="Manual" \
archive | xcbeautify --renderer github-actions
- name: "Generate ExportOptions.plist"
run: |
cat <<EOF > ${RUNNER_TEMP}/Build/ExportOptions.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>destination</key>
<string>export</string>
<key>method</key>
<string>app-store</string>
<key>signingStyle</key>
<string>manual</string>
<key>generateAppStoreInformation</key>
<true/>
<key>stripSwiftSymbols</key>
<true/>
<key>teamID</key>
<string>${{ secrets.APPSTORE_TEAM_ID }}</string>
<key>uploadSymbols</key>
<true/>
<key>provisioningProfiles</key>
<dict>
<key>${{ secrets.BUNDLE_ID }}</key>
<string>MoneyFlowGHActionDistributionProvisioning</string>
</dict>
</dict>
</plist>
EOF
- id: export_archive
name: export archive
run: |
xcrun xcodebuild \
-exportArchive \
-exportOptionsPlist "${RUNNER_TEMP}/Build/ExportOptions.plist" \
-archivePath "${RUNNER_TEMP}/Build/Archives/MoneyFlow.xcarchive" \
-exportPath "${RUNNER_TEMP}/Build/Archives/MoneyFlow.xcarchive" \
PRODUCT_BUNDLE_IDENTIFIER="${{ secrets.BUNDLE_ID }}" | xcbeautify --renderer github-actions
echo "ipa_path=${RUNNER_TEMP}/Build/Archives/MoneyFlow.xcarchive/MoneyFlow.ipa" >> $GITHUB_ENV
- uses: Apple-Actions/upload-testflight-build@v3
with:
app-path: ${{ env.ipa_path }}
issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }}
api-key-id: ${{ secrets.APPSTORE_KEY_ID }}
api-private-key: ${{ secrets.APPSTORE_PRIVATE_KEY }}
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
workflow_dispatch:
inputs:
platforms:
description: 'Platforms to build for prerelease'
required: true
type: choice
options:
- all
- android
- ios
default: 'all'
pull_request_target:
types:
- labeled
push:
tags:
- '*-all'
- '*-android'
- '*-ios'
jobs:
android:
if: (github.event_name == 'workflow_dispatch' && (github.event.inputs.platforms == 'all' || github.event.inputs.platforms == 'android')) || (github.event_name == 'push' && (endsWith(github.ref, '-all') || endsWith(github.ref, '-android'))) || (github.event_name == 'pull_request_target' && github.event.action == 'labeled' && github.event.label.name == 'Generate Build')
uses: ./.github/workflows/android-release.yml
secrets:
GRADLE_CACHE_ENCRYPTION_KEY: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
KEYSTORE_FILE: ${{ secrets.KEYSTORE_FILE }}
KEYSTORE_PASSPHRASE: ${{ secrets.KEYSTORE_PASSPHRASE }}
KEYSTORE_KEY_ALIAS: ${{ secrets.KEYSTORE_KEY_ALIAS }}
KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }}
KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }}
PLAY_CONFIG: ${{ secrets.PLAY_CONFIG_JSON }}
ios:
if: (github.event_name == 'workflow_dispatch' && (github.event.inputs.platforms == 'all' || github.event.inputs.platforms == 'ios')) || (github.event_name == 'push' && (endsWith(github.ref, '-all') || endsWith(github.ref, '-ios'))) || (github.event_name == 'pull_request_target' && github.event.action == 'labeled' && github.event.label.name == 'Generate Build')
uses: ./.github/workflows/ios-release.yml
secrets:
GRADLE_CACHE_ENCRYPTION_KEY: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
CERTIFICATES_P12: ${{ secrets.CERTIFICATES_P12 }}
CERTIFICATES_PASSWORD: ${{ secrets.CERTIFICATES_PASSWORD }}
BUNDLE_ID: ${{ secrets.BUNDLE_ID }}
APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }}
APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }}
APPSTORE_PRIVATE_KEY: ${{ secrets.APPSTORE_PRIVATE_KEY }}
APPSTORE_TEAM_ID: ${{ secrets.APPSTORE_TEAM_ID }}
================================================
FILE: .github/workflows/roborazzi.yml
================================================
name: Roborazzi Snapshots
on:
pull_request:
branches:
- '*'
permissions:
contents: write
jobs:
record-snapshots:
name: Record Roborazzi Snapshots
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup environment
uses: ./.github/actions/setup-gradle
with:
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
- name: Record Roborazzi snapshots
run: ./gradlew recordRoborazziDebug
- name: Commit Roborazzi snapshots
if: always() && !github.event.pull_request.head.repo.fork
run: |
BRANCH="${{ github.event.pull_request.head.ref }}"
if git status --porcelain image/roborazzi | grep .; then
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add image/roborazzi
git commit -m "chore: update Roborazzi snapshots"
git push origin HEAD:"$BRANCH"
else
echo "No snapshot changes to commit."
fi
================================================
FILE: .gitignore
================================================
# Dependencies
node_modules/
.pnp/
.pnp.js
# Build outputs
dist/
build/
*.tsbuildinfo
reader-playground/frontend/.vite/
# Environment files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# IDE and editor files
.idea/
.vscode/
*.swp
*.swo
.DS_Store
# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Testing
coverage/
# Temporary files
tmp/
temp/
.idea
.DS_Store
build
captures
.externalNativeBuild
.cxx
xcuserdata
Plist
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Log/OS Files
*.log
# Android Studio generated files and folders
captures/
.externalNativeBuild/
.cxx/
*.apk
output.json
# IntelliJ
*.iml
.idea/
misc.xml
deploymentTargetDropDown.xml
render.experimental.xml
# Keystore files
*.jks
*.keystore
keystore.properties
# Google Services (e.g. APIs or Firebase)
google-services.json
GoogleService-Info.plist
# Android Profiling
*.hprof
# Compiled class file
*.class
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
*.aab
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
!/gradle/wrapper/gradle-wrapper.jar
/fastlane/report.xml
/fastlane/README.md
.gradle
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
/iosApp/Plist/Prefix
/iosApp/GoogleService-Info.plist
/iosApp/GoogleService-Info-dev.plist
/iosApp/Assets/Config.xcconfig
/desktopApp/src/jvmMain/resources/props.properties
/desktopApp/embedded.provisionprofile
/desktopApp/runtime.provisionprofile
/website/public/
/.kotlin/
/play_config.json
/iosApp/buildServer.json
/build-logic/.kotlin/
# xcode-build-server files
buildServer.json
.compile
# Local build artifacts
.build/
================================================
FILE: AGENTS.md
================================================
# AGENTS.md
## Project Overview
MoneyFlow is a Kotlin Multiplatform (KMP) personal finance app targeting Android and iOS with Compose Multiplatform.
The app is actively being rewritten to keep Android/iOS shells thin while concentrating UI and business logic in the shared module.
## Project Structure & Module Organization
### Module Structure
- `shared/`: All KMP code including Compose UI
- `androidApp/`: Android launcher shell (minimal)
- `iosApp/`: Xcode project wrapper (minimal)
- `build-logic/`: Gradle convention plugins
- `config/`: Detekt and quality configurations
## Build, Test, and Development Commands
### Build Commands
All Gradle commands should use `-q --console=plain` for readable output.
- `./gradlew :androidApp:assembleDebug` -> Build Android app
- `./gradlew test` -> Run all tests for Android & iOS
- `./gradlew detekt` -> Run static analysis with Detekt for Shared code, Android and Desktop
- `./gradlew recordRoborazziDebug` -> Record new snapshots
### Building for iOS Simulator
To build MoneyFlow for iPhone 17 Pro simulator:
```bash
mcp__XcodeBuildMCP__build_sim_name_proj projectPath: "/Users/mg/Workspace/MoneyFlow/iosApp/MoneyFlow.xcodeproj" scheme: "MoneyFlow" simulatorName: "iPhone 17 Pro"
```
There could be different project path on your machine. Always use the first one. The alternative paths will be:
```bash
mcp__XcodeBuildMCP__build_sim_name_proj projectPath: "/Users/mg/Workspace/tmp/MoneyFlow/iosApp/MoneyFlow.xcodeproj" scheme: "MoneyFlow" simulatorName: "iPhone 17 Pro"
```
To launch MoneyFlow for iPhone 17 Pro simulator:
```bash
mcp__XcodeBuildMCP__launch_app_sim projectPath: "/Users/mg/Workspace/MoneyFlow/iosApp/MoneyFlow.xcodeproj" scheme: "MoneyFlow" simulatorName: "iPhone 17 Pro"
```
### Build Verification Process
IMPORTANT: When editing code, you MUST:
1. Build the project after making changes
2. Fix any compilation errors before proceeding
Be sure to build ONLY for the platform you are working on to save time.
## Handing off
Before handing off you must run `./gradlew detekt` to ensure all checks pass - don't run it if you modified only swift files
## General rules:
- DO NOT write comments for every function or class. Only write comments when the code is not self-explanatory.
- DO NOT write tests unless specifically told to do so.
- DO NOT excessively use try/catch blocks for every function. Use them only for the top caller or the bottom callers, depending on the cases.
- ALWAYS run gradle tasks with the following flag: `--quiet --console=plain`
### Git Commit Messages
When creating commits:
- Use simple, one-liner commit messages
- DO NOT include phase numbers (e.g., "Phase 1", "Phase 2")
- DO NOT add "Generated with Claude Code" attribution
- DO NOT add "Co-Authored-By: Claude" attribution
- Example: `git commit -m "Add foundation for unified article parsing system"`
## Testing
- Unit tests: `shared/src/commonTest/` (cross-platform), `androidUnitTest/`, `iosTest/`
- Screenshot tests: Use Roborazzi, run `recordRoborazziDebug` to update snapshots
- When modifying UI composables, search for related tests and update snapshots
## CI/CD Notes
- `./setup.sh` installs Android SDK if you are an agent running on a sandbox environment (Codex Cloud, Claude Code Web).
DO NOT run it on local-machine.
================================================
FILE: README.md
================================================
# MoneyFlow
MoneyFlow is a Kotlin Multiplatform personal finance app targeting Android and iOS with Compose Multiplatform.
<div align="center">
<img src="image/money-flow-light.png">
</div>
## Current direction
- The app is in the middle of a rewrite with an updated architecture to better support Compose Multiplatform and keep the Android/iOS shells thin.
- Expect fast-moving changes while core features are rebuilt.
- The shared module remains the source of truth, and platform-specific code is being pared back to minimal glue.
## Project history
- MoneyFlow originally explored several shared-architecture approaches for KMP + Jetpack Compose + SwiftUI; the ongoing rewrite changes direction to simplify the stack and improve maintainability.
- Legacy articles documenting the earlier architecture remain for context:
- [Improving shared architecture for a Kotlin Multiplatform, Jetpack Compose and SwiftUI app](https://www.marcogomiero.com/posts/2022/improved-kmm-shared-app-arch/)
- [Choosing the right architecture for a [new] Kotlin Multiplatform, Jetpack Compose and SwiftUI app](https://www.marcogomiero.com/posts/2020/kmm-shared-app-architecture/)
## Feature roadmap
- ✅ Transaction Entry
- 🏗 Transaction List
- 💭 Edit Transaction
- 💭 Add custom category
- 💭 Recap screen with plots
- 💭 Budgeting feature
- 💭 Import from CSV
- 💭 CSV data export
- 💭 Change currency
- 🏗 Lock view with biometrics
Legend:
- ✅ Implemented
- 💭 Not yet implemented
- 🏗 In progress
## License 📄
```
Copyright 2020-2022 Marco Gomiero
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.
```
================================================
FILE: androidApp/build.gradle.kts
================================================
import java.util.Properties
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.triplet.play)
alias(libs.plugins.kmp.detekt)
}
apply(from = "../versioning.gradle.kts")
val appVersionCode: () -> Int by extra
val appVersionName: () -> String by extra
val local = Properties()
val localProperties: File = rootProject.file("keystore.properties")
if (localProperties.exists()) {
localProperties.inputStream().use { local.load(it) }
}
val appVersionCode: String by project
val appVersionName: String by project
val javaVersion: JavaVersion by rootProject.extra
android {
namespace = "com.prof18.moneyflow.androidApp"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
applicationId = "com.prof18.moneyflow"
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
versionCode = appVersionCode()
versionName = appVersionName()
}
signingConfigs {
create("release") {
keyAlias = local.getProperty("keyAlias")
keyPassword = local.getProperty("keyPassword")
storeFile = file(local.getProperty("storeFile") ?: "NOT_FOUND")
storePassword = local.getProperty("storePassword")
}
}
buildTypes {
getByName("debug") {
applicationIdSuffix = ".debug"
versionNameSuffix = "-debug"
}
getByName("release") {
isMinifyEnabled = true
isShrinkResources = true
signingConfig = signingConfigs.getByName("release")
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}
}
compileOptions {
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
}
buildFeatures {
compose = true
buildConfig = true
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}
kotlin {
jvmToolchain(21)
}
dependencies {
implementation(project(":shared"))
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.lifecycle.viewmodel.ktx)
implementation(libs.koin.android)
implementation(libs.androidx.biometric.ktx)
debugImplementation(libs.compose.ui.tooling)
}
play {
// The play_config.json file will be provided on CI
serviceAccountCredentials.set(file("../play_config.json"))
track.set("internal")
}
================================================
FILE: androidApp/proguard-rules.pro
================================================
# Okhttp
# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**
# A resource is loaded with a relative path so the package of this class must be preserved.
-adaptresourcefilenames okhttp3/internal/publicsuffix/PublicSuffixDatabase.gz
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*
# OkHttp platform used only on JVM and when Conscrypt and other security providers are available.
-dontwarn okhttp3.internal.platform.**
-dontwarn org.conscrypt.**
-dontwarn org.bouncycastle.**
-dontwarn org.openjsse.**
# Okio
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*
-dontwarn org.slf4j.impl.StaticLoggerBinder
# Keep LinkOpeningPreference enum and its serialization
-keepclassmembers class * {
@kotlinx.serialization.Serializable <fields>;
}
# Keep the DateTimeComponents class and all its members
# TODO: remove when https://github.com/Kotlin/kotlinx-datetime/issues/519 is closed
-keep class kotlinx.datetime.format.DateTimeComponents { *; }
================================================
FILE: androidApp/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
</adaptive-icon>
================================================
FILE: androidApp/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:name="com.prof18.moneyflow.MoneyFlowApp"
android:label="MoneyFlow"
android:icon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/Theme.MoneyFlow"
>
<activity
android:name="com.prof18.moneyflow.MainActivity"
android:exported="true"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
================================================
FILE: androidApp/src/main/kotlin/com/prof18/moneyflow/MainActivity.kt
================================================
package com.prof18.moneyflow
import android.content.res.Configuration
import android.graphics.Color
import android.os.Bundle
import android.view.WindowManager
import androidx.activity.SystemBarStyle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.fragment.app.FragmentActivity
import com.prof18.moneyflow.presentation.MoneyFlowApp
import org.koin.androidx.viewmodel.ext.android.viewModel
class MainActivity : FragmentActivity() {
private val viewModel: MainViewModel by viewModel()
private val biometricAuthenticator by lazy { AndroidBiometricAuthenticator(this) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val isDarkMode = (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) ==
Configuration.UI_MODE_NIGHT_YES
enableEdgeToEdge(
statusBarStyle = SystemBarStyle.auto(
lightScrim = Color.TRANSPARENT,
darkScrim = Color.TRANSPARENT,
) { isDarkMode },
navigationBarStyle = SystemBarStyle.auto(
lightScrim = Color.TRANSPARENT,
darkScrim = Color.TRANSPARENT,
) { isDarkMode },
)
window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE,
)
setContent {
MoneyFlowApp(
biometricAuthenticator = biometricAuthenticator,
)
}
}
override fun onResume() {
super.onResume()
performAuth()
}
override fun onStop() {
super.onStop()
viewModel.lockIfNeeded(biometricAuthenticator)
}
private fun performAuth() {
viewModel.performAuthentication(biometricAuthenticator)
}
}
================================================
FILE: androidApp/src/main/kotlin/com/prof18/moneyflow/MoneyFlowApp.kt
================================================
package com.prof18.moneyflow
import android.app.Application
import android.content.Context
import com.prof18.moneyflow.di.initKoin
import org.koin.dsl.module
class MoneyFlowApp : Application() {
override fun onCreate() {
super.onCreate()
// TODO: do proper logging setup like FeedFlow
// Logger.setMinSeverity(if (BuildConfig.DEBUG) Severity.Verbose else Severity.Warn)
initKoin(
listOf(
module {
single<Context> { this@MoneyFlowApp }
},
),
)
}
}
================================================
FILE: androidApp/src/release/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
</adaptive-icon>
================================================
FILE: build-logic/convention/build.gradle.kts
================================================
plugins {
`kotlin-dsl`
}
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
dependencies {
compileOnly(libs.detekt.gradle)
compileOnly(libs.detekt.formatting)
}
gradlePlugin {
plugins {
register("detekt") {
id = "com.moneyflow.detekt"
implementationClass = "DetektConventionPlugin"
}
}
}
================================================
FILE: build-logic/convention/src/main/kotlin/DetektConventionPlugin.kt
================================================
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
import io.gitlab.arturbosch.detekt.extensions.DetektExtension.Companion.DEFAULT_SRC_DIR_JAVA
import io.gitlab.arturbosch.detekt.extensions.DetektExtension.Companion.DEFAULT_SRC_DIR_KOTLIN
import io.gitlab.arturbosch.detekt.extensions.DetektExtension.Companion.DEFAULT_TEST_SRC_DIR_JAVA
import io.gitlab.arturbosch.detekt.extensions.DetektExtension.Companion.DEFAULT_TEST_SRC_DIR_KOTLIN
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.getByType
class DetektConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("io.gitlab.arturbosch.detekt")
}
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
extensions.configure<DetektExtension> {
config.setFrom("$rootDir/config/detekt/detekt.yml")
parallel = true
source.setFrom(
files(
"src",
DEFAULT_SRC_DIR_JAVA,
DEFAULT_TEST_SRC_DIR_JAVA,
DEFAULT_SRC_DIR_KOTLIN,
DEFAULT_TEST_SRC_DIR_KOTLIN,
),
)
dependencies {
add("detektPlugins", libs.findLibrary("detekt-formatting").get())
add("detektPlugins", libs.findLibrary("detekt-compose-rules").get())
}
}
}
}
}
================================================
FILE: build-logic/settings.gradle.kts
================================================
pluginManagement {
repositories {
google()
gradlePluginPortal()
mavenCentral()
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
rootProject.name = "build-logic"
include(":convention")
================================================
FILE: build.gradle.kts
================================================
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.parcelize) apply false
alias(libs.plugins.kotlin.multiplatform) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.android.library) apply false
alias(libs.plugins.detekt) apply false
alias(libs.plugins.sqldelight) apply false
alias(libs.plugins.compose.multiplatform) apply false
alias(libs.plugins.compose.compiler) apply false
alias(libs.plugins.triplet.play) apply false
}
val javaVersion by extra { JavaVersion.VERSION_21 }
================================================
FILE: config/detekt/detekt.yml
================================================
build:
maxIssues: 0
excludeCorrectable: false
weights:
# complexity: 2
# LongParameterList: 1
# style: 1
# comments: 1
config:
validation: true
warningsAsErrors: false
checkExhaustiveness: false
# when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]'
excludes: ''
processors:
active: true
exclude:
- 'DetektProgressListener'
# - 'KtFileCountProcessor'
# - 'PackageCountProcessor'
# - 'ClassCountProcessor'
# - 'FunctionCountProcessor'
# - 'PropertyCountProcessor'
# - 'ProjectComplexityProcessor'
# - 'ProjectCognitiveComplexityProcessor'
# - 'ProjectLLOCProcessor'
# - 'ProjectCLOCProcessor'
# - 'ProjectLOCProcessor'
# - 'ProjectSLOCProcessor'
# - 'LicenseHeaderLoaderExtension'
console-reports:
active: true
exclude:
- 'ProjectStatisticsReport'
- 'ComplexityReport'
- 'NotificationReport'
- 'FindingsReport'
- 'FileBasedFindingsReport'
# - 'LiteFindingsReport'
output-reports:
active: true
# exclude:
# - 'TxtOutputReport'
# - 'XmlOutputReport'
# - 'HtmlOutputReport'
# - 'MdOutputReport'
# - 'SarifOutputReport'
comments:
active: true
AbsentOrWrongFileLicense:
active: false
licenseTemplateFile: 'license.template'
licenseTemplateIsRegex: false
CommentOverPrivateFunction:
active: false
CommentOverPrivateProperty:
active: false
DeprecatedBlockTag:
active: false
EndOfSentenceFormat:
active: false
endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)'
KDocReferencesNonPublicProperty:
active: false
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
OutdatedDocumentation:
active: false
matchTypeParameters: true
matchDeclarationsOrder: true
allowParamOnConstructorProperties: false
UndocumentedPublicClass:
active: false
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
searchInNestedClass: true
searchInInnerClass: true
searchInInnerObject: true
searchInInnerInterface: true
searchInProtectedClass: false
UndocumentedPublicFunction:
active: false
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
searchProtectedFunction: false
UndocumentedPublicProperty:
active: false
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
searchProtectedProperty: false
complexity:
active: true
CognitiveComplexMethod:
active: false
threshold: 15
ComplexCondition:
active: true
threshold: 5
ComplexInterface:
active: false
threshold: 10
includeStaticDeclarations: false
includePrivateDeclarations: false
ignoreOverloaded: false
CyclomaticComplexMethod:
active: true
threshold: 16
ignoreSingleWhenExpression: false
ignoreSimpleWhenEntries: false
ignoreNestingFunctions: false
nestingFunctions:
- 'also'
- 'apply'
- 'forEach'
- 'isNotNull'
- 'ifNull'
- 'let'
- 'run'
- 'use'
- 'with'
LabeledExpression:
active: false
ignoredLabels: []
LargeClass:
active: false
threshold: 600
LongMethod:
active: false
threshold: 60
LongParameterList:
active: false
functionThreshold: 10
constructorThreshold: 7
ignoreDefaultParameters: true
ignoreDataClasses: true
ignoreAnnotatedParameter: []
MethodOverloading:
active: false
threshold: 6
NamedArguments:
active: false
threshold: 3
ignoreArgumentsMatchingNames: false
NestedBlockDepth:
active: true
threshold: 4
NestedScopeFunctions:
active: false
threshold: 1
functions:
- 'kotlin.apply'
- 'kotlin.run'
- 'kotlin.with'
- 'kotlin.let'
- 'kotlin.also'
ReplaceSafeCallChainWithRun:
active: false
StringLiteralDuplication:
active: false
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
threshold: 3
ignoreAnnotation: true
excludeStringsWithLessThan5Characters: true
ignoreStringsRegex: '$^'
TooManyFunctions:
active: false
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
thresholdInFiles: 12
thresholdInClasses: 12
thresholdInInterfaces: 12
thresholdInObjects: 12
thresholdInEnums: 12
ignoreDeprecated: false
ignorePrivate: false
ignoreOverridden: false
coroutines:
active: true
GlobalCoroutineUsage:
active: false
InjectDispatcher:
active: true
dispatcherNames:
- 'IO'
- 'Default'
- 'Unconfined'
RedundantSuspendModifier:
active: true
SleepInsteadOfDelay:
active: true
SuspendFunSwallowedCancellation:
active: false
SuspendFunWithCoroutineScopeReceiver:
active: false
SuspendFunWithFlowReturnType:
active: true
empty-blocks:
active: true
EmptyCatchBlock:
active: true
allowedExceptionNameRegex: '_|(ignore|expected).*'
EmptyClassBlock:
active: true
EmptyDefaultConstructor:
active: true
EmptyDoWhileBlock:
active: true
EmptyElseBlock:
active: true
EmptyFinallyBlock:
active: true
EmptyForBlock:
active: true
EmptyFunctionBlock:
active: true
ignoreOverridden: false
EmptyIfBlock:
active: true
EmptyInitBlock:
active: true
EmptyKtFile:
active: true
EmptySecondaryConstructor:
active: true
EmptyTryBlock:
active: true
EmptyWhenBlock:
active: true
EmptyWhileBlock:
active: true
exceptions:
active: true
ExceptionRaisedInUnexpectedLocation:
active: true
methodNames:
- 'equals'
- 'finalize'
- 'hashCode'
- 'toString'
InstanceOfCheckForException:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
NotImplementedDeclaration:
active: false
ObjectExtendsThrowable:
active: false
PrintStackTrace:
active: true
RethrowCaughtException:
active: true
ReturnFromFinally:
active: true
ignoreLabeled: false
SwallowedException:
active: true
ignoredExceptionTypes:
- 'InterruptedException'
- 'MalformedURLException'
- 'NumberFormatException'
- 'ParseException'
allowedExceptionNameRegex: '_|(ignore|expected).*'
ThrowingExceptionFromFinally:
active: true
ThrowingExceptionInMain:
active: false
ThrowingExceptionsWithoutMessageOrCause:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
exceptions:
- 'ArrayIndexOutOfBoundsException'
- 'Exception'
- 'IllegalArgumentException'
- 'IllegalMonitorStateException'
- 'IllegalStateException'
- 'IndexOutOfBoundsException'
- 'NullPointerException'
- 'RuntimeException'
- 'Throwable'
ThrowingNewInstanceOfSameException:
active: true
TooGenericExceptionCaught:
active: false
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
exceptionNames:
- 'ArrayIndexOutOfBoundsException'
- 'Error'
- 'Exception'
- 'IllegalMonitorStateException'
- 'IndexOutOfBoundsException'
- 'NullPointerException'
- 'RuntimeException'
- 'Throwable'
allowedExceptionNameRegex: '_|(ignore|expected).*'
TooGenericExceptionThrown:
active: true
exceptionNames:
- 'Error'
- 'Exception'
- 'RuntimeException'
- 'Throwable'
naming:
active: true
BooleanPropertyNaming:
active: false
allowedPattern: '^(is|has|are)'
ClassNaming:
active: true
classPattern: '[A-Z][a-zA-Z0-9]*'
ConstructorParameterNaming:
active: true
parameterPattern: '[a-z][A-Za-z0-9]*'
privateParameterPattern: '[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^'
EnumNaming:
active: true
enumEntryPattern: '[A-Z][_a-zA-Z0-9]*'
ForbiddenClassName:
active: false
forbiddenName: []
FunctionMaxLength:
active: false
maximumFunctionNameLength: 30
FunctionMinLength:
active: false
minimumFunctionNameLength: 3
FunctionNaming:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$'
excludeClassPattern: '$^'
ignoreAnnotated: ['Composable']
FunctionParameterNaming:
active: true
parameterPattern: '[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^'
InvalidPackageDeclaration:
active: true
rootPackage: ''
requireRootInDeclaration: false
LambdaParameterNaming:
active: false
parameterPattern: '[a-z][A-Za-z0-9]*|_'
MatchingDeclarationName:
active: false
mustBeFirst: true
MemberNameEqualsClassName:
active: true
ignoreOverridden: true
NoNameShadowing:
active: true
NonBooleanPropertyPrefixedWithIs:
active: false
ObjectPropertyNaming:
active: true
constantPattern: '[A-Za-z][_A-Za-z0-9]*'
propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
PackageNaming:
active: true
packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*'
TopLevelPropertyNaming:
active: true
constantPattern: '[A-Z][_A-Za-z0-9]*'
propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
VariableMaxLength:
active: false
maximumVariableNameLength: 64
VariableMinLength:
active: false
minimumVariableNameLength: 1
VariableNaming:
active: true
variablePattern: '[a-z][A-Za-z0-9]*'
privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^'
performance:
active: true
ArrayPrimitive:
active: true
CouldBeSequence:
active: false
threshold: 3
ForEachOnRange:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
SpreadOperator:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
UnnecessaryPartOfBinaryExpression:
active: false
UnnecessaryTemporaryInstantiation:
active: true
potential-bugs:
active: true
AvoidReferentialEquality:
active: true
forbiddenTypePatterns:
- 'kotlin.String'
CastNullableToNonNullableType:
active: false
CastToNullableType:
active: false
Deprecation:
active: false
DontDowncastCollectionTypes:
active: false
DoubleMutabilityForCollection:
active: true
mutableTypes:
- 'kotlin.collections.MutableList'
- 'kotlin.collections.MutableMap'
- 'kotlin.collections.MutableSet'
- 'java.util.ArrayList'
- 'java.util.LinkedHashSet'
- 'java.util.HashSet'
- 'java.util.LinkedHashMap'
- 'java.util.HashMap'
ElseCaseInsteadOfExhaustiveWhen:
active: false
ignoredSubjectTypes: []
EqualsAlwaysReturnsTrueOrFalse:
active: true
EqualsWithHashCodeExist:
active: true
ExitOutsideMain:
active: false
ExplicitGarbageCollectionCall:
active: true
HasPlatformType:
active: true
IgnoredReturnValue:
active: true
restrictToConfig: true
returnValueAnnotations:
- 'CheckResult'
- '*.CheckResult'
- 'CheckReturnValue'
- '*.CheckReturnValue'
ignoreReturnValueAnnotations:
- 'CanIgnoreReturnValue'
- '*.CanIgnoreReturnValue'
returnValueTypes:
- 'kotlin.sequences.Sequence'
- 'kotlinx.coroutines.flow.*Flow'
- 'java.util.stream.*Stream'
ignoreFunctionCall: []
ImplicitDefaultLocale:
active: true
ImplicitUnitReturnType:
active: false
allowExplicitReturnType: true
InvalidRange:
active: true
IteratorHasNextCallsNextMethod:
active: true
IteratorNotThrowingNoSuchElementException:
active: true
LateinitUsage:
active: false
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
ignoreOnClassesPattern: ''
MapGetWithNotNullAssertionOperator:
active: true
MissingPackageDeclaration:
active: false
excludes: ['**/*.kts']
NullCheckOnMutableProperty:
active: false
NullableToStringCall:
active: false
PropertyUsedBeforeDeclaration:
active: false
UnconditionalJumpStatementInLoop:
active: false
UnnecessaryNotNullCheck:
active: false
UnnecessaryNotNullOperator:
active: true
UnnecessarySafeCall:
active: true
UnreachableCatchBlock:
active: true
UnreachableCode:
active: true
UnsafeCallOnNullableType:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
UnsafeCast:
active: true
UnusedUnaryOperator:
active: true
UselessPostfixExpression:
active: true
WrongEqualsTypeParameter:
active: true
style:
active: true
AlsoCouldBeApply:
active: false
BracesOnIfStatements:
active: false
singleLine: 'never'
multiLine: 'always'
BracesOnWhenStatements:
active: false
singleLine: 'necessary'
multiLine: 'consistent'
CanBeNonNullable:
active: false
CascadingCallWrapping:
active: false
includeElvis: true
ClassOrdering:
active: false
CollapsibleIfStatements:
active: false
DataClassContainsFunctions:
active: false
conversionFunctionPrefix:
- 'to'
allowOperators: false
DataClassShouldBeImmutable:
active: false
DestructuringDeclarationWithTooManyEntries:
active: true
maxDestructuringEntries: 3
DoubleNegativeLambda:
active: false
negativeFunctions:
- reason: 'Use `takeIf` instead.'
value: 'takeUnless'
- reason: 'Use `all` instead.'
value: 'none'
negativeFunctionNameParts:
- 'not'
- 'non'
EqualsNullCall:
active: true
EqualsOnSignatureLine:
active: false
ExplicitCollectionElementAccessMethod:
active: false
ExplicitItLambdaParameter:
active: true
ExpressionBodySyntax:
active: false
includeLineWrapping: false
ForbiddenAnnotation:
active: false
annotations:
- reason: 'it is a java annotation. Use `Suppress` instead.'
value: 'java.lang.SuppressWarnings'
- reason: 'it is a java annotation. Use `kotlin.Deprecated` instead.'
value: 'java.lang.Deprecated'
- reason: 'it is a java annotation. Use `kotlin.annotation.MustBeDocumented` instead.'
value: 'java.lang.annotation.Documented'
- reason: 'it is a java annotation. Use `kotlin.annotation.Target` instead.'
value: 'java.lang.annotation.Target'
- reason: 'it is a java annotation. Use `kotlin.annotation.Retention` instead.'
value: 'java.lang.annotation.Retention'
- reason: 'it is a java annotation. Use `kotlin.annotation.Repeatable` instead.'
value: 'java.lang.annotation.Repeatable'
- reason: 'Kotlin does not support @Inherited annotation, see https://youtrack.jetbrains.com/issue/KT-22265'
value: 'java.lang.annotation.Inherited'
ForbiddenComment:
active: false
comments:
- reason: 'Forbidden FIXME todo marker in comment, please fix the problem.'
value: 'FIXME:'
- reason: 'Forbidden STOPSHIP todo marker in comment, please address the problem before shipping the code.'
value: 'STOPSHIP:'
- reason: 'Forbidden TODO todo marker in comment, please do the changes.'
value: 'TODO:'
allowedPatterns: ''
ForbiddenImport:
active: false
imports: []
forbiddenPatterns: ''
ForbiddenMethodCall:
active: false
methods:
- reason: 'print does not allow you to configure the output stream. Use a logger instead.'
value: 'kotlin.io.print'
- reason: 'println does not allow you to configure the output stream. Use a logger instead.'
value: 'kotlin.io.println'
ForbiddenSuppress:
active: false
rules: []
ForbiddenVoid:
active: true
ignoreOverridden: false
ignoreUsageInGenerics: false
FunctionOnlyReturningConstant:
active: true
ignoreOverridableFunction: true
ignoreActualFunction: true
excludedFunctions: []
LoopWithTooManyJumpStatements:
active: true
maxJumpCount: 1
MagicNumber:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts']
ignoreNumbers:
- '-1'
- '0'
- '1'
- '2'
ignoreHashCodeFunction: true
ignorePropertyDeclaration: true
ignoreLocalVariableDeclaration: false
ignoreConstantDeclaration: true
ignoreCompanionObjectPropertyDeclaration: true
ignoreAnnotation: false
ignoreNamedArgument: true
ignoreEnums: false
ignoreRanges: true
ignoreExtensionFunctions: true
MandatoryBracesLoops:
active: false
MaxChainedCallsOnSameLine:
active: false
maxChainedCalls: 5
MaxLineLength:
active: true
maxLineLength: 120
excludePackageStatements: true
excludeImportStatements: true
excludeCommentStatements: false
excludeRawStrings: true
MayBeConst:
active: true
ModifierOrder:
active: true
MultilineLambdaItParameter:
active: false
MultilineRawStringIndentation:
active: false
indentSize: 4
trimmingMethods:
- 'trimIndent'
- 'trimMargin'
NestedClassesVisibility:
active: true
NewLineAtEndOfFile:
active: true
NoTabs:
active: false
NullableBooleanCheck:
active: false
ObjectLiteralToLambda:
active: true
OptionalAbstractKeyword:
active: true
OptionalUnit:
active: false
PreferToOverPairSyntax:
active: false
ProtectedMemberInFinalClass:
active: true
RedundantExplicitType:
active: false
RedundantHigherOrderMapUsage:
active: true
RedundantVisibilityModifierRule:
active: false
ReturnCount:
active: true
max: 5
excludedFunctions:
- 'equals'
excludeLabeled: false
excludeReturnFromLambda: true
excludeGuardClauses: false
SafeCast:
active: true
SerialVersionUIDInSerializableClass:
active: true
SpacingBetweenPackageAndImports:
active: false
StringShouldBeRawString:
active: false
maxEscapedCharacterCount: 2
ignoredCharacters: []
ThrowsCount:
active: true
max: 2
excludeGuardClauses: false
TrailingWhitespace:
active: false
TrimMultilineRawString:
active: false
trimmingMethods:
- 'trimIndent'
- 'trimMargin'
UnderscoresInNumericLiterals:
active: false
acceptableLength: 4
allowNonStandardGrouping: false
UnnecessaryAbstractClass:
active: true
UnnecessaryAnnotationUseSiteTarget:
active: false
UnnecessaryApply:
active: true
UnnecessaryBackticks:
active: false
UnnecessaryBracesAroundTrailingLambda:
active: false
UnnecessaryFilter:
active: true
UnnecessaryInheritance:
active: true
UnnecessaryInnerClass:
active: false
UnnecessaryLet:
active: false
UnnecessaryParentheses:
active: false
allowForUnclearPrecedence: false
UntilInsteadOfRangeTo:
active: false
UnusedImports:
active: false
UnusedParameter:
active: true
allowedNames: 'ignored|expected'
UnusedPrivateClass:
active: true
UnusedPrivateMember:
active: true
allowedNames: ''
ignoreAnnotated: ['Preview', 'PreviewPhone', 'PreviewTablet', 'PreviewFoldable']
UnusedPrivateProperty:
active: true
allowedNames: '_|ignored|expected|serialVersionUID'
UseAnyOrNoneInsteadOfFind:
active: true
UseArrayLiteralsInAnnotations:
active: true
UseCheckNotNull:
active: true
UseCheckOrError:
active: true
UseDataClass:
active: false
allowVars: false
UseEmptyCounterpart:
active: false
UseIfEmptyOrIfBlank:
active: false
UseIfInsteadOfWhen:
active: false
ignoreWhenContainingVariableDeclaration: false
UseIsNullOrEmpty:
active: true
UseLet:
active: false
UseOrEmpty:
active: true
UseRequire:
active: true
UseRequireNotNull:
active: true
UseSumOfInsteadOfFlatMapSize:
active: false
UselessCallOnNotNull:
active: true
UtilityClassWithPublicConstructor:
active: true
VarCouldBeVal:
active: true
ignoreLateinitVar: false
WildcardImport:
active: true
excludeImports:
- 'java.util.*'
formatting:
active: true
android: false
autoCorrect: true
AnnotationOnSeparateLine:
active: true
autoCorrect: true
indentSize: 4
AnnotationSpacing:
active: true
autoCorrect: true
ArgumentListWrapping:
active: true
autoCorrect: true
indentSize: 4
maxLineLength: 120
BlockCommentInitialStarAlignment:
active: true
autoCorrect: true
ChainWrapping:
active: true
autoCorrect: true
indentSize: 4
ClassName:
active: false
CommentSpacing:
active: true
autoCorrect: true
CommentWrapping:
active: true
autoCorrect: true
indentSize: 4
ContextReceiverMapping:
active: false
autoCorrect: true
maxLineLength: 120
indentSize: 4
DiscouragedCommentLocation:
active: false
autoCorrect: true
EnumEntryNameCase:
active: true
autoCorrect: true
EnumWrapping:
active: false
autoCorrect: true
indentSize: 4
Filename:
active: true
FinalNewline:
active: true
autoCorrect: true
insertFinalNewLine: true
FunKeywordSpacing:
active: true
autoCorrect: true
FunctionName:
active: false
FunctionReturnTypeSpacing:
active: true
autoCorrect: true
maxLineLength: 120
FunctionSignature:
active: false
autoCorrect: true
forceMultilineWhenParameterCountGreaterOrEqualThan: 2147483647
functionBodyExpressionWrapping: 'default'
maxLineLength: 120
indentSize: 4
FunctionStartOfBodySpacing:
active: true
autoCorrect: true
FunctionTypeReferenceSpacing:
active: true
autoCorrect: true
IfElseBracing:
active: false
autoCorrect: true
indentSize: 4
IfElseWrapping:
active: false
autoCorrect: true
indentSize: 4
ImportOrdering:
active: true
autoCorrect: true
layout: '*,java.**,javax.**,kotlin.**,^'
Indentation:
active: true
autoCorrect: true
indentSize: 4
KdocWrapping:
active: true
autoCorrect: true
indentSize: 4
MaximumLineLength:
active: false
maxLineLength: 120
ignoreBackTickedIdentifier: false
ModifierListSpacing:
active: true
autoCorrect: true
ModifierOrdering:
active: true
autoCorrect: true
MultiLineIfElse:
active: true
autoCorrect: true
indentSize: 4
MultilineExpressionWrapping:
active: false
autoCorrect: true
indentSize: 4
NoBlankLineBeforeRbrace:
active: true
autoCorrect: true
NoBlankLineInList:
active: false
autoCorrect: true
NoBlankLinesInChainedMethodCalls:
active: true
autoCorrect: true
NoConsecutiveBlankLines:
active: true
autoCorrect: true
NoConsecutiveComments:
active: false
NoEmptyClassBody:
active: true
autoCorrect: true
NoEmptyFirstLineInClassBody:
active: false
autoCorrect: true
indentSize: 4
NoEmptyFirstLineInMethodBlock:
active: true
autoCorrect: true
NoLineBreakAfterElse:
active: true
autoCorrect: true
NoLineBreakBeforeAssignment:
active: true
autoCorrect: true
NoMultipleSpaces:
active: true
autoCorrect: true
NoSemicolons:
active: true
autoCorrect: true
NoSingleLineBlockComment:
active: false
autoCorrect: true
indentSize: 4
NoTrailingSpaces:
active: true
autoCorrect: true
NoUnitReturn:
active: true
autoCorrect: true
NoUnusedImports:
active: true
autoCorrect: true
NoWildcardImports:
active: true
packagesToUseImportOnDemandProperty: 'java.util.*,kotlinx.android.synthetic.**'
NullableTypeSpacing:
active: true
autoCorrect: true
PackageName:
active: true
autoCorrect: true
ParameterListSpacing:
active: false
autoCorrect: true
ParameterListWrapping:
active: true
autoCorrect: true
maxLineLength: 120
indentSize: 4
ParameterWrapping:
active: true
autoCorrect: true
indentSize: 4
maxLineLength: 120
PropertyName:
active: false
PropertyWrapping:
active: true
autoCorrect: true
indentSize: 4
maxLineLength: 120
SpacingAroundAngleBrackets:
active: true
autoCorrect: true
SpacingAroundColon:
active: true
autoCorrect: true
SpacingAroundComma:
active: true
autoCorrect: true
SpacingAroundCurly:
active: true
autoCorrect: true
SpacingAroundDot:
active: true
autoCorrect: true
SpacingAroundDoubleColon:
active: true
autoCorrect: true
SpacingAroundKeyword:
active: true
autoCorrect: true
SpacingAroundOperators:
active: true
autoCorrect: true
SpacingAroundParens:
active: true
autoCorrect: true
SpacingAroundRangeOperator:
active: true
autoCorrect: true
SpacingAroundUnaryOperator:
active: true
autoCorrect: true
SpacingBetweenDeclarationsWithAnnotations:
active: true
autoCorrect: true
SpacingBetweenDeclarationsWithComments:
active: true
autoCorrect: true
SpacingBetweenFunctionNameAndOpeningParenthesis:
active: true
autoCorrect: true
StringTemplate:
active: true
autoCorrect: true
StringTemplateIndent:
active: false
autoCorrect: true
indentSize: 4
TrailingCommaOnCallSite:
active: true
autoCorrect: true
useTrailingCommaOnCallSite: true
TrailingCommaOnDeclarationSite:
active: true
autoCorrect: true
useTrailingCommaOnDeclarationSite: true
TryCatchFinallySpacing:
active: false
autoCorrect: true
indentSize: 4
TypeArgumentListSpacing:
active: false
autoCorrect: true
indentSize: 4
TypeParameterListSpacing:
active: false
autoCorrect: true
indentSize: 4
UnnecessaryParenthesesBeforeTrailingLambda:
active: true
autoCorrect: true
Wrapping:
active: true
autoCorrect: true
indentSize: 4
maxLineLength: 120
Compose:
ComposableAnnotationNaming:
active: true
ComposableNaming:
active: true
# -- You can optionally disable the checks in this rule for regex matches against the composable name (e.g. molecule presenters)
# allowedComposableFunctionNames: .*Presenter,.*MoleculePresenter
ComposableParamOrder:
active: true
# -- You can optionally have a list of types to be treated as lambdas (e.g. typedefs or fun interfaces not picked up automatically)
# treatAsLambda: MyLambdaType
CompositionLocalAllowlist:
active: true
# -- You can optionally define a list of CompositionLocals that are allowed here
allowedCompositionLocals: LocalComponentContext, LocalFeedFlowStrings
CompositionLocalNaming:
active: true
ContentEmitterReturningValues:
active: true
# -- You can optionally add your own composables here
# contentEmitters: MyComposable,MyOtherComposable
DefaultsVisibility:
active: true
LambdaParameterInRestartableEffect:
active: true
# -- You can optionally have a list of types to be treated as lambdas (e.g. typedefs or fun interfaces not picked up automatically)
# treatAsLambda: MyLambdaType
ModifierClickableOrder:
active: true
# -- You can optionally add your own Modifier types
# customModifiers: BananaModifier,PotatoModifier
ModifierComposable:
active: true
# -- You can optionally add your own Modifier types
# customModifiers: BananaModifier,PotatoModifier
ModifierMissing:
active: true
# -- You can optionally control the visibility of which composables to check for here
# -- Possible values are: `only_public`, `public_and_internal` and `all` (default is `only_public`)
# checkModifiersForVisibility: only_public
# -- You can optionally add your own Modifier types
# customModifiers: BananaModifier,PotatoModifier
ModifierNaming:
active: true
# -- You can optionally add your own Modifier types
# customModifiers: BananaModifier,PotatoModifier
ModifierNotUsedAtRoot:
active: true
# -- You can optionally add your own composables here
# contentEmitters: MyComposable,MyOtherComposable
# -- You can optionally add your own Modifier types
# customModifiers: BananaModifier,PotatoModifier
ModifierReused:
active: true
# -- You can optionally add your own Modifier types
# customModifiers: BananaModifier,PotatoModifier
ModifierWithoutDefault:
active: true
MultipleEmitters:
active: true
# -- You can optionally add your own composables here that will count as content emitters
# contentEmitters: MyComposable,MyOtherComposable
# -- You can add composables here that you don't want to count as content emitters (e.g. custom dialogs or modals)
# contentEmittersDenylist: MyNonEmitterComposable
MutableParams:
active: true
MutableStateParam:
active: true
PreviewAnnotationNaming:
active: true
PreviewPublic:
active: true
RememberMissing:
active: true
RememberContentMissing:
active: true
UnstableCollections:
active: true
ViewModelForwarding:
active: true
# -- You can optionally use this rule on things other than types ending in "ViewModel" or "Presenter" (which are the defaults). You can add your own via a regex here:
# allowedStateHolderNames: .*ViewModel,.*Presenter
# -- You can optionally add an allowlist for Composable names that won't be affected by this rule
# allowedForwarding: .*Content,.*FancyStuff
ViewModelInjection:
active: true
# -- You can optionally add your own ViewModel factories here
# viewModelFactories: hiltViewModel,potatoViewModelk
================================================
FILE: gradle/libs.versions.toml
================================================
[versions]
activity-compose = "1.12.0"
android-compileSdk = "36"
android-minSdk = "26"
android-targetSdk = "36"
androidx-test = "1.7.0"
androidx-test-runner = "1.7.0"
androidx-test-ext = "1.3.0"
biometric = "1.2.0-alpha05"
compose = "1.10.0-beta02"
compose-androidx = "1.9.5"
immutable-collections = "0.4.0"
coroutines = "1.10.2"
agp = "8.10.1"
detekt = "1.23.8"
detekt-compose-rules = "0.4.28"
java = "21"
kermit = "2.0.8"
koin = "4.1.1"
kotlin = "2.2.20"
kotlinx-date-time = "0.7.1"
composeMaterialIconsExtended = "1.7.3"
multiplatform-settings = "1.3.0"
lifecycle-navigation3 = "2.10.0-alpha05"
navigation3 = "1.0.0-alpha05"
kotlinx-serialization = "1.8.0"
lifecycle = "2.10.0"
sqlDelight = "2.2.1"
turbine = "1.2.1"
triplet-play = "3.12.2"
roborazzi = "1.52.0"
robolectric = "4.12.2"
compose-material3 = "1.9.0-beta03"
[libraries]
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity-compose" }
androidx-biometric-ktx = { module = "androidx.biometric:biometric-ktx", version.ref = "biometric" }
androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test" }
androidx-test-ext-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-test-ext" }
androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidx-test" }
androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidx-test-runner" }
cashapp-turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" }
androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel", version.ref = "lifecycle" }
androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
compose-components-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "compose" }
compose-ui-test = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "compose-androidx" }
compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose-androidx" }
compose-foundation = { module = "org.jetbrains.compose.foundation:foundation", version.ref = "compose" }
jetbrains-ui-tooling-preview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "compose" }
kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinx-serialization" }
androidx-navigation3-ui = { module = "org.jetbrains.androidx.navigation3:navigation3-ui", version.ref = "navigation3" }
androidx-lifecycle-viewmodel-navigation3 = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-navigation3", version.ref = "lifecycle-navigation3" }
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koin" }
koin-compose-viewmodel-navigation = { module = "io.insert-koin:koin-compose-viewmodel-navigation", version.ref = "koin" }
koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin" }
kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
kotlinx-coroutine-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
kotlinx-coroutine-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-date-time" }
compose-material = { module = "org.jetbrains.compose.material:material", version.ref = "compose" }
compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "compose-material3" }
compose-material-icons-extended = { module = "org.jetbrains.compose.material:material-icons-extended", version.ref = "composeMaterialIconsExtended" }
compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "compose" }
russhwolf-multiplatform-settings = { module = "com.russhwolf:multiplatform-settings", version.ref = "multiplatform-settings" }
russhwolf-multiplatform-settings-test = { module = "com.russhwolf:multiplatform-settings-test", version.ref = "multiplatform-settings" }
sqldelight-android-driver = { module = "app.cash.sqldelight:android-driver", version.ref = "sqlDelight" }
sqldelight-coroutine-extensions = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqlDelight" }
sqldelight-native-driver = { module = "app.cash.sqldelight:native-driver", version.ref = "sqlDelight" }
sqldelight-runtime = { module = "app.cash.sqldelight:runtime", version.ref = "sqlDelight" }
sqldelight-sqlite-driver = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "sqlDelight" }
touchlab-kermit = { module = "co.touchlab:kermit", version.ref = "kermit" }
detekt-gradle = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" }
detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" }
detekt-compose-rules = { module = "io.nlopez.compose.rules:detekt", version.ref = "detekt-compose-rules" }
roborazzi = { module = "io.github.takahirom.roborazzi:roborazzi", version.ref = "roborazzi" }
roborazzi-compose = { module = "io.github.takahirom.roborazzi:roborazzi-compose", version.ref = "roborazzi" }
roborazziJunitRule = { module = "io.github.takahirom.roborazzi:roborazzi-junit-rule", version.ref = "roborazzi" }
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "compose" }
immutable-collections = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "immutable-collections" }
[bundles]
androidx-test = ["androidx-test-core", "androidx-test-ext-junit", "androidx-test-rules", "androidx-test-runner"]
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
kmp-detekt = { id = "com.moneyflow.detekt", version = "unspecified" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqlDelight" }
compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "compose" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
triplet-play = { id = "com.github.triplet.play", version.ref = "triplet-play" }
roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" }
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: gradle.properties
================================================
appVersionCode=1
appVersionName=1.0
releaseBuild=false
kotlin.code.style=official
org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M"
android.useAndroidX=true
android.enableJetifier=false
kotlin.mpp.enableCInteropCommonization=true
kotlin.mpp.stability.nowarn=true
kotlin.mpp.androidSourceSetLayoutVersion=2
xcodeproj=iosApp/iosApp.xcworkspace
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
# Speed up the build
org.gradle.caching=true
org.gradle.parallel=true
android.nonFinalResIds=false
// TODO: Add configuration cache
================================================
FILE: gradlew
================================================
#!/bin/sh
#
# Copyright © 2015 the original authors.
#
# 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
#
# https://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.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# 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 ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
# 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
if ! command -v java >/dev/null 2>&1
then
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
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@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=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@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="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 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!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: iosApp/.scripts/version.sh
================================================
#!/bin/sh -euo pipefail
VERSION_PROPERTIES_FILE="${SRCROOT}/../version.properties"
if [ ! -f "$VERSION_PROPERTIES_FILE" ]; then
echo "Error: version.properties not found at $VERSION_PROPERTIES_FILE. SRCROOT is $SRCROOT"
exit 1
fi
MAJOR=$(grep '^MAJOR=' "$VERSION_PROPERTIES_FILE" | cut -d'=' -f2)
MINOR=$(grep '^MINOR=' "$VERSION_PROPERTIES_FILE" | cut -d'=' -f2)
PATCH=$(grep '^PATCH=' "$VERSION_PROPERTIES_FILE" | cut -d'=' -f2)
if [ -z "$MAJOR" ] || [ -z "$MINOR" ] || [ -z "$PATCH" ]; then
echo "Error: Could not read MAJOR, MINOR, or PATCH from $VERSION_PROPERTIES_FILE"
exit 1
fi
VERSION_NAME="${MAJOR}.${MINOR}.${PATCH}"
if [ -n "${GITHUB_RUN_NUMBER:-}" ]; then
VERSION_CODE=$((GITHUB_RUN_NUMBER + 1000))
else
VERSION_CODE="2"
fi
mkdir -p "${SRCROOT}/Plist"
cat <<EOF > "${SRCROOT}/Plist/Prefix"
#define VERSION_NAME ${VERSION_NAME}
#define VERSION_CODE ${VERSION_CODE}
EOF
================================================
FILE: iosApp/Assets/DebugIcon.icon/icon.json
================================================
{
"fill" : {
"solid" : "srgb:1.00000,0.75686,0.02745,1.00000"
},
"groups" : [
{
"layers" : [
{
"glass" : true,
"image-name" : "savings_24dp_E3E3E3_FILL0_wght400_GRAD0_opsz24 2.svg",
"name" : "savings_24dp_E3E3E3_FILL0_wght400_GRAD0_opsz24 2",
"position" : {
"scale" : 40,
"translation-in-points" : [
4,
-3
]
}
}
],
"name" : "Group",
"shadow" : {
"kind" : "neutral",
"opacity" : 0.5
},
"translucency" : {
"enabled" : true,
"value" : 0.5
}
}
],
"supported-platforms" : {
"circles" : [
"watchOS"
],
"squares" : "shared"
}
}
================================================
FILE: iosApp/Assets/Icon.icon/icon.json
================================================
{
"fill" : {
"solid" : "srgb:0.00000,0.58824,0.53333,1.00000"
},
"groups" : [
{
"layers" : [
{
"glass" : true,
"image-name" : "savings_24dp_E3E3E3_FILL0_wght400_GRAD0_opsz24 2.svg",
"name" : "savings_24dp_E3E3E3_FILL0_wght400_GRAD0_opsz24 2",
"position" : {
"scale" : 40,
"translation-in-points" : [
4,
-3
]
}
}
],
"name" : "Group",
"shadow" : {
"kind" : "neutral",
"opacity" : 0.5
},
"translucency" : {
"enabled" : true,
"value" : 0.5
}
}
],
"supported-platforms" : {
"circles" : [
"watchOS"
],
"squares" : "shared"
}
}
================================================
FILE: iosApp/Assets/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleDisplayName</key>
<string>MoneyFlow</string>
<key>CFBundleShortVersionString</key>
<string>VERSION_NAME</string>
<key>CFBundleVersion</key>
<string>VERSION_CODE</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchScreen</key>
<dict/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>NSFaceIDUsageDescription</key>
<string>Unlock your MoneyFlow data with Face ID.</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
</dict>
</plist>
================================================
FILE: iosApp/Configuration/Config.xcconfig
================================================
TEAM_ID=
PRODUCT_NAME=MoneyFlow
PRODUCT_BUNDLE_IDENTIFIER=com.prof18.moneyflow.MoneyFlow$(TEAM_ID)
CURRENT_PROJECT_VERSION=1
MARKETING_VERSION=1.0
================================================
FILE: iosApp/MoneyFlow.entitlements
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.prof18.moneyflow</string>
</array>
</dict>
</plist>
================================================
FILE: iosApp/MoneyFlow.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 77;
objects = {
/* Begin PBXAggregateTarget section */
13F46F372EDA1FAC003E22ED /* VersionGenerator */ = {
isa = PBXAggregateTarget;
buildConfigurationList = 13F46F3A2EDA1FAC003E22ED /* Build configuration list for PBXAggregateTarget "VersionGenerator" */;
buildPhases = (
13F46F3B2EDA1FBE003E22ED /* Generate Version */,
);
dependencies = (
);
name = VersionGenerator;
packageProductDependencies = (
);
productName = VersionGenerator;
};
/* End PBXAggregateTarget section */
/* Begin PBXContainerItemProxy section */
13F46F3C2EDA1FF5003E22ED /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 1BCCA59B08481063B39D0DC7 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 13F46F372EDA1FAC003E22ED;
remoteInfo = VersionGenerator;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
13F46F0F2ED9A30F003E22ED /* MoneyFlow.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MoneyFlow.entitlements; sourceTree = "<group>"; };
74302460264A6AC12ABA3020 /* MoneyFlow.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MoneyFlow.app; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
13F46DDF2ED99F4A003E22ED /* Exceptions for "Assets" folder in "MoneyFlow" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Info.plist,
);
target = 5E0725B4FAD85C89202B3C65 /* MoneyFlow */;
};
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
13F46DDE2ED99F2C003E22ED /* Assets */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
13F46DDF2ED99F4A003E22ED /* Exceptions for "Assets" folder in "MoneyFlow" target */,
);
path = Assets;
sourceTree = "<group>";
};
6E007A3526BC97399FEA46B2 /* Source */ = {
isa = PBXFileSystemSynchronizedRootGroup;
path = Source;
sourceTree = "<group>";
};
/* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */
8EB6971E0154A666A3741BB2 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
B7DE119FFFE2D8F48EB1B463 /* Products */ = {
isa = PBXGroup;
children = (
74302460264A6AC12ABA3020 /* MoneyFlow.app */,
);
name = Products;
sourceTree = "<group>";
};
D9916947448D05C97BC70C16 = {
isa = PBXGroup;
children = (
13F46F0F2ED9A30F003E22ED /* MoneyFlow.entitlements */,
13F46DDE2ED99F2C003E22ED /* Assets */,
6E007A3526BC97399FEA46B2 /* Source */,
B7DE119FFFE2D8F48EB1B463 /* Products */,
);
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
5E0725B4FAD85C89202B3C65 /* MoneyFlow */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0506720A08F22077D1ED93AA /* Build configuration list for PBXNativeTarget "MoneyFlow" */;
buildPhases = (
B1ED7E9E2F2C4795BA9C3825 /* Generate Version */,
18C25BC25EBD5C0382F00434 /* Compile Kotlin Framework */,
2D7CA7880B8FD75751B1FE34 /* Sources */,
8EB6971E0154A666A3741BB2 /* Frameworks */,
9A86C77568EE9331093603CE /* Resources */,
);
buildRules = (
);
dependencies = (
13F46F3D2EDA1FF5003E22ED /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
13F46DDE2ED99F2C003E22ED /* Assets */,
6E007A3526BC97399FEA46B2 /* Source */,
);
name = MoneyFlow;
packageProductDependencies = (
);
productName = MoneyFlow;
productReference = 74302460264A6AC12ABA3020 /* MoneyFlow.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
1BCCA59B08481063B39D0DC7 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1620;
LastUpgradeCheck = 1620;
TargetAttributes = {
13F46F372EDA1FAC003E22ED = {
CreatedOnToolsVersion = 26.1.1;
};
5E0725B4FAD85C89202B3C65 = {
CreatedOnToolsVersion = 16.2;
};
};
};
buildConfigurationList = E8BA6EDC49E528ED52578D5F /* Build configuration list for PBXProject "MoneyFlow" */;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = D9916947448D05C97BC70C16;
minimizedProjectReferenceProxies = 1;
preferredProjectObjectVersion = 77;
productRefGroup = B7DE119FFFE2D8F48EB1B463 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
5E0725B4FAD85C89202B3C65 /* MoneyFlow */,
13F46F372EDA1FAC003E22ED /* VersionGenerator */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
9A86C77568EE9331093603CE /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
13F46F3B2EDA1FBE003E22ED /* Generate Version */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Generate Version";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"$SRCROOT\"/.scripts/version.sh\n";
};
18C25BC25EBD5C0382F00434 /* Compile Kotlin Framework */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Compile Kotlin Framework";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :shared:embedAndSignAppleFrameworkForXcode\n";
};
B1ED7E9E2F2C4795BA9C3825 /* Generate Version */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Generate Version";
outputFileListPaths = (
);
outputPaths = (
"$(SRCROOT)/Plist/Prefix",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"$SRCROOT\"/.scripts/version.sh\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
2D7CA7880B8FD75751B1FE34 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
13F46F3D2EDA1FF5003E22ED /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 13F46F372EDA1FAC003E22ED /* VersionGenerator */;
targetProxy = 13F46F3C2EDA1FF5003E22ED /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
13F46F382EDA1FAC003E22ED /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = Q7CUB3RNAK;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
13F46F392EDA1FAC003E22ED /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = Q7CUB3RNAK;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
49119FB90DF9E7FB34FEBE73 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = arm64;
ASSETCATALOG_COMPILER_APPICON_NAME = DebugIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = MoneyFlow.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = Q7CUB3RNAK;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = Assets/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = MoneyFlow;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_PREFIX_HEADER = Plist/Prefix;
INFOPLIST_PREPROCESS = YES;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.0;
OTHER_LDFLAGS = (
"-lsqlite3",
"$(inherited)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.prof18.moneyflow.dev;
PRODUCT_NAME = MoneyFlow;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
58BB095DEEA5C79A2751010D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
72DCDEE7F3F29D583F48F4FE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = arm64;
ASSETCATALOG_COMPILER_APPICON_NAME = Icon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = MoneyFlow.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = Q7CUB3RNAK;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = Assets/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = MoneyFlow;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_PREFIX_HEADER = Plist/Prefix;
INFOPLIST_PREPROCESS = YES;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.0;
OTHER_LDFLAGS = (
"-lsqlite3",
"$(inherited)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.prof18.moneyflow;
PRODUCT_NAME = MoneyFlow;
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = MoneyFlowGHActionDistributionProvisioning;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
E3B514BB52F07F94A4AE1E8C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
0506720A08F22077D1ED93AA /* Build configuration list for PBXNativeTarget "MoneyFlow" */ = {
isa = XCConfigurationList;
buildConfigurations = (
49119FB90DF9E7FB34FEBE73 /* Debug */,
72DCDEE7F3F29D583F48F4FE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
13F46F3A2EDA1FAC003E22ED /* Build configuration list for PBXAggregateTarget "VersionGenerator" */ = {
isa = XCConfigurationList;
buildConfigurations = (
13F46F382EDA1FAC003E22ED /* Debug */,
13F46F392EDA1FAC003E22ED /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
E8BA6EDC49E528ED52578D5F /* Build configuration list for PBXProject "MoneyFlow" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58BB095DEEA5C79A2751010D /* Debug */,
E3B514BB52F07F94A4AE1E8C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 1BCCA59B08481063B39D0DC7 /* Project object */;
}
================================================
FILE: iosApp/MoneyFlow.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
================================================
FILE: iosApp/MoneyFlow.xcodeproj/xcshareddata/xcschemes/MoneyFlow.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2610"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5E0725B4FAD85C89202B3C65"
BuildableName = "MoneyFlow.app"
BlueprintName = "MoneyFlow"
ReferencedContainer = "container:MoneyFlow.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5E0725B4FAD85C89202B3C65"
BuildableName = "MoneyFlow.app"
BlueprintName = "MoneyFlow"
ReferencedContainer = "container:MoneyFlow.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5E0725B4FAD85C89202B3C65"
BuildableName = "MoneyFlow.app"
BlueprintName = "MoneyFlow"
ReferencedContainer = "container:MoneyFlow.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
================================================
FILE: iosApp/Source/ContentView.swift
================================================
import UIKit
import SwiftUI
import MoneyFlowKit
struct ComposeView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
MainViewControllerKt.MainViewController()
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}
struct ContentView: View {
var body: some View {
ComposeView()
.ignoresSafeArea()
}
}
================================================
FILE: iosApp/Source/DI/Koin.swift
================================================
//
// DIContainer.swift
// App ()
//
// Created by Marco Gomiero on 05/11/2020.
//
import Foundation
import MoneyFlowKit
func startKoin() {
_ = KoinIosKt.doInitKoinIos()
}
================================================
FILE: iosApp/Source/MoneyFlowApp.swift
================================================
import SwiftUI
@main
struct MoneyFlowApp: App {
init() {
startKoin()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
================================================
FILE: renovate.json
================================================
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
],
"packageRules": [
{
"matchPackagePatterns": [
"androidx.compose.compiler:compiler"
],
"groupName": "kotlin"
},
{
"matchPackagePatterns": [
"org.jetbrains.kotlin.*"
],
"groupName": "kotlin"
},
{
"matchPackagePatterns": [
"com.google.devtools.ksp"
],
"groupName": "kotlin"
}
]
}
================================================
FILE: settings.gradle.kts
================================================
pluginManagement {
includeBuild("build-logic")
repositories {
google()
gradlePluginPortal()
mavenCentral()
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
mavenLocal()
}
}
plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}
rootProject.name = "money-flow"
include(":shared")
include(":androidApp")
================================================
FILE: setup.sh
================================================
#!/bin/bash
set -ex
export ANDROID_SDK_ROOT="/root/android-sdk"
apt-get update && apt-get install -y expect
mkdir -p "$ANDROID_SDK_ROOT/licenses"
echo "8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_SDK_ROOT/licenses/android-sdk-license"
mkdir -p "$ANDROID_SDK_ROOT/cmdline-tools"
cd "$ANDROID_SDK_ROOT/cmdline-tools"
curl -sSL -o tools.zip https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
unzip -q tools.zip
rm tools.zip
mkdir -p latest
mv cmdline-tools/* latest/
export PATH="$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/platform-tools:$PATH"
yes | "$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" --licenses > /dev/null
expect <<EOF
spawn sdkmanager --sdk_root="$ANDROID_SDK_ROOT" --no_https \
"platform-tools" \
"platforms;android-35" \
"build-tools;35.0.0"
expect {
"Accept? (y/N):" { send "y\r"; exp_continue }
eof
}
EOF
gradle help --no-daemon
================================================
FILE: shared/.gitignore
================================================
/build
*.iml
local.properties
================================================
FILE: shared/build.gradle.kts
================================================
import java.net.URI
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.android.library)
alias(libs.plugins.compose.multiplatform)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlin.parcelize)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.sqldelight)
alias(libs.plugins.roborazzi)
alias(libs.plugins.kmp.detekt)
}
val javaVersion: JavaVersion by rootProject.extra
kotlin {
jvmToolchain(21)
androidTarget {
@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
freeCompilerArgs.add("-opt-in=androidx.compose.ui.ExperimentalComposeUiApi")
freeCompilerArgs.add("-Xexpect-actual-classes")
}
}
listOf(
iosArm64(),
iosSimulatorArm64(),
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "MoneyFlowKit"
isStatic = true
linkerOpts += listOf("-lsqlite3")
}
}
sourceSets {
applyDefaultHierarchyTemplate()
sourceSets.all {
languageSettings.optIn("kotlin.RequiresOptIn")
languageSettings.optIn("kotlinx.coroutines.ExperimentalCoroutinesApi")
languageSettings.optIn("com.russhwolf.settings.ExperimentalSettingsImplementation")
languageSettings.optIn("kotlin.experimental.ExperimentalObjCRefinement")
languageSettings.optIn("kotlin.experimental.ExperimentalObjCName")
languageSettings.optIn("kotlin.time.ExperimentalTime")
}
val commonMain by getting {
dependencies {
api(libs.compose.runtime)
api(libs.compose.foundation)
api(libs.compose.foundation)
api(libs.compose.material3)
implementation(libs.compose.material.icons.extended)
api(libs.compose.ui)
implementation(libs.compose.components.resources)
implementation(libs.jetbrains.ui.tooling.preview)
implementation(libs.androidx.lifecycle.viewmodel)
implementation(libs.koin.compose.viewmodel.navigation)
implementation(libs.koin.compose)
implementation(libs.kotlinx.serialization.core)
implementation(libs.androidx.navigation3.ui)
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
implementation(libs.kotlinx.coroutine.core)
implementation(libs.koin.core)
implementation(libs.kotlinx.datetime)
implementation(libs.russhwolf.multiplatform.settings)
implementation(libs.immutable.collections)
api(libs.touchlab.kermit)
implementation(libs.sqldelight.runtime)
implementation(libs.sqldelight.coroutine.extensions)
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
implementation(libs.koin.test)
implementation(libs.kotlinx.coroutine.test)
implementation(libs.cashapp.turbine)
implementation(libs.russhwolf.multiplatform.settings.test)
}
}
val androidMain by getting {
dependencies {
api(libs.androidx.activity.compose)
api(libs.androidx.lifecycle.viewmodel.ktx)
api(libs.koin.android)
api(libs.androidx.biometric.ktx)
implementation(libs.sqldelight.android.driver)
}
}
val androidUnitTest by getting {
dependencies {
implementation(kotlin("test"))
implementation(libs.kotlin.test.junit)
implementation(libs.bundles.androidx.test)
implementation(libs.kotlinx.coroutine.test)
implementation(libs.sqldelight.sqlite.driver)
implementation(libs.roborazzi)
implementation(libs.roborazziJunitRule)
implementation(libs.roborazzi.compose)
implementation(libs.compose.ui.test)
implementation(libs.robolectric)
}
}
val iosMain by getting {
dependencies {
implementation(libs.kotlinx.coroutine.core)
implementation(libs.sqldelight.native.driver)
}
}
}
}
android {
namespace = "com.prof18.moneyflow"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt()
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
testOptions {
unitTests.isIncludeAndroidResources = true
}
compileOptions {
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
}
buildFeatures { compose = true }
}
dependencies {
debugImplementation(libs.jetbrains.ui.tooling.preview)
androidTestImplementation(libs.compose.ui.test)
}
tasks.withType<Test>().configureEach {
systemProperty("roborazzi.test.record.dir", rootProject.layout.projectDirectory.dir("image/roborazzi").asFile.path)
listOf(
"http" to System.getenv("http_proxy"),
"https" to System.getenv("https_proxy"),
).forEach { (scheme, proxyValue) ->
proxyValue
?.takeIf { it.isNotBlank() }
?.let(::URI)
?.let { proxyUri ->
proxyUri.host?.let { host ->
systemProperty("$scheme.proxyHost", host)
}
proxyUri.port.takeIf { it != -1 }?.let { port ->
systemProperty("$scheme.proxyPort", port)
}
}
}
}
sqldelight {
databases {
create("MoneyFlowDB") {
packageName.set("com.prof18.moneyflow.db")
schemaOutputDirectory.set(file("src/commonMain/sqldelight/com/prof18/moneyflow/schema"))
}
}
}
================================================
FILE: shared/src/androidMain/kotlin/com/prof18/moneyflow/AndroidBiometricAuthenticator.kt
================================================
package com.prof18.moneyflow
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import com.prof18.moneyflow.features.authentication.BiometricAuthenticator
public class AndroidBiometricAuthenticator(
private val activity: FragmentActivity,
) : BiometricAuthenticator {
private var onSuccess: (() -> Unit)? = null
private var onFailure: (() -> Unit)? = null
private var onError: (() -> Unit)? = null
private val biometricPrompt: BiometricPrompt by lazy {
val executor = ContextCompat.getMainExecutor(activity)
BiometricPrompt(
activity,
executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(
errorCode: Int,
errString: CharSequence,
) {
super.onAuthenticationError(errorCode, errString)
onError?.invoke()
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
onSuccess?.invoke()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
onFailure?.invoke()
}
},
)
}
private val promptInfo: BiometricPrompt.PromptInfo by lazy {
BiometricPrompt.PromptInfo.Builder()
.setTitle("MoneyFlow")
.setSubtitle("Unlock MoneyFlow")
.setAllowedAuthenticators(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)
.build()
}
override fun canAuthenticate(): Boolean {
val biometricManager = BiometricManager.from(activity)
return biometricManager.canAuthenticate(BIOMETRIC_STRONG or DEVICE_CREDENTIAL) ==
BiometricManager.BIOMETRIC_SUCCESS
}
override fun authenticate(
onSuccess: () -> Unit,
onFailure: () -> Unit,
onError: () -> Unit,
) {
this.onSuccess = onSuccess
this.onFailure = onFailure
this.onError = onError
biometricPrompt.authenticate(promptInfo)
}
}
================================================
FILE: shared/src/androidMain/kotlin/com/prof18/moneyflow/AndroidBiometricAvailabilityChecker.kt
================================================
package com.prof18.moneyflow
import android.content.Context
import androidx.biometric.BiometricManager
import co.touchlab.kermit.Logger
import com.prof18.moneyflow.features.settings.BiometricAvailabilityChecker
internal class AndroidBiometricAvailabilityChecker(
private val context: Context,
) : BiometricAvailabilityChecker {
override fun isBiometricSupported(): Boolean {
val biometricManager = BiometricManager.from(context)
val authResult = biometricManager.canAuthenticate(
BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL,
)
return when (authResult) {
BiometricManager.BIOMETRIC_SUCCESS -> true
else -> {
Logger.d { "Biometric not supported or not available on this device." }
false
}
}
}
}
================================================
FILE: shared/src/androidMain/kotlin/com/prof18/moneyflow/database/DatabaseDriverFactory.kt
================================================
package com.prof18.moneyflow.database
import android.content.Context
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
import com.prof18.moneyflow.db.MoneyFlowDB
internal fun createDatabaseDriver(context: Context, useDebugDatabaseName: Boolean = false): SqlDriver {
return AndroidSqliteDriver(
schema = MoneyFlowDB.Schema,
context = context,
name = if (useDebugDatabaseName) {
DatabaseHelper.APP_DATABASE_NAME_DEBUG
} else {
DatabaseHelper.APP_DATABASE_NAME_PROD
},
)
}
================================================
FILE: shared/src/androidMain/kotlin/com/prof18/moneyflow/di/KoinAndroid.kt
================================================
package com.prof18.moneyflow.di
import com.prof18.moneyflow.AndroidBiometricAvailabilityChecker
import com.prof18.moneyflow.database.DatabaseHelper
import com.prof18.moneyflow.database.createDatabaseDriver
import com.prof18.moneyflow.features.settings.BiometricAvailabilityChecker
import com.russhwolf.settings.Settings
import com.russhwolf.settings.SharedPreferencesSettings
import kotlinx.coroutines.Dispatchers
import org.koin.core.module.Module
import org.koin.dsl.module
internal actual val platformModule: Module = module {
single { createDatabaseDriver(get()) }
single { DatabaseHelper(get(), Dispatchers.Default) }
single {
val factory: Settings.Factory = SharedPreferencesSettings.Factory(get())
factory.create()
}
single<BiometricAvailabilityChecker> { AndroidBiometricAvailabilityChecker(get()) }
}
================================================
FILE: shared/src/androidMain/kotlin/com/prof18/moneyflow/utils/LocalAppLocale.android.kt
================================================
package com.prof18.moneyflow.utils
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ProvidedValue
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalResources
import java.util.Locale
internal actual object LocalAppLocale {
private var default: Locale? = null
actual val current: String
@Composable get() = Locale.getDefault().toString()
@Composable
actual infix fun provides(value: String?): ProvidedValue<*> {
val configuration = LocalConfiguration.current
if (default == null) {
default = Locale.getDefault()
}
val new = when (value) {
null -> default!!
else -> Locale(value)
}
Locale.setDefault(new)
configuration.setLocale(new)
val resources = LocalResources.current
resources.updateConfiguration(configuration, resources.displayMetrics)
return LocalConfiguration.provides(configuration)
}
}
================================================
FILE: shared/src/androidMain/kotlin/com/prof18/moneyflow/utils/LocalAppTheme.android.kt
================================================
package com.prof18.moneyflow.utils
import android.content.res.Configuration
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ProvidedValue
import androidx.compose.ui.platform.LocalConfiguration
internal actual object LocalAppTheme {
actual val current: Boolean
@Composable get() = (LocalConfiguration.current.uiMode and UI_MODE_NIGHT_MASK) == UI_MODE_NIGHT_YES
@Composable
actual infix fun provides(value: Boolean?): ProvidedValue<*> {
val new = if (value == null) {
LocalConfiguration.current
} else {
Configuration(LocalConfiguration.current).apply {
uiMode = when (value) {
true -> (uiMode and UI_MODE_NIGHT_MASK.inv()) or UI_MODE_NIGHT_YES
false -> (uiMode and UI_MODE_NIGHT_MASK.inv()) or UI_MODE_NIGHT_NO
}
}
}
return LocalConfiguration.provides(new)
}
}
================================================
FILE: shared/src/androidMain/res/values/themes.xml
================================================
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Platform.Theme.MoneyFlow" parent="android:Theme.Material.Light" />
<style name="Base.Theme.MoneyFlow" parent="Platform.Theme.MoneyFlow">
<item name="android:windowActionModeOverlay">true</item>
</style>
<style name="Theme.MoneyFlow" parent="Base.Theme.MoneyFlow">
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">true</item>
</style>
</resources>
================================================
FILE: shared/src/androidMain/res/values-night/themes.xml
================================================
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Platform.Theme.MoneyFlow" parent="android:Theme.Material" />
<style name="Theme.MoneyFlow" parent="Base.Theme.MoneyFlow">
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:statusBarColor">#121212</item>
<item name="android:windowLightStatusBar">false</item>
</style>
</resources>
================================================
FILE: shared/src/androidUnitTest/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name="androidx.activity.ComponentActivity"
android:exported="true"
android:theme="@android:style/Theme.Material.Light.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
================================================
FILE: shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/AddTransactionRoborazziTest.kt
================================================
package com.prof18.moneyflow
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
import com.prof18.moneyflow.database.model.TransactionType
import com.prof18.moneyflow.presentation.addtransaction.AddTransactionScreen
import com.prof18.moneyflow.ui.style.MoneyFlowTheme
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
import kotlin.time.Clock
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(
sdk = [33],
qualifiers = RobolectricDeviceQualifiers.Pixel7Pro,
)
class AddTransactionRoborazziTest : RoborazziTestBase() {
@Test
fun captureAddTransactionScreen() {
composeRule.setContent {
MoneyFlowTheme {
AddTransactionScreen(
categoryState = remember { mutableStateOf(RoborazziSampleData.sampleCategory) },
navigateUp = {},
navigateToCategoryList = {},
addTransaction = {},
amountText = "10.00",
updateAmountText = {},
descriptionText = "Pizza 🍕",
updateDescriptionText = {},
selectedTransactionType = TransactionType.OUTCOME,
updateTransactionType = {},
updateSelectedDate = {},
dateLabel = "11 July 2021",
selectedDateMillis = Clock.System.now().toEpochMilliseconds(),
addTransactionAction = null,
resetAction = {},
currencyConfig = RoborazziSampleData.sampleCurrencyConfig,
)
}
}
capture("add_transaction_screen")
}
@Test
fun captureAddTransactionDatePicker() {
val dateLabel = "11 July 2021"
composeRule.setContent {
MoneyFlowTheme {
AddTransactionScreen(
categoryState = remember { mutableStateOf(RoborazziSampleData.sampleCategory) },
navigateUp = {},
navigateToCategoryList = {},
addTransaction = {},
amountText = "10.00",
updateAmountText = {},
descriptionText = "Pizza 🍕",
updateDescriptionText = {},
selectedTransactionType = TransactionType.OUTCOME,
updateTransactionType = {},
updateSelectedDate = {},
dateLabel = dateLabel,
selectedDateMillis = Clock.System.now().toEpochMilliseconds(),
addTransactionAction = null,
resetAction = {},
currencyConfig = RoborazziSampleData.sampleCurrencyConfig,
)
}
}
composeRule.onNodeWithText(dateLabel).performClick()
capture("add_transaction_screen_date_picker")
}
}
================================================
FILE: shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/AllTransactionsRoborazziTest.kt
================================================
package com.prof18.moneyflow
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
import com.prof18.moneyflow.features.alltransactions.AllTransactionsUiState
import com.prof18.moneyflow.presentation.alltransactions.AllTransactionsScreen
import com.prof18.moneyflow.ui.style.MoneyFlowTheme
import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(
sdk = [33],
qualifiers = RobolectricDeviceQualifiers.Pixel7Pro,
)
class AllTransactionsRoborazziTest : RoborazziTestBase() {
@Test
fun captureAllTransactionsScreen() {
composeRule.setContent {
MoneyFlowTheme {
AllTransactionsScreen(
stateFlow = MutableStateFlow(
AllTransactionsUiState(
transactions = RoborazziSampleData.sampleTransactions,
currencyConfig = RoborazziSampleData.sampleCurrencyConfig,
),
),
loadNextPage = {},
)
}
}
capture("all_transactions_screen")
}
}
================================================
FILE: shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/AuthRoborazziTest.kt
================================================
package com.prof18.moneyflow
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
import com.prof18.moneyflow.presentation.auth.AuthScreen
import com.prof18.moneyflow.presentation.auth.AuthState
import com.prof18.moneyflow.ui.style.MoneyFlowTheme
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(
sdk = [33],
qualifiers = RobolectricDeviceQualifiers.Pixel7Pro,
)
class AuthRoborazziTest : RoborazziTestBase() {
@Test
fun captureAuthScreen() {
composeRule.setContent {
MoneyFlowTheme {
AuthScreen(
authState = AuthState.AUTH_IN_PROGRESS,
onRetryClick = {},
)
}
}
capture("auth_screen")
}
}
================================================
FILE: shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/BudgetAndRecapRoborazziTest.kt
================================================
package com.prof18.moneyflow
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
import com.prof18.moneyflow.presentation.budget.BudgetScreen
import com.prof18.moneyflow.presentation.recap.RecapScreen
import com.prof18.moneyflow.ui.style.MoneyFlowTheme
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(
sdk = [33],
qualifiers = RobolectricDeviceQualifiers.Pixel7Pro,
)
class BudgetAndRecapRoborazziTest : RoborazziTestBase() {
@Test
fun captureBudgetScreen() {
composeRule.setContent {
MoneyFlowTheme {
BudgetScreen()
}
}
capture("budget_screen")
}
@Test
fun captureRecapScreen() {
composeRule.setContent {
MoneyFlowTheme {
RecapScreen()
}
}
capture("recap_screen")
}
}
================================================
FILE: shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/CategoriesRoborazziTest.kt
================================================
package com.prof18.moneyflow
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
import com.prof18.moneyflow.database.model.TransactionType
import com.prof18.moneyflow.domain.entities.Category
import com.prof18.moneyflow.presentation.categories.CategoriesScreen
import com.prof18.moneyflow.presentation.categories.CategoryModel
import com.prof18.moneyflow.presentation.model.CategoryIcon
import com.prof18.moneyflow.ui.style.MoneyFlowTheme
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(
sdk = [33],
qualifiers = RobolectricDeviceQualifiers.Pixel7Pro,
)
class CategoriesRoborazziTest : RoborazziTestBase() {
@Test
fun captureCategoriesScreen() {
composeRule.setContent {
MoneyFlowTheme {
CategoriesScreen(
navigateUp = {},
sendCategoryBack = {},
isFromAddTransaction = true,
categoryModel = CategoryModel.CategoryState(
categories = listOf(
Category(
id = 0,
name = "Food",
icon = CategoryIcon.IC_HAMBURGER_SOLID,
type = TransactionType.OUTCOME,
createdAtMillis = 0,
),
Category(
id = 1,
name = "Drinks",
icon = CategoryIcon.IC_COCKTAIL_SOLID,
type = TransactionType.OUTCOME,
createdAtMillis = 0,
),
),
),
)
}
}
capture("categories_screen")
}
}
================================================
FILE: shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/ComponentsRoborazziTest.kt
================================================
package com.prof18.moneyflow
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
import com.prof18.moneyflow.presentation.model.UIErrorMessage
import com.prof18.moneyflow.ui.components.MFTopBar
import com.prof18.moneyflow.ui.components.TransactionCard
import com.prof18.moneyflow.ui.style.MoneyFlowTheme
import money_flow.shared.generated.resources.Res
import money_flow.shared.generated.resources.error_get_categories_message
import money_flow.shared.generated.resources.settings_screen
import org.jetbrains.compose.resources.stringResource
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(
sdk = [33],
qualifiers = RobolectricDeviceQualifiers.Pixel7Pro,
)
class ComponentsRoborazziTest : RoborazziTestBase() {
@Test
fun captureTopBar() {
composeRule.setContent {
MoneyFlowTheme {
MFTopBar(
topAppBarText = stringResource(Res.string.settings_screen),
actionTitle = "Save",
onBackPressed = {},
onActionClicked = {},
actionEnabled = false,
)
}
}
capture("component_top_bar")
}
@Test
fun captureTransactionCard() {
composeRule.setContent {
MoneyFlowTheme {
TransactionCard(
transaction = RoborazziSampleData.sampleTransactions.first(),
onLongPress = {},
onClick = {},
hideSensitiveData = true,
currencyConfig = RoborazziSampleData.sampleCurrencyConfig,
)
}
}
capture("component_transaction_card")
}
@Test
fun captureErrorView() {
composeRule.setContent {
MoneyFlowTheme {
com.prof18.moneyflow.ui.components.ErrorView(
uiErrorMessage = UIErrorMessage(
message = Res.string.error_get_categories_message,
),
)
}
}
capture("component_error_view")
}
}
================================================
FILE: shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/HomeRoborazziTest.kt
================================================
package com.prof18.moneyflow
import androidx.compose.material3.Scaffold
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
import com.prof18.moneyflow.domain.entities.BalanceRecap
import com.prof18.moneyflow.presentation.home.HomeModel
import com.prof18.moneyflow.presentation.home.HomeScreen
import com.prof18.moneyflow.ui.style.MoneyFlowTheme
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(
sdk = [33],
qualifiers = RobolectricDeviceQualifiers.Pixel7Pro,
)
class HomeRoborazziTest : RoborazziTestBase() {
@Test
fun captureHomeScreen() {
composeRule.setContent {
MoneyFlowTheme {
Scaffold {
HomeScreen(
homeModel = HomeModel.HomeState(
balanceRecap = BalanceRecap(
totalBalanceCents = 500_000,
monthlyIncomeCents = 100_000,
monthlyExpensesCents = 50_00,
),
latestTransactions = RoborazziSampleData.sampleTransactions,
currencyConfig = RoborazziSampleData.sampleCurrencyConfig,
),
hideSensitiveDataState = false,
navigateToAllTransactions = {},
paddingValues = it,
)
}
}
}
capture("home_screen")
}
}
================================================
FILE: shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/MoneyFlowLockedRoborazziTest.kt
================================================
package com.prof18.moneyflow
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
import com.prof18.moneyflow.data.MoneyRepository
import com.prof18.moneyflow.data.SettingsRepository
import com.prof18.moneyflow.data.settings.SettingsSource
import com.prof18.moneyflow.features.addtransaction.AddTransactionViewModel
import com.prof18.moneyflow.features.alltransactions.AllTransactionsViewModel
import com.prof18.moneyflow.features.authentication.BiometricAuthenticator
import com.prof18.moneyflow.features.categories.CategoriesViewModel
import com.prof18.moneyflow.features.home.HomeViewModel
import com.prof18.moneyflow.features.settings.BiometricAvailabilityChecker
import com.prof18.moneyflow.features.settings.SettingsViewModel
import com.prof18.moneyflow.presentation.MoneyFlowApp
import com.prof18.moneyflow.presentation.MoneyFlowErrorMapper
import com.prof18.moneyflow.ui.style.MoneyFlowTheme
import com.prof18.moneyflow.utilities.closeDriver
import com.prof18.moneyflow.utilities.createDriver
import com.prof18.moneyflow.utilities.getDatabaseHelper
import com.russhwolf.settings.MapSettings
import com.russhwolf.settings.Settings
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
import org.koin.core.module.dsl.viewModel
import org.koin.dsl.module
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(
sdk = [33],
qualifiers = RobolectricDeviceQualifiers.Pixel7Pro,
)
class MoneyFlowLockedRoborazziTest : RoborazziTestBase() {
private val fakeBiometricAuthenticator = object : BiometricAuthenticator {
override fun canAuthenticate(): Boolean = true
override fun authenticate(
onSuccess: () -> Unit,
onFailure: () -> Unit,
onError: () -> Unit,
) {
onFailure()
}
}
@Before
fun setup() {
createDriver()
stopKoin() // Ensure Koin is stopped before starting
val koinApplication = startKoin {
modules(
module {
single { getDatabaseHelper() }
single<Settings> { MapSettings() }
single { SettingsSource(get()) }
single { SettingsRepository(get()) }
single { MoneyRepository(get()) }
single { MoneyFlowErrorMapper() }
single<BiometricAvailabilityChecker> {
object : BiometricAvailabilityChecker {
override fun isBiometricSupported(): Boolean = true
}
}
viewModel { HomeViewModel(get(), get(), get()) }
viewModel { AddTransactionViewModel(get(), get()) }
viewModel { CategoriesViewModel(get(), get()) }
viewModel { AllTransactionsViewModel(get(), get()) }
viewModel { SettingsViewModel(get()) }
viewModel { MainViewModel(get(), get()) }
},
)
}
koinApplication.koin.get<SettingsRepository>().setBiometric(true)
}
@After
fun teardownResources() {
stopKoin()
closeDriver()
}
@Test
fun captureMoneyFlowLockedUi() {
composeRule.setContent {
MoneyFlowTheme {
MoneyFlowApp(
biometricAuthenticator = fakeBiometricAuthenticator,
)
}
}
capture("money_flow_locked")
}
}
================================================
FILE: shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/MoneyFlowNavHostRoborazziTest.kt
================================================
package com.prof18.moneyflow
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
import com.prof18.moneyflow.data.MoneyRepository
import com.prof18.moneyflow.data.SettingsRepository
import com.prof18.moneyflow.data.settings.SettingsSource
import com.prof18.moneyflow.features.addtransaction.AddTransactionViewModel
import com.prof18.moneyflow.features.alltransactions.AllTransactionsViewModel
import com.prof18.moneyflow.features.categories.CategoriesViewModel
import com.prof18.moneyflow.features.home.HomeViewModel
import com.prof18.moneyflow.features.settings.BiometricAvailabilityChecker
import com.prof18.moneyflow.features.settings.SettingsViewModel
import com.prof18.moneyflow.navigation.MoneyFlowNavHost
import com.prof18.moneyflow.presentation.MoneyFlowErrorMapper
import com.prof18.moneyflow.ui.style.MoneyFlowTheme
import com.prof18.moneyflow.utilities.closeDriver
import com.prof18.moneyflow.utilities.createDriver
import com.prof18.moneyflow.utilities.getDatabaseHelper
import com.russhwolf.settings.MapSettings
import com.russhwolf.settings.Settings
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
import org.koin.core.module.dsl.viewModel
import org.koin.dsl.module
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(
sdk = [33],
qualifiers = RobolectricDeviceQualifiers.Pixel7Pro,
)
class MoneyFlowNavHostRoborazziTest : RoborazziTestBase() {
@Before
fun setup() {
createDriver()
stopKoin() // Ensure Koin is stopped before starting
startKoin {
modules(
module {
single { getDatabaseHelper() }
single<Settings> { MapSettings() }
single { SettingsSource(get()) }
single { SettingsRepository(get()) }
single { MoneyRepository(get()) }
single { MoneyFlowErrorMapper() }
single<BiometricAvailabilityChecker> {
object : BiometricAvailabilityChecker {
override fun isBiometricSupported(): Boolean = true
}
}
viewModel { HomeViewModel(get(), get(), get()) }
viewModel { AddTransactionViewModel(get(), get()) }
viewModel { CategoriesViewModel(get(), get()) }
viewModel { AllTransactionsViewModel(get(), get()) }
viewModel { SettingsViewModel(get()) }
},
)
}
}
@After
fun teardownResources() {
stopKoin()
closeDriver()
}
@Test
fun captureMoneyFlowNavHost() {
composeRule.setContent {
MoneyFlowTheme {
MoneyFlowNavHost()
}
}
capture("money_flow_nav_host")
}
}
================================================
FILE: shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/RoborazziRule.kt
================================================
package com.prof18.moneyflow
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.onRoot
import androidx.test.ext.junit.rules.ActivityScenarioRule
import com.github.takahirom.roborazzi.RoborazziRule
internal fun roborazziOf(
scenario: AndroidComposeTestRule<ActivityScenarioRule<ComponentActivity>, ComponentActivity>,
captureType: RoborazziRule.CaptureType = RoborazziRule.CaptureType.None,
): RoborazziRule {
return RoborazziRule(
composeRule = scenario,
captureRoot = scenario.onRoot(),
options = RoborazziRule.Options(
captureType = captureType,
),
)
}
================================================
FILE: shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/RoborazziTestBase.kt
================================================
package com.prof18.moneyflow
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onRoot
import androidx.test.ext.junit.rules.ActivityScenarioRule
import com.github.takahirom.roborazzi.RoborazziRule
import com.github.takahirom.roborazzi.captureRoboImage
import com.prof18.moneyflow.domain.entities.CurrencyConfig
import com.prof18.moneyflow.domain.entities.MoneyTransaction
import com.prof18.moneyflow.domain.entities.TransactionTypeUI
import com.prof18.moneyflow.presentation.categories.data.CategoryUIData
import com.prof18.moneyflow.presentation.model.CategoryIcon
import org.junit.After
import org.junit.Rule
import java.io.File
open class RoborazziTestBase(
captureType: RoborazziRule.CaptureType = RoborazziRule.CaptureType.None,
) {
@get:Rule
val composeRule: AndroidComposeTestRule<ActivityScenarioRule<ComponentActivity>, ComponentActivity> =
createAndroidComposeRule()
@get:Rule
val roborazziRule: RoborazziRule = roborazziOf(composeRule, captureType)
private val snapshotDir: File = run {
val defaultDir = System.getProperty("user.dir")
?.let { File(it).resolve("image/roborazzi").path }
?: "image/roborazzi"
val path = System.getProperty("roborazzi.test.record.dir") ?: defaultDir
File(path)
}.also { directory ->
directory.mkdirs()
}
@After
fun tearDown() {
composeRule.activityRule.scenario.recreate()
}
protected fun capture(name: String) {
val target = snapshotDir.resolve("$name.png")
composeRule.waitForIdle()
composeRule.onRoot().captureRoboImage(target.path)
}
}
internal object RoborazziSampleData {
val sampleCurrencyConfig = CurrencyConfig(
code = "EUR",
symbol = "€",
decimalPlaces = 2,
)
val sampleCategory = CategoryUIData(
id = 1,
name = "Food",
icon = CategoryIcon.IC_HAMBURGER_SOLID,
)
val sampleTransactions = listOf(
MoneyTransaction(
id = 0,
title = "Ice Cream",
icon = CategoryIcon.IC_ICE_CREAM_SOLID,
amountCents = 1_000,
type = TransactionTypeUI.EXPENSE,
milliseconds = 0,
formattedDate = "12 July 2021",
),
MoneyTransaction(
id = 1,
title = "Tip",
icon = CategoryIcon.IC_MONEY_CHECK_ALT_SOLID,
amountCents = 5_000,
type = TransactionTypeUI.INCOME,
milliseconds = 0,
formattedDate = "12 July 2021",
),
)
}
================================================
FILE: shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/SettingsRoborazziTest.kt
================================================
package com.prof18.moneyflow
import androidx.compose.material3.Scaffold
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
import com.prof18.moneyflow.features.settings.BiometricAvailabilityChecker
import com.prof18.moneyflow.presentation.settings.SettingsScreen
import com.prof18.moneyflow.ui.style.MoneyFlowTheme
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(
sdk = [33],
qualifiers = RobolectricDeviceQualifiers.Pixel7Pro,
)
class SettingsRoborazziTest : RoborazziTestBase() {
@Test
fun captureSettingsScreen() {
composeRule.setContent {
MoneyFlowTheme {
Scaffold {
SettingsScreen(
biometricAvailabilityChecker = object : BiometricAvailabilityChecker {
override fun isBiometricSupported(): Boolean = true
},
biometricState = true,
onBiometricEnabled = {},
hideSensitiveDataState = true,
onHideSensitiveDataEnabled = {},
paddingValues = it,
)
}
}
}
capture("settings_screen")
}
}
================================================
FILE: shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/utilities/TestUtilsAndroid.kt
================================================
package com.prof18.moneyflow.utilities
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
import com.prof18.moneyflow.database.DatabaseHelper
import com.prof18.moneyflow.db.MoneyFlowDB
import kotlinx.coroutines.Dispatchers
internal actual fun createDriver() {
val jdbcDriver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)
MoneyFlowDB.Schema.create(jdbcDriver)
databaseHelper = DatabaseHelper(jdbcDriver, Dispatchers.Unconfined)
driver = jdbcDriver
}
internal actual fun closeDriver() {
driver?.close()
databaseHelper = null
driver = null
}
internal actual fun getDatabaseHelper(): DatabaseHelper = requireNotNull(databaseHelper)
private var driver: app.cash.sqldelight.db.SqlDriver? = null
private var databaseHelper: DatabaseHelper? = null
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_address_book.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M6,4L6,9L5,9L5,11L8,11L8,6L24,6L24,26L8,26L8,23L6,23L6,28L26,28L26,4ZM16,10C13.8008,10 12,11.8008 12,14C12,15.1133 12.4766,16.1172 13.2188,16.8438C11.8867,17.7461 11,19.2852 11,21L13,21C13,19.3438 14.3438,18 16,18C17.6563,18 19,19.3438 19,21L21,21C21,19.2852 20.1133,17.7461 18.7813,16.8438C19.5234,16.1172 20,15.1133 20,14C20,11.8008 18.1992,10 16,10ZM6,12L6,14L5,14L5,16L8,16L8,12ZM16,12C17.1172,12 18,12.8828 18,14C18,15.1172 17.1172,16 16,16C14.8828,16 14,15.1172 14,14C14,12.8828 14.8828,12 16,12ZM6,17L6,19L5,19L5,21L8,21L8,17Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_address_card.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M3,6L3,26L29,26L29,6ZM5,8L27,8L27,24L23.5938,24C23.5156,23.8633 23.5508,23.6758 23.4375,23.5625C23.0586,23.1836 22.5234,23 22,23C21.4766,23 20.9414,23.1836 20.5625,23.5625C20.4492,23.6758 20.4844,23.8633 20.4063,24L11.5938,24C11.5156,23.8633 11.5508,23.6758 11.4375,23.5625C11.0586,23.1836 10.5234,23 10,23C9.4766,23 8.9414,23.1836 8.5625,23.5625C8.4492,23.6758 8.4844,23.8633 8.4063,24L5,24ZM12,10C9.8008,10 8,11.8008 8,14C8,15.1133 8.4766,16.1172 9.2188,16.8438C7.8867,17.7461 7,19.2852 7,21L9,21C9,19.3438 10.3438,18 12,18C13.6563,18 15,19.3438 15,21L17,21C17,19.2852 16.1133,17.7461 14.7813,16.8438C15.5234,16.1172 16,15.1133 16,14C16,11.8008 14.1992,10 12,10ZM12,12C13.1172,12 14,12.8828 14,14C14,15.1172 13.1172,16 12,16C10.8828,16 10,15.1172 10,14C10,12.8828 10.8828,12 12,12ZM19,13L19,15L25,15L25,13ZM19,17L19,19L25,19L25,17Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_adjust_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M16,4C9.3828,4 4,9.3828 4,16C4,22.6172 9.3828,28 16,28C22.6172,28 28,22.6172 28,16C28,9.3828 22.6172,4 16,4ZM16,6L16,26C10.4648,26 6,21.5352 6,16C6,10.4648 10.4648,6 16,6Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_air_freshener_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M16,2C14.346,2 13,3.346 13,5C13,5.8539 13.3628,6.6211 13.9375,7.168L9.0566,14L11.1328,14L7.1328,20L15,20L15,22L9,22L9,28L23,28L23,22L17,22L17,20L24.8672,20L20.8672,14L22.9434,14L18.0645,7.168C18.6385,6.6211 19,5.8534 19,5C19,3.346 17.654,2 16,2zM16,4C16.552,4 17,4.449 17,5C17,5.4037 16.7559,5.7485 16.4102,5.9063L16.334,5.9336C16.2284,5.9716 16.1185,6 16,6C15.8815,6 15.7716,5.9716 15.666,5.9336L15.5898,5.9063C15.2441,5.7485 15,5.4037 15,5C15,4.449 15.448,4 16,4zM15.8066,7.9922C15.8449,7.9949 15.8834,7.9911 15.9219,7.9922C15.9485,7.9929 15.9732,8 16,8C16.0268,8 16.0515,7.9929 16.0781,7.9922C16.1173,7.9911 16.1564,7.9949 16.1953,7.9922L19.0566,12L17.1328,12L21.1328,18L10.8672,18L14.8672,12L12.9434,12L15.8066,7.9922zM11,24L21,24L21,26L11,26L11,24z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_algolia.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M5,5L5,27L27,27L27,5L5,5zM7,7L25,7L25,25L7,25L7,7zM15,8C14.448,8 14,8.448 14,9L14,10.2949C14.634,10.1049 15.305,10 16,10C16.695,10 17.366,10.1049 18,10.2949L18,9C18,8.448 17.552,8 17,8L15,8zM10.707,10C10.4512,10 10.195,10.0994 10,10.2949L9.293,11C8.902,11.391 8.902,12.0241 9.293,12.4141L10.1094,13.2324C10.6554,12.3814 11.3795,11.6573 12.2305,11.1113L11.4141,10.2949C11.2186,10.0994 10.9629,10 10.707,10zM16,11C12.686,11 10,13.686 10,17C10,20.314 12.686,23 16,23C19.314,23 22,20.314 22,17C22,13.686 19.314,11 16,11zM16,13C18.206,13 20,14.794 20,17C20,19.206 18.206,21 16,21C13.794,21 12,19.206 12,17C12,14.794 13.794,13 16,13zM16,14L16,17L18.5938,15.5176C18.0758,14.6146 17.115,14 16,14z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_allergies_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M16,2C14.617,2 13.469,2.969 13.125,4.25C12.773,4.105 12.402,4 12,4C10.355,4 9,5.355 9,7L9,17.75L8.125,16.875C6.961,15.711 5.039,15.711 3.875,16.875C2.711,18.039 2.711,19.961 3.875,21.125L10.6563,27.9063C11.8322,29.0783 13.457,30 15.375,30L20,30C23.855,30 27,26.855 27,23L27,11C27,9.355 25.645,8 24,8C23.648,8 23.316,8.0735 23,8.1875L23,7C23,5.355 21.645,4 20,4C19.598,4 19.227,4.105 18.875,4.25C18.531,2.969 17.383,2 16,2zM16,4C16.566,4 17,4.434 17,5L17,15L19,15L19,7C19,6.434 19.434,6 20,6C20.566,6 21,6.434 21,7L21,15L23,15L23,11C23,10.434 23.434,10 24,10C24.566,10 25,10.434 25,11L25,23C25,25.773 22.773,28 20,28L15.375,28C14.102,28 13.0077,27.3787 12.0938,26.4688L5.2813,19.7188C4.8833,19.3207 4.8833,18.6803 5.2813,18.2813C5.6792,17.8832 6.3197,17.8832 6.7188,18.2813L9.2813,20.875L11,22.5938L11,7C11,6.434 11.434,6 12,6C12.566,6 13,6.434 13,7L13,15L15,15L15,5C15,4.434 15.434,4 16,4zM16,17A1,1 0,0 0,16 19A1,1 0,0 0,16 17zM22,17A1,1 0,0 0,22 19A1,1 0,0 0,22 17zM18,20A1,1 0,0 0,18 22A1,1 0,0 0,18 20zM14,21A1,1 0,0 0,14 23A1,1 0,0 0,14 21zM22,21A1,1 0,0 0,22 23A1,1 0,0 0,22 21zM18,24A1,1 0,0 0,18 26A1,1 0,0 0,18 24z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_ambulance_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M18,3C16.8945,3 16,3.8945 16,5L16,6L1,6L1,25L4.1563,25C4.6016,26.7188 6.1484,28 8,28C9.8516,28 11.3984,26.7188 11.8438,25L20.1563,25C20.6016,26.7188 22.1484,28 24,28C25.8516,28 27.3984,26.7188 27.8438,25L31,25L31,13.625L30.75,13.3438L24.75,6.3438L24.4688,6L20,6L20,5C20,3.8945 19.1055,3 18,3ZM3,8L23.5313,8L29,14.375L29,23L27.8438,23C27.3984,21.2813 25.8516,20 24,20C22.1484,20 20.6016,21.2813 20.1563,23L11.8438,23C11.3984,21.2813 9.8516,20 8,20C6.1484,20 4.6016,21.2813 4.1563,23L3,23ZM14,11L14,14L11,14L11,16L14,16L14,19L16,19L16,16L19,16L19,14L16,14L16,11ZM8,22C9.1172,22 10,22.8828 10,24C10,25.1172 9.1172,26 8,26C6.8828,26 6,25.1172 6,24C6,22.8828 6.8828,22 8,22ZM24,22C25.1172,22 26,22.8828 26,24C26,25.1172 25.1172,26 24,26C22.8828,26 22,25.1172 22,24C22,22.8828 22.8828,22 24,22Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_anchor_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M16,4C13.8008,4 12,5.8008 12,8C12,9.8516 13.2813,11.3984 15,11.8438L15,14L12,14L12,16L15,16L15,23.9688C12.5742,23.7773 10.9609,22.8906 9.8125,21.875C8.9375,21.1016 8.3516,20.25 7.9375,19.5313L9.5313,18.75L5.5313,16.9375L4.4688,21.25L6.125,20.4375C6.5977,21.2852 7.332,22.3711 8.4688,23.375C9.9219,24.6641 12.0664,25.7305 15,25.9375L15,26L16,27L17,26L17,25.9375C19.9336,25.7305 22.0781,24.6641 23.5313,23.375C24.668,22.3711 25.4023,21.2852 25.875,20.4375L27.5313,21.25L26.4688,16.9375L22.4688,18.75L24.0625,19.5313C23.6484,20.25 23.0625,21.1016 22.1875,21.875C21.0391,22.8906 19.4258,23.7773 17,23.9688L17,16L20,16L20,14L17,14L17,11.8438C18.7188,11.3984 20,9.8516 20,8C20,5.8008 18.1992,4 16,4ZM16,6C17.1172,6 18,6.8828 18,8C18,9.1172 17.1172,10 16,10C14.8828,10 14,9.1172 14,8C14,6.8828 14.8828,6 16,6Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_android.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M10.5313,3.5C10.3633,3.543 10.2305,3.6719 10.1797,3.8359C10.1289,4.0039 10.168,4.1836 10.2813,4.3125L11.75,6.4375C10.082,7.7188 9,9.7422 9,12L9,22C9,22.7031 9.4414,23.2031 10,23.5625L10,26.5C10,27.8672 11.1328,29 12.5,29C13.8672,29 15,27.8672 15,26.5L15,24L17,24L17,26.5C17,27.8672 18.1328,29 19.5,29C20.8672,29 22,27.8672 22,26.5L22,23.5625C22.5586,23.2031 23,22.7031 23,22L23,12C23,9.7422 21.918,7.7188 20.25,6.4375L21.7188,4.3125C21.8398,4.1602 21.8633,3.9492 21.7773,3.7773C21.6875,3.6016 21.5078,3.4922 21.3125,3.5C21.2813,3.4961 21.25,3.4961 21.2188,3.5C21.0859,3.5352 20.9727,3.625 20.9063,3.75L19.4063,5.875C18.3984,5.3125 17.2305,5 16,5C14.7695,5 13.6016,5.3125 12.5938,5.875L11.0938,3.75C11.0078,3.6016 10.8555,3.5078 10.6875,3.5C10.6563,3.4961 10.625,3.4961 10.5938,3.5C10.5742,3.5 10.5508,3.5 10.5313,3.5ZM16,7C18.4258,7 20.4414,8.7148 20.9063,11L11.0938,11C11.5586,8.7148 13.5742,7 16,7ZM14,8C13.4492,8 13,8.4492 13,9C13,9.5508 13.4492,10 14,10C14.5508,10 15,9.5508 15,9C15,8.4492 14.5508,8 14,8ZM18,8C17.4492,8 17,8.4492 17,9C17,9.5508 17.4492,10 18,10C18.5508,10 19,9.5508 19,9C19,8.4492 18.5508,8 18,8ZM6,11L6,21L8,21L8,11ZM24,11L24,21L26,21L26,11ZM11,13L21,13L21,22L20,22L20,26.5C20,26.7852 19.7852,27 19.5,27C19.2148,27 19,26.7852 19,26.5L19,22L13,22L13,26.5C13,26.7852 12.7852,27 12.5,27C12.2148,27 12,26.7852 12,26.5L12,22L11,22Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_angle_down_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M4.2188,10.7813L2.7813,12.2188L15.2813,24.7188L16,25.4063L16.7188,24.7188L29.2188,12.2188L27.7813,10.7813L16,22.5625Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_angle_left_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M19.0313,4.2813L8.0313,15.2813L7.3438,16L8.0313,16.7188L19.0313,27.7188L20.4688,26.2813L10.1875,16L20.4688,5.7188Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_angle_right_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M12.9688,4.2813L11.5313,5.7188L21.8125,16L11.5313,26.2813L12.9688,27.7188L23.9688,16.7188L24.6563,16L23.9688,15.2813Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_angle_up_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M16,6.5938L15.2813,7.2813L2.7813,19.7813L4.2188,21.2188L16,9.4375L27.7813,21.2188L29.2188,19.7813L16.7188,7.2813Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_apple.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M20.8438,2C19.2031,2 17.5469,2.8516 16.4375,4.1563L16.4375,4.1875C15.6484,5.168 14.793,6.7148 15.0625,8.5C14.9336,8.4492 14.9258,8.4648 14.7813,8.4063C14.0898,8.125 13.2344,7.8125 12.2188,7.8125C8.2383,7.8125 5.2188,11.418 5.2188,16.1563C5.2188,19.2227 6.25,22.0977 7.625,24.25C8.3125,25.3281 9.0938,26.2148 9.9063,26.875C10.7188,27.5352 11.5703,28 12.5313,28C13.4922,28 14.2109,27.6758 14.75,27.4375C15.2891,27.1992 15.707,27 16.5,27C17.2148,27 17.5781,27.1953 18.125,27.4375C18.6719,27.6797 19.418,28 20.4063,28C21.4766,28 22.3867,27.4766 23.125,26.8125C23.8633,26.1484 24.4844,25.293 25,24.4688C25.5156,23.6445 25.9219,22.8359 26.2188,22.1875C26.3672,21.8633 26.4766,21.5938 26.5625,21.375C26.6484,21.1563 26.6914,21.0938 26.75,20.8438L26.9375,20.0313L26.1875,19.6875C26.0117,19.6094 25.3125,19.2617 24.6875,18.625C24.0625,17.9883 23.5313,17.1172 23.5313,15.7813C23.5313,14.4883 24.0273,13.6211 24.5625,13C24.8281,12.6914 25.1055,12.4375 25.3125,12.2813C25.418,12.2031 25.5,12.1641 25.5625,12.125C25.625,12.0859 25.6133,12.0977 25.7188,12.0313L26.5625,11.5L26,10.6563C24.3672,8.1445 21.7539,7.8125 20.7188,7.8125C20.2383,7.8125 19.8984,7.9805 19.4688,8.0625C19.7109,7.8359 20.0234,7.6953 20.2188,7.4375C20.2227,7.4336 20.2148,7.4102 20.2188,7.4063C20.2305,7.3945 20.2422,7.3867 20.25,7.375L20.2813,7.375C21.3477,6.1836 21.9375,4.582 21.8438,2.9375L21.7813,2ZM19.6563,4.3125C19.4844,4.9727 19.2031,5.6016 18.75,6.0938L18.6875,6.1563C18.3047,6.6719 17.7148,7.0547 17.125,7.2813C17.2891,6.6289 17.5742,5.9688 17.9688,5.4688C17.9766,5.457 17.9922,5.4492 18,5.4375C18.4375,4.9375 19.043,4.5625 19.6563,4.3125ZM12.2188,9.8125C12.7773,9.8125 13.3906,10.0234 14.0313,10.2813C14.6719,10.5391 15.2695,10.875 16.125,10.875C16.9766,10.875 17.6211,10.5391 18.375,10.2813C19.1289,10.0234 19.9336,9.8125 20.7188,9.8125C21.2422,9.8125 22.5352,10.1445 23.625,11.1563C23.4336,11.3281 23.2656,11.4531 23.0625,11.6875C22.3008,12.5703 21.5313,13.9453 21.5313,15.7813C21.5313,17.6875 22.3633,19.1211 23.25,20.0313C23.8008,20.5938 24.1406,20.7266 24.5625,20.9688C24.5078,21.0938 24.4766,21.1914 24.4063,21.3438C24.1367,21.9297 23.7656,22.6797 23.3125,23.4063C22.8594,24.1328 22.3086,24.8398 21.7813,25.3125C21.2539,25.7852 20.7813,26 20.4063,26C19.8398,26 19.5078,25.8438 18.9375,25.5938C18.3672,25.3438 17.582,25 16.5,25C15.3633,25 14.5234,25.3359 13.9375,25.5938C13.3516,25.8516 13.0469,26 12.5313,26C12.2852,26 11.7539,25.8008 11.1563,25.3125C10.5586,24.8242 9.9023,24.082 9.3125,23.1563C8.1289,21.3047 7.2188,18.7617 7.2188,16.1563C7.2188,12.2148 9.418,9.8125 12.2188,9.8125Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_apple_alt_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M10.375,3.9688C10.082,3.9727 9.9063,4 9.9063,4L8.875,4.0938L9,5.125C9,5.125 9.2461,7.4102 10.875,9.0938C7.5703,9.6055 5,12.332 5,15.6563C5,19.2422 6.5977,22.0859 8.4688,24C9.4023,24.957 10.3945,25.6797 11.3438,26.1875C12.293,26.6953 13.1641,27 14,27C14.5469,27 15.0781,26.9063 15.5625,26.75C15.8555,26.6563 16.1445,26.6563 16.4375,26.75C16.9219,26.9063 17.4531,27 18,27C18.9063,27 19.7773,26.6172 20.7188,26.0313C21.6602,25.4453 22.6406,24.6328 23.5625,23.625C25.4063,21.6133 27,18.8203 27,15.6563C27,11.9648 23.8242,9 20,9C18.9727,9 18.0234,9.25 17.1563,9.6563C17.1523,9.6289 17.1328,9.582 17.125,9.5313C17.6367,6.8633 19.5,5.875 19.5,5.875L18.5,4.125C18.5,4.125 17.0898,4.9727 16.0625,6.7813C15.7383,6.2852 15.3086,5.7969 14.7813,5.375C13.1953,4.1094 11.25,3.9609 10.375,3.9688ZM11.3438,6.0938C12.0352,6.1992 12.8906,6.4258 13.5313,6.9375C14.1836,7.457 14.6211,8.2188 14.875,8.875C14.1797,8.7734 13.3086,8.5195 12.6563,8C12.0117,7.4844 11.5977,6.7461 11.3438,6.0938ZM12,11C13.3008,11 14.457,11.4727 15.3438,12.2188L16,12.75L16.6563,12.2188C17.543,11.4727 18.6992,11 20,11C22.8047,11 25,13.1133 25,15.6563C25,18.1367 23.6992,20.5313 22.0938,22.2813C21.293,23.1563 20.4219,23.8398 19.6563,24.3125C18.8906,24.7852 18.1953,25 18,25C17.6719,25 17.3633,24.9414 17.0625,24.8438C16.375,24.6211 15.625,24.6211 14.9375,24.8438C14.6367,24.9414 14.3281,25 14,25C13.7305,25 13.0391,24.8438 12.2813,24.4375C11.5234,24.0313 10.6641,23.4023 9.875,22.5938C8.2969,20.9766 7,18.6563 7,15.6563C7,13.1133 9.1953,11 12,11Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_archive_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M4,5L4,11L5,11L5,27L27,27L27,11L28,11L28,5ZM6,7L26,7L26,9L6,9ZM7,11L25,11L25,25L7,25ZM12.8125,13C12.2617,13.0508 11.8555,13.543 11.9063,14.0938C11.957,14.6445 12.4492,15.0508 13,15L19,15C19.3594,15.0039 19.6953,14.8164 19.8789,14.5039C20.0586,14.1914 20.0586,13.8086 19.8789,13.4961C19.6953,13.1836 19.3594,12.9961 19,13L13,13C12.9688,13 12.9375,13 12.9063,13C12.875,13 12.8438,13 12.8125,13Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_archway_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M3,6L3,12L5,12L5,24L3,24L3,26L5,26L13,26L13,24L13,19C13,17.346 14.346,16 16,16C17.654,16 19,17.346 19,19L19,24L19,26L27,26L29,26L29,24L27,24L27,12L29,12L29,6L3,6zM5,8L27,8L27,10L5,10L5,8zM7,12L25,12L25,24L21,24L21,19C21,16.243 18.757,14 16,14C13.243,14 11,16.243 11,19L11,24L7,24L7,12z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_arrow_down_rotate.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<group
android:rotation="-45"
android:pivotY="16"
android:pivotX="16">
<path
android:fillColor="#FF000000"
android:pathData="M15,4L15,24.0625L8.2188,17.2813L6.7813,18.7188L15.2813,27.2188L16,27.9063L16.7188,27.2188L25.2188,18.7188L23.7813,17.2813L17,24.0625L17,4Z"/>
</group>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_arrow_down_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M15,4L15,24.0625L8.2188,17.2813L6.7813,18.7188L15.2813,27.2188L16,27.9063L16.7188,27.2188L25.2188,18.7188L23.7813,17.2813L17,24.0625L17,4Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_arrow_left_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M13.2813,6.7813L4.7813,15.2813L4.0938,16L4.7813,16.7188L13.2813,25.2188L14.7188,23.7813L7.9375,17L28,17L28,15L7.9375,15L14.7188,8.2188Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_arrow_right_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M18.7188,6.7813L17.2813,8.2188L24.0625,15L4,15L4,17L24.0625,17L17.2813,23.7813L18.7188,25.2188L27.2188,16.7188L27.9063,16L27.2188,15.2813Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_arrow_up_rotate.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<group
android:pivotX="16"
android:pivotY="16"
android:rotation="-45">
<path
android:fillColor="#FF000000"
android:pathData="M16,4.0938L15.2813,4.7813L6.7813,13.2813L8.2188,14.7188L15,7.9375L15,28L17,28L17,7.9375L23.7813,14.7188L25.2188,13.2813L16.7188,4.7813Z" />
</group>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_arrow_up_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M16,4.0938L15.2813,4.7813L6.7813,13.2813L8.2188,14.7188L15,7.9375L15,28L17,28L17,7.9375L23.7813,14.7188L25.2188,13.2813L16.7188,4.7813Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_asterisk_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M17.6992,17L23.8984,25.3984L21.5,27L16,18.3008L10.5,27L8.1992,25.3984L14.3984,17L5.1016,14.6016L6,12L15.1016,15.1992L14.5,5L17.5,5L17,15.1992L26,12L26.8008,14.6992Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_at_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M16.1875,4C8.8242,3.8906 2.9141,10.4844 4.1563,18.0313C5,23.1523 9.2031,27.1797 14.3438,27.875C17.8086,28.3438 21.0469,27.3438 23.5,25.375L22.25,23.8125C20.2031,25.4531 17.5117,26.3008 14.5938,25.9063C10.3398,25.332 6.8516,21.9531 6.1563,17.7188C5.1094,11.3477 9.9922,5.9102 16.1563,6C21.4102,6.0781 25.8398,10.4336 26,15.6875C26.0039,15.8008 26,15.9102 26,16.0313C25.9922,18.2344 24.207,20 22,20C20.8828,20 20,19.1172 20,18L20,10L18,10L18,10.7813C17.2813,10.293 16.4258,10 15.5,10C13.0273,10 11,12.0273 11,14.5L11,17.5C11,19.9727 13.0273,22 15.5,22C16.8477,22 18.0469,21.3867 18.875,20.4375C19.6094,21.3711 20.7305,22 22,22C25.2852,22 27.9883,19.3203 28,16.0313C28,15.8984 28.0039,15.7695 28,15.625C27.8086,9.2891 22.5234,4.0938 16.1875,4ZM15.5,12C16.8906,12 18,13.1094 18,14.5L18,17.5C18,18.8906 16.8906,20 15.5,20C14.1094,20 13,18.8906 13,17.5L13,14.5C13,13.1094 14.1094,12 15.5,12Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_atlas_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M9,4C7.355,4 6,5.355 6,7L6,25C6,26.645 7.355,28 9,28L26,28L26,4L9,4zM9,6L24,6L24,22L9,22C8.648,22 8.316,22.0735 8,22.1875L8,7C8,6.434 8.434,6 9,6zM16,8C12.699,8 10,10.699 10,14C10,17.301 12.699,20 16,20C19.301,20 22,17.301 22,14C22,10.699 19.301,8 16,8zM16.6875,10.0938C17.1715,10.1757 17.6252,10.3235 18.0313,10.5625L18.0957,11.2813L17.627,11.0938L17.252,11.4063L17.3145,12.3125L18.2832,12.0313L19.4707,12.4063L19.1582,12.9375L18.4375,12.5L17.6563,12.625L16.9063,13.1875L16.4688,14.5L17.3125,15.1875C17.3125,15.1875 18.205,15.0313 18.252,15.0313C18.299,15.0313 18.627,15.8457 18.627,15.8457L18.127,17.377C17.513,17.759 16.785,18 16,18C15.766,18 15.5355,17.9452 15.3125,17.9063L15.123,17.5938L15.5938,15.8438L13.8125,14.5L12.1563,14.5L12.0313,14.25C12.0262,14.164 12,14.086 12,14C12,13.785 12.0305,13.578 12.0625,13.375L12.9063,12.7188L14.6875,11.875L14.4063,10.7188L15.1875,10.5625L15.5313,11.0625L16.877,10.8125L16.6875,10.0938zM13.0625,16.7188L13.377,16.7188L13.9707,17.4375C13.6297,17.2375 13.3275,17.0037 13.0625,16.7188zM9,24L24,24L24,26L9,26C8.434,26 8,25.566 8,25C8,24.434 8.434,24 9,24z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_atom_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M16,3C12.9063,3 10.5078,7.5781 10.0781,13.9375C10.6328,14.5273 11.2852,15.1133 12.0078,15.6836C12.0156,15.0195 12.0547,14.3867 12.1094,13.7656C12.6914,13.3633 13.4219,12.8711 14,12.5352C16.6602,11 19.3984,9.9883 21.707,9.6875C22.1602,9.6289 22.5859,9.6016 22.9727,9.6016C24.2695,9.6016 25.1875,9.9141 25.5273,10.5C25.832,11.0313 25.6953,11.875 25.1836,12.8633C25.707,13.3477 26.2031,13.8398 26.6445,14.3359C27.7695,12.4727 28,10.7813 27.2578,9.5C26.375,7.9727 24.3164,7.332 21.4531,7.7031C18.8867,8.0352 15.8867,9.1367 13,10.8047C12.8164,10.9102 12.6445,11.0273 12.4648,11.1406C13.1836,7.4414 14.6445,5 16,5C16.6328,5 17.2891,5.5391 17.8828,6.4766C18.5625,6.2656 19.2344,6.082 19.8867,5.9453C18.8516,4.0898 17.5078,3 16,3ZM9.2188,7.6133C7.0508,7.5742 5.4805,8.2227 4.7422,9.5C3.9375,10.8906 4.2734,12.7578 5.6563,14.8086C5.7813,15 5.918,15.2031 6.0898,15.4258C7.6602,17.4805 10.1133,19.5273 13,21.1953C13.1836,21.3008 13.375,21.3945 13.5625,21.5C12.418,21.8945 11.3086,22.1797 10.2891,22.3125C8.3398,22.5664 6.9141,22.2617 6.4766,21.5C6.168,20.9688 6.3047,20.125 6.8164,19.1367C6.293,18.6523 5.7969,18.1602 5.3516,17.668C4.2305,19.5273 4,21.2188 4.7422,22.5C5.4609,23.7461 6.9609,24.4023 9.0391,24.4023C9.5117,24.4023 10.0195,24.3672 10.5469,24.2969C10.8516,24.2578 11.1563,24.207 11.4688,24.1445L11.6875,24.0977C11.7031,24.0977 11.7148,24.0938 11.7305,24.0898C12.2617,23.9766 12.8477,23.8203 13.5195,23.6094L14.1797,23.4063C15.1602,23.0703 16.1641,22.6523 17.168,22.1641C17.3867,21.4375 17.582,20.5898 17.7305,19.6133C17.1523,19.9375 16.5703,20.2305 15.9922,20.5039C15.3242,20.1914 14.6523,19.8438 14,19.4648C11.3359,17.9258 9.0898,16.0625 7.6797,14.2148C6.4844,12.6484 6.0352,11.2617 6.4766,10.5C6.7773,9.9766 7.5742,9.6797 8.6719,9.625C8.8281,8.918 9.0078,8.2461 9.2188,7.6133ZM20.7422,11.8984C19.9531,12.082 19.1172,12.3516 18.2617,12.6914C18.8281,13.0273 19.3672,13.3867 19.8906,13.7461C19.9531,14.4727 20,15.2148 20,16C20,21.8984 18.1445,26.3984 16.3594,26.9414C16.2383,26.9805 16.1133,27 16,27C15.3672,27 14.7109,26.4648 14.1172,25.5195C13.4375,25.7344 12.7656,25.918 12.1133,26.0547C13.1484,27.9102 14.4922,29 16,29C16.1992,29 16.3906,28.9805 16.5781,28.9414C19.707,28.3477 22,22.9883 22,16C22,15.7852 21.9844,15.5781 21.9805,15.3633C22.8945,16.1563 23.6992,16.9727 24.3242,17.7852C25.5156,19.3516 25.9648,20.7383 25.5273,21.5C25.2188,22.0273 24.4336,22.332 23.3281,22.3867C23.1719,23.0859 22.9961,23.7617 22.7852,24.3906C22.8398,24.3906 22.9063,24.4023 22.9609,24.4023C25.0391,24.4023 26.5391,23.7461 27.2578,22.5C28.1406,20.9727 27.6641,18.8672 25.9102,16.5742C24.668,14.9492 22.8672,13.332 20.7422,11.8984ZM16,14C14.8945,14 14,14.8945 14,16C14,17.1055 14.8945,18 16,18C17.1055,18 18,17.1055 18,16C18,14.8945 17.1055,14 16,14Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_award_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M16,3C15.375,3 14.7539,3.2109 14.2188,3.5938L12.5625,4.75L10.6563,5L10.625,5L10.5938,5.0313C9.3203,5.3164 8.3164,6.3203 8.0313,7.5938L8,7.625L8,7.6563L7.75,9.5938L6.5938,11.0938L6.5625,11.125L6.5625,11.1563C5.8633,12.2734 5.832,13.7148 6.5938,14.7813L7.7813,16.4375L8.0938,18.1563L4.875,23.0625L3.8438,24.5938L8.625,24.5938L9.7813,27.2813L10.5,29L11.5313,27.4375L14.6875,22.6875C15.5352,23.0352 16.4922,23.0664 17.3125,22.6875L20.4688,27.4375L21.5,29L22.2188,27.2813L23.375,24.5938L28.1563,24.5938L27.125,23.0625L24,18.3125L24.25,16.4375L25.4063,14.7813L25.4375,14.75L25.4375,14.7188C26.1367,13.6016 26.168,12.1914 25.4063,11.125L24.25,9.4688L23.875,7.5938L23.9063,7.5938C23.9023,7.5703 23.8789,7.5547 23.875,7.5313C23.6953,6.2227 22.6602,5.1602 21.3438,5L21.3125,5L19.4375,4.75L17.7813,3.5938C17.2461,3.2109 16.625,3 16,3ZM16,5.0313C16.2305,5.0313 16.457,5.1016 16.625,5.2188L18.4063,6.5L18.625,6.6563L18.875,6.6875L21.0625,7L21.0938,7C21.543,7.0508 21.8555,7.3633 21.9063,7.8125L21.9063,7.875L22.3125,10.0938L22.3438,10.3125L22.5,10.5L23.7813,12.2813C24.0195,12.6133 24.0508,13.1758 23.75,13.6563L22.3438,15.625L22.3125,15.875L22,18.0625L22,18.0938C21.9805,18.2578 21.9258,18.4102 21.8438,18.5313L21.7813,18.5625L21.7813,18.5938C21.6367,18.7656 21.4375,18.8789 21.1875,18.9063L21.125,18.9063L18.8438,19.3125L18.5938,19.3438L18.4063,19.5L16.625,20.7813C16.293,21.0195 15.6992,21.0508 15.2188,20.75L13.5938,19.5L13.4063,19.3438L13.125,19.3125L10.9375,19L10.9063,19C10.5977,18.9648 10.3594,18.8047 10.2188,18.5625C10.1563,18.4531 10.1094,18.3242 10.0938,18.1875L10.0938,18.125L9.6875,15.8438L9.6563,15.5938L9.5,15.4063L8.2188,13.625C7.9805,13.293 7.9492,12.6992 8.25,12.2188L9.5,10.5938L9.6563,10.4063L9.6875,10.125L9.9688,8.0313C9.9727,8.0156 9.9961,8.0156 10,8C10.125,7.5117 10.5117,7.125 11,7C11.0156,6.9961 11.0156,6.9727 11.0313,6.9688L13.125,6.6875L13.375,6.6563L13.5938,6.5L15.375,5.2188C15.543,5.1016 15.7695,5.0313 16,5.0313ZM22.9063,20.25L24.4375,22.5938L22.0313,22.5938L21.7813,23.2188L21.0938,24.8125L18.9688,21.5625L19.4375,21.2188L21.4063,20.875L21.4063,20.9063C21.4297,20.9023 21.4453,20.8789 21.4688,20.875C22.0078,20.8008 22.4961,20.5742 22.9063,20.25ZM9.0938,20.2813C9.5195,20.6641 10.0625,20.9297 10.6563,21C10.668,21 10.6758,21 10.6875,21L12.5938,21.25L13.0313,21.5938L10.9063,24.8125L10.2188,23.2188L9.9688,22.5938L7.5625,22.5938Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_baby_carriage_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M18,4L18,13L9.6875,13C9.457,12.457 9.0898,11.7305 8.4688,10.9063C7.4141,9.5039 5.6055,8 3,8L3,10C4.8555,10 6.0469,10.9961 6.875,12.0938C7.6641,13.1406 7.9688,14.125 8,14.2188C8.082,17.6094 10.082,20.5391 12.9375,22C11.3203,22.0352 10,23.375 10,25C10,26.6445 11.3555,28 13,28C14.6445,28 16,26.6445 16,25C16,24.1133 15.6172,23.3008 15,22.75C15.6445,22.8984 16.3125,23 17,23L20,23C20.6875,23 21.3555,22.8984 22,22.75C21.3828,23.3008 21,24.1133 21,25C21,26.6445 22.3555,28 24,28C25.6445,28 27,26.6445 27,25C27,23.375 25.6797,22.0352 24.0625,22C26.6953,20.6523 28.5898,18.0547 28.9375,15C28.9688,14.7109 28.9961,14.4219 29,14.125C29,14.1055 29,14.082 29,14.0625C29,14.0625 29.0078,13.8984 29,13.625L29,13L28.9375,13C28.8633,12.043 28.6641,10.5938 27.9375,9.0625C26.7461,6.5547 24.0469,4 19,4ZM20,6.125C23.6133,6.4219 25.293,8.1211 26.1563,9.9375C26.7461,11.1836 26.8789,12.2305 26.9375,13L20,13ZM10.2188,15L26.7813,15C26.2734,18.3633 23.5,21 20,21L17,21C13.5,21 10.7266,18.3633 10.2188,15ZM13,24C13.5625,24 14,24.4375 14,25C14,25.5625 13.5625,26 13,26C12.4375,26 12,25.5625 12,25C12,24.4375 12.4375,24 13,24ZM24,24C24.5625,24 25,24.4375 25,25C25,25.5625 24.5625,26 24,26C23.4375,26 23,25.5625 23,25C23,24.4375 23.4375,24 24,24Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_bacon_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M10.4375,5C7.6172,5 5.3438,7.25 5.3438,7.25L5,7.5625L5,15L6.4375,15L6.7188,14.7813C6.9023,14.6367 7.4844,14.1719 8.2188,13.75C8.9531,13.3281 9.8242,13 10.4375,13C11.2773,13 11.9844,13.2148 12.8438,13.4688C13.7031,13.7227 14.7148,14 16,14C17.2852,14 18.2578,13.7188 19.0938,13.4688C19.9297,13.2188 20.6328,13 21.5625,13C22.3008,13 23.1875,13.3516 23.9063,13.7813C24.625,14.2109 25.1172,14.6367 25.25,14.75L25.5313,15L27,15L27,7.5625L26.6563,7.25C26.6563,7.25 24.3828,5 21.5625,5C20.3008,5 19.332,5.2813 18.5,5.5313C17.668,5.7813 16.9609,6 16,6C15.0391,6 14.332,5.7813 13.5,5.5313C12.668,5.2813 11.6992,5 10.4375,5ZM10.4375,7C11.3984,7 12.1055,7.2188 12.9375,7.4688C13.7695,7.7188 14.7383,8 16,8C17.2617,8 18.2305,7.7188 19.0625,7.4688C19.8945,7.2188 20.6016,7 21.5625,7C22.9609,7 24.5352,8.1445 25,8.5L25,9.1563C24.9414,9.1211 24.9063,9.0977 24.8438,9.0625C23.9648,8.5391 22.8008,8 21.5625,8C20.4023,8 19.5195,8.2813 18.6875,8.5313C17.8555,8.7813 17.0625,9 16,9C14.9375,9 14.1016,8.7813 13.25,8.5313C12.3984,8.2813 11.5273,8 10.4375,8C9.2656,8 8.1172,8.543 7.2188,9.0625C7.125,9.1172 7.0859,9.1641 7,9.2188L7,8.5C7.4648,8.1445 9.0391,7 10.4375,7ZM10.4375,9C11.3594,9 12.1133,9.2188 12.9688,9.4688C13.8242,9.7188 14.7891,10 16,10C17.2109,10 18.1328,9.7188 18.9688,9.4688C19.8047,9.2188 20.5547,9 21.5625,9C22.4922,9 23.543,9.4609 24.3438,9.9375C24.6133,10.0977 24.8047,10.2344 25,10.375L25,12.0938C24.9688,12.0742 24.9688,12.082 24.9375,12.0625C24.0508,11.5352 22.8906,11 21.5625,11C20.3242,11 19.3633,11.2813 18.5313,11.5313C17.6992,11.7813 16.9922,12 16,12C15.0078,12 14.2539,11.7773 13.4063,11.5313C12.5586,11.2852 11.6094,11 10.4375,11C9.2031,11 8.1055,11.5234 7.2188,12.0313C7.1055,12.0977 7.1055,12.1211 7,12.1875L7,10.4063C7.2188,10.2617 7.4219,10.1094 7.7188,9.9375C8.543,9.457 9.5977,9 10.4375,9ZM10.4375,17C7.6172,17 5.3438,19.25 5.3438,19.25L5,19.5625L5,27L6.4375,27L6.7188,26.7813C6.9023,26.6367 7.4844,26.1719 8.2188,25.75C8.9531,25.3281 9.8242,25 10.4375,25C11.2773,25 11.9844,25.2148 12.8438,25.4688C13.7031,25.7227 14.7148,26 16,26C17.2852,26 18.2578,25.7188 19.0938,25.4688C19.9297,25.2188 20.6328,25 21.5625,25C22.3008,25 23.1875,25.3516 23.9063,25.7813C24.625,26.2109 25.1172,26.6367 25.25,26.75L25.5313,27L27,27L27,19.5625L26.6563,19.25C26.6563,19.25 24.3828,17 21.5625,17C20.3008,17 19.332,17.2813 18.5,17.5313C17.668,17.7813 16.9609,18 16,18C15.0391,18 14.332,17.7813 13.5,17.5313C12.668,17.2813 11.6992,17 10.4375,17ZM10.4375,19C11.3984,19 12.1055,19.2188 12.9375,19.4688C13.7695,19.7188 14.7383,20 16,20C17.2617,20 18.2305,19.7188 19.0625,19.4688C19.8945,19.2188 20.6016,19 21.5625,19C22.9609,19 24.5352,20.1445 25,20.5L25,22.1563C24.9414,22.1211 24.9063,22.0977 24.8438,22.0625C23.9648,21.5391 22.8008,21 21.5625,21C20.4023,21 19.5195,21.2813 18.6875,21.5313C17.8555,21.7813 17.0625,22 16,22C14.9375,22 14.1016,21.7813 13.25,21.5313C12.3984,21.2813 11.5273,21 10.4375,21C9.2656,21 8.1172,21.543 7.2188,22.0625C7.125,22.1172 7.0859,22.1641 7,22.2188L7,20.5C7.4648,20.1445 9.0391,19 10.4375,19ZM10.4375,22C11.3594,22 12.1133,22.2188 12.9688,22.4688C13.8242,22.7188 14.7891,23 16,23C17.2109,23 18.1328,22.7188 18.9688,22.4688C19.8047,22.2188 20.5547,22 21.5625,22C22.4922,22 23.543,22.4609 24.3438,22.9375C24.6133,23.0977 24.8047,23.2344 25,23.375L25,24.0938C24.9688,24.0742 24.9688,24.082 24.9375,24.0625C24.0508,23.5352 22.8906,23 21.5625,23C20.3242,23 19.3633,23.2813 18.5313,23.5313C17.6992,23.7813 16.9922,24 16,24C15.0078,24 14.2539,23.7773 13.4063,23.5313C12.5586,23.2852 11.6094,23 10.4375,23C9.2031,23 8.1055,23.5234 7.2188,24.0313C7.1055,24.0977 7.1055,24.1211 7,24.1875L7,23.4063C7.2188,23.2617 7.4219,23.1094 7.7188,22.9375C8.543,22.457 9.5977,22 10.4375,22Z"/>
</vector>
================================================
FILE: shared/src/commonMain/composeResources/drawable/ic_balance_scale_left_solid.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M23.5234,5.1094L18.2813,7.0762C17.7305,6.4241 16.9182,6 16,6C14.346,6 13,7.346 13,9C13,9.0214 13.0054,9.0412 13.0059,9.0625L8,11L3,19.75L3,20.0156C3,22.7716 5.243,25 8,25C10.757,25 13,22.7716 13,20.0156L13,19.75L9.0098,12.7656L13.7246,10.9316C14.0632,11.3299 14.4983,11.6374 15,11.8164L15,24L15,26L17,26L21,26L21,24L17,24L17,11.8164C18.1613,11.4021 19,10.3016 19,9C19,8.9813 18.9945,8.964 18.9941,8.9453L23.2109,7.3633L19,14.7344L19,15C19,17.757 21.243,20 24,20C26.757,20 29,17.757 29,15L29,14.7344L24.5098,6.877L23.5234,5.1094zM16,8C16.552,8 17,8.449 17,9C17,9.551 16.552,10 16,10C15.448,10 15,9.551 15,9C15,8.449 15.448,8 16,8zM24,10.0156L26.2773,14L21.7227,14L24,10.0156zM8,15.0313L10.2676,19L5.7324,19L8,15.0313zM21.1855,16L26.8145,16C26.4002,17.1616 25.3022,18 24,18C22.6978,18 21.5998,17.1616 21.1855,16zM5.1797,21L10.8203,21C10.41,22.169 9.3074,23.0156 8,23.0156C6.6926,2
gitextract_z7ii1zwp/ ├── .claude/ │ └── settings.local.json ├── .github/ │ ├── FUNDING.yml │ ├── actions/ │ │ └── setup-gradle/ │ │ └── action.yml │ └── workflows/ │ ├── android-release.yml │ ├── checks.yml │ ├── ios-release.yml │ ├── release.yml │ └── roborazzi.yml ├── .gitignore ├── AGENTS.md ├── README.md ├── androidApp/ │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src/ │ ├── debug/ │ │ └── res/ │ │ └── mipmap-anydpi-v26/ │ │ └── ic_launcher.xml │ ├── main/ │ │ ├── AndroidManifest.xml │ │ └── kotlin/ │ │ └── com/ │ │ └── prof18/ │ │ └── moneyflow/ │ │ ├── MainActivity.kt │ │ └── MoneyFlowApp.kt │ └── release/ │ └── res/ │ └── mipmap-anydpi-v26/ │ └── ic_launcher.xml ├── build-logic/ │ ├── convention/ │ │ ├── build.gradle.kts │ │ └── src/ │ │ └── main/ │ │ └── kotlin/ │ │ └── DetektConventionPlugin.kt │ └── settings.gradle.kts ├── build.gradle.kts ├── config/ │ └── detekt/ │ └── detekt.yml ├── gradle/ │ ├── libs.versions.toml │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── iosApp/ │ ├── .scripts/ │ │ └── version.sh │ ├── Assets/ │ │ ├── DebugIcon.icon/ │ │ │ └── icon.json │ │ ├── Icon.icon/ │ │ │ └── icon.json │ │ └── Info.plist │ ├── Configuration/ │ │ └── Config.xcconfig │ ├── MoneyFlow.entitlements │ ├── MoneyFlow.xcodeproj/ │ │ ├── project.pbxproj │ │ ├── project.xcworkspace/ │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ └── MoneyFlow.xcscheme │ └── Source/ │ ├── ContentView.swift │ ├── DI/ │ │ └── Koin.swift │ └── MoneyFlowApp.swift ├── renovate.json ├── settings.gradle.kts ├── setup.sh ├── shared/ │ ├── .gitignore │ ├── build.gradle.kts │ └── src/ │ ├── androidMain/ │ │ ├── kotlin/ │ │ │ └── com/ │ │ │ └── prof18/ │ │ │ └── moneyflow/ │ │ │ ├── AndroidBiometricAuthenticator.kt │ │ │ ├── AndroidBiometricAvailabilityChecker.kt │ │ │ ├── database/ │ │ │ │ └── DatabaseDriverFactory.kt │ │ │ ├── di/ │ │ │ │ └── KoinAndroid.kt │ │ │ └── utils/ │ │ │ ├── LocalAppLocale.android.kt │ │ │ └── LocalAppTheme.android.kt │ │ └── res/ │ │ ├── values/ │ │ │ └── themes.xml │ │ └── values-night/ │ │ └── themes.xml │ ├── androidUnitTest/ │ │ ├── AndroidManifest.xml │ │ └── kotlin/ │ │ └── com/ │ │ └── prof18/ │ │ └── moneyflow/ │ │ ├── AddTransactionRoborazziTest.kt │ │ ├── AllTransactionsRoborazziTest.kt │ │ ├── AuthRoborazziTest.kt │ │ ├── BudgetAndRecapRoborazziTest.kt │ │ ├── CategoriesRoborazziTest.kt │ │ ├── ComponentsRoborazziTest.kt │ │ ├── HomeRoborazziTest.kt │ │ ├── MoneyFlowLockedRoborazziTest.kt │ │ ├── MoneyFlowNavHostRoborazziTest.kt │ │ ├── RoborazziRule.kt │ │ ├── RoborazziTestBase.kt │ │ ├── SettingsRoborazziTest.kt │ │ └── utilities/ │ │ └── TestUtilsAndroid.kt │ ├── commonMain/ │ │ ├── composeResources/ │ │ │ ├── drawable/ │ │ │ │ ├── ic_address_book.xml │ │ │ │ ├── ic_address_card.xml │ │ │ │ ├── ic_adjust_solid.xml │ │ │ │ ├── ic_air_freshener_solid.xml │ │ │ │ ├── ic_algolia.xml │ │ │ │ ├── ic_allergies_solid.xml │ │ │ │ ├── ic_ambulance_solid.xml │ │ │ │ ├── ic_anchor_solid.xml │ │ │ │ ├── ic_android.xml │ │ │ │ ├── ic_angle_down_solid.xml │ │ │ │ ├── ic_angle_left_solid.xml │ │ │ │ ├── ic_angle_right_solid.xml │ │ │ │ ├── ic_angle_up_solid.xml │ │ │ │ ├── ic_apple.xml │ │ │ │ ├── ic_apple_alt_solid.xml │ │ │ │ ├── ic_archive_solid.xml │ │ │ │ ├── ic_archway_solid.xml │ │ │ │ ├── ic_arrow_down_rotate.xml │ │ │ │ ├── ic_arrow_down_solid.xml │ │ │ │ ├── ic_arrow_left_solid.xml │ │ │ │ ├── ic_arrow_right_solid.xml │ │ │ │ ├── ic_arrow_up_rotate.xml │ │ │ │ ├── ic_arrow_up_solid.xml │ │ │ │ ├── ic_asterisk_solid.xml │ │ │ │ ├── ic_at_solid.xml │ │ │ │ ├── ic_atlas_solid.xml │ │ │ │ ├── ic_atom_solid.xml │ │ │ │ ├── ic_award_solid.xml │ │ │ │ ├── ic_baby_carriage_solid.xml │ │ │ │ ├── ic_bacon_solid.xml │ │ │ │ ├── ic_balance_scale_left_solid.xml │ │ │ │ ├── ic_band_aid_solid.xml │ │ │ │ ├── ic_baseball_ball_solid.xml │ │ │ │ ├── ic_basketball_ball_solid.xml │ │ │ │ ├── ic_bath_solid.xml │ │ │ │ ├── ic_battery_three_quarters_solid.xml │ │ │ │ ├── ic_bed_solid.xml │ │ │ │ ├── ic_beer_solid.xml │ │ │ │ ├── ic_bell.xml │ │ │ │ ├── ic_bell_slash.xml │ │ │ │ ├── ic_bicycle_solid.xml │ │ │ │ ├── ic_biking_solid.xml │ │ │ │ ├── ic_binoculars_solid.xml │ │ │ │ ├── ic_birthday_cake_solid.xml │ │ │ │ ├── ic_bitcoin.xml │ │ │ │ ├── ic_black_tie.xml │ │ │ │ ├── ic_blender_solid.xml │ │ │ │ ├── ic_blind_solid.xml │ │ │ │ ├── ic_bolt_solid.xml │ │ │ │ ├── ic_bomb_solid.xml │ │ │ │ ├── ic_bone_solid.xml │ │ │ │ ├── ic_bong_solid.xml │ │ │ │ ├── ic_book_open_solid.xml │ │ │ │ ├── ic_book_solid.xml │ │ │ │ ├── ic_bookmark.xml │ │ │ │ ├── ic_bowling_ball_solid.xml │ │ │ │ ├── ic_box_solid.xml │ │ │ │ ├── ic_brain_solid.xml │ │ │ │ ├── ic_bread_slice_solid.xml │ │ │ │ ├── ic_briefcase_medical_solid.xml │ │ │ │ ├── ic_briefcase_solid.xml │ │ │ │ ├── ic_broadcast_tower_solid.xml │ │ │ │ ├── ic_broom_solid.xml │ │ │ │ ├── ic_brush_solid.xml │ │ │ │ ├── ic_bug_solid.xml │ │ │ │ ├── ic_building.xml │ │ │ │ ├── ic_bullhorn_solid.xml │ │ │ │ ├── ic_bullseye_solid.xml │ │ │ │ ├── ic_burn_solid.xml │ │ │ │ ├── ic_bus_solid.xml │ │ │ │ ├── ic_calculator_solid.xml │ │ │ │ ├── ic_calendar.xml │ │ │ │ ├── ic_camera_solid.xml │ │ │ │ ├── ic_campground_solid.xml │ │ │ │ ├── ic_candy_cane_solid.xml │ │ │ │ ├── ic_capsules_solid.xml │ │ │ │ ├── ic_car_alt_solid.xml │ │ │ │ ├── ic_car_side_solid.xml │ │ │ │ ├── ic_caret_down_solid.xml │ │ │ │ ├── ic_caret_left_solid.xml │ │ │ │ ├── ic_caret_right_solid.xml │ │ │ │ ├── ic_caret_up_solid.xml │ │ │ │ ├── ic_carrot_solid.xml │ │ │ │ ├── ic_cart_arrow_down_solid.xml │ │ │ │ ├── ic_cash_register_solid.xml │ │ │ │ ├── ic_cat_solid.xml │ │ │ │ ├── ic_certificate_solid.xml │ │ │ │ ├── ic_chair_solid.xml │ │ │ │ ├── ic_chalkboard_solid.xml │ │ │ │ ├── ic_chalkboard_teacher_solid.xml │ │ │ │ ├── ic_charging_station_solid.xml │ │ │ │ ├── ic_chart_area_solid.xml │ │ │ │ ├── ic_chart_bar.xml │ │ │ │ ├── ic_chart_line_solid.xml │ │ │ │ ├── ic_chart_pie_solid.xml │ │ │ │ ├── ic_check_circle.xml │ │ │ │ ├── ic_cheese_solid.xml │ │ │ │ ├── ic_church_solid.xml │ │ │ │ ├── ic_city_solid.xml │ │ │ │ ├── ic_clinic_medical_solid.xml │ │ │ │ ├── ic_clipboard.xml │ │ │ │ ├── ic_clock.xml │ │ │ │ ├── ic_cloud_download_alt_solid.xml │ │ │ │ ├── ic_cloud_solid.xml │ │ │ │ ├── ic_cloud_upload_alt_solid.xml │ │ │ │ ├── ic_cocktail_solid.xml │ │ │ │ ├── ic_code_branch_solid.xml │ │ │ │ ├── ic_code_solid.xml │ │ │ │ ├── ic_coffee_solid.xml │ │ │ │ ├── ic_cog_solid.xml │ │ │ │ ├── ic_coins_solid.xml │ │ │ │ ├── ic_comment_alt.xml │ │ │ │ ├── ic_compact_disc_solid.xml │ │ │ │ ├── ic_compass.xml │ │ │ │ ├── ic_concierge_bell_solid.xml │ │ │ │ ├── ic_cookie_bite_solid.xml │ │ │ │ ├── ic_couch_solid.xml │ │ │ │ ├── ic_credit_card.xml │ │ │ │ ├── ic_crown_solid.xml │ │ │ │ ├── ic_cubes_solid.xml │ │ │ │ ├── ic_cut_solid.xml │ │ │ │ ├── ic_desktop_solid.xml │ │ │ │ ├── ic_diaspora.xml │ │ │ │ ├── ic_dice_d6_solid.xml │ │ │ │ ├── ic_dna_solid.xml │ │ │ │ ├── ic_dog_solid.xml │ │ │ │ ├── ic_dollar_sign.xml │ │ │ │ ├── ic_dollar_sign_solid.xml │ │ │ │ ├── ic_dolly_flatbed_solid.xml │ │ │ │ ├── ic_dolly_solid.xml │ │ │ │ ├── ic_donate_solid.xml │ │ │ │ ├── ic_drafting_compass_solid.xml │ │ │ │ ├── ic_drum_solid.xml │ │ │ │ ├── ic_drumstick_bite_solid.xml │ │ │ │ ├── ic_dumbbell_solid.xml │ │ │ │ ├── ic_dumpster_solid.xml │ │ │ │ ├── ic_edit.xml │ │ │ │ ├── ic_egg_solid.xml │ │ │ │ ├── ic_envelope.xml │ │ │ │ ├── ic_envelope_open.xml │ │ │ │ ├── ic_eraser_solid.xml │ │ │ │ ├── ic_euro_sign.xml │ │ │ │ ├── ic_euro_sign_solid.xml │ │ │ │ ├── ic_exchange_alt_solid.xml │ │ │ │ ├── ic_exclamation_circle_solid.xml │ │ │ │ ├── ic_exclamation_triangle_solid.xml │ │ │ │ ├── ic_expeditedssl.xml │ │ │ │ ├── ic_external_link_alt_solid.xml │ │ │ │ ├── ic_eye_dropper_solid.xml │ │ │ │ ├── ic_fan_solid.xml │ │ │ │ ├── ic_fax_solid.xml │ │ │ │ ├── ic_feather_alt_solid.xml │ │ │ │ ├── ic_female_solid.xml │ │ │ │ ├── ic_fighter_jet_solid.xml │ │ │ │ ├── ic_file.xml │ │ │ │ ├── ic_file_alt.xml │ │ │ │ ├── ic_file_audio.xml │ │ │ │ ├── ic_file_code.xml │ │ │ │ ├── ic_file_csv_solid.xml │ │ │ │ ├── ic_file_export_solid.xml │ │ │ │ ├── ic_file_import_solid.xml │ │ │ │ ├── ic_file_invoice_dollar_solid.xml │ │ │ │ ├── ic_file_invoice_solid.xml │ │ │ │ ├── ic_file_pdf.xml │ │ │ │ ├── ic_fill_solid.xml │ │ │ │ ├── ic_film_solid.xml │ │ │ │ ├── ic_fire_alt_solid.xml │ │ │ │ ├── ic_fire_extinguisher_solid.xml │ │ │ │ ├── ic_first_aid_solid.xml │ │ │ │ ├── ic_fish_solid.xml │ │ │ │ ├── ic_flag.xml │ │ │ │ ├── ic_flag_checkered_solid.xml │ │ │ │ ├── ic_flask_solid.xml │ │ │ │ ├── ic_fly.xml │ │ │ │ ├── ic_folder.xml │ │ │ │ ├── ic_football_ball_solid.xml │ │ │ │ ├── ic_fort_awesome.xml │ │ │ │ ├── ic_frown.xml │ │ │ │ ├── ic_futbol.xml │ │ │ │ ├── ic_gamepad_solid.xml │ │ │ │ ├── ic_gas_pump_solid.xml │ │ │ │ ├── ic_gavel_solid.xml │ │ │ │ ├── ic_gift_solid.xml │ │ │ │ ├── ic_glass_cheers_solid.xml │ │ │ │ ├── ic_glass_martini_alt_solid.xml │ │ │ │ ├── ic_globe_solid.xml │ │ │ │ ├── ic_golf_ball_solid.xml │ │ │ │ ├── ic_gopuram_solid.xml │ │ │ │ ├── ic_graduation_cap_solid.xml │ │ │ │ ├── ic_guitar_solid.xml │ │ │ │ ├── ic_hamburger_solid.xml │ │ │ │ ├── ic_hammer_solid.xml │ │ │ │ ├── ic_hat_cowboy_solid.xml │ │ │ │ ├── ic_hdd.xml │ │ │ │ ├── ic_headphones_solid.xml │ │ │ │ ├── ic_helicopter_solid.xml │ │ │ │ ├── ic_highlighter_solid.xml │ │ │ │ ├── ic_hiking_solid.xml │ │ │ │ ├── ic_home_solid.xml │ │ │ │ ├── ic_horse_head_solid.xml │ │ │ │ ├── ic_hospital.xml │ │ │ │ ├── ic_hotdog_solid.xml │ │ │ │ ├── ic_hourglass_half_solid.xml │ │ │ │ ├── ic_ice_cream_solid.xml │ │ │ │ ├── ic_id_card.xml │ │ │ │ ├── ic_image.xml │ │ │ │ ├── ic_inbox_solid.xml │ │ │ │ ├── ic_industry_solid.xml │ │ │ │ ├── ic_itunes_note.xml │ │ │ │ ├── ic_key_solid.xml │ │ │ │ ├── ic_keyboard.xml │ │ │ │ ├── ic_landmark_solid.xml │ │ │ │ ├── ic_laptop_solid.xml │ │ │ │ ├── ic_lightbulb.xml │ │ │ │ ├── ic_list_ul_solid.xml │ │ │ │ ├── ic_luggage_cart_solid.xml │ │ │ │ ├── ic_mail_bulk_solid.xml │ │ │ │ ├── ic_male_solid.xml │ │ │ │ ├── ic_map_marked_alt_solid.xml │ │ │ │ ├── ic_marker_solid.xml │ │ │ │ ├── ic_mars_solid.xml │ │ │ │ ├── ic_mask_solid.xml │ │ │ │ ├── ic_medal_solid.xml │ │ │ │ ├── ic_medapps.xml │ │ │ │ ├── ic_medkit_solid.xml │ │ │ │ ├── ic_mercury_solid.xml │ │ │ │ ├── ic_microchip_solid.xml │ │ │ │ ├── ic_microphone_alt_solid.xml │ │ │ │ ├── ic_microscope_solid.xml │ │ │ │ ├── ic_mobile_solid.xml │ │ │ │ ├── ic_money_bill_wave.xml │ │ │ │ ├── ic_money_check_alt_solid.xml │ │ │ │ ├── ic_mortar_pestle_solid.xml │ │ │ │ ├── ic_motorcycle_solid.xml │ │ │ │ ├── ic_mountain_solid.xml │ │ │ │ ├── ic_mug_hot_solid.xml │ │ │ │ ├── ic_oil_can_solid.xml │ │ │ │ ├── ic_pager_solid.xml │ │ │ │ ├── ic_paint_roller_solid.xml │ │ │ │ ├── ic_paperclip_solid.xml │ │ │ │ ├── ic_parachute_box_solid.xml │ │ │ │ ├── ic_parking_solid.xml │ │ │ │ ├── ic_passport_solid.xml │ │ │ │ ├── ic_paw_solid.xml │ │ │ │ ├── ic_pen_alt_solid.xml │ │ │ │ ├── ic_pen_solid.xml │ │ │ │ ├── ic_phone_solid.xml │ │ │ │ ├── ic_photo_video_solid.xml │ │ │ │ ├── ic_piggy_bank_solid.xml │ │ │ │ ├── ic_pills_solid.xml │ │ │ │ ├── ic_pizza_slice_solid.xml │ │ │ │ ├── ic_plane_solid.xml │ │ │ │ ├── ic_plug_solid.xml │ │ │ │ ├── ic_pound_sign.xml │ │ │ │ ├── ic_pound_sign_solid.xml │ │ │ │ ├── ic_prescription_bottle_solid.xml │ │ │ │ ├── ic_print_solid.xml │ │ │ │ ├── ic_question_circle.xml │ │ │ │ ├── ic_readme.xml │ │ │ │ ├── ic_recycle_solid.xml │ │ │ │ ├── ic_restroom_solid.xml │ │ │ │ ├── ic_road_solid.xml │ │ │ │ ├── ic_robot_solid.xml │ │ │ │ ├── ic_rocket_solid.xml │ │ │ │ ├── ic_running_solid.xml │ │ │ │ ├── ic_screwdriver_solid.xml │ │ │ │ ├── ic_scroll_solid.xml │ │ │ │ ├── ic_seedling_solid.xml │ │ │ │ ├── ic_server_solid.xml │ │ │ │ ├── ic_shield_alt_solid.xml │ │ │ │ ├── ic_ship_solid.xml │ │ │ │ ├── ic_shipping_fast_solid.xml │ │ │ │ ├── ic_shopping_bag_solid.xml │ │ │ │ ├── ic_shopping_cart_solid.xml │ │ │ │ ├── ic_shuttle_van_solid.xml │ │ │ │ ├── ic_signal_solid.xml │ │ │ │ ├── ic_sim_card_solid.xml │ │ │ │ ├── ic_skating_solid.xml │ │ │ │ ├── ic_skiing_nordic_solid.xml │ │ │ │ ├── ic_skiing_solid.xml │ │ │ │ ├── ic_smoking_solid.xml │ │ │ │ ├── ic_sms_solid.xml │ │ │ │ ├── ic_snowboarding_solid.xml │ │ │ │ ├── ic_snowflake.xml │ │ │ │ ├── ic_socks_solid.xml │ │ │ │ ├── ic_spider_solid.xml │ │ │ │ ├── ic_spray_can_solid.xml │ │ │ │ ├── ic_stamp_solid.xml │ │ │ │ ├── ic_star_of_life_solid.xml │ │ │ │ ├── ic_stethoscope_solid.xml │ │ │ │ ├── ic_sticky_note.xml │ │ │ │ ├── ic_stopwatch_solid.xml │ │ │ │ ├── ic_store_alt_solid.xml │ │ │ │ ├── ic_subway_solid.xml │ │ │ │ ├── ic_suitcase_solid.xml │ │ │ │ ├── ic_swimmer_solid.xml │ │ │ │ ├── ic_syringe_solid.xml │ │ │ │ ├── ic_table_tennis_solid.xml │ │ │ │ ├── ic_tablet_solid.xml │ │ │ │ ├── ic_tachometer_alt_solid.xml │ │ │ │ ├── ic_tag_solid.xml │ │ │ │ ├── ic_taxi_solid.xml │ │ │ │ ├── ic_temperature_high_solid.xml │ │ │ │ ├── ic_terminal_solid.xml │ │ │ │ ├── ic_theater_masks_solid.xml │ │ │ │ ├── ic_thermometer_full_solid.xml │ │ │ │ ├── ic_ticket_alt_solid.xml │ │ │ │ ├── ic_tint_solid.xml │ │ │ │ ├── ic_toilet_paper_solid.xml │ │ │ │ ├── ic_toolbox_solid.xml │ │ │ │ ├── ic_tools_solid.xml │ │ │ │ ├── ic_tooth_solid.xml │ │ │ │ ├── ic_tractor_solid.xml │ │ │ │ ├── ic_train_solid.xml │ │ │ │ ├── ic_trash_alt.xml │ │ │ │ ├── ic_tree_solid.xml │ │ │ │ ├── ic_trophy_solid.xml │ │ │ │ ├── ic_truck_loading_solid.xml │ │ │ │ ├── ic_truck_moving_solid.xml │ │ │ │ ├── ic_truck_pickup_solid.xml │ │ │ │ ├── ic_tshirt_solid.xml │ │ │ │ ├── ic_tv_solid.xml │ │ │ │ ├── ic_university_solid.xml │ │ │ │ ├── ic_user.xml │ │ │ │ ├── ic_user_friends_solid.xml │ │ │ │ ├── ic_utensils_solid.xml │ │ │ │ ├── ic_venus_solid.xml │ │ │ │ ├── ic_vial_solid.xml │ │ │ │ ├── ic_video_solid.xml │ │ │ │ ├── ic_volleyball_ball_solid.xml │ │ │ │ ├── ic_volume_up_solid.xml │ │ │ │ ├── ic_walking_solid.xml │ │ │ │ ├── ic_wallet_solid.xml │ │ │ │ ├── ic_wine_glass_solid.xml │ │ │ │ ├── ic_wrench_solid.xml │ │ │ │ └── ic_yen_sign_solid.xml │ │ │ └── values/ │ │ │ └── strings.xml │ │ ├── kotlin/ │ │ │ └── com/ │ │ │ └── prof18/ │ │ │ └── moneyflow/ │ │ │ ├── MainViewModel.kt │ │ │ ├── data/ │ │ │ │ ├── MoneyRepository.kt │ │ │ │ ├── SettingsRepository.kt │ │ │ │ └── settings/ │ │ │ │ └── SettingsSource.kt │ │ │ ├── database/ │ │ │ │ ├── DatabaseHelper.kt │ │ │ │ ├── default/ │ │ │ │ │ └── DefaultValues.kt │ │ │ │ └── model/ │ │ │ │ └── TransactionType.kt │ │ │ ├── di/ │ │ │ │ └── Koin.kt │ │ │ ├── domain/ │ │ │ │ └── entities/ │ │ │ │ ├── BalanceRecap.kt │ │ │ │ ├── Category.kt │ │ │ │ ├── CurrencyConfig.kt │ │ │ │ ├── DBImportExportException.kt │ │ │ │ ├── MoneyFlowError.kt │ │ │ │ ├── MoneyFlowResult.kt │ │ │ │ ├── MoneySummary.kt │ │ │ │ ├── MoneyTransaction.kt │ │ │ │ └── TransactionTypeUI.kt │ │ │ ├── features/ │ │ │ │ ├── addtransaction/ │ │ │ │ │ └── AddTransactionViewModel.kt │ │ │ │ ├── alltransactions/ │ │ │ │ │ └── AllTransactionsViewModel.kt │ │ │ │ ├── authentication/ │ │ │ │ │ └── BiometricAuthenticator.kt │ │ │ │ ├── categories/ │ │ │ │ │ └── CategoriesViewModel.kt │ │ │ │ ├── home/ │ │ │ │ │ └── HomeViewModel.kt │ │ │ │ └── settings/ │ │ │ │ ├── BiometricAvailabilityChecker.kt │ │ │ │ └── SettingsViewModel.kt │ │ │ ├── navigation/ │ │ │ │ ├── AppRoute.kt │ │ │ │ └── MoneyFlowNavHost.kt │ │ │ ├── presentation/ │ │ │ │ ├── MoneyFlowApp.kt │ │ │ │ ├── MoneyFlowErrorMapper.kt │ │ │ │ ├── addtransaction/ │ │ │ │ │ ├── AddTransactionAction.kt │ │ │ │ │ ├── AddTransactionScreen.kt │ │ │ │ │ ├── TransactionToSave.kt │ │ │ │ │ └── components/ │ │ │ │ │ ├── IconTextClicableRow.kt │ │ │ │ │ ├── MFTextField.kt │ │ │ │ │ └── TransactionTypeTabBar.kt │ │ │ │ ├── alltransactions/ │ │ │ │ │ └── AllTransactionsScreen.kt │ │ │ │ ├── auth/ │ │ │ │ │ ├── AuthInProgressScreen.kt │ │ │ │ │ └── AuthState.kt │ │ │ │ ├── budget/ │ │ │ │ │ └── BudgetScreen.kt │ │ │ │ ├── categories/ │ │ │ │ │ ├── CategoriesScreen.kt │ │ │ │ │ ├── CategoryModel.kt │ │ │ │ │ ├── IconCategoryMapper.kt │ │ │ │ │ ├── components/ │ │ │ │ │ │ └── CategoryCard.kt │ │ │ │ │ └── data/ │ │ │ │ │ └── CategoryUIData.kt │ │ │ │ ├── home/ │ │ │ │ │ ├── HomeModel.kt │ │ │ │ │ ├── HomeScreen.kt │ │ │ │ │ └── components/ │ │ │ │ │ ├── HeaderNavigator.kt │ │ │ │ │ └── HomeRecap.kt │ │ │ │ ├── model/ │ │ │ │ │ ├── CategoryIcon.kt │ │ │ │ │ ├── UIErrorMessage.kt │ │ │ │ │ └── UIErrorMessageFactory.kt │ │ │ │ ├── recap/ │ │ │ │ │ └── RecapScreen.kt │ │ │ │ └── settings/ │ │ │ │ └── SettingsScreen.kt │ │ │ ├── ui/ │ │ │ │ ├── components/ │ │ │ │ │ ├── ArrowCircleIcon.kt │ │ │ │ │ ├── ErrorView.kt │ │ │ │ │ ├── HideableTextField.kt │ │ │ │ │ ├── Loader.kt │ │ │ │ │ ├── MFTopBar.kt │ │ │ │ │ ├── SwitchWithText.kt │ │ │ │ │ └── TransactionCard.kt │ │ │ │ └── style/ │ │ │ │ ├── Color.kt │ │ │ │ ├── Margins.kt │ │ │ │ ├── Shape.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Typography.kt │ │ │ └── utils/ │ │ │ ├── CurrencyFormatter.kt │ │ │ ├── DispatcherProvider.kt │ │ │ ├── LocalAppDensity.kt │ │ │ ├── LocalAppLocale.kt │ │ │ ├── LocalAppTheme.kt │ │ │ └── Utils.kt │ │ └── sqldelight/ │ │ └── com/ │ │ └── prof18/ │ │ └── moneyflow/ │ │ └── db/ │ │ ├── AccountTable.sq │ │ ├── CategoryTable.sq │ │ └── TransactionTable.sq │ ├── commonTest/ │ │ └── kotlin/ │ │ └── com/ │ │ └── prof18/ │ │ └── moneyflow/ │ │ └── utilities/ │ │ └── TestDatabaseHelper.kt │ ├── iosMain/ │ │ └── kotlin/ │ │ └── com/ │ │ └── prof18/ │ │ └── moneyflow/ │ │ ├── IosBiometricAuthenticator.kt │ │ ├── IosBiometricAvailabilityChecker.kt │ │ ├── MainViewController.kt │ │ ├── database/ │ │ │ └── DatabaseDriverFactory.kt │ │ ├── di/ │ │ │ └── KoinIos.kt │ │ └── utils/ │ │ ├── LocalAppLocale.ios.kt │ │ └── LocalAppTheme.ios.kt │ └── iosTest/ │ └── kotlin/ │ └── com/ │ └── prof18/ │ └── moneyflow/ │ └── utilities/ │ ├── DatabaseHelperIosTest.kt │ └── TestUtilsIos.kt ├── version.properties └── versioning.gradle.kts
Condensed preview — 490 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (831K chars).
[
{
"path": ".claude/settings.local.json",
"chars": 196,
"preview": "{\n \"permissions\": {\n \"allow\": [\n \"Bash(lipo -info:*)\",\n \"Bash(./gradlew:*)\",\n \"Bash(find:*)\",\n \""
},
{
"path": ".github/FUNDING.yml",
"chars": 91,
"preview": "# These are supported funding model platforms\n\ncustom: https://www.paypal.me/MarcoGomiero\n\n"
},
{
"path": ".github/actions/setup-gradle/action.yml",
"chars": 1273,
"preview": "name: Setup Environment\ndescription: Setup the environment (Java and Gradle)\n\ninputs:\n gradle-cache-encryption-key:\n "
},
{
"path": ".github/workflows/android-release.yml",
"chars": 1801,
"preview": "name: Android Release\n\non:\n workflow_call:\n secrets:\n GRADLE_CACHE_ENCRYPTION_KEY:\n required: true\n "
},
{
"path": ".github/workflows/checks.yml",
"chars": 1820,
"preview": "name: Code Checks\non:\n push:\n branches:\n - '*'\n pull_request:\n branches:\n - '*'\n\njobs:\n checks:\n r"
},
{
"path": ".github/workflows/ios-release.yml",
"chars": 4245,
"preview": "name: iOS Release\n\non:\n workflow_call:\n secrets:\n GRADLE_CACHE_ENCRYPTION_KEY:\n required: true\n CER"
},
{
"path": ".github/workflows/release.yml",
"chars": 2185,
"preview": "name: Release\n\non:\n workflow_dispatch:\n inputs:\n platforms:\n description: 'Platforms to build for prerel"
},
{
"path": ".github/workflows/roborazzi.yml",
"chars": 1171,
"preview": "name: Roborazzi Snapshots\n\non:\n pull_request:\n branches:\n - '*'\n\npermissions:\n contents: write\n\njobs:\n record"
},
{
"path": ".gitignore",
"chars": 2227,
"preview": "# Dependencies\nnode_modules/\n.pnp/\n.pnp.js\n\n# Build outputs\ndist/\nbuild/\n*.tsbuildinfo\nreader-playground/frontend/.vite/"
},
{
"path": "AGENTS.md",
"chars": 3303,
"preview": "# AGENTS.md\n\n## Project Overview\n\nMoneyFlow is a Kotlin Multiplatform (KMP) personal finance app targeting Android and i"
},
{
"path": "README.md",
"chars": 2098,
"preview": "# MoneyFlow\nMoneyFlow is a Kotlin Multiplatform personal finance app targeting Android and iOS with Compose Multiplatfor"
},
{
"path": "androidApp/build.gradle.kts",
"chars": 2657,
"preview": "import java.util.Properties\n\nplugins {\n alias(libs.plugins.android.application)\n alias(libs.plugins.kotlin.android"
},
{
"path": "androidApp/proguard-rules.pro",
"chars": 1178,
"preview": "# Okhttp\n\n# JSR 305 annotations are for embedding nullability information.\n-dontwarn javax.annotation.**\n\n# A resource i"
},
{
"path": "androidApp/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml",
"chars": 328,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <bac"
},
{
"path": "androidApp/src/main/AndroidManifest.xml",
"chars": 737,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <applic"
},
{
"path": "androidApp/src/main/kotlin/com/prof18/moneyflow/MainActivity.kt",
"chars": 1848,
"preview": "package com.prof18.moneyflow\n\nimport android.content.res.Configuration\nimport android.graphics.Color\nimport android.os.B"
},
{
"path": "androidApp/src/main/kotlin/com/prof18/moneyflow/MoneyFlowApp.kt",
"chars": 575,
"preview": "package com.prof18.moneyflow\n\nimport android.app.Application\nimport android.content.Context\nimport com.prof18.moneyflow."
},
{
"path": "androidApp/src/release/res/mipmap-anydpi-v26/ic_launcher.xml",
"chars": 328,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <bac"
},
{
"path": "build-logic/convention/build.gradle.kts",
"chars": 407,
"preview": "plugins {\n `kotlin-dsl`\n}\n\njava {\n sourceCompatibility = JavaVersion.VERSION_21\n targetCompatibility = JavaVers"
},
{
"path": "build-logic/convention/src/main/kotlin/DetektConventionPlugin.kt",
"chars": 1727,
"preview": "import io.gitlab.arturbosch.detekt.extensions.DetektExtension\nimport io.gitlab.arturbosch.detekt.extensions.DetektExtens"
},
{
"path": "build-logic/settings.gradle.kts",
"chars": 392,
"preview": "pluginManagement {\n repositories {\n google()\n gradlePluginPortal()\n mavenCentral()\n }\n}\n\ndepe"
},
{
"path": "build.gradle.kts",
"chars": 584,
"preview": "plugins {\n alias(libs.plugins.android.application) apply false\n alias(libs.plugins.kotlin.parcelize) apply false\n "
},
{
"path": "config/detekt/detekt.yml",
"chars": 31126,
"preview": "build:\n maxIssues: 0\n excludeCorrectable: false\n weights:\n # complexity: 2\n # LongParameterList: 1\n # style: 1\n #"
},
{
"path": "gradle/libs.versions.toml",
"chars": 7008,
"preview": "[versions]\nactivity-compose = \"1.12.0\"\nandroid-compileSdk = \"36\"\nandroid-minSdk = \"26\"\nandroid-targetSdk = \"36\"\nandroidx"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 252,
"preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
},
{
"path": "gradle.properties",
"chars": 762,
"preview": "appVersionCode=1\nappVersionName=1.0\nreleaseBuild=false\n\nkotlin.code.style=official\norg.gradle.jvmargs=-Xmx2048M -Dkotlin"
},
{
"path": "gradlew",
"chars": 8595,
"preview": "#!/bin/sh\n\n#\n# Copyright © 2015 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\")"
},
{
"path": "gradlew.bat",
"chars": 2896,
"preview": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (th"
},
{
"path": "iosApp/.scripts/version.sh",
"chars": 913,
"preview": "#!/bin/sh -euo pipefail\n\nVERSION_PROPERTIES_FILE=\"${SRCROOT}/../version.properties\"\n\nif [ ! -f \"$VERSION_PROPERTIES_FILE"
},
{
"path": "iosApp/Assets/DebugIcon.icon/icon.json",
"chars": 766,
"preview": "{\n \"fill\" : {\n \"solid\" : \"srgb:1.00000,0.75686,0.02745,1.00000\"\n },\n \"groups\" : [\n {\n \"layers\" : [\n "
},
{
"path": "iosApp/Assets/Icon.icon/icon.json",
"chars": 766,
"preview": "{\n \"fill\" : {\n \"solid\" : \"srgb:0.00000,0.58824,0.53333,1.00000\"\n },\n \"groups\" : [\n {\n \"layers\" : [\n "
},
{
"path": "iosApp/Assets/Info.plist",
"chars": 1684,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "iosApp/Configuration/Config.xcconfig",
"chars": 148,
"preview": "TEAM_ID=\n\nPRODUCT_NAME=MoneyFlow\nPRODUCT_BUNDLE_IDENTIFIER=com.prof18.moneyflow.MoneyFlow$(TEAM_ID)\n\nCURRENT_PROJECT_VER"
},
{
"path": "iosApp/MoneyFlow.entitlements",
"chars": 303,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "iosApp/MoneyFlow.xcodeproj/project.pbxproj",
"chars": 17296,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 77;\n\tobjects = {\n\n/* Begin PBXAggregateTarget sec"
},
{
"path": "iosApp/MoneyFlow.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 135,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:\">\n </FileRef"
},
{
"path": "iosApp/MoneyFlow.xcodeproj/xcshareddata/xcschemes/MoneyFlow.xcscheme",
"chars": 2899,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"2610\"\n version = \"1.7\">\n <BuildAction\n "
},
{
"path": "iosApp/Source/ContentView.swift",
"chars": 436,
"preview": "import UIKit\nimport SwiftUI\nimport MoneyFlowKit\n\nstruct ComposeView: UIViewControllerRepresentable {\n func makeUIView"
},
{
"path": "iosApp/Source/DI/Koin.swift",
"chars": 181,
"preview": "//\n// DIContainer.swift\n// App ()\n//\n// Created by Marco Gomiero on 05/11/2020.\n//\n\nimport Foundation\nimport MoneyFlo"
},
{
"path": "iosApp/Source/MoneyFlowApp.swift",
"chars": 191,
"preview": "import SwiftUI\n\n@main\nstruct MoneyFlowApp: App {\n \n init() {\n startKoin()\n }\n \n var body: some Sce"
},
{
"path": "renovate.json",
"chars": 489,
"preview": "{\n \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n \"extends\": [\n \"config:base\"\n ],\n \"packageRule"
},
{
"path": "settings.gradle.kts",
"chars": 432,
"preview": "pluginManagement {\n includeBuild(\"build-logic\")\n repositories {\n google()\n gradlePluginPortal()\n "
},
{
"path": "setup.sh",
"chars": 939,
"preview": "#!/bin/bash\nset -ex\n\nexport ANDROID_SDK_ROOT=\"/root/android-sdk\"\n\napt-get update && apt-get install -y expect\n\nmkdir -p "
},
{
"path": "shared/.gitignore",
"chars": 29,
"preview": "/build\n*.iml\nlocal.properties"
},
{
"path": "shared/build.gradle.kts",
"chars": 6264,
"preview": "import java.net.URI\n\nimport org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi\nimport org.jetbrains.kotlin.gr"
},
{
"path": "shared/src/androidMain/kotlin/com/prof18/moneyflow/AndroidBiometricAuthenticator.kt",
"chars": 2483,
"preview": "package com.prof18.moneyflow\n\nimport androidx.biometric.BiometricManager\nimport androidx.biometric.BiometricManager.Auth"
},
{
"path": "shared/src/androidMain/kotlin/com/prof18/moneyflow/AndroidBiometricAvailabilityChecker.kt",
"chars": 886,
"preview": "package com.prof18.moneyflow\n\nimport android.content.Context\nimport androidx.biometric.BiometricManager\nimport co.touchl"
},
{
"path": "shared/src/androidMain/kotlin/com/prof18/moneyflow/database/DatabaseDriverFactory.kt",
"chars": 596,
"preview": "package com.prof18.moneyflow.database\n\nimport android.content.Context\nimport app.cash.sqldelight.db.SqlDriver\nimport app"
},
{
"path": "shared/src/androidMain/kotlin/com/prof18/moneyflow/di/KoinAndroid.kt",
"chars": 852,
"preview": "package com.prof18.moneyflow.di\n\nimport com.prof18.moneyflow.AndroidBiometricAvailabilityChecker\nimport com.prof18.money"
},
{
"path": "shared/src/androidMain/kotlin/com/prof18/moneyflow/utils/LocalAppLocale.android.kt",
"chars": 1017,
"preview": "package com.prof18.moneyflow.utils\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.ProvidedV"
},
{
"path": "shared/src/androidMain/kotlin/com/prof18/moneyflow/utils/LocalAppTheme.android.kt",
"chars": 1131,
"preview": "package com.prof18.moneyflow.utils\n\nimport android.content.res.Configuration\nimport android.content.res.Configuration.UI"
},
{
"path": "shared/src/androidMain/res/values/themes.xml",
"chars": 677,
"preview": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n <!-- Base application theme. -->\n <style name=\"Platfor"
},
{
"path": "shared/src/androidMain/res/values-night/themes.xml",
"chars": 502,
"preview": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n <!-- Base application theme. -->\n <style name=\"Platfor"
},
{
"path": "shared/src/androidUnitTest/AndroidManifest.xml",
"chars": 577,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n <appli"
},
{
"path": "shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/AddTransactionRoborazziTest.kt",
"chars": 3238,
"preview": "package com.prof18.moneyflow\n\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nim"
},
{
"path": "shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/AllTransactionsRoborazziTest.kt",
"chars": 1364,
"preview": "package com.prof18.moneyflow\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport com.github.takahirom.roborazzi"
},
{
"path": "shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/AuthRoborazziTest.kt",
"chars": 971,
"preview": "package com.prof18.moneyflow\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport com.github.takahirom.roborazzi"
},
{
"path": "shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/BudgetAndRecapRoborazziTest.kt",
"chars": 1073,
"preview": "package com.prof18.moneyflow\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport com.github.takahirom.roborazzi"
},
{
"path": "shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/CategoriesRoborazziTest.kt",
"chars": 2070,
"preview": "package com.prof18.moneyflow\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport com.github.takahirom.roborazzi"
},
{
"path": "shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/ComponentsRoborazziTest.kt",
"chars": 2337,
"preview": "package com.prof18.moneyflow\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport com.github.takahirom.roborazzi"
},
{
"path": "shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/HomeRoborazziTest.kt",
"chars": 1714,
"preview": "package com.prof18.moneyflow\n\nimport androidx.compose.material3.Scaffold\nimport androidx.test.ext.junit.runners.AndroidJ"
},
{
"path": "shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/MoneyFlowLockedRoborazziTest.kt",
"chars": 3774,
"preview": "package com.prof18.moneyflow\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport com.github.takahirom.roborazzi"
},
{
"path": "shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/MoneyFlowNavHostRoborazziTest.kt",
"chars": 3132,
"preview": "package com.prof18.moneyflow\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport com.github.takahirom.roborazzi"
},
{
"path": "shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/RoborazziRule.kt",
"chars": 703,
"preview": "package com.prof18.moneyflow\n\nimport androidx.activity.ComponentActivity\nimport androidx.compose.ui.test.junit4.AndroidC"
},
{
"path": "shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/RoborazziTestBase.kt",
"chars": 2718,
"preview": "package com.prof18.moneyflow\n\nimport androidx.activity.ComponentActivity\nimport androidx.compose.ui.test.junit4.AndroidC"
},
{
"path": "shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/SettingsRoborazziTest.kt",
"chars": 1465,
"preview": "package com.prof18.moneyflow\n\nimport androidx.compose.material3.Scaffold\nimport androidx.test.ext.junit.runners.AndroidJ"
},
{
"path": "shared/src/androidUnitTest/kotlin/com/prof18/moneyflow/utilities/TestUtilsAndroid.kt",
"chars": 786,
"preview": "package com.prof18.moneyflow.utilities\n\nimport app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver\nimport com.prof18"
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_address_book.xml",
"chars": 797,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_address_card.xml",
"chars": 1097,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_adjust_solid.xml",
"chars": 435,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_air_freshener_solid.xml",
"chars": 1017,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_algolia.xml",
"chars": 952,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_allergies_solid.xml",
"chars": 1387,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_ambulance_solid.xml",
"chars": 1052,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_anchor_solid.xml",
"chars": 1066,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_android.xml",
"chars": 1613,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_angle_down_solid.xml",
"chars": 381,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_angle_left_solid.xml",
"chars": 378,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_angle_right_solid.xml",
"chars": 381,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_angle_up_solid.xml",
"chars": 377,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_apple.xml",
"chars": 2823,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_apple_alt_solid.xml",
"chars": 1766,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_archive_solid.xml",
"chars": 656,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_archway_solid.xml",
"chars": 549,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_arrow_down_rotate.xml",
"chars": 524,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_arrow_down_solid.xml",
"chars": 402,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_arrow_left_solid.xml",
"chars": 399,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_arrow_right_solid.xml",
"chars": 402,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_arrow_up_rotate.xml",
"chars": 572,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:wi"
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_arrow_up_solid.xml",
"chars": 399,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_asterisk_solid.xml",
"chars": 428,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_at_solid.xml",
"chars": 1149,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_atlas_solid.xml",
"chars": 1359,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_atom_solid.xml",
"chars": 3000,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_award_solid.xml",
"chars": 2614,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_baby_carriage_solid.xml",
"chars": 1486,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bacon_solid.xml",
"chars": 3954,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_balance_scale_left_solid.xml",
"chars": 1171,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_band_aid_solid.xml",
"chars": 2223,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_baseball_ball_solid.xml",
"chars": 1476,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_basketball_ball_solid.xml",
"chars": 1279,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bath_solid.xml",
"chars": 777,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_battery_three_quarters_solid.xml",
"chars": 355,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bed_solid.xml",
"chars": 934,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_beer_solid.xml",
"chars": 2424,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bell.xml",
"chars": 1039,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bell_slash.xml",
"chars": 1240,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bicycle_solid.xml",
"chars": 1322,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_biking_solid.xml",
"chars": 1358,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_binoculars_solid.xml",
"chars": 1827,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_birthday_cake_solid.xml",
"chars": 1699,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bitcoin.xml",
"chars": 661,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_black_tie.xml",
"chars": 373,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_blender_solid.xml",
"chars": 778,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_blind_solid.xml",
"chars": 1175,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bolt_solid.xml",
"chars": 499,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bomb_solid.xml",
"chars": 1813,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bone_solid.xml",
"chars": 1438,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bong_solid.xml",
"chars": 901,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_book_open_solid.xml",
"chars": 658,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_book_solid.xml",
"chars": 510,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bookmark.xml",
"chars": 387,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bowling_ball_solid.xml",
"chars": 621,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_box_solid.xml",
"chars": 711,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_brain_solid.xml",
"chars": 2437,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bread_slice_solid.xml",
"chars": 697,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_briefcase_medical_solid.xml",
"chars": 579,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_briefcase_solid.xml",
"chars": 605,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_broadcast_tower_solid.xml",
"chars": 1017,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_broom_solid.xml",
"chars": 1149,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_brush_solid.xml",
"chars": 2380,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bug_solid.xml",
"chars": 1469,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_building.xml",
"chars": 682,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bullhorn_solid.xml",
"chars": 900,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bullseye_solid.xml",
"chars": 1061,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_burn_solid.xml",
"chars": 1790,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_bus_solid.xml",
"chars": 641,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_calculator_solid.xml",
"chars": 577,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_calendar.xml",
"chars": 649,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_camera_solid.xml",
"chars": 762,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_campground_solid.xml",
"chars": 1462,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_candy_cane_solid.xml",
"chars": 1390,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_capsules_solid.xml",
"chars": 1180,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_car_alt_solid.xml",
"chars": 1597,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_car_side_solid.xml",
"chars": 1352,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_caret_down_solid.xml",
"chars": 393,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_caret_left_solid.xml",
"chars": 390,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_caret_right_solid.xml",
"chars": 393,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_caret_up_solid.xml",
"chars": 390,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_carrot_solid.xml",
"chars": 2248,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_cart_arrow_down_solid.xml",
"chars": 1021,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_cash_register_solid.xml",
"chars": 1055,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_cat_solid.xml",
"chars": 1414,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_certificate_solid.xml",
"chars": 2614,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_chair_solid.xml",
"chars": 863,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_chalkboard_solid.xml",
"chars": 531,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_chalkboard_teacher_solid.xml",
"chars": 880,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_charging_station_solid.xml",
"chars": 1077,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_chart_area_solid.xml",
"chars": 750,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_chart_bar.xml",
"chars": 376,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_chart_line_solid.xml",
"chars": 949,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_chart_pie_solid.xml",
"chars": 682,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_check_circle.xml",
"chars": 700,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_cheese_solid.xml",
"chars": 1167,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_church_solid.xml",
"chars": 828,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_city_solid.xml",
"chars": 829,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_clinic_medical_solid.xml",
"chars": 452,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_clipboard.xml",
"chars": 499,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_clock.xml",
"chars": 519,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_cloud_download_alt_solid.xml",
"chars": 1004,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_cloud_solid.xml",
"chars": 949,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_cloud_upload_alt_solid.xml",
"chars": 1010,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_cocktail_solid.xml",
"chars": 724,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_code_branch_solid.xml",
"chars": 1363,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_code_solid.xml",
"chars": 505,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_coffee_solid.xml",
"chars": 747,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_cog_solid.xml",
"chars": 2270,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_coins_solid.xml",
"chars": 2935,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_comment_alt.xml",
"chars": 359,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_compact_disc_solid.xml",
"chars": 956,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_compass.xml",
"chars": 881,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_concierge_bell_solid.xml",
"chars": 499,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_cookie_bite_solid.xml",
"chars": 1956,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_couch_solid.xml",
"chars": 739,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_credit_card.xml",
"chars": 532,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_crown_solid.xml",
"chars": 1168,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_cubes_solid.xml",
"chars": 980,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_cut_solid.xml",
"chars": 1068,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_desktop_solid.xml",
"chars": 354,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_diaspora.xml",
"chars": 698,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_dice_d6_solid.xml",
"chars": 2306,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_dna_solid.xml",
"chars": 1201,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_dog_solid.xml",
"chars": 1203,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_dollar_sign.xml",
"chars": 915,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_dollar_sign_solid.xml",
"chars": 915,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_dolly_flatbed_solid.xml",
"chars": 828,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_dolly_solid.xml",
"chars": 1168,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_donate_solid.xml",
"chars": 2837,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_drafting_compass_solid.xml",
"chars": 844,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
},
{
"path": "shared/src/commonMain/composeResources/drawable/ic_drum_solid.xml",
"chars": 1472,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"32dp\"\n android:height=\"32dp\"\n "
}
]
// ... and 290 more files (download for full content)
About this extraction
This page contains the full source code of the prof18/MoneyFlow GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 490 files (743.0 KB), approximately 316.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.