Merge pull request #16 from Myzel394/version-5

Version 5
This commit is contained in:
Myzel394 2024-07-22 16:08:58 +02:00 committed by GitHub
commit 5439dde324
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 1187 additions and 284 deletions

View File

@ -3,6 +3,9 @@ name: Build and push debug app
on:
pull_request:
env:
JAVA_OPTS: -Xmx12G
jobs:
debug-builds:
runs-on: ubuntu-latest

View File

@ -4,6 +4,9 @@ on:
release:
types: [ published ]
env:
JAVA_OPTS: -Xmx12G
jobs:
release-app-github:
runs-on: ubuntu-latest

View File

@ -4,6 +4,9 @@ on:
release:
types: [ published ]
env:
JAVA_OPTS: -Xmx12G
jobs:
release-app-google-play:
runs-on: ubuntu-latest

View File

@ -26,7 +26,7 @@
<string name="checked_filter_description">Filtre vérifié</string>
<string name="clear_history_label">Vider l\'historique</string>
<string name="clear_input_description">Effacer l\'entrée</string>
<string name="clear_label">Clear</string>
<string name="clear_label">Effacer</string>
<string name="click_to_try_again_label">Cliquez pour réessayer</string>
<string name="comma">Virgule</string>
<string name="converter_favorite_button_description">Ajouter ou supprimer une unité des favoris</string>
@ -61,7 +61,7 @@
<string name="drop_down_description">Ouvrir ou fermer le menu déroulant</string>
<string name="enabled_label">Activé</string>
<string name="error_label">Erreur</string>
<string name="hello_label">Bonjour!</string>
<string name="hello_label">Bonjour !</string>
<string name="loading_label">Chargement…</string>
<string name="navigate_up_description">Naviguer vers le haut</string>
@ -78,16 +78,16 @@ Used in this dialog window. Should be short -->
<!-- https://s3.eu-west-1.amazonaws.com/po-pub/i/uWOHJmIq9riqsq7PO82ZQp3a.png -->
<string name="select_time_label">Choisir le temps</string>
<string name="settings_about_unitto">À propos d\'NumberHub</string>
<string name="settings_about_unitto">À propos de NumberHub</string>
<string name="settings_about_unitto_support">En savoir plus sur l\'application</string>
<string name="settings_additional">Additional</string>
<string name="settings_additional">Supplémentaires</string>
<string name="settings_amoled_dark">AMOLED Noir</string>
<string name="settings_amoled_dark_support">Utiliser un fond noir pour les thèmes sombres</string>
<string name="settings_auto">Auto</string>
<string name="settings_calculator_support">Vue historique</string>
<string name="settings_clear_cache">Effacer le cache</string>
<string name="settings_color_scheme">Palette de couleurs</string>
<string name="settings_color_theme">Thème couleur</string>
<string name="settings_color_theme">Couleurs du thème</string>
<string name="settings_color_theme_support">Choisir un mode de thème</string>
<string name="settings_converter_support">Groupes d\'unités, tri, formatage</string>
<string name="settings_currency_rates_note_text">Les taux de change sont mis à jour quotidiennement. L\'application ne permet pas de suivre le marché en temps réel.</string>
@ -101,9 +101,9 @@ Used in this dialog window. Should be short -->
<string name="settings_enable_unit_group_description">Activer le groupe d\'unités</string>
<string name="settings_exponential_notation">Notation exponentielle</string>
<string name="settings_exponential_notation_support">Remplacer une partie du nombre par E</string>
<string name="settings_format_time">Formatter l\'heure</string>
<string name="settings_format_time">Formater l\'heure</string>
<string name="settings_format_time_support">Exemple : Afficher 130 minutes comme 2h 10m</string>
<string name="settings_formatting">Formattage</string>
<string name="settings_formatting">Formatage</string>
<string name="settings_formatting_preview">Aperçu (cliquez pour changer)</string>
<!-- https://s3.eu-west-1.amazonaws.com/po-pub/i/Gj9ZCNPXIfKddDQgccvboVFz.jpg
@ -258,175 +258,175 @@ Maybe this can be labeled better? Let me know. It should be something that can d
<string name="unit_cubic_millimeter_short">mm³</string>
<string name="unit_currency_ada">Cardano</string>
<string name="unit_currency_aed">Dirham des Émirats arabes unis</string>
<string name="unit_currency_afn">Afghan afghani</string>
<string name="unit_currency_all">Albanian lek</string>
<string name="unit_currency_amd">Armenian dram</string>
<string name="unit_currency_ang">Netherlands Antillean Guilder</string>
<string name="unit_currency_aoa">Angolan kwanza</string>
<string name="unit_currency_ars">Argentine peso</string>
<string name="unit_currency_aud">Australian dollar</string>
<string name="unit_currency_awg">Aruban florin</string>
<string name="unit_currency_azn">Azerbaijani manat</string>
<string name="unit_currency_bam">Bosnia-Herzegovina Convertible Mark</string>
<string name="unit_currency_bbd">Bajan dollar</string>
<string name="unit_currency_bdt">Bangladeshi taka</string>
<string name="unit_currency_bgn">Bulgarian lev</string>
<string name="unit_currency_bhd">Bahraini dinar</string>
<string name="unit_currency_bif">Burundian Franc</string>
<string name="unit_currency_bmd">Bermudan dollar</string>
<string name="unit_currency_bnd">Brunei dollar</string>
<string name="unit_currency_bob">Bolivian boliviano</string>
<string name="unit_currency_brl">Brazilian real</string>
<string name="unit_currency_bsd">Bahamian dollar</string>
<string name="unit_currency_btn">Bhutan currency</string>
<string name="unit_currency_bwp">Botswanan Pula</string>
<string name="unit_currency_byn">New Belarusian Ruble</string>
<string name="unit_currency_byr">Belarusian Ruble</string>
<string name="unit_currency_bzd">Belize dollar</string>
<string name="unit_currency_cad">Canadian dollar</string>
<string name="unit_currency_cdf">Congolese franc</string>
<string name="unit_currency_chf">Swiss franc</string>
<string name="unit_currency_afn">Afghani</string>
<string name="unit_currency_all">Lek</string>
<string name="unit_currency_amd">Dram</string>
<string name="unit_currency_ang">Florin des Antilles néerlandaises</string>
<string name="unit_currency_aoa">Kwanza</string>
<string name="unit_currency_ars">Peso argentin</string>
<string name="unit_currency_aud">Dollar australien</string>
<string name="unit_currency_awg">Florin arubais</string>
<string name="unit_currency_azn">Manat azerbaïdjanais</string>
<string name="unit_currency_bam">Mark convertible de Bosnie-Herzégovine</string>
<string name="unit_currency_bbd">Dollar barbadien</string>
<string name="unit_currency_bdt">Taka</string>
<string name="unit_currency_bgn">Lev bulgare</string>
<string name="unit_currency_bhd">Dinar bahreïni</string>
<string name="unit_currency_bif">Franc burundais</string>
<string name="unit_currency_bmd">Dollar bermudien</string>
<string name="unit_currency_bnd">Dollar de Brunei</string>
<string name="unit_currency_bob">Boliviano</string>
<string name="unit_currency_brl">Réal brésilien</string>
<string name="unit_currency_bsd">Dollar bahaméen</string>
<string name="unit_currency_btn">Ngultrum</string>
<string name="unit_currency_bwp">Pula</string>
<string name="unit_currency_byn">Nouveau rouble biélorusse</string>
<string name="unit_currency_byr">Rouble biélorusse</string>
<string name="unit_currency_bzd">Dollar bélizien</string>
<string name="unit_currency_cad">Dollar canadien</string>
<string name="unit_currency_cdf">Franc congolais</string>
<string name="unit_currency_chf">Franc suisse</string>
<string name="unit_currency_clf">Chilean Unit of Account (UF)</string>
<string name="unit_currency_clp">Chilean peso</string>
<string name="unit_currency_cny">Chinese Yuan</string>
<string name="unit_currency_cop">Colombian peso</string>
<string name="unit_currency_crc">Costa Rican Colón</string>
<string name="unit_currency_cuc">Cuban convertible peso</string>
<string name="unit_currency_cup">Cuban Peso</string>
<string name="unit_currency_cve">Cape Verdean escudo</string>
<string name="unit_currency_czk">Czech koruna</string>
<string name="unit_currency_clp">Peso chilien</string>
<string name="unit_currency_cny">Yuan</string>
<string name="unit_currency_cop">Peso colombien</string>
<string name="unit_currency_crc">Colón costaricien</string>
<string name="unit_currency_cuc">Peso cubain convertible</string>
<string name="unit_currency_cup">Peso cubain</string>
<string name="unit_currency_cve">Escudo cap-verdien</string>
<string name="unit_currency_czk">Couronne tchèque</string>
<string name="unit_currency_dai">Dai</string>
<string name="unit_currency_djf">Djiboutian franc</string>
<string name="unit_currency_dkk">Danish krone</string>
<string name="unit_currency_dop">Dominican peso</string>
<string name="unit_currency_dzd">Algerian dinar</string>
<string name="unit_currency_egp">Egyptian pound</string>
<string name="unit_currency_ern">Eritrean nakfa</string>
<string name="unit_currency_etb">Ethiopian birr</string>
<string name="unit_currency_djf">Franc Djibouti</string>
<string name="unit_currency_dkk">Couronne danoise</string>
<string name="unit_currency_dop">Peso dominicain</string>
<string name="unit_currency_dzd">Dinar algérien</string>
<string name="unit_currency_egp">Livre égyptienne</string>
<string name="unit_currency_ern">Nakfa érythréen</string>
<string name="unit_currency_etb">Birr éthiopien</string>
<string name="unit_currency_eur">Euro</string>
<string name="unit_currency_fjd">Fijian dollar</string>
<string name="unit_currency_fkp">Falkland Islands pound</string>
<string name="unit_currency_gbp">Pound sterling</string>
<string name="unit_currency_gel">Georgian lari</string>
<string name="unit_currency_ghs">Ghanaian cedi</string>
<string name="unit_currency_gip">Gibraltar pound</string>
<string name="unit_currency_gmd">Gambian dalasi</string>
<string name="unit_currency_gnf">Guinean franc</string>
<string name="unit_currency_gtq">Guatemalan quetzal</string>
<string name="unit_currency_gyd">Guyanaese Dollar</string>
<string name="unit_currency_hkd">Hong Kong dollar</string>
<string name="unit_currency_hnl">Honduran lempira</string>
<string name="unit_currency_hrk">Croatian kuna</string>
<string name="unit_currency_htg">Haitian gourde</string>
<string name="unit_currency_huf">Hungarian forint</string>
<string name="unit_currency_idr">Indonesian rupiah</string>
<string name="unit_currency_ils">Israeli New Shekel</string>
<string name="unit_currency_inr">Indian rupee</string>
<string name="unit_currency_iqd">Iraqi dinar</string>
<string name="unit_currency_irr">Iranian rial</string>
<string name="unit_currency_isk">Icelandic króna</string>
<string name="unit_currency_jep">Jersey Pound</string>
<string name="unit_currency_jmd">Jamaican dollar</string>
<string name="unit_currency_jod">Jordanian dinar</string>
<string name="unit_currency_jpy">Japanese yen</string>
<string name="unit_currency_kes">Kenyan shilling</string>
<string name="unit_currency_kgs">Kyrgystani Som</string>
<string name="unit_currency_khr">Cambodian riel</string>
<string name="unit_currency_kmf">Comorian franc</string>
<string name="unit_currency_kpw">North Korean won</string>
<string name="unit_currency_krw">South Korean won</string>
<string name="unit_currency_kwd">Kuwaiti dinar</string>
<string name="unit_currency_kyd">Cayman Islands dollar</string>
<string name="unit_currency_kzt">Kazakhstani tenge</string>
<string name="unit_currency_lak">Laotian Kip</string>
<string name="unit_currency_lbp">Lebanese pound</string>
<string name="unit_currency_lkr">Sri Lankan rupee</string>
<string name="unit_currency_lrd">Liberian dollar</string>
<string name="unit_currency_lsl">Lesotho loti</string>
<string name="unit_currency_ltl">Lithuanian litas</string>
<string name="unit_currency_lvl">Latvian lats</string>
<string name="unit_currency_lyd">Libyan dinar</string>
<string name="unit_currency_mad">Moroccan dirham</string>
<string name="unit_currency_mdl">Moldovan leu</string>
<string name="unit_currency_mga">Malagasy ariary</string>
<string name="unit_currency_mkd">Macedonian denar</string>
<string name="unit_currency_mmk">Myanmar Kyat</string>
<string name="unit_currency_mnt">Mongolian tugrik</string>
<string name="unit_currency_mop">Macanese pataca</string>
<string name="unit_currency_mro">Mauritanian ouguiya</string>
<string name="unit_currency_mur">Mauritian rupee</string>
<string name="unit_currency_mvr">Maldivian rufiyaa</string>
<string name="unit_currency_mwk">Malawian kwacha</string>
<string name="unit_currency_mxn">Mexican peso</string>
<string name="unit_currency_myr">Malaysian ringgit</string>
<string name="unit_currency_mzn">Mozambican Metical</string>
<string name="unit_currency_nad">Namibian dollar</string>
<string name="unit_currency_ngn">Nigerian naira</string>
<string name="unit_currency_nio">Nicaraguan córdoba</string>
<string name="unit_currency_nok">Norwegian krone</string>
<string name="unit_currency_npr">Nepalese rupee</string>
<string name="unit_currency_nzd">New Zealand dollar</string>
<string name="unit_currency_omr">Omani rial</string>
<string name="unit_currency_pab">Panamanian balboa</string>
<string name="unit_currency_pen">Sol</string>
<string name="unit_currency_pgk">Papua New Guinean kina</string>
<string name="unit_currency_php">Philippine peso</string>
<string name="unit_currency_pkr">Pakistani rupee</string>
<string name="unit_currency_pln">Poland złoty</string>
<string name="unit_currency_pyg">Paraguayan guarani</string>
<string name="unit_currency_qar">Qatari Rial</string>
<string name="unit_currency_ron">Romanian leu</string>
<string name="unit_currency_rsd">Serbian dinar</string>
<string name="unit_currency_rub">Russian ruble</string>
<string name="unit_currency_rwf">Rwandan Franc</string>
<string name="unit_currency_sar">Saudi riyal</string>
<string name="unit_currency_sbd">Solomon Islands dollar</string>
<string name="unit_currency_scr">Seychellois rupee</string>
<string name="unit_currency_sdg">Sudanese pound</string>
<string name="unit_currency_sek">Swedish krona</string>
<string name="unit_currency_sgd">Singapore dollar</string>
<string name="unit_currency_fjd">Dollar fidjien</string>
<string name="unit_currency_fkp">Livre des îles Malouines</string>
<string name="unit_currency_gbp">Livre sterling</string>
<string name="unit_currency_gel">Lari géorgien</string>
<string name="unit_currency_ghs">Cedi ghanéen</string>
<string name="unit_currency_gip">Livre de Gibraltar</string>
<string name="unit_currency_gmd">Dalasi gambien</string>
<string name="unit_currency_gnf">Franc guinéen</string>
<string name="unit_currency_gtq">Quetzal guatémaltèque</string>
<string name="unit_currency_gyd">Dollar guyanien</string>
<string name="unit_currency_hkd">Dollar de Hong Kong</string>
<string name="unit_currency_hnl">Lempira hondurien</string>
<string name="unit_currency_hrk">Kuna croate</string>
<string name="unit_currency_htg">Gourde haïtienne</string>
<string name="unit_currency_huf">Forint hongrois</string>
<string name="unit_currency_idr">Roupie indonésienne</string>
<string name="unit_currency_ils">Shekel israélien</string>
<string name="unit_currency_inr">Roupie indienne</string>
<string name="unit_currency_iqd">Dinar irakien</string>
<string name="unit_currency_irr">Rial iranien</string>
<string name="unit_currency_isk">Couronne islandaise</string>
<string name="unit_currency_jep">Livre de Jersey</string>
<string name="unit_currency_jmd">Dollar jamaïcain</string>
<string name="unit_currency_jod">Dinar jordanien</string>
<string name="unit_currency_jpy">Yen japonais</string>
<string name="unit_currency_kes">Shilling kényan</string>
<string name="unit_currency_kgs">Som kirghize</string>
<string name="unit_currency_khr">Riel cambodgien</string>
<string name="unit_currency_kmf">Franc comorien</string>
<string name="unit_currency_kpw">Won nord-coréen</string>
<string name="unit_currency_krw">Won sud-coréen</string>
<string name="unit_currency_kwd">Dinar koweïtien</string>
<string name="unit_currency_kyd">Dollar des îles Caïmans</string>
<string name="unit_currency_kzt">Tenge kazakh</string>
<string name="unit_currency_lak">Kip laotien</string>
<string name="unit_currency_lbp">Livre libanaise</string>
<string name="unit_currency_lkr">Roupie srilankaise</string>
<string name="unit_currency_lrd">Dollar libérien</string>
<string name="unit_currency_lsl">Loti lesothan</string>
<string name="unit_currency_ltl">Litas lituanien</string>
<string name="unit_currency_lvl">Lats letton</string>
<string name="unit_currency_lyd">Dinar libyen</string>
<string name="unit_currency_mad">Dirham marocain</string>
<string name="unit_currency_mdl">Leu moldave</string>
<string name="unit_currency_mga">Ariary malgache</string>
<string name="unit_currency_mkd">Denar macédonien</string>
<string name="unit_currency_mmk">Kyat birman</string>
<string name="unit_currency_mnt">Tugrik mongol</string>
<string name="unit_currency_mop">Pataca macanaise</string>
<string name="unit_currency_mro">Ouguiya mauritanien</string>
<string name="unit_currency_mur">Roupie mauricienne</string>
<string name="unit_currency_mvr">Rufiyaa maldivienne</string>
<string name="unit_currency_mwk">Kwacha malawien</string>
<string name="unit_currency_mxn">Peso mexicain</string>
<string name="unit_currency_myr">Ringgit malaisien</string>
<string name="unit_currency_mzn">Metical mozambicain</string>
<string name="unit_currency_nad">Dollar namibien</string>
<string name="unit_currency_ngn">Naira nirian</string>
<string name="unit_currency_nio">Córdoba nicaraguayen</string>
<string name="unit_currency_nok">Couronne norvégienne</string>
<string name="unit_currency_npr">Roupie népalaise</string>
<string name="unit_currency_nzd">Dollar néo-zélandais</string>
<string name="unit_currency_omr">Rial omanais</string>
<string name="unit_currency_pab">Balboa panaméen</string>
<string name="unit_currency_pen">Sol péruvien</string>
<string name="unit_currency_pgk">Kina papouasien</string>
<string name="unit_currency_php">Peso philippin</string>
<string name="unit_currency_pkr">Roupie pakistanaise</string>
<string name="unit_currency_pln">Złoty polonais</string>
<string name="unit_currency_pyg">Guaraní paraguayen</string>
<string name="unit_currency_qar">Riyal qatari</string>
<string name="unit_currency_ron">Leu roumain</string>
<string name="unit_currency_rsd">Dinar serbe</string>
<string name="unit_currency_rub">Rouble russe</string>
<string name="unit_currency_rwf">Franc rwandais</string>
<string name="unit_currency_sar">Riyal saoudien</string>
<string name="unit_currency_sbd">Dollar des îles Salomon</string>
<string name="unit_currency_scr">Roupie seychelloise</string>
<string name="unit_currency_sdg">Livre soudanaise</string>
<string name="unit_currency_sek">Couronne suédoise</string>
<string name="unit_currency_sgd">Dollar de Singapour</string>
<string name="unit_currency_shib">Shiba Inu</string>
<string name="unit_currency_shp">Saint Helena pound</string>
<string name="unit_currency_sll">Sierra Leonean leone</string>
<string name="unit_currency_sos">Somali shilling</string>
<string name="unit_currency_srd">Surinamese dollar</string>
<string name="unit_currency_std">São Tomé and Príncipe Dobra (pre-2018)</string>
<string name="unit_currency_svc">Salvadoran Colón</string>
<string name="unit_currency_syp">Syrian pound</string>
<string name="unit_currency_szl">Swazi lilangeni</string>
<string name="unit_currency_thb">Thai baht</string>
<string name="unit_currency_shp">Livre de Sainte-Hélène</string>
<string name="unit_currency_sll">Leone sierra-léonais</string>
<string name="unit_currency_sos">Shilling somalien</string>
<string name="unit_currency_srd">Dollar surinamais</string>
<string name="unit_currency_std">Dobra santoméen (avant 2018)</string>
<string name="unit_currency_svc">Colón salvadorien</string>
<string name="unit_currency_syp">Livre syrienne</string>
<string name="unit_currency_szl">Lilangeni swazi</string>
<string name="unit_currency_thb">Baht thaïlandais</string>
<string name="unit_currency_theta">Theta</string>
<string name="unit_currency_tjs">Tajikistani somoni</string>
<string name="unit_currency_tmt">Turkmenistani manat</string>
<string name="unit_currency_tnd">Tunisian dinar</string>
<string name="unit_currency_top">Tongan Paʻanga</string>
<string name="unit_currency_try">Turkish lira</string>
<string name="unit_currency_ttd">Trinidad &amp; Tobago Dollar</string>
<string name="unit_currency_twd">New Taiwan dollar</string>
<string name="unit_currency_tzs">Tanzanian shilling</string>
<string name="unit_currency_uah">Ukrainian hryvnia</string>
<string name="unit_currency_ugx">Ugandan shilling</string>
<string name="unit_currency_tjs">Somoni tadjik</string>
<string name="unit_currency_tmt">Manat turkmène</string>
<string name="unit_currency_tnd">Dinar tunisien</string>
<string name="unit_currency_top">Paʻanga tongan</string>
<string name="unit_currency_try">Livre turque</string>
<string name="unit_currency_ttd">Dollar de Trinité-et-Tobago</string>
<string name="unit_currency_twd">Nouveau dollar taïwanais</string>
<string name="unit_currency_tzs">Shilling tanzanien</string>
<string name="unit_currency_uah">Hryvnia ukrainienne</string>
<string name="unit_currency_ugx">Shilling ougandais</string>
<string name="unit_currency_uni">Universe</string>
<string name="unit_currency_usd">United States dollar</string>
<string name="unit_currency_usd">Dollar américain</string>
<string name="unit_currency_usdc">USD Coin</string>
<string name="unit_currency_uyu">Uruguayan peso</string>
<string name="unit_currency_uzs">Uzbekistani som</string>
<string name="unit_currency_vef">Sovereign Bolivar</string>
<string name="unit_currency_vnd">Vietnamese dong</string>
<string name="unit_currency_vuv">Vanuatu vatu</string>
<string name="unit_currency_uyu">Peso uruguayen</string>
<string name="unit_currency_uzs">Som ouzbek</string>
<string name="unit_currency_vef">Bolivar souverain</string>
<string name="unit_currency_vnd">Dong vietnamien</string>
<string name="unit_currency_vuv">Vatu vanuatu</string>
<string name="unit_currency_wbtc">Wrapped Bitcoin</string>
<string name="unit_currency_wst">Samoan tala</string>
<string name="unit_currency_xaf">Central African CFA franc</string>
<string name="unit_currency_xag">Silver Ounce</string>
<string name="unit_currency_xcd">East Caribbean dollar</string>
<string name="unit_currency_xdr">Special Drawing Rights</string>
<string name="unit_currency_xof">West African CFA franc</string>
<string name="unit_currency_xpf">CFP franc</string>
<string name="unit_currency_yer">Yemeni rial</string>
<string name="unit_currency_zar">South African rand</string>
<string name="unit_currency_zmk">Zambian kwacha</string>
<string name="unit_currency_zmw">Zambian Kwacha</string>
<string name="unit_currency_zwl">Zimbabwean Dollar</string>
<string name="unit_currency_wst">Tālā</string>
<string name="unit_currency_xaf">Franc CFA d\'Afrique centrale</string>
<string name="unit_currency_xag">Once d\'argent</string>
<string name="unit_currency_xcd">Dollar des Caraïbes orientales</string>
<string name="unit_currency_xdr">Droits de tirage spéciaux</string>
<string name="unit_currency_xof">Franc CFA d\'Afrique de l\'Ouest</string>
<string name="unit_currency_xpf">Franc CFP</string>
<string name="unit_currency_yer">Rial yéménite</string>
<string name="unit_currency_zar">Rand sud-africain</string>
<string name="unit_currency_zmk">Kwacha zambien</string>
<string name="unit_currency_zmw">Kwacha zambien</string>
<string name="unit_currency_zwl">Dollar zimbabwéen</string>
<string name="unit_day">Jour</string>
<string name="unit_day_short">j</string>
<string name="unit_decimal">Décimal</string>
@ -459,7 +459,7 @@ Maybe this can be labeled better? Let me know. It should be something that can d
<string name="unit_earth_surface_gravity">Gravité de surface de la Terre</string>
<string name="unit_earth_surface_gravity_short">G Terre</string>
<string name="unit_earths_orbital_speed">Vitesse orbitale de la Terre</string>
<string name="unit_electron_cross_section">Electron cross section</string>
<string name="unit_electron_cross_section">Section efficace de l\'électron</string>
<string name="unit_electron_cross_section_short">ecs</string>
<string name="unit_electron_mass_rest">Masse électronique</string>
<string name="unit_electron_mass_rest_short">me</string>
@ -548,7 +548,7 @@ Maybe this can be labeled better? Let me know. It should be something that can d
<string name="unit_group_data_transfer">Transfert de données</string>
<string name="unit_group_electrostatic_capacitance">Capacité</string>
<string name="unit_group_energy">Énergie</string>
<string name="unit_group_flow_rate">Flux</string>
<string name="unit_group_flow_rate">Mesure de débit</string>
<string name="unit_group_flux">Flux</string>
<string name="unit_group_force">Force</string>
<string name="unit_group_length">Longueur</string>
@ -560,7 +560,7 @@ Maybe this can be labeled better? Let me know. It should be something that can d
<string name="unit_group_pressure">Pression</string>
<string name="unit_group_speed">Vélocité</string>
<string name="unit_group_temperature">Température</string>
<string name="unit_group_time">Time</string>
<string name="unit_group_time">Temps</string>
<string name="unit_group_torque">Couple</string>
<string name="unit_group_volume">Volume</string>
<string name="unit_hectare">Hectare</string>
@ -577,18 +577,18 @@ Maybe this can be labeled better? Let me know. It should be something that can d
<string name="unit_hour_short">h</string>
<string name="unit_imperial_cup">Tasse impériale</string>
<string name="unit_imperial_cup_short">cup</string>
<string name="unit_imperial_fluid_ounce">Imperial fluid ounce</string>
<string name="unit_imperial_fluid_ounce">Once liquide impériale</string>
<string name="unit_imperial_fluid_ounce_short">fl oz</string>
<string name="unit_imperial_gallon">Gallon imperial</string>
<string name="unit_imperial_gallon">Gallon impérial</string>
<string name="unit_imperial_gallon_short">gal</string>
<string name="unit_imperial_pint">Pinte impériale</string>
<string name="unit_imperial_pint_short">pt</string>
<string name="unit_imperial_quart">Quart impérial</string>
<string name="unit_imperial_quart_short">qt</string>
<string name="unit_imperial_tablespoon">Imperial tablespoon</string>
<string name="unit_imperial_tablespoon_short">tablespoon</string>
<string name="unit_imperial_teaspoon">Imperial teaspoon</string>
<string name="unit_imperial_teaspoon_short">teaspoon</string>
<string name="unit_imperial_tablespoon">Cuillère à soupe impériale</string>
<string name="unit_imperial_tablespoon_short">cuillère à soupe</string>
<string name="unit_imperial_teaspoon">Cuillère à café impériale</string>
<string name="unit_imperial_teaspoon_short">cuillère à café</string>
<string name="unit_imperial_ton">Tonne impériale</string>
<string name="unit_imperial_ton_short">t</string>
<string name="unit_inch">Pouce</string>
@ -790,7 +790,7 @@ Maybe this can be labeled better? Let me know. It should be something that can d
<string name="unit_milliliter_per_second_short">mL/s</string>
<string name="unit_milliliter_short">mL</string>
<string name="unit_millimeter">Millimètre</string>
<string name="unit_millimeter_of_mercury">Millimeter of mercury</string>
<string name="unit_millimeter_of_mercury">Millimètre de mercure</string>
<string name="unit_millimeter_of_mercury_short">mm Hg</string>
<string name="unit_millimeter_per_hour">Millimètre/heure</string>
<string name="unit_millimeter_per_hour_short">mm/h</string>
@ -1023,7 +1023,7 @@ Maybe this can be labeled better? Let me know. It should be something that can d
<string name="unit_uranus_mass_short">M Uranus</string>
<string name="unit_uranus_surface_gravity">Gravité de surface d\'Uranus</string>
<string name="unit_uranus_surface_gravity_short">G Uranus</string>
<string name="unit_us_fluid_ounce">US fluid ounce</string>
<string name="unit_us_fluid_ounce">Once liquide américaine</string>
<string name="unit_us_fluid_ounce_short">fl oz</string>
<string name="unit_us_legal_cup">Tasse américaine</string>
<string name="unit_us_legal_cup_short">cup</string>
@ -1034,9 +1034,9 @@ Maybe this can be labeled better? Let me know. It should be something that can d
<string name="unit_us_liquid_quart">Quarts américain</string>
<string name="unit_us_liquid_quart_short">qt</string>
<string name="unit_us_tablespoon">Cuillère à soupe américaine</string>
<string name="unit_us_tablespoon_short">tablespoon</string>
<string name="unit_us_tablespoon_short">cuillère à soupe</string>
<string name="unit_us_teaspoon">Cuillère à café américaine</string>
<string name="unit_us_teaspoon_short">teaspoon</string>
<string name="unit_us_teaspoon_short">cuillère à café</string>
<string name="unit_velocity_of_light_in_vacuum">Vitesse de la lumière dans le vide</string>
<string name="unit_venus_equatorial_radius">Radius équatorial de Vénus</string>
<string name="unit_venus_equatorial_radius_short">R Vénus</string>

View File

@ -25,7 +25,6 @@ import androidx.compose.foundation.layout.FlowRowScope
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
/**
@ -51,8 +50,8 @@ fun KeypadFlow(
@IntRange(0, 100) verticalPadding: Int = 10,
content: @Composable FlowRowScope.(width: Float, height: Float) -> Unit,
) {
val height: Float = remember { (1f - verticalPadding / 100f) / rows }
val width: Float = remember { (1f - horizontalPadding / 100f) / columns }
val height: Float = (1f - verticalPadding / 100f) / rows
val width: Float = (1f - horizontalPadding / 100f) / columns
FlowRow(
modifier = modifier,

View File

@ -102,17 +102,232 @@ class UnitsRepositoryImpl @Inject constructor(
suspend fun getPairId(id: String): String = withContext(Dispatchers.IO) {
val basedUnitPair = getUnitStats(id).pairedUnitId
if (basedUnitPair != null) return@withContext basedUnitPair
if (basedUnitPair != null) {
return@withContext basedUnitPair
}
val inMemoryUnit = inMemory.first { it.id == id }
val collection = inMemory.filter { it.group == inMemoryUnit.group }
val pair = collection
.map { getById(it.id) to getUnitStats(it.id) }
.sortedByDescending { it.second.frequency }
.firstOrNull { it.second.isFavorite }?.first ?: collection.first()
return@withContext when (inMemoryUnit.id) {
// === === === Length === === ===
UnitID.nanometer -> UnitID.micrometer
UnitID.micrometer -> UnitID.millimeter
UnitID.millimeter -> UnitID.centimeter
return@withContext pair.id
UnitID.centimeter -> UnitID.inch
UnitID.inch -> UnitID.centimeter
UnitID.decimeter -> UnitID.centimeter
UnitID.foot -> UnitID.meter
UnitID.yard -> UnitID.meter
UnitID.meter -> UnitID.foot
UnitID.kilometer -> UnitID.mile
UnitID.mile -> UnitID.kilometer
UnitID.nautical_mile -> UnitID.kilometer
UnitID.mercury_equatorial_radius -> UnitID.kilometer
UnitID.mars_equatorial_radius -> UnitID.kilometer
UnitID.venus_equatorial_radius -> UnitID.kilometer
UnitID.earth_equatorial_radius -> UnitID.kilometer
UnitID.neptune_equatorial_radius -> UnitID.kilometer
UnitID.uranus_equatorial_radius -> UnitID.kilometer
UnitID.saturn_equatorial_radius -> UnitID.kilometer
UnitID.jupiter_equatorial_radius -> UnitID.kilometer
UnitID.sun_equatorial_radius -> UnitID.kilometer
UnitID.light_year -> UnitID.kilometer
UnitID.parsec -> UnitID.light_year
UnitID.kiloparsec -> UnitID.parsec
UnitID.megaparsec -> UnitID.kiloparsec
// === === === Mass === === ===
UnitID.electron_mass_rest -> UnitID.atomic_mass_unit
UnitID.atomic_mass_unit -> UnitID.electron_mass_rest
UnitID.microgram -> UnitID.milligram
UnitID.milligram -> UnitID.gram
UnitID.grain -> UnitID.gram
UnitID.carat -> UnitID.gram
UnitID.gram -> UnitID.carat
UnitID.ounce -> UnitID.gram
UnitID.pound -> UnitID.kilogram
UnitID.kilogram -> UnitID.pound
UnitID.metric_ton -> UnitID.kilogram
UnitID.imperial_ton -> UnitID.pound
UnitID.mercury_mass -> UnitID.kilogram
UnitID.mars_mass -> UnitID.kilogram
UnitID.venus_mass -> UnitID.kilogram
UnitID.earth_mass -> UnitID.kilogram
UnitID.uranus_mass -> UnitID.kilogram
UnitID.neptune_mass -> UnitID.kilogram
UnitID.saturn_mass -> UnitID.kilogram
UnitID.jupiter_mass -> UnitID.kilogram
UnitID.sun_mass -> UnitID.kilogram
// === === === Speed === === ===
UnitID.millimeter_per_hour -> UnitID.millimeter_per_second
UnitID.millimeter_per_second -> UnitID.millimeter_per_hour
UnitID.millimeter_per_minute -> UnitID.millimeter_per_second
UnitID.centimeter_per_hour -> UnitID.centimeter_per_second
UnitID.centimeter_per_second -> UnitID.centimeter_per_hour
UnitID.centimeter_per_minute -> UnitID.centimeter_per_second
UnitID.meter_per_hour -> UnitID.meter_per_second
UnitID.meter_per_second -> UnitID.meter_per_hour
UnitID.meter_per_minute -> UnitID.meter_per_second
UnitID.kilometer_per_hour -> UnitID.mile_per_hour
UnitID.kilometer_per_second -> UnitID.mile_per_second
UnitID.kilometer_per_minute -> UnitID.kilometer_per_second
UnitID.mile_per_hour -> UnitID.kilometer_per_hour
UnitID.mile_per_second -> UnitID.kilometer_per_second
UnitID.mile_per_minute -> UnitID.mile_per_second
UnitID.foot_per_hour -> UnitID.foot_per_second
UnitID.foot_per_second -> UnitID.foot_per_hour
UnitID.foot_per_minute -> UnitID.foot_per_second
UnitID.yard_per_hour -> UnitID.yard_per_second
UnitID.yard_per_second -> UnitID.yard_per_hour
UnitID.yard_per_minute -> UnitID.yard_per_second
UnitID.knot -> UnitID.kilometer_per_hour
UnitID.mach -> UnitID.kilometer_per_hour
UnitID.velocity_of_light_in_vacuum -> UnitID.kilometer_per_hour
UnitID.earths_orbital_speed -> UnitID.kilometer_per_hour
UnitID.cosmic_velocity_first -> UnitID.kilometer_per_hour
UnitID.cosmic_velocity_second -> UnitID.kilometer_per_hour
UnitID.cosmic_velocity_third -> UnitID.kilometer_per_hour
// === === === Temperature === === ===
UnitID.celsius -> UnitID.fahrenheit
UnitID.fahrenheit -> UnitID.celsius
UnitID.kelvin -> UnitID.celsius
// === === === Area === === ===
UnitID.square_micrometer -> UnitID.square_millimeter
UnitID.square_millimeter -> UnitID.square_centimeter
UnitID.square_centimeter -> UnitID.square_meter
UnitID.square_decimeter -> UnitID.square_meter
UnitID.square_meter -> UnitID.square_kilometer
UnitID.square_kilometer -> UnitID.square_meter
UnitID.square_inch -> UnitID.square_foot
UnitID.square_foot -> UnitID.square_inch
UnitID.square_yard -> UnitID.square_meter
UnitID.square_mile -> UnitID.square_kilometer
UnitID.acre -> UnitID.square_meter
UnitID.hectare -> UnitID.square_meter
UnitID.cent -> UnitID.square_meter
// === === === Time === === ===
UnitID.attosecond -> UnitID.nanosecond
UnitID.nanosecond -> UnitID.microsecond
UnitID.microsecond -> UnitID.millisecond
UnitID.millisecond -> UnitID.second
UnitID.jiffy -> UnitID.millisecond
UnitID.second -> UnitID.millisecond
UnitID.minute -> UnitID.second
UnitID.hour -> UnitID.minute
UnitID.day -> UnitID.hour
UnitID.week -> UnitID.day
// === === === Data === === ===
// TODO: Add tibibyte, exibyte
UnitID.bit -> UnitID.byte
UnitID.byte -> UnitID.kilobyte
UnitID.kilobyte -> UnitID.megabyte
UnitID.megabyte -> UnitID.gigabyte
UnitID.gigabyte -> UnitID.terabyte
UnitID.terabyte -> UnitID.petabyte
UnitID.petabyte -> UnitID.exabyte
UnitID.kilobit -> UnitID.kilobyte
UnitID.megabit -> UnitID.megabyte
UnitID.gigabit -> UnitID.gigabyte
UnitID.terabit -> UnitID.terabyte
UnitID.petabit -> UnitID.petabyte
UnitID.exabit -> UnitID.exabyte
UnitID.kibibit -> UnitID.kilobyte
UnitID.mebibit -> UnitID.megabyte
UnitID.gibibit -> UnitID.gigabyte
UnitID.kibibyte -> UnitID.kilobyte
UnitID.mebibyte -> UnitID.megabyte
UnitID.gibibyte -> UnitID.gigabyte
// === === === Acceleration === === ===
UnitID.millimeter_per_square_second -> UnitID.centimeter_per_square_second
UnitID.centimeter_per_square_second -> UnitID.meter_per_square_second
UnitID.decimeter_per_square_second -> UnitID.meter_per_square_second
UnitID.meter_per_square_second -> UnitID.kilometer_per_square_second
UnitID.mercury_surface_gravity -> UnitID.meter_per_square_second
UnitID.mars_surface_gravity -> UnitID.meter_per_square_second
UnitID.venus_surface_gravity -> UnitID.meter_per_square_second
UnitID.uranus_surface_gravity -> UnitID.meter_per_square_second
UnitID.earth_surface_gravity -> UnitID.meter_per_square_second
UnitID.saturn_surface_gravity -> UnitID.meter_per_square_second
UnitID.neptune_surface_gravity -> UnitID.meter_per_square_second
UnitID.jupiter_surface_gravity -> UnitID.meter_per_square_second
UnitID.sun_surface_gravity -> UnitID.meter_per_square_second
// === === === Power === === ===
UnitID.attowatt -> UnitID.watt
UnitID.watt -> UnitID.kilowatt
UnitID.kilowatt -> UnitID.watt
UnitID.megawatt -> UnitID.kilowatt
// === === === Angle === === ===
UnitID.degree -> UnitID.radian
UnitID.radian -> UnitID.degree
// === === === Data Transfer === === ===
UnitID.bit_per_second -> UnitID.byte_per_second
UnitID.kilobit_per_second -> UnitID.kilobyte_per_second
UnitID.megabit_per_second -> UnitID.megabyte_per_second
UnitID.gigabit_per_second -> UnitID.gigabyte_per_second
UnitID.terabit_per_second -> UnitID.terabyte_per_second
UnitID.petabit_per_second -> UnitID.petabyte_per_second
UnitID.exabit_per_second -> UnitID.exabyte_per_second
UnitID.byte_per_second -> UnitID.kilobyte_per_second
UnitID.kilobyte_per_second -> UnitID.megabyte_per_second
UnitID.megabyte_per_second -> UnitID.gigabyte_per_second
UnitID.gigabyte_per_second -> UnitID.megabyte_per_second
UnitID.terabyte_per_second -> UnitID.gigabyte_per_second
UnitID.petabyte_per_second -> UnitID.terabyte_per_second
// === === === Fuel === === ===
UnitID.kilometer_per_liter -> UnitID.mile_us_per_liter
UnitID.mile_us_per_liter -> UnitID.kilometer_per_liter
else ->
(collection
.map { getById(it.id) to getUnitStats(it.id) }
.sortedByDescending { it.second.frequency }
.firstOrNull { it.second.isFavorite }?.first ?: collection.first()).id
}
}
suspend fun incrementCounter(id: String) = withContext(Dispatchers.IO) {

View File

@ -26,9 +26,19 @@ import kotlin.math.asin
import kotlin.math.atan
import kotlin.math.pow
// Dirty hack to avoid -0.000000000001
val PI_THRESHOLD = BigDecimal("0.000000000000001")
internal fun BigDecimal.sin(radianMode: Boolean): BigDecimal {
val angle: Double = if (radianMode) this.toDouble() else Math.toRadians(this.toDouble())
return kotlin.math.sin(angle).toBigDecimal()
val result = kotlin.math.sin(angle).toBigDecimal()
if (result.abs() < PI_THRESHOLD) {
return BigDecimal.ZERO
}
return result
}
internal fun BigDecimal.arsin(radianMode: Boolean): BigDecimal {
@ -38,7 +48,14 @@ internal fun BigDecimal.arsin(radianMode: Boolean): BigDecimal {
internal fun BigDecimal.cos(radianMode: Boolean): BigDecimal {
val angle: Double = if (radianMode) this.toDouble() else Math.toRadians(this.toDouble())
return kotlin.math.cos(angle).toBigDecimal()
val result = kotlin.math.cos(angle).toBigDecimal()
if (result.abs() < PI_THRESHOLD) {
return BigDecimal.ZERO
}
return result
}
internal fun BigDecimal.arcos(radianMode: Boolean): BigDecimal {

View File

@ -93,10 +93,14 @@ class Expression(
}
moveIfMatched(Token.Operator.divide) -> {
val divisor = parseFactor()
// Adding those `setScale` calls have been added in
// 36a931c7d175b5cad52ff3abceb7784b0bb82aac to fix #18
// I'm not sure if this is the correct way to fix it, but it
// seems to be working
val divisor = parseFactor().setScale(MAX_PRECISION)
if (divisor.compareTo(BigDecimal.ZERO) == 0) throw ExpressionException.DivideByZero()
expression = expression.divide(divisor, roundingMode)
expression = expression.setScale(MAX_PRECISION).divide(divisor, roundingMode)
}
}
}
@ -166,7 +170,8 @@ class Expression(
// sin
if (moveIfMatched(Token.Func.sin)) {
expr = parseFuncParentheses().sin(radianMode)
val x = parseFuncParentheses()
expr = x.sin(radianMode)
}
// cos
@ -211,7 +216,13 @@ class Expression(
// Power
if (moveIfMatched(Token.Operator.power)) {
expr = expr.pow(parseFactor())
val factor = parseFactor()
if (factor.compareTo(BigDecimal.ZERO) == 0 && expr.compareTo(BigDecimal.ZERO) == 0) {
throw ExpressionException.BadExpression()
}
expr = expr.pow(factor)
}
// Modulo

View File

@ -57,4 +57,7 @@ class ExpressionComplexTest {
@Test
fun expression12() = assertExpr("2×(3+4)×(52)÷6", "7")
@Test
fun shouldDivideRootAndDivideInCorrectOrder() = assertExpr("(√9)÷6", "0.5")
}

View File

@ -111,4 +111,55 @@ class ExpressionSimpleTest {
@Test
fun expression29() = assertExpr("0!", "1")
@Test
fun factorial() = assertExpr("5!", "120")
@Test
fun singleNumber() = assertExpr("42", "42")
@Test(expected = NumberFormatException::class)
fun divisionByZero() = assertExpr("1÷0", "")
@Test(expected = NumberFormatException::class)
fun invalidExpressionMissingOperand() = assertExpr("42+", "")
@Test
fun largeNumbers() = assertExpr("9999999999*9999999999", "99999999980000000001")
@Test
fun highPrecision() = assertExpr("1÷3", "0.3333333333")
@Test
fun deeplyNestedParentheses() = assertExpr("((((((42))))))", "42")
@Test
fun constantsAndFunctions() = assertExpr("π+e", "${Math.PI + Math.E}")
@Test(expected = NumberFormatException::class)
fun edgeMathematicalCases() = assertExpr("0^0", "")
@Test
fun zeroExponent() = assertExpr("5^0", "1")
@Test
fun negativeExponent() = assertExpr("5^(2)", "0.04")
@Test
fun trigonometricLimits() = assertExpr("sin(0)", "0")
@Test
fun sinAtPi() = assertExpr("sin(π)", "0")
@Test
fun sinAtHalfPi() = assertExpr("sin(π÷2)", "1")
@Test
fun cosAtPi() = assertExpr("cos(π)", "-1")
@Test
fun cosAtHalfPi() = assertExpr("cos(π÷2)", "0")
@Test
fun cosAtPiOverThree() = assertExpr("cos(π÷3)", "0.5")
}

View File

@ -37,6 +37,16 @@ sealed interface BasicUnit {
interface NumberBase : BasicUnit {
fun convert(unitTo: NumberBase, value: String): String
companion object {
val Hexadecimal = NumberBaseUnit(
"hexadecimal",
BigDecimal(16),
UnitGroup.NUMBER_BASE,
0,
0,
)
}
}
interface Default : BasicUnit {

View File

@ -120,7 +120,7 @@ internal fun Preferences.getUnitConverterFormatTime(): Boolean {
internal fun Preferences.getUnitConverterSorting(): UnitsListSorting {
return this[PrefsKeys.UNIT_CONVERTER_SORTING]
?.let { UnitsListSorting.valueOf(it) } ?: UnitsListSorting.USAGE
?.let { UnitsListSorting.valueOf(it) } ?: UnitsListSorting.SCALE_ASC
}
internal fun Preferences.getShownUnitGroups(): List<UnitGroup> {

View File

@ -1,33 +1,25 @@
Calculator, converter and more.
Calculator, converter and so much more!
Look for <b>Calculator</b> on your home screen after installing the app.
NumberHub will be installed as "Calculator" on your home screen.
• Deep customization: themes, number formatter and etc.
• No ads, in-app purchases or asking for donations
• No ads or in-app purchases
• Open source
<b>Calculator</b>
Calculator
• Copy, paste, save and share expression results
• Trigonometric functions
• Fractional output
<b>Unit converter</b>
Unit converter
• 570 units
• Built-in currency converter
• Favorite units
• Organize unit groups
• Smart search algorithm
Over 500 units! Convert miles to kilometers, Dollar to Euro, Bytes to Gigabytes and so much more!
<b>Date calculator</b>
• Add and subtract dates
• Calculate difference
• Create events in calendar
Date calculator
Add and subtract dates with ease
<b>Time converter</b>
• Add to favorites
• Add labels to time zones
Time converter
Convert time zones
The app uses https://github.com/fawazahmed0/currency-api by https://github.com/fawazahmed0
Requests are send to cdn.jsdelivr.net.
Requests are sent to cdn.jsdelivr.net.

View File

@ -1 +1 @@
Unitto — calculator and unit converter
NumberHub

View File

@ -22,6 +22,7 @@ import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandHorizontally
@ -31,6 +32,11 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.AnchoredDraggableState
import androidx.compose.foundation.gestures.DraggableAnchors
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.anchoredDraggable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@ -40,6 +46,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.SwapHoriz
@ -64,6 +71,7 @@ import androidx.compose.ui.draw.rotate
import androidx.compose.ui.focus.onFocusEvent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
@ -94,11 +102,14 @@ import app.myzel394.numberhub.data.common.isExpression
import app.myzel394.numberhub.data.converter.ConverterResult
import app.myzel394.numberhub.data.converter.UnitID
import app.myzel394.numberhub.data.model.converter.UnitGroup
import app.myzel394.numberhub.feature.converter.components.BaseCalculationSummary
import app.myzel394.numberhub.feature.converter.components.DefaultKeyboard
import app.myzel394.numberhub.feature.converter.components.NumberBaseKeyboard
import app.myzel394.numberhub.feature.converter.components.UnitSelectionButton
import app.myzel394.numberhub.feature.converter.components.ValueOneSummary
import java.math.BigDecimal
import java.util.Locale
import kotlin.math.absoluteValue
@Composable
internal fun ConverterRoute(
@ -203,10 +214,30 @@ private fun NumberBase(
convert()
}
val density = LocalDensity.current
val dragState = remember {
AnchoredDraggableState(
initialValue = DragState.CLOSED,
anchors = DraggableAnchors {
DragState.CLOSED at 0f
DragState.OPEN at with(density) { -60.dp.toPx() }
},
positionalThreshold = { 0f },
velocityThreshold = { 0f },
animationSpec = tween(easing = LinearEasing, durationMillis = 50),
)
}
PortraitLandscape(
modifier = modifier.fillMaxSize(),
content1 = { contentModifier ->
ColumnWithConstraints(modifier = contentModifier) {
ColumnWithConstraints(
modifier = contentModifier
.anchoredDraggable(
state = dragState,
orientation = Orientation.Vertical,
),
) {
val textFieldModifier = Modifier.weight(2f)
NumberBaseTextField(
@ -224,6 +255,22 @@ private fun NumberBase(
)
AnimatedUnitShortName(stringResource(uiState.unitTo.shortName))
if (uiState.result is ConverterResult.NumberBase) {
BaseCalculationSummary(
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState())
.then(with(density) { Modifier.height(dragState.offset.absoluteValue.toDp()) }),
basis = uiState.unitTo,
result = uiState.result,
onResultChange = { newValue ->
val valueConverted = uiState.unitTo.convert(uiState.unitFrom, newValue)
updateInput1(TextFieldValue(valueConverted))
},
)
}
Spacer(modifier = Modifier.height(it.maxHeight * 0.03f))
UnitSelectionButtons(
@ -250,6 +297,7 @@ private fun NumberBase(
addDigit = { updateInput1(uiState.input.addTokens(it)) },
deleteDigit = { updateInput1(uiState.input.deleteTokens()) },
clearInput = { updateInput1(TextFieldValue()) },
basis = uiState.unitFrom,
)
},
)
@ -284,6 +332,20 @@ private fun Default(
}
var focusedOnInput1 by rememberSaveable { mutableStateOf(true) }
val density = LocalDensity.current
val dragState = remember {
AnchoredDraggableState(
initialValue = DragState.CLOSED,
anchors = DraggableAnchors {
DragState.CLOSED at 0f
DragState.OPEN at with(density) { -60.dp.toPx() }
},
positionalThreshold = { 0f },
velocityThreshold = { 0f },
animationSpec = tween(easing = LinearEasing, durationMillis = 50),
)
}
LaunchedEffect(connection) {
if ((connection == ConnectionState.Available) and (uiState.result is ConverterResult.Error)) {
val unitFrom = uiState.unitFrom
@ -304,7 +366,13 @@ private fun Default(
PortraitLandscape(
modifier = modifier.fillMaxSize(),
content1 = { contentModifier ->
ColumnWithConstraints(modifier = contentModifier) { boxWithConstraintsScope ->
ColumnWithConstraints(
modifier = contentModifier
.anchoredDraggable(
state = dragState,
orientation = Orientation.Vertical,
),
) { boxWithConstraintsScope ->
val textFieldModifier = Modifier
.fillMaxWidth()
.weight(2f)
@ -425,6 +493,18 @@ private fun Default(
),
)
if (uiState.result is ConverterResult.Default && uiState.unitTo.factor >= BigDecimal.ZERO) {
ValueOneSummary(
modifier = with(density) {
Modifier
.fillMaxWidth()
.height(dragState.offset.absoluteValue.toDp())
.horizontalScroll(rememberScrollState())
},
uiState = uiState,
)
}
Spacer(modifier = Modifier.height(boxWithConstraintsScope.maxHeight * 0.03f))
UnitSelectionButtons(

View File

@ -0,0 +1,3 @@
package app.myzel394.numberhub.feature.converter
internal enum class DragState { CLOSED, OPEN }

View File

@ -156,7 +156,7 @@ private fun UnitFromSelectorScreenPreview() {
selectedUnitGroup = UnitGroup.SPEED,
shownUnitGroups = UnitGroup.entries,
showFavoritesOnly = false,
sorting = UnitsListSorting.USAGE,
sorting = UnitsListSorting.SCALE_ASC,
),
onQueryChange = {},
toggleFavoritesOnly = {},

View File

@ -155,7 +155,7 @@ private fun UnitToSelectorPreview() {
query = TextFieldValue("test"),
units = units,
showFavoritesOnly = false,
sorting = UnitsListSorting.USAGE,
sorting = UnitsListSorting.SCALE_ASC,
input = "100",
scale = 3,
outputFormat = OutputFormat.PLAIN,

View File

@ -0,0 +1,86 @@
package app.myzel394.numberhub.feature.converter.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import app.myzel394.numberhub.data.converter.ConverterResult
import app.myzel394.numberhub.data.model.converter.unit.BasicUnit
@Composable
internal fun BaseCalculationSummary(
modifier: Modifier = Modifier,
basis: BasicUnit.NumberBase,
result: ConverterResult.NumberBase,
onResultChange: (String) -> Unit,
) {
val fontStyle = MaterialTheme.typography.headlineSmall
Row(
modifier,
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically,
) {
for (index in 0..<result.value.length) {
val character = result.value[index]
val digit = character.digitToInt(16);
val base = basis.factor.toInt()
Row(
modifier = Modifier
.clip(MaterialTheme.shapes.small)
.clickable {
val newCurrentValue = (digit + 1) % base
val newResultText = result.value.substring(
0,
index,
) + newCurrentValue.toString(16) + result.value.substring(index + 1)
onResultChange(newResultText)
}
.padding(vertical = 2.dp, horizontal = 6.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = "$digit",
style = fontStyle,
color = MaterialTheme.colorScheme.primary,
)
Text(
text = " · $base",
style = fontStyle,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Text(
text = "${result.value.length - index - 1}",
modifier = Modifier.offset(
y = -(MaterialTheme.typography.bodySmall.fontSize.div(
2,
)).value.dp,
),
style = fontStyle.copy(
fontSize = MaterialTheme.typography.bodySmall.fontSize,
),
color = MaterialTheme.colorScheme.tertiary,
)
}
if (index < result.value.length - 1) {
Text(
text = " + ",
style = fontStyle,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}
}
}

View File

@ -18,18 +18,33 @@
package app.myzel394.numberhub.feature.converter.components
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.core.tween
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import app.myzel394.numberhub.core.base.R
import app.myzel394.numberhub.core.base.Token
import app.myzel394.numberhub.core.ui.LocalWindowSize
import app.myzel394.numberhub.core.ui.WindowHeightSizeClass
import app.myzel394.numberhub.core.ui.WindowWidthSizeClass
import app.myzel394.numberhub.core.ui.common.ColumnWithConstraints
import app.myzel394.numberhub.core.ui.common.KeyboardButtonFilled
import app.myzel394.numberhub.core.ui.common.KeyboardButtonLight
import app.myzel394.numberhub.core.ui.common.KeyboardButtonTertiary
@ -65,6 +80,9 @@ import app.myzel394.numberhub.core.ui.common.icons.iconpack.Plus
import app.myzel394.numberhub.core.ui.common.icons.iconpack.Power
import app.myzel394.numberhub.core.ui.common.icons.iconpack.RightBracket
import app.myzel394.numberhub.core.ui.common.icons.iconpack.Root
import app.myzel394.numberhub.data.model.converter.unit.BasicUnit
import app.myzel394.numberhub.feature.converter.createSortedArray
import kotlin.math.ceil
@Composable
internal fun DefaultKeyboard(
@ -79,92 +97,401 @@ internal fun DefaultKeyboard(
) {
val fractionalIcon = remember(fractional) { if (fractional == Token.PERIOD) IconPack.Dot else IconPack.Comma }
val fractionalIconDescription = remember(fractional) { if (fractional == Token.PERIOD) R.string.keyboard_dot else R.string.comma }
val contentHeight: Float = if (LocalWindowSize.current.heightSizeClass < WindowHeightSizeClass.Medium) KeyboardButtonToken.CONTENT_HEIGHT_SHORT else KeyboardButtonToken.CONTENT_HEIGHT_TALL
val contentHeight: Float =
if (LocalWindowSize.current.heightSizeClass < WindowHeightSizeClass.Medium) KeyboardButtonToken.CONTENT_HEIGHT_SHORT else KeyboardButtonToken.CONTENT_HEIGHT_TALL
KeypadFlow(
modifier = modifier,
rows = 5,
columns = 4,
) { width, height ->
val bModifier = Modifier.fillMaxWidth(width).fillMaxHeight(height)
val bModifier = Modifier
.fillMaxWidth(width)
.fillMaxHeight(height)
if (acButton) {
KeyboardButtonTertiary(bModifier, IconPack.Clear, stringResource(R.string.delete_label), contentHeight) { clearInput() }
KeyboardButtonFilled(bModifier, IconPack.Brackets, stringResource(R.string.keyboard_brackets), contentHeight) { addBracket() }
KeyboardButtonTertiary(
bModifier,
IconPack.Clear,
stringResource(R.string.delete_label),
contentHeight,
) { clearInput() }
KeyboardButtonFilled(
bModifier,
IconPack.Brackets,
stringResource(R.string.keyboard_brackets),
contentHeight,
) { addBracket() }
} else {
KeyboardButtonFilled(bModifier, IconPack.LeftBracket, stringResource(R.string.keyboard_left_bracket), contentHeight) { addDigit(Token.Operator.leftBracket) }
KeyboardButtonFilled(bModifier, IconPack.RightBracket, stringResource(R.string.keyboard_right_bracket), contentHeight) { addDigit(Token.Operator.rightBracket) }
KeyboardButtonFilled(
bModifier,
IconPack.LeftBracket,
stringResource(R.string.keyboard_left_bracket),
contentHeight,
) { addDigit(Token.Operator.leftBracket) }
KeyboardButtonFilled(
bModifier,
IconPack.RightBracket,
stringResource(R.string.keyboard_right_bracket),
contentHeight,
) { addDigit(Token.Operator.rightBracket) }
}
KeyboardButtonFilled(bModifier, IconPack.Power, stringResource(R.string.keyboard_power), contentHeight) { addDigit(Token.Operator.power) }
KeyboardButtonFilled(bModifier, IconPack.Root, stringResource(R.string.keyboard_root), contentHeight) { addDigit(Token.Operator.sqrt) }
KeyboardButtonFilled(
bModifier,
IconPack.Power,
stringResource(R.string.keyboard_power),
contentHeight,
) { addDigit(Token.Operator.power) }
KeyboardButtonFilled(
bModifier,
IconPack.Root,
stringResource(R.string.keyboard_root),
contentHeight,
) { addDigit(Token.Operator.sqrt) }
KeyboardButtonLight(bModifier, IconPack.Key7, Token.Digit._7, contentHeight) { addDigit(Token.Digit._7) }
KeyboardButtonLight(bModifier, IconPack.Key8, Token.Digit._8, contentHeight) { addDigit(Token.Digit._8) }
KeyboardButtonLight(bModifier, IconPack.Key9, Token.Digit._9, contentHeight) { addDigit(Token.Digit._9) }
KeyboardButtonFilled(bModifier, IconPack.Divide, stringResource(R.string.keyboard_divide), contentHeight) { addDigit(Token.Operator.divide) }
KeyboardButtonLight(
bModifier,
IconPack.Key7,
Token.Digit._7,
contentHeight,
) { addDigit(Token.Digit._7) }
KeyboardButtonLight(
bModifier,
IconPack.Key8,
Token.Digit._8,
contentHeight,
) { addDigit(Token.Digit._8) }
KeyboardButtonLight(
bModifier,
IconPack.Key9,
Token.Digit._9,
contentHeight,
) { addDigit(Token.Digit._9) }
KeyboardButtonFilled(
bModifier,
IconPack.Divide,
stringResource(R.string.keyboard_divide),
contentHeight,
) { addDigit(Token.Operator.divide) }
KeyboardButtonLight(bModifier, IconPack.Key4, Token.Digit._4, contentHeight) { addDigit(Token.Digit._4) }
KeyboardButtonLight(bModifier, IconPack.Key5, Token.Digit._5, contentHeight) { addDigit(Token.Digit._5) }
KeyboardButtonLight(bModifier, IconPack.Key6, Token.Digit._6, contentHeight) { addDigit(Token.Digit._6) }
KeyboardButtonFilled(bModifier, IconPack.Multiply, stringResource(R.string.keyboard_multiply), contentHeight) { addDigit(Token.Operator.multiply) }
KeyboardButtonLight(
bModifier,
IconPack.Key4,
Token.Digit._4,
contentHeight,
) { addDigit(Token.Digit._4) }
KeyboardButtonLight(
bModifier,
IconPack.Key5,
Token.Digit._5,
contentHeight,
) { addDigit(Token.Digit._5) }
KeyboardButtonLight(
bModifier,
IconPack.Key6,
Token.Digit._6,
contentHeight,
) { addDigit(Token.Digit._6) }
KeyboardButtonFilled(
bModifier,
IconPack.Multiply,
stringResource(R.string.keyboard_multiply),
contentHeight,
) { addDigit(Token.Operator.multiply) }
KeyboardButtonLight(bModifier, IconPack.Key1, Token.Digit._1, contentHeight) { addDigit(Token.Digit._1) }
KeyboardButtonLight(bModifier, IconPack.Key2, Token.Digit._2, contentHeight) { addDigit(Token.Digit._2) }
KeyboardButtonLight(bModifier, IconPack.Key3, Token.Digit._3, contentHeight) { addDigit(Token.Digit._3) }
KeyboardButtonFilled(bModifier, IconPack.Minus, stringResource(R.string.keyboard_minus), contentHeight) { addDigit(Token.Operator.minus) }
KeyboardButtonLight(
bModifier,
IconPack.Key1,
Token.Digit._1,
contentHeight,
) { addDigit(Token.Digit._1) }
KeyboardButtonLight(
bModifier,
IconPack.Key2,
Token.Digit._2,
contentHeight,
) { addDigit(Token.Digit._2) }
KeyboardButtonLight(
bModifier,
IconPack.Key3,
Token.Digit._3,
contentHeight,
) { addDigit(Token.Digit._3) }
KeyboardButtonFilled(
bModifier,
IconPack.Minus,
stringResource(R.string.keyboard_minus),
contentHeight,
) { addDigit(Token.Operator.minus) }
if (middleZero) {
KeyboardButtonLight(bModifier, fractionalIcon, stringResource(fractionalIconDescription), contentHeight) { addDigit(Token.Digit.dot) }
KeyboardButtonLight(bModifier, IconPack.Key0, Token.Digit._0, contentHeight) { addDigit(Token.Digit._0) }
KeyboardButtonLight(
bModifier,
fractionalIcon,
stringResource(fractionalIconDescription),
contentHeight,
) { addDigit(Token.Digit.dot) }
KeyboardButtonLight(
bModifier,
IconPack.Key0,
Token.Digit._0,
contentHeight,
) { addDigit(Token.Digit._0) }
} else {
KeyboardButtonLight(bModifier, IconPack.Key0, Token.Digit._0, contentHeight) { addDigit(Token.Digit._0) }
KeyboardButtonLight(bModifier, fractionalIcon, stringResource(fractionalIconDescription), contentHeight) { addDigit(Token.Digit.dot) }
KeyboardButtonLight(
bModifier,
IconPack.Key0,
Token.Digit._0,
contentHeight,
) { addDigit(Token.Digit._0) }
KeyboardButtonLight(
bModifier,
fractionalIcon,
stringResource(fractionalIconDescription),
contentHeight,
) { addDigit(Token.Digit.dot) }
}
KeyboardButtonLight(bModifier, IconPack.Backspace, stringResource(R.string.delete_label), contentHeight, onLongClick = clearInput) { deleteDigit() }
KeyboardButtonFilled(bModifier, IconPack.Plus, stringResource(R.string.keyboard_plus), contentHeight) { addDigit(Token.Operator.plus) }
KeyboardButtonLight(
bModifier,
IconPack.Backspace,
stringResource(R.string.delete_label),
contentHeight,
onLongClick = clearInput,
) { deleteDigit() }
KeyboardButtonFilled(
bModifier,
IconPack.Plus,
stringResource(R.string.keyboard_plus),
contentHeight,
) { addDigit(Token.Operator.plus) }
}
}
val AVAILABLE_NUMBERS = mapOf<String, ImageVector>(
Token.Digit._0 to IconPack.Key0,
Token.Digit._1 to IconPack.Key1,
Token.Digit._2 to IconPack.Key2,
Token.Digit._3 to IconPack.Key3,
Token.Digit._4 to IconPack.Key4,
Token.Digit._5 to IconPack.Key5,
Token.Digit._6 to IconPack.Key6,
Token.Digit._7 to IconPack.Key7,
Token.Digit._8 to IconPack.Key8,
Token.Digit._9 to IconPack.Key9,
Token.Letter._A to IconPack.KeyA,
Token.Letter._B to IconPack.KeyB,
Token.Letter._C to IconPack.KeyC,
Token.Letter._D to IconPack.KeyD,
Token.Letter._E to IconPack.KeyE,
Token.Letter._F to IconPack.KeyF,
)
@Composable
internal fun NumberBaseKeyboard(
modifier: Modifier,
addDigit: (String) -> Unit,
clearInput: () -> Unit,
deleteDigit: () -> Unit,
basis: BasicUnit.NumberBase,
) {
val contentHeight: Float = if (LocalWindowSize.current.heightSizeClass < WindowHeightSizeClass.Medium) KeyboardButtonToken.CONTENT_HEIGHT_SHORT else KeyboardButtonToken.CONTENT_HEIGHT_TALL
val isUsingColumn =
(LocalWindowSize.current.widthSizeClass > WindowWidthSizeClass.Expanded) or (LocalWindowSize.current.heightSizeClass > WindowHeightSizeClass.Compact)
KeypadFlow(
modifier = modifier,
rows = 6,
columns = 3,
) { width, height ->
val bModifier = Modifier.fillMaxWidth(width).fillMaxHeight(height)
val wideButtonModifier = Modifier.fillMaxHeight(height).fillMaxWidth(width * 2)
var direction by remember {
mutableStateOf(AnimatedContentTransitionScope.SlideDirection.Right)
}
KeyboardButtonFilled(bModifier, IconPack.KeyA, Token.Letter._A, contentHeight) { addDigit(Token.Letter._A) }
KeyboardButtonFilled(bModifier, IconPack.KeyB, Token.Letter._B, contentHeight) { addDigit(Token.Letter._B) }
KeyboardButtonFilled(bModifier, IconPack.KeyC, Token.Letter._C, contentHeight) { addDigit(Token.Letter._C) }
LaunchedEffect(isUsingColumn) {
direction = if (isUsingColumn) {
AnimatedContentTransitionScope.SlideDirection.Right
} else {
AnimatedContentTransitionScope.SlideDirection.Up
}
}
KeyboardButtonFilled(bModifier, IconPack.KeyD, Token.Letter._D, contentHeight) { addDigit(Token.Letter._D) }
KeyboardButtonFilled(bModifier, IconPack.KeyE, Token.Letter._E, contentHeight) { addDigit(Token.Letter._E) }
KeyboardButtonFilled(bModifier, IconPack.KeyF, Token.Letter._F, contentHeight) { addDigit(Token.Letter._F) }
LaunchedEffect(basis) {
direction = when (direction) {
AnimatedContentTransitionScope.SlideDirection.Up -> AnimatedContentTransitionScope.SlideDirection.Down
AnimatedContentTransitionScope.SlideDirection.Down -> AnimatedContentTransitionScope.SlideDirection.Up
AnimatedContentTransitionScope.SlideDirection.Left -> AnimatedContentTransitionScope.SlideDirection.Right
AnimatedContentTransitionScope.SlideDirection.Right -> AnimatedContentTransitionScope.SlideDirection.Left
else -> direction
}
}
KeyboardButtonLight(bModifier, IconPack.Key7, Token.Digit._7, contentHeight) { addDigit(Token.Digit._7) }
KeyboardButtonLight(bModifier, IconPack.Key8, Token.Digit._8, contentHeight) { addDigit(Token.Digit._8) }
KeyboardButtonLight(bModifier, IconPack.Key9, Token.Digit._9, contentHeight) { addDigit(Token.Digit._9) }
ColumnWithConstraints(modifier) { constraints ->
AnimatedContent(
transitionSpec = {
slideIntoContainer(animationSpec = tween(600), towards = direction).togetherWith(
slideOutOfContainer(animationSpec = tween(600), towards = direction),
)
},
targetState = basis.factor.toInt(),
label = "ConverterKeyboard-FlowRow",
) { amount ->
val columns: Int = when {
amount == 5 -> 2
amount == 3 -> 2
amount == 7 -> 2
amount == 10 -> 3
amount < 10 -> if (amount.and(1) == 0) 2 else if (amount % 3 == 0) 3 else amount % 3
else -> 3
}
val rows = when {
amount == 5 -> 3
amount == 3 -> 2
amount == 7 -> 4
amount == 10 -> 4
amount < 10 -> ceil(amount.toDouble() / columns.toDouble()).toInt() + 1
amount == 11 -> 5
amount == 12 -> 5
amount == 13 -> 5
else -> 6
}
val horizontalSpacing = 8.dp
val verticalSpacing = 12.dp
val height: Float = (1f - (verticalSpacing * (rows - 1) / constraints.maxHeight)) / rows
KeyboardButtonLight(bModifier, IconPack.Key4, Token.Digit._4, contentHeight) { addDigit(Token.Digit._4) }
KeyboardButtonLight(bModifier, IconPack.Key5, Token.Digit._5, contentHeight) { addDigit(Token.Digit._5) }
KeyboardButtonLight(bModifier, IconPack.Key6, Token.Digit._6, contentHeight) { addDigit(Token.Digit._6) }
FlowRow(
modifier = modifier,
maxItemsInEachRow = columns,
horizontalArrangement = Arrangement.spacedBy(horizontalSpacing),
verticalArrangement = Arrangement.spacedBy(verticalSpacing),
) {
val bModifier = Modifier
.fillMaxHeight(height)
.fillMaxWidth()
KeyboardButtonLight(bModifier, IconPack.Key1, Token.Digit._1, contentHeight) { addDigit(Token.Digit._1) }
KeyboardButtonLight(bModifier, IconPack.Key2, Token.Digit._2, contentHeight) { addDigit(Token.Digit._2) }
KeyboardButtonLight(bModifier, IconPack.Key3, Token.Digit._3, contentHeight) { addDigit(Token.Digit._3) }
when {
amount in arrayOf(3, 5, 7) -> {
for (int in createSortedArray(1..<amount, columns)) {
val key = AVAILABLE_NUMBERS.keys.elementAt(int)
val icon = AVAILABLE_NUMBERS[key]!!
KeyboardButtonLight(
bModifier.weight(1f),
icon,
key,
contentHeight,
) { addDigit(key) }
}
// TODO Should be a separate o use custom widthFillFactors and heightFillFactors
KeyboardButtonLight(bModifier, IconPack.Key0, Token.Digit._0, contentHeight) { addDigit(Token.Digit._0) }
KeyboardButtonLight(wideButtonModifier, IconPack.Backspace, stringResource(R.string.delete_label), contentHeight, clearInput) { deleteDigit() }
KeyboardButtonLight(
bModifier.weight(1f),
IconPack.Key0,
Token.Digit._0,
contentHeight,
) { addDigit(Token.Digit._0) }
KeyboardButtonTertiary(
bModifier.weight(1f),
IconPack.Backspace,
stringResource(R.string.delete_label),
contentHeight,
clearInput,
) { deleteDigit() }
}
amount < 10 -> {
for (int in createSortedArray(0..<amount.coerceAtMost(10), columns)) {
val key = AVAILABLE_NUMBERS.keys.elementAt(int)
val icon = AVAILABLE_NUMBERS[key]!!
KeyboardButtonLight(
bModifier.weight(1f),
icon,
key,
contentHeight,
) { addDigit(key) }
}
KeyboardButtonTertiary(
bModifier,
IconPack.Backspace,
stringResource(R.string.delete_label),
contentHeight,
clearInput,
) { deleteDigit() }
}
amount == 10 -> {
for (int in createSortedArray(1..9, columns)) {
val key = AVAILABLE_NUMBERS.keys.elementAt(int)
val icon = AVAILABLE_NUMBERS[key]!!
KeyboardButtonLight(
bModifier.weight(1f),
icon,
key,
contentHeight,
) { addDigit(key) }
}
KeyboardButtonLight(
bModifier.weight(1f),
IconPack.Key0,
Token.Digit._0,
contentHeight,
) { addDigit(Token.Digit._0) }
KeyboardButtonTertiary(
bModifier.weight(2f),
IconPack.Backspace,
stringResource(R.string.delete_label),
contentHeight,
clearInput,
) { deleteDigit() }
}
else -> {
val rowsAmount = ceil((amount - 10) / 3f).toInt()
for (row in (rowsAmount - 1) downTo 0) {
Row(
Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(horizontalSpacing),
) {
val rowStart = 10 + (row) * columns
val rowEnd = (rowStart + 3).coerceAtMost(amount.coerceAtMost(16))
for (index in createSortedArray(rowStart..<rowEnd, columns)) {
val key = AVAILABLE_NUMBERS.keys.elementAt(index)
val icon = AVAILABLE_NUMBERS[key]!!
KeyboardButtonFilled(
bModifier.weight(1f),
icon,
key,
contentHeight,
) { addDigit(key) }
}
}
}
for (int in createSortedArray(1..9, columns)) {
val key = AVAILABLE_NUMBERS.keys.elementAt(int)
val icon = AVAILABLE_NUMBERS[key]!!
KeyboardButtonLight(
bModifier.weight(1f),
icon,
key,
contentHeight,
) { addDigit(key) }
}
KeyboardButtonLight(
bModifier.weight(1f),
IconPack.Key0,
Token.Digit._0,
contentHeight,
) { addDigit(Token.Digit._0) }
KeyboardButtonTertiary(
bModifier.weight(2f),
IconPack.Backspace,
stringResource(R.string.delete_label),
contentHeight,
clearInput,
) { deleteDigit() }
}
}
}
}
}
}
@ -191,5 +518,6 @@ private fun PreviewConverterKeyboardNumberBase() {
addDigit = {},
clearInput = {},
deleteDigit = {},
basis = BasicUnit.NumberBase.Hexadecimal,
)
}

View File

@ -0,0 +1,74 @@
package app.myzel394.numberhub.feature.converter.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import app.myzel394.numberhub.data.common.format
import app.myzel394.numberhub.feature.converter.UnitConverterUIState
import java.math.BigDecimal
@Composable
internal fun ValueOneSummary(
modifier: Modifier = Modifier,
uiState: UnitConverterUIState.Default,
) {
val unitFromLabel = LocalContext.current.getString(uiState.unitFrom.displayName)
val unitToLabel = LocalContext.current.getString(uiState.unitTo.displayName)
val value = uiState.unitFrom.convert(uiState.unitTo, BigDecimal(1))
.format(uiState.scale, uiState.outputFormat)
val fontStyle = MaterialTheme.typography.headlineSmall
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End,
) {
Text(
1.toString(),
style = fontStyle,
color = MaterialTheme.colorScheme.primary,
)
Text(
" ",
style = fontStyle,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Text(
unitFromLabel,
style = fontStyle,
color = MaterialTheme.colorScheme.tertiary,
)
Text(
" = ",
style = fontStyle,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Text(
value,
style = fontStyle,
color = MaterialTheme.colorScheme.primary,
)
Text(
" ",
style = fontStyle,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Text(
unitToLabel,
style = fontStyle,
color = MaterialTheme.colorScheme.tertiary,
)
}
}

View File

@ -0,0 +1,25 @@
package app.myzel394.numberhub.feature.converter
import kotlin.math.ceil
// So here's the problem. If we just go down from 3 downto 0 with columns 2, this results in:
// [3, 2]
// [1, 0]
// While this is correct, the ordering is incorrect. It should be
// [2, 3]
// [0, 1]
// TODO: Add support for rtl languages
internal fun createSortedArray(range: IntRange, columns: Int): List<Int> {
val result = mutableListOf<Int>()
val rows = ceil(range.count().toDouble() / columns.toDouble()).toInt()
for (row in rows downTo 0) {
for (column in 0 until columns) {
val index = row * columns + column + range.first
if (index <= range.last) {
result.add(index)
}
}
}
return result
}

View File

@ -139,7 +139,7 @@ private fun PreviewConverterSettingsScreen() {
precision = 3,
outputFormat = OutputFormat.PLAIN,
unitConverterFormatTime = false,
unitConverterSorting = UnitsListSorting.USAGE,
unitConverterSorting = UnitsListSorting.SCALE_ASC,
shownUnitGroups = UnitGroup.entries,
unitConverterFavoritesOnly = false,
enableToolsExperiment = false,

View File

@ -1,29 +1,29 @@
[versions]
versionCode = "4"
versionName = "NumberHub"
versionCode = "5"
versionName = "0.5.0"
androidxBrowserBrowser = "1.8.0"
androidGradlePlugin = "8.3.2"
androidxActivityActivityCompose = "1.9.0"
androidxAppCompatAppCompat = "1.6.1"
androidxCompose = "1.6.7"
androidxAppCompatAppCompat = "1.7.0"
androidxCompose = "1.6.8"
androidxComposeCompiler = "1.5.9"
androidxComposeMaterial3 = "1.2.1"
androidxCoreCoreKts = "1.13.1"
androidxGlanceGlance = "1.0.0"
androidxGlanceGlance = "1.1.0"
androidxDatastoreDatastorePreferences = "1.1.1"
androidxEspresso = "3.5.1"
androidxEspresso = "3.6.1"
androidxHiltHiltNavigationCompose = "1.2.0"
androidxMacroBenchmark = "1.2.4"
androidxLifecycleLifecycleRuntimeCompose = "2.7.0"
androidxLifecycleLifecycleRuntimeCompose = "2.8.3"
androidxNavigationNavigationCompose = "2.7.7"
androidxProfileinstallerProfileinstaller = "1.3.1"
androidxRoom = "2.6.1"
androidxTest = "1.5.0"
androidxTestExtJunitKtx = "1.1.5"
androidxTestRunner = "1.5.2"
androidxTest = "1.6.1"
androidxTestExtJunitKtx = "1.2.1"
androidxTestRunner = "1.6.1"
androidxUiAutomator = "2.3.0"
androidxWindowWindow = "1.2.0"
androidxWindowWindow = "1.3.0"
androidxWorkWorkRuntimeKtx = "2.9.0"
comAndroidToolsDesugarJdkLibs = "2.0.4"
comGithubSadellieThemmo = "1.3.0"