From ef4e59178c4714f55c5c7c61d3e80997937b1481 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Tue, 24 Mar 2026 17:33:49 +0530 Subject: [PATCH 01/18] feat: Added kaomoji.json --- app/src/main/assets/kaomoji.json | 3032 ++++++++++++++++++++++++++++++ 1 file changed, 3032 insertions(+) create mode 100644 app/src/main/assets/kaomoji.json diff --git a/app/src/main/assets/kaomoji.json b/app/src/main/assets/kaomoji.json new file mode 100644 index 000000000..dac7dd4ca --- /dev/null +++ b/app/src/main/assets/kaomoji.json @@ -0,0 +1,3032 @@ +{ + "kaomoji": [ + { + "category": "joy", + "value": "(* ^ ω ^)" + }, + { + "category": "joy", + "value": "(´ ∀ ` *)" + }, + { + "category": "joy", + "value": "٩(◕‿◕。)۶" + }, + { + "category": "joy", + "value": "☆*:.。.o(≧▽≦)o.。.:*☆" + }, + { + "category": "joy", + "value": "(o^▽^o)" + }, + { + "category": "joy", + "value": "(⌒▽⌒)☆" + }, + { + "category": "joy", + "value": "<( ̄︶ ̄)>" + }, + { + "category": "joy", + "value": "。.:☆*:・'(*⌒―⌒*)))" + }, + { + "category": "joy", + "value": "ヽ(・∀・)ノ" + }, + { + "category": "joy", + "value": "(´。• ω •。`)" + }, + { + "category": "joy", + "value": "( ̄ω ̄)" + }, + { + "category": "joy", + "value": "`;:゛;`;・(°ε° )" + }, + { + "category": "joy", + "value": "(o・ω・o)" + }, + { + "category": "joy", + "value": "(@^◡^)" + }, + { + "category": "joy", + "value": "ヽ(*・ω・)ノ" + }, + { + "category": "joy", + "value": "(o_ _)ノ彡☆" + }, + { + "category": "joy", + "value": "(^人^)" + }, + { + "category": "joy", + "value": "(o´▽`o)" + }, + { + "category": "joy", + "value": "(*´▽`*)" + }, + { + "category": "joy", + "value": "。゚( ゚^∀^゚)゚。" + }, + { + "category": "joy", + "value": "( ´ ω ` )" + }, + { + "category": "joy", + "value": "(((o(*°▽°*)o)))" + }, + { + "category": "joy", + "value": "(≧◡≦)" + }, + { + "category": "joy", + "value": "(o´∀`o)" + }, + { + "category": "joy", + "value": "(´• ω •`)" + }, + { + "category": "joy", + "value": "(^▽^)" + }, + { + "category": "joy", + "value": "(⌒ω⌒)" + }, + { + "category": "joy", + "value": "∑d(°∀°d)" + }, + { + "category": "joy", + "value": "╰(▔∀▔)╯" + }, + { + "category": "joy", + "value": "(─‿‿─)" + }, + { + "category": "joy", + "value": "(*^‿^*)" + }, + { + "category": "joy", + "value": "ヽ(o^ ^o)ノ" + }, + { + "category": "joy", + "value": "(✯◡✯)" + }, + { + "category": "joy", + "value": "(◕‿◕)" + }, + { + "category": "joy", + "value": "(*≧ω≦*)" + }, + { + "category": "joy", + "value": "(☆▽☆)" + }, + { + "category": "joy", + "value": "(⌒‿⌒)" + }, + { + "category": "joy", + "value": "\(≧▽≦)/" + }, + { + "category": "joy", + "value": "ヽ(o^▽^o)ノ" + }, + { + "category": "joy", + "value": "☆ ~('▽^人)" + }, + { + "category": "joy", + "value": "(*°▽°*)" + }, + { + "category": "joy", + "value": "٩(。•́‿•̀。)۶" + }, + { + "category": "joy", + "value": "(✧ω✧)" + }, + { + "category": "joy", + "value": "ヽ(*⌒▽⌒*)ノ" + }, + { + "category": "joy", + "value": "(´。• ᵕ •。`)" + }, + { + "category": "joy", + "value": "( ´ ▽ ` )" + }, + { + "category": "joy", + "value": "( ̄▽ ̄)" + }, + { + "category": "joy", + "value": "╰(*´︶`*)╯" + }, + { + "category": "joy", + "value": "ヽ(>∀<☆)ノ" + }, + { + "category": "joy", + "value": "o(≧▽≦)o" + }, + { + "category": "joy", + "value": "(☆ω☆)" + }, + { + "category": "joy", + "value": "(っ˘ω˘ς )" + }, + { + "category": "joy", + "value": "\( ̄▽ ̄)/" + }, + { + "category": "joy", + "value": "(*¯︶¯*)" + }, + { + "category": "joy", + "value": "\(^▽^)/" + }, + { + "category": "joy", + "value": "٩(◕‿◕)۶" + }, + { + "category": "joy", + "value": "(o˘◡˘o)" + }, + { + "category": "joy", + "value": "(★ω★)/" + }, + { + "category": "joy", + "value": "(^ヮ^)/" + }, + { + "category": "joy", + "value": "(〃^▽^〃)" + }, + { + "category": "joy", + "value": "(╯✧▽✧)╯" + }, + { + "category": "joy", + "value": "o(>ω<)o" + }, + { + "category": "joy", + "value": "o( ❛ᴗ❛ )o" + }, + { + "category": "joy", + "value": "。゚(TヮT)゚。" + }, + { + "category": "joy", + "value": "( ‾́ ◡ ‾́ )" + }, + { + "category": "joy", + "value": "(ノ´ヮ`)ノ*: ・゚" + }, + { + "category": "joy", + "value": "(b ᵔ▽ᵔ)b" + }, + { + "category": "joy", + "value": "(๑˃ᴗ˂)ﻭ" + }, + { + "category": "joy", + "value": "(๑˘︶˘๑)" + }, + { + "category": "joy", + "value": "( ˙꒳​˙ )" + }, + { + "category": "joy", + "value": "(*꒦ິ꒳꒦ີ)" + }, + { + "category": "joy", + "value": "°˖✧◝(⁰▿⁰)◜✧˖°" + }, + { + "category": "joy", + "value": "(´・ᴗ・ ` )" + }, + { + "category": "joy", + "value": "(ノ◕ヮ◕)ノ*:・゚✧" + }, + { + "category": "joy", + "value": "(„• ֊ •„)" + }, + { + "category": "joy", + "value": "(.❛ ᴗ ❛.)" + }, + { + "category": "joy", + "value": "(⁀ᗢ⁀)" + }, + { + "category": "joy", + "value": "(¬‿¬ )" + }, + { + "category": "joy", + "value": "(¬‿¬ )" + }, + { + "category": "joy", + "value": "(* ̄▽ ̄)b" + }, + { + "category": "joy", + "value": "( ˙▿˙ )" + }, + { + "category": "joy", + "value": "(¯▿¯)" + }, + { + "category": "joy", + "value": "( ◕▿◕ )" + }, + { + "category": "joy", + "value": "\(٥⁀▽⁀ )/" + }, + { + "category": "joy", + "value": "(„• ᴗ •„)" + }, + { + "category": "joy", + "value": "(ᵔ◡ᵔ)" + }, + { + "category": "joy", + "value": "( ´ ▿ ` )" + }, + { + "category": "love", + "value": "(ノ´ з `)ノ" + }, + { + "category": "love", + "value": "(♡μ_μ)" + }, + { + "category": "love", + "value": "(*^^*)♡" + }, + { + "category": "love", + "value": "☆⌒ヽ(*'、^*)chu" + }, + { + "category": "love", + "value": "(♡-_-♡)" + }, + { + "category": "love", + "value": "( ̄ε ̄@)" + }, + { + "category": "love", + "value": "ヽ(♡‿♡)ノ" + }, + { + "category": "love", + "value": "( ´ ∀ `)ノ~ ♡" + }, + { + "category": "love", + "value": "(─‿‿─)♡" + }, + { + "category": "love", + "value": "(´。• ᵕ •。`) ♡" + }, + { + "category": "love", + "value": "(*♡∀♡)" + }, + { + "category": "love", + "value": "(。・//ε//・。)" + }, + { + "category": "love", + "value": "(´ ω `♡)" + }, + { + "category": "love", + "value": "♡( ◡‿◡ )" + }, + { + "category": "love", + "value": "(◕‿◕)♡" + }, + { + "category": "love", + "value": "(/▽\*)。o○♡" + }, + { + "category": "love", + "value": "(ღ˘⌣˘ღ)" + }, + { + "category": "love", + "value": "(♡°▽°♡)" + }, + { + "category": "love", + "value": "♡(。- ω -)" + }, + { + "category": "love", + "value": "♡ ~('▽^人)" + }, + { + "category": "love", + "value": "(´• ω •`) ♡" + }, + { + "category": "love", + "value": "(´ ε ` )♡" + }, + { + "category": "love", + "value": "(´。• ω •。`) ♡" + }, + { + "category": "love", + "value": "( ´ ▽ ` ).。o♡" + }, + { + "category": "love", + "value": "╰(*´︶`*)╯♡" + }, + { + "category": "love", + "value": "(*˘︶˘*).。.:*♡" + }, + { + "category": "love", + "value": "(♡˙︶˙♡)" + }, + { + "category": "love", + "value": "♡\( ̄▽ ̄)/♡" + }, + { + "category": "love", + "value": "(≧◡≦) ♡" + }, + { + "category": "love", + "value": "(⌒▽⌒)♡" + }, + { + "category": "love", + "value": "(*¯ ³¯*)♡" + }, + { + "category": "love", + "value": "(っ˘з(˘⌣˘ ) ♡" + }, + { + "category": "love", + "value": "♡ (˘▽˘>ԅ( ˘⌣˘)" + }, + { + "category": "love", + "value": "( ˘⌣˘)♡(˘⌣˘ )" + }, + { + "category": "love", + "value": "(/^-^(^ ^*)/ ♡" + }, + { + "category": "love", + "value": "٩(♡ε♡)۶" + }, + { + "category": "love", + "value": "σ(≧ε≦σ) ♡" + }, + { + "category": "love", + "value": "♡ (⇀ 3 ↼)" + }, + { + "category": "love", + "value": "♡ ( ̄З ̄)" + }, + { + "category": "love", + "value": "(❤ω❤)" + }, + { + "category": "love", + "value": "(˘∀˘)/(μ‿μ) ❤" + }, + { + "category": "love", + "value": "❤ (ɔˆз(ˆ⌣ˆc)" + }, + { + "category": "love", + "value": "(´♡‿♡`)" + }, + { + "category": "love", + "value": "(°◡°♡)" + }, + { + "category": "love", + "value": "Σ>―(〃°ω°〃)♡→" + }, + { + "category": "love", + "value": "(´,,•ω•,,)♡" + }, + { + "category": "love", + "value": "(´꒳`)♡" + }, + { + "category": "embarassment", + "value": "(⌒_⌒;)" + }, + { + "category": "embarassment", + "value": "(o^ ^o)" + }, + { + "category": "embarassment", + "value": "(*/ω\)" + }, + { + "category": "embarassment", + "value": "(*/。\)" + }, + { + "category": "embarassment", + "value": "(*/_\)" + }, + { + "category": "embarassment", + "value": "(*ノωノ)" + }, + { + "category": "embarassment", + "value": "(o-_-o)" + }, + { + "category": "embarassment", + "value": "(*μ_μ)" + }, + { + "category": "embarassment", + "value": "( ◡‿◡ *)" + }, + { + "category": "embarassment", + "value": "(ᵔ.ᵔ)" + }, + { + "category": "embarassment", + "value": "(*ノ∀`*)" + }, + { + "category": "embarassment", + "value": "(//▽//)" + }, + { + "category": "embarassment", + "value": "(//ω//)" + }, + { + "category": "embarassment", + "value": "(ノ*°▽°*)" + }, + { + "category": "embarassment", + "value": "(*^.^*)" + }, + { + "category": "embarassment", + "value": "(*ノ▽ノ)" + }, + { + "category": "embarassment", + "value": "( ̄▽ ̄*)ゞ" + }, + { + "category": "embarassment", + "value": "(⁄ ⁄•⁄ω⁄•⁄ ⁄)" + }, + { + "category": "embarassment", + "value": "(*/▽\*)" + }, + { + "category": "embarassment", + "value": "(⁄ ⁄>⁄ ▽ ⁄<⁄ ⁄)" + }, + { + "category": "embarassment", + "value": "(„ಡωಡ„)" + }, + { + "category": "embarassment", + "value": "(ง ื▿ ื)ว" + }, + { + "category": "embarassment", + "value": "( 〃▽〃)" + }, + { + "category": "embarassment", + "value": "(/▿\ )" + }, + { + "category": "embarassment", + "value": "(/// ̄  ̄///)" + }, + { + "category": "sympathy", + "value": "(ノ_<。)ヾ(´ ▽ ` )" + }, + { + "category": "sympathy", + "value": "。・゚・(ノД`)ヽ( ̄ω ̄ )" + }, + { + "category": "sympathy", + "value": "ρ(- ω -、)ヾ( ̄ω ̄; )" + }, + { + "category": "sympathy", + "value": "ヽ( ̄ω ̄(。。 )ゝ" + }, + { + "category": "sympathy", + "value": "(*´ I `)ノ゚(ノД`゚)゚。" + }, + { + "category": "sympathy", + "value": "ヽ(~_~(・_・ )ゝ" + }, + { + "category": "sympathy", + "value": "(ノ_;)ヾ(´ ∀ ` )" + }, + { + "category": "sympathy", + "value": "(; ω ; )ヾ(´∀`* )" + }, + { + "category": "sympathy", + "value": "(*´ー)ノ(ノд`)" + }, + { + "category": "sympathy", + "value": "(´-ω-`( _ _ )" + }, + { + "category": "sympathy", + "value": "(っ´ω`)ノ(╥ω╥)" + }, + { + "category": "sympathy", + "value": "(o・_・)ノ”(ノ_<、)" + }, + { + "category": "dissatisfaction", + "value": "(#><)" + }, + { + "category": "dissatisfaction", + "value": "(;⌣̀_⌣́)" + }, + { + "category": "dissatisfaction", + "value": "☆o(><;)○" + }, + { + "category": "dissatisfaction", + "value": "( ̄  ̄|||)" + }, + { + "category": "dissatisfaction", + "value": "(; ̄Д ̄)" + }, + { + "category": "dissatisfaction", + "value": "( ̄□ ̄」)" + }, + { + "category": "dissatisfaction", + "value": "(# ̄0 ̄)" + }, + { + "category": "dissatisfaction", + "value": "(# ̄ω ̄)" + }, + { + "category": "dissatisfaction", + "value": "(¬_¬;)" + }, + { + "category": "dissatisfaction", + "value": "(>m<)" + }, + { + "category": "dissatisfaction", + "value": "(」°ロ°)」" + }, + { + "category": "dissatisfaction", + "value": "(〃>_<;〃)" + }, + { + "category": "dissatisfaction", + "value": "(^^#)" + }, + { + "category": "dissatisfaction", + "value": "(︶︹︺)" + }, + { + "category": "dissatisfaction", + "value": "( ̄ヘ ̄)" + }, + { + "category": "dissatisfaction", + "value": "<( ̄ ﹌  ̄)>" + }, + { + "category": "dissatisfaction", + "value": "( ̄︿ ̄)" + }, + { + "category": "dissatisfaction", + "value": "(>﹏<)" + }, + { + "category": "dissatisfaction", + "value": "(--_--)" + }, + { + "category": "dissatisfaction", + "value": "凸( ̄ヘ ̄)" + }, + { + "category": "dissatisfaction", + "value": "ヾ(  ̄O ̄)ツ" + }, + { + "category": "dissatisfaction", + "value": "(⇀‸↼‶)" + }, + { + "category": "dissatisfaction", + "value": "o(>< )o" + }, + { + "category": "dissatisfaction", + "value": "(」><)」" + }, + { + "category": "dissatisfaction", + "value": "(ᗒᗣᗕ)՞" + }, + { + "category": "dissatisfaction", + "value": "(눈_눈)" + }, + { + "category": "anger", + "value": "(#`Д´)" + }, + { + "category": "anger", + "value": "(`皿´#)" + }, + { + "category": "anger", + "value": "( ` ω ´ )" + }, + { + "category": "anger", + "value": "ヽ( `д´*)ノ" + }, + { + "category": "anger", + "value": "(・`ω´・)" + }, + { + "category": "anger", + "value": "(`ー´)" + }, + { + "category": "anger", + "value": "ヽ(`⌒´メ)ノ" + }, + { + "category": "anger", + "value": "凸(`△´#)" + }, + { + "category": "anger", + "value": "( `ε´ )" + }, + { + "category": "anger", + "value": "ψ( ` ∇ ´ )ψ" + }, + { + "category": "anger", + "value": "ヾ(`ヘ´)ノ゙" + }, + { + "category": "anger", + "value": "ヽ(‵﹏´)ノ" + }, + { + "category": "anger", + "value": "(メ` ロ ´)" + }, + { + "category": "anger", + "value": "(╬`益´)" + }, + { + "category": "anger", + "value": "┌∩┐(◣_◢)┌∩┐" + }, + { + "category": "anger", + "value": "凸( ` ロ ´ )凸" + }, + { + "category": "anger", + "value": "Σ(▼□▼メ)" + }, + { + "category": "anger", + "value": "(°ㅂ°╬)" + }, + { + "category": "anger", + "value": "ψ(▼へ▼メ)~→" + }, + { + "category": "anger", + "value": "(ノ°益°)ノ" + }, + { + "category": "anger", + "value": "(҂ `з´ )" + }, + { + "category": "anger", + "value": "(‡▼益▼)" + }, + { + "category": "anger", + "value": "(҂` ロ ´)凸" + }, + { + "category": "anger", + "value": "((╬◣﹏◢))" + }, + { + "category": "anger", + "value": "٩(╬ʘ益ʘ╬)۶" + }, + { + "category": "anger", + "value": "(╬ Ò﹏Ó)" + }, + { + "category": "anger", + "value": "\\٩(๑`^´๑)۶//" + }, + { + "category": "anger", + "value": "(凸ಠ益ಠ)凸" + }, + { + "category": "anger", + "value": "↑_(ΦwΦ)Ψ" + }, + { + "category": "anger", + "value": "←~(Ψ▼ー▼)∈" + }, + { + "category": "anger", + "value": "୧((#Φ益Φ#))୨" + }, + { + "category": "anger", + "value": "٩(ఠ益ఠ)۶" + }, + { + "category": "anger", + "value": "(ノಥ益ಥ)ノ" + }, + { + "category": "sadness", + "value": "(ノ_<。)" + }, + { + "category": "sadness", + "value": "(-_-)" + }, + { + "category": "sadness", + "value": "(´-ω-`)" + }, + { + "category": "sadness", + "value": ".・゚゚・(/ω\)・゚゚・." + }, + { + "category": "sadness", + "value": "(μ_μ)" + }, + { + "category": "sadness", + "value": "(ノД`)" + }, + { + "category": "sadness", + "value": "(-ω-、)" + }, + { + "category": "sadness", + "value": "。゜゜(´O`) ゜゜。" + }, + { + "category": "sadness", + "value": "o(TヘTo)" + }, + { + "category": "sadness", + "value": "( ; ω ; )" + }, + { + "category": "sadness", + "value": "(。╯︵╰。)" + }, + { + "category": "sadness", + "value": "。・゚゚*(>д<)*゚゚・。" + }, + { + "category": "sadness", + "value": "( ゚,_ゝ`)" + }, + { + "category": "sadness", + "value": "(个_个)" + }, + { + "category": "sadness", + "value": "(╯︵╰,)" + }, + { + "category": "sadness", + "value": "。・゚(゚><゚)゚・。" + }, + { + "category": "sadness", + "value": "( ╥ω╥ )" + }, + { + "category": "sadness", + "value": "(╯_╰)" + }, + { + "category": "sadness", + "value": "(╥_╥)" + }, + { + "category": "sadness", + "value": ".。・゚゚・(>_<)・゚゚・。." + }, + { + "category": "sadness", + "value": "(/ˍ・、)" + }, + { + "category": "sadness", + "value": "(ノ_<、)" + }, + { + "category": "sadness", + "value": "(╥﹏╥)" + }, + { + "category": "sadness", + "value": "。゚(。ノωヽ。)゚。" + }, + { + "category": "sadness", + "value": "(つω`。)" + }, + { + "category": "sadness", + "value": "(。T ω T。)" + }, + { + "category": "sadness", + "value": "(ノω・、)" + }, + { + "category": "sadness", + "value": "・゚・(。>ω<。)・゚・" + }, + { + "category": "sadness", + "value": "(T_T)" + }, + { + "category": "sadness", + "value": "(>_<)" + }, + { + "category": "sadness", + "value": "(っ˘̩╭╮˘̩)っ" + }, + { + "category": "sadness", + "value": "。゚・ (>﹏<) ・゚。" + }, + { + "category": "sadness", + "value": "o(〒﹏〒)o" + }, + { + "category": "sadness", + "value": "(。•́︿•̀。)" + }, + { + "category": "sadness", + "value": "(ಥ﹏ಥ)" + }, + { + "category": "sadness", + "value": "(ಡ‸ಡ)" + }, + { + "category": "pain", + "value": "~(>_<~)" + }, + { + "category": "pain", + "value": "☆⌒(> _ <)" + }, + { + "category": "pain", + "value": "☆⌒(>。<)" + }, + { + "category": "pain", + "value": "(☆_@)" + }, + { + "category": "pain", + "value": "(×_×)" + }, + { + "category": "pain", + "value": "(x_x)" + }, + { + "category": "pain", + "value": "(×_×)⌒☆" + }, + { + "category": "pain", + "value": "(x_x)⌒☆" + }, + { + "category": "pain", + "value": "(×﹏×)" + }, + { + "category": "pain", + "value": "☆(#××)" + }, + { + "category": "pain", + "value": "(+_+)" + }, + { + "category": "pain", + "value": "[ ± _ ± ]" + }, + { + "category": "pain", + "value": "٩(× ×)۶" + }, + { + "category": "pain", + "value": "_:(´ཀ`」 ∠):_" + }, + { + "category": "pain", + "value": "(メ﹏メ)" + }, + { + "category": "fear", + "value": "(ノωヽ)" + }, + { + "category": "fear", + "value": "(/。\)" + }, + { + "category": "fear", + "value": "(ノ_ヽ)" + }, + { + "category": "fear", + "value": "..・ヾ(。><)シ" + }, + { + "category": "fear", + "value": "(″ロ゛)" + }, + { + "category": "fear", + "value": "(;;;*_*)" + }, + { + "category": "fear", + "value": "(・人・)" + }, + { + "category": "fear", + "value": "\(〇_o)/" + }, + { + "category": "fear", + "value": "(/ω\)" + }, + { + "category": "fear", + "value": "(/_\)" + }, + { + "category": "fear", + "value": "〜(><)〜" + }, + { + "category": "fear", + "value": "Σ(°△°|||)︴" + }, + { + "category": "fear", + "value": "(((><)))" + }, + { + "category": "fear", + "value": "{{ (>_<) }}" + }, + { + "category": "fear", + "value": "\(º □ º l|l)/" + }, + { + "category": "fear", + "value": "〣( ºΔº )〣" + }, + { + "category": "fear", + "value": "▓▒░(°◡°)░▒▓" + }, + { + "category": "indifference", + "value": "ヽ(ー_ー )ノ" + }, + { + "category": "indifference", + "value": "ヽ(´ー` )┌" + }, + { + "category": "indifference", + "value": "┐(‘~` )┌" + }, + { + "category": "indifference", + "value": "ヽ(  ̄д ̄)ノ" + }, + { + "category": "indifference", + "value": "┐( ̄ヘ ̄)┌" + }, + { + "category": "indifference", + "value": "ヽ( ̄~ ̄ )ノ" + }, + { + "category": "indifference", + "value": "╮( ̄_ ̄)╭" + }, + { + "category": "indifference", + "value": "ヽ(ˇヘˇ)ノ" + }, + { + "category": "indifference", + "value": "┐( ̄~ ̄)┌" + }, + { + "category": "indifference", + "value": "┐(︶▽︶)┌" + }, + { + "category": "indifference", + "value": "╮( ̄~ ̄)╭" + }, + { + "category": "indifference", + "value": "¯_(ツ)_/¯" + }, + { + "category": "indifference", + "value": "┐( ´ д ` )┌" + }, + { + "category": "indifference", + "value": "╮(︶︿︶)╭" + }, + { + "category": "indifference", + "value": "┐( ̄∀ ̄)┌" + }, + { + "category": "indifference", + "value": "┐( ˘ 、 ˘ )┌" + }, + { + "category": "indifference", + "value": "╮(︶▽︶)╭" + }, + { + "category": "indifference", + "value": "╮( ˘ 、 ˘ )╭" + }, + { + "category": "indifference", + "value": "┐( ˘_˘ )┌" + }, + { + "category": "indifference", + "value": "╮( ˘_˘ )╭" + }, + { + "category": "indifference", + "value": "┐( ̄ヮ ̄)┌" + }, + { + "category": "indifference", + "value": "ᕕ( ᐛ )ᕗ" + }, + { + "category": "indifference", + "value": "┐(シ)┌" + }, + { + "category": "confusion", + "value": "( ̄ω ̄;)" + }, + { + "category": "confusion", + "value": "σ( ̄、 ̄〃)" + }, + { + "category": "confusion", + "value": "( ̄~ ̄;)" + }, + { + "category": "confusion", + "value": "(-_-;)・・・" + }, + { + "category": "confusion", + "value": "┐('~`;)┌" + }, + { + "category": "confusion", + "value": "(・_・ヾ" + }, + { + "category": "confusion", + "value": "(〃 ̄ω ̄〃ゞ" + }, + { + "category": "confusion", + "value": "┐( ̄ヘ ̄;)┌" + }, + { + "category": "confusion", + "value": "(・_・;)" + }, + { + "category": "confusion", + "value": "( ̄_ ̄)・・・" + }, + { + "category": "confusion", + "value": "╮( ̄ω ̄;)╭" + }, + { + "category": "confusion", + "value": "(¯ . ¯;)" + }, + { + "category": "confusion", + "value": "(@_@)" + }, + { + "category": "confusion", + "value": "(・・;)ゞ" + }, + { + "category": "confusion", + "value": "Σ( ̄。 ̄ノ)" + }, + { + "category": "confusion", + "value": "(・・ ) ?" + }, + { + "category": "confusion", + "value": "(•ิ_•ิ)?" + }, + { + "category": "confusion", + "value": "(◎ ◎)ゞ" + }, + { + "category": "confusion", + "value": "(ーー;)" + }, + { + "category": "confusion", + "value": "ლ(ಠ_ಠ ლ)" + }, + { + "category": "confusion", + "value": "ლ(¯ロ¯\"ლ)" + }, + { + "category": "confusion", + "value": "(¯ . ¯٥)" + }, + { + "category": "confusion", + "value": "(¯ ¯٥)" + }, + { + "category": "doubt", + "value": "(¬_¬)" + }, + { + "category": "doubt", + "value": "(→_→)" + }, + { + "category": "doubt", + "value": "(¬ ¬)" + }, + { + "category": "doubt", + "value": "(¬‿¬ )" + }, + { + "category": "doubt", + "value": "(¬_¬ )" + }, + { + "category": "doubt", + "value": "(←_←)" + }, + { + "category": "doubt", + "value": "(¬ ¬ )" + }, + { + "category": "doubt", + "value": "(¬‿¬ )" + }, + { + "category": "doubt", + "value": "(↼_↼)" + }, + { + "category": "doubt", + "value": "(⇀_⇀)" + }, + { + "category": "doubt", + "value": "(ᓀ ᓀ)" + }, + { + "category": "surprise", + "value": "w(°o°)w" + }, + { + "category": "surprise", + "value": "ヽ(°〇°)ノ" + }, + { + "category": "surprise", + "value": "Σ(O_O)" + }, + { + "category": "surprise", + "value": "Σ(°ロ°)" + }, + { + "category": "surprise", + "value": "(⊙_⊙)" + }, + { + "category": "surprise", + "value": "(o_O)" + }, + { + "category": "surprise", + "value": "(O_O;)" + }, + { + "category": "surprise", + "value": "(O.O)" + }, + { + "category": "surprise", + "value": "(°ロ°) !" + }, + { + "category": "surprise", + "value": "(o_O) !" + }, + { + "category": "surprise", + "value": "(□_□)" + }, + { + "category": "surprise", + "value": "Σ(□_□)" + }, + { + "category": "surprise", + "value": "∑(O_O;)" + }, + { + "category": "surprise", + "value": "( : ౦ ‸ ౦ : )" + }, + { + "category": "greeting", + "value": "(*・ω・)ノ" + }, + { + "category": "greeting", + "value": "( ̄▽ ̄)ノ" + }, + { + "category": "greeting", + "value": "(°▽°)/" + }, + { + "category": "greeting", + "value": "( ´ ∀ ` )ノ" + }, + { + "category": "greeting", + "value": "(^-^*)/" + }, + { + "category": "greeting", + "value": "(@´ー`)ノ゙" + }, + { + "category": "greeting", + "value": "(´• ω •`)ノ" + }, + { + "category": "greeting", + "value": "( ° ∀ ° )ノ゙" + }, + { + "category": "greeting", + "value": "ヾ(*'▽'*)" + }, + { + "category": "greeting", + "value": "\(⌒▽⌒)" + }, + { + "category": "greeting", + "value": "ヾ(☆▽☆)" + }, + { + "category": "greeting", + "value": "( ´ ▽ ` )ノ" + }, + { + "category": "greeting", + "value": "(^0^)ノ" + }, + { + "category": "greeting", + "value": "~ヾ(・ω・)" + }, + { + "category": "greeting", + "value": "(・∀・)ノ" + }, + { + "category": "greeting", + "value": "ヾ(・ω・*)" + }, + { + "category": "greeting", + "value": "(*°ー°)ノ" + }, + { + "category": "greeting", + "value": "(・_・)ノ" + }, + { + "category": "greeting", + "value": "(o´ω`o)ノ" + }, + { + "category": "greeting", + "value": "( ´ ▽ ` )/" + }, + { + "category": "greeting", + "value": "( ̄ω ̄)/" + }, + { + "category": "greeting", + "value": "( ´ ω ` )ノ゙" + }, + { + "category": "greeting", + "value": "(⌒ω⌒)ノ" + }, + { + "category": "greeting", + "value": "(o^ ^o)/" + }, + { + "category": "greeting", + "value": "(≧▽≦)/" + }, + { + "category": "greeting", + "value": "(✧∀✧)/" + }, + { + "category": "greeting", + "value": "(o´▽`o)ノ" + }, + { + "category": "greeting", + "value": "( ̄▽ ̄)/" + }, + { + "category": "hugging", + "value": "(づ ̄ ³ ̄)づ" + }, + { + "category": "hugging", + "value": "(つ≧▽≦)つ" + }, + { + "category": "hugging", + "value": "(つ✧ω✧)つ" + }, + { + "category": "hugging", + "value": "(づ ◕‿◕ )づ" + }, + { + "category": "hugging", + "value": "(⊃。•́‿•̀。)⊃" + }, + { + "category": "hugging", + "value": "(つ . •́ _ʖ •̀ .)つ" + }, + { + "category": "hugging", + "value": "(っಠ‿ಠ)っ" + }, + { + "category": "hugging", + "value": "(づ◡﹏◡)づ" + }, + { + "category": "hugging", + "value": "⊂(´• ω •`⊂)" + }, + { + "category": "hugging", + "value": "⊂(・ω・*⊂)" + }, + { + "category": "hugging", + "value": "⊂( ̄▽ ̄)⊃" + }, + { + "category": "hugging", + "value": "⊂( ´ ▽ ` )⊃" + }, + { + "category": "hugging", + "value": "( ~*-*)~" + }, + { + "category": "hugging", + "value": "(。•̀ᴗ-)✧" + }, + { + "category": "winking", + "value": "(^_~)" + }, + { + "category": "winking", + "value": "( ゚o⌒)" + }, + { + "category": "winking", + "value": "(^_-)≡☆" + }, + { + "category": "winking", + "value": "(^ω~)" + }, + { + "category": "winking", + "value": "(>ω^)" + }, + { + "category": "winking", + "value": "(~人^)" + }, + { + "category": "winking", + "value": "(^_-)" + }, + { + "category": "winking", + "value": "( -_・)" + }, + { + "category": "winking", + "value": "(^_<)〜☆" + }, + { + "category": "winking", + "value": "(^人<)〜☆" + }, + { + "category": "winking", + "value": "☆⌒(≧▽​° )" + }, + { + "category": "winking", + "value": "☆⌒(ゝ。∂)" + }, + { + "category": "winking", + "value": "(^_<)" + }, + { + "category": "winking", + "value": "(^_−)☆" + }, + { + "category": "winking", + "value": "(・ω<)☆" + }, + { + "category": "winking", + "value": "(^.~)☆" + }, + { + "category": "winking", + "value": "(^.~)" + }, + { + "category": "apologizing", + "value": "m(_ _)m" + }, + { + "category": "apologizing", + "value": "(シ_ _)シ" + }, + { + "category": "apologizing", + "value": "m(. .)m" + }, + { + "category": "apologizing", + "value": "<(_ _)>" + }, + { + "category": "apologizing", + "value": "人(_ _*)" + }, + { + "category": "apologizing", + "value": "(*_ _)人" + }, + { + "category": "apologizing", + "value": "m(_ _;m)" + }, + { + "category": "apologizing", + "value": "(m;_ _)m" + }, + { + "category": "apologizing", + "value": "(シ. .)シ" + }, + { + "category": "nosebleeding", + "value": "(* ̄ii ̄)" + }, + { + "category": "nosebleeding", + "value": "( ̄ハ ̄*)" + }, + { + "category": "nosebleeding", + "value": "( ̄ハ ̄)" + }, + { + "category": "nosebleeding", + "value": "(^་།^)" + }, + { + "category": "nosebleeding", + "value": "(^〃^)" + }, + { + "category": "nosebleeding", + "value": "( ̄ ¨ヽ ̄)" + }, + { + "category": "nosebleeding", + "value": "( ̄ ; ̄)" + }, + { + "category": "nosebleeding", + "value": "( ̄ ;; ̄)" + }, + { + "category": "hiding", + "value": "|・ω・)" + }, + { + "category": "hiding", + "value": "ヘ(・_|" + }, + { + "category": "hiding", + "value": "|ω・)ノ" + }, + { + "category": "hiding", + "value": "ヾ(・|" + }, + { + "category": "hiding", + "value": "|д・)" + }, + { + "category": "hiding", + "value": "|_ ̄))" + }, + { + "category": "hiding", + "value": "|▽//)" + }, + { + "category": "hiding", + "value": "┬┴┬┴┤(・_├┬┴┬┴" + }, + { + "category": "hiding", + "value": "┬┴┬┴┤・ω・)ノ" + }, + { + "category": "hiding", + "value": "┬┴┬┴┤( ͡° ͜ʖ├┬┴┬┴" + }, + { + "category": "hiding", + "value": "┬┴┬┴┤(・_├┬┴┬┴" + }, + { + "category": "hiding", + "value": "|_・)" + }, + { + "category": "hiding", + "value": "|・д・)ノ" + }, + { + "category": "hiding", + "value": "|ʘ‿ʘ)╯" + }, + { + "category": "writing", + "value": "__φ(..)" + }, + { + "category": "writing", + "value": "(  ̄ー ̄)φ__" + }, + { + "category": "writing", + "value": "__φ(。。)" + }, + { + "category": "writing", + "value": "__φ(..;)" + }, + { + "category": "writing", + "value": "ヾ( `ー´)シφ__" + }, + { + "category": "writing", + "value": "__〆( ̄ー ̄ )" + }, + { + "category": "writing", + "value": "....φ(・∀・*)" + }, + { + "category": "writing", + "value": "___〆(・∀・)" + }, + { + "category": "writing", + "value": "( ^▽^)ψ__" + }, + { + "category": "writing", + "value": "....φ(︶▽︶)φ...." + }, + { + "category": "writing", + "value": "( . .)φ__" + }, + { + "category": "writing", + "value": "__φ(◎◎ヘ)" + }, + { + "category": "running", + "value": "☆ミ(o*・ω・)ノ" + }, + { + "category": "running", + "value": "C= C= C= C= C=┌(;・ω・)┘" + }, + { + "category": "running", + "value": "─=≡Σ((( つ><)つ" + }, + { + "category": "running", + "value": "ε=ε=ε=ε=┌(; ̄▽ ̄)┘" + }, + { + "category": "running", + "value": "ε=ε=┌( >_<)┘" + }, + { + "category": "running", + "value": "C= C= C= C=┌( `ー´)┘" + }, + { + "category": "running", + "value": "ε===(っ≧ω≦)っ" + }, + { + "category": "running", + "value": "ヽ( ̄д ̄;)ノ=3=3=3" + }, + { + "category": "running", + "value": "。。。ミヽ(。><)ノ" + }, + { + "category": "sleeping", + "value": "[(--)]..zzZ" + }, + { + "category": "sleeping", + "value": "(-_-) zzZ" + }, + { + "category": "sleeping", + "value": "(∪。∪)。。。zzZ" + }, + { + "category": "sleeping", + "value": "(-ω-) zzZ" + }, + { + "category": "sleeping", + "value": "( ̄o ̄) zzZZzzZZ" + }, + { + "category": "sleeping", + "value": "(( _ _ ))..zzzZZ" + }, + { + "category": "sleeping", + "value": "( ̄ρ ̄)..zzZZ" + }, + { + "category": "sleeping", + "value": "(-.-)...zzz" + }, + { + "category": "sleeping", + "value": "(_ _*) Z z z" + }, + { + "category": "cat", + "value": "(=^・ω・^=)" + }, + { + "category": "cat", + "value": "(=^・ェ・^=)" + }, + { + "category": "cat", + "value": "(=①ω①=)" + }, + { + "category": "cat", + "value": "( =ω=)..nyaa" + }, + { + "category": "cat", + "value": "(= ; ェ ; =)" + }, + { + "category": "cat", + "value": "(=`ω´=)" + }, + { + "category": "cat", + "value": "(=^‥^=)" + }, + { + "category": "cat", + "value": "( =ノωヽ=)" + }, + { + "category": "cat", + "value": "(=⌒‿‿⌒=)" + }, + { + "category": "cat", + "value": "(=^ ◡ ^=)" + }, + { + "category": "cat", + "value": "(=^-ω-^=)" + }, + { + "category": "cat", + "value": "ヾ(=`ω´=)ノ”" + }, + { + "category": "cat", + "value": "(^• ω •^)" + }, + { + "category": "cat", + "value": "(/ =ω=)/" + }, + { + "category": "cat", + "value": "ฅ(•ㅅ•❀)ฅ" + }, + { + "category": "cat", + "value": "ฅ(• ɪ •)ฅ" + }, + { + "category": "cat", + "value": "ଲ(ⓛ ω ⓛ)ଲ" + }, + { + "category": "cat", + "value": "(^=◕ᴥ◕=^)" + }, + { + "category": "cat", + "value": "( =ω= )" + }, + { + "category": "cat", + "value": "(^˵◕ω◕˵^)" + }, + { + "category": "cat", + "value": "(^◔ᴥ◔^)" + }, + { + "category": "cat", + "value": "(^◕ᴥ◕^)" + }, + { + "category": "cat", + "value": "ต(=ω=)ต" + }, + { + "category": "cat", + "value": "( Φ ω Φ )" + }, + { + "category": "cat", + "value": "ฅ(^◕ᴥ◕^)ฅ" + }, + { + "category": "bear", + "value": "( ´(エ)ˋ )" + }, + { + "category": "bear", + "value": "(* ̄(エ) ̄*)" + }, + { + "category": "bear", + "value": "ヽ( ̄(エ) ̄)ノ" + }, + { + "category": "bear", + "value": "(/ ̄(エ) ̄)/" + }, + { + "category": "bear", + "value": "( ̄(エ) ̄)" + }, + { + "category": "bear", + "value": "ヽ( ˋ(エ)´ )ノ" + }, + { + "category": "bear", + "value": "⊂( ̄(エ) ̄)⊃" + }, + { + "category": "bear", + "value": "(/(エ)\)" + }, + { + "category": "bear", + "value": "⊂(´(ェ)ˋ)⊃" + }, + { + "category": "bear", + "value": "(/-(エ)-\)" + }, + { + "category": "bear", + "value": "(/°(エ)°)/" + }, + { + "category": "bear", + "value": "ʕ ᵔᴥᵔ ʔ" + }, + { + "category": "bear", + "value": "ʕ •ᴥ• ʔ" + }, + { + "category": "bear", + "value": "ʕ •̀ ω •́ ʔ" + }, + { + "category": "bear", + "value": "ʕ •̀ o •́ ʔ" + }, + { + "category": "bear", + "value": "ʕಠᴥಠʔ" + }, + { + "category": "dog", + "value": "∪^ェ^∪" + }, + { + "category": "dog", + "value": "∪・ω・∪" + }, + { + "category": "dog", + "value": "∪ ̄- ̄∪" + }, + { + "category": "dog", + "value": "∪・ェ・∪" + }, + { + "category": "dog", + "value": "U^皿^U" + }, + { + "category": "dog", + "value": "UTェTU" + }, + { + "category": "dog", + "value": "U^ェ^U" + }, + { + "category": "dog", + "value": "V●ᴥ●V" + }, + { + "category": "dog", + "value": "U・ᴥ・U" + }, + { + "category": "rabbit", + "value": "/(≧ x ≦)\" + }, + { + "category": "rabbit", + "value": "/(・ × ・)\" + }, + { + "category": "rabbit", + "value": "/(=´x`=)\" + }, + { + "category": "rabbit", + "value": "/(^ x ^)\" + }, + { + "category": "rabbit", + "value": "/(=・ x ・=)\" + }, + { + "category": "rabbit", + "value": "/(^ × ^)\" + }, + { + "category": "rabbit", + "value": "/(>×<)\" + }, + { + "category": "rabbit", + "value": "/(˃ᆺ˂)\" + }, + { + "category": "pig", + "value": "( ´(00)ˋ )" + }, + { + "category": "pig", + "value": "( ̄(ω) ̄)" + }, + { + "category": "pig", + "value": "ヽ( ˋ(00)´ )ノ" + }, + { + "category": "pig", + "value": "( ´(oo)ˋ )" + }, + { + "category": "pig", + "value": "\( ̄(oo) ̄)/" + }, + { + "category": "pig", + "value": "。゚(゚´(00)`゚)゚。" + }, + { + "category": "pig", + "value": "( ̄(00) ̄)" + }, + { + "category": "pig", + "value": "(ˆ(oo)ˆ)" + }, + { + "category": "bird", + "value": "( ̄Θ ̄)" + }, + { + "category": "bird", + "value": "(`・Θ・´)" + }, + { + "category": "bird", + "value": "( ˋ Θ ´ )" + }, + { + "category": "bird", + "value": "(◉Θ◉)" + }, + { + "category": "bird", + "value": "\( ˋ Θ ´ )/" + }, + { + "category": "bird", + "value": "(・θ・)" + }, + { + "category": "bird", + "value": "(・Θ・)" + }, + { + "category": "bird", + "value": "ヾ( ̄◇ ̄)ノ〃" + }, + { + "category": "bird", + "value": "(・Θ・)" + }, + { + "category": "fish", + "value": "(°)#))<<" + }, + { + "category": "fish", + "value": "<・ )))><<" + }, + { + "category": "fish", + "value": "ζ°)))彡" + }, + { + "category": "fish", + "value": ">°))))彡" + }, + { + "category": "fish", + "value": "(°))<<" + }, + { + "category": "fish", + "value": ">^)))<~~" + }, + { + "category": "fish", + "value": "≧( ° ° )≦" + }, + { + "category": "spider", + "value": "/╲/╭(ఠఠ益ఠఠ)╮/╱" + }, + { + "category": "spider", + "value": "/╲/╭(ರರ⌓ರರ)╮/╱" + }, + { + "category": "spider", + "value": "/╲/╭༼ ººل͟ºº ༽╮/╱" + }, + { + "category": "spider", + "value": "/╲/╭( ͡°͡° ͜ʖ ͡°͡°)╮/╱" + }, + { + "category": "spider", + "value": "/╲/╭[ ᴼᴼ ౪ ᴼᴼ]╮/╱" + }, + { + "category": "spider", + "value": "/╲/( •̀ ω •́ )/╱" + }, + { + "category": "spider", + "value": "/╲/╭[☉﹏☉]╮/╱" + }, + { + "category": "friends", + "value": "ヾ(・ω・)メ(・ω・)ノ" + }, + { + "category": "friends", + "value": "ヽ(∀° )人( °∀)ノ" + }, + { + "category": "friends", + "value": "ヽ( ⌒o⌒)人(⌒-⌒ )ノ" + }, + { + "category": "friends", + "value": "(*^ω^)八(⌒▽⌒)八(-‿‿- )ヽ" + }, + { + "category": "friends", + "value": "\(^∀^)メ(^∀^)ノ" + }, + { + "category": "friends", + "value": "ヾ( ̄ー ̄(≧ω≦*)ゝ" + }, + { + "category": "friends", + "value": "ヽ( ⌒ω⌒)人(=^‥^= )ノ" + }, + { + "category": "friends", + "value": "ヽ(≧◡≦)八(o^ ^o)ノ" + }, + { + "category": "friends", + "value": "(*・∀・)爻(・∀・*)" + }, + { + "category": "friends", + "value": "。*:☆(・ω・人・ω・)。:゜☆。" + }, + { + "category": "friends", + "value": "o(^^o)(o^^o)(o^^o)(o^^)o" + }, + { + "category": "friends", + "value": "((( ̄( ̄( ̄▽ ̄) ̄) ̄)))" + }, + { + "category": "friends", + "value": "(°(°ω(°ω°(☆ω☆)°ω°)ω°)°)" + }, + { + "category": "friends", + "value": "ヾ(・ω・`)ノヾ(´・ω・)ノ゛" + }, + { + "category": "friends", + "value": "Ψ( `∀)(∀´ )Ψ" + }, + { + "category": "friends", + "value": "(っ˘▽˘)(˘▽˘)˘▽˘ς)" + }, + { + "category": "friends", + "value": "(((*°▽°*)八(*°▽°*)))" + }, + { + "category": "friends", + "value": "☆ヾ(*´・∀・)ノヾ(・∀・`*)ノ☆" + }, + { + "category": "friends", + "value": "(*^ω^)人(^ω^*)" + }, + { + "category": "friends", + "value": "٩(๑・ิᴗ・ิ)۶٩(・ิᴗ・ิ๑)۶" + }, + { + "category": "friends", + "value": "(☞°ヮ°)☞ ☜(°ヮ°☜)" + }, + { + "category": "friends", + "value": "\(▽ ̄ ( ̄▽ ̄) /  ̄▽)/" + }, + { + "category": "friends", + "value": "( ˙▿˙ )/( ˙▿˙ )/" + }, + { + "category": "enemies", + "value": "ヽ( ・∀・)ノ_θ彡☆Σ(ノ `Д´)ノ" + }, + { + "category": "enemies", + "value": "(*´∇`)┌θ☆(ノ>_<)ノ" + }, + { + "category": "enemies", + "value": "(  ̄ω ̄)ノ゙⌒☆ミ(o _ _)o" + }, + { + "category": "enemies", + "value": "(*`0´)θ☆(メ°皿°)ノ" + }, + { + "category": "enemies", + "value": "(o¬‿¬o )...☆ミ(*x_x)" + }, + { + "category": "enemies", + "value": "(╬ ̄皿 ̄)=○#( ̄#)3 ̄)" + }, + { + "category": "enemies", + "value": "(; -_-)――――――C<―_-)" + }, + { + "category": "enemies", + "value": "<(  ̄︿ ̄)︵θ︵θ︵☆(>口<-)" + }, + { + "category": "enemies", + "value": "( ̄ε(# ̄)☆╰╮o( ̄▽ ̄///)" + }, + { + "category": "enemies", + "value": "ヽ(>_<ヽ) ―⊂|=0ヘ(^‿^ )" + }, + { + "category": "enemies", + "value": "ヘ(>_<ヘ) ¬o( ̄‿ ̄メ)" + }, + { + "category": "enemies", + "value": ",,(((  ̄□)_/ \_(○ ̄ ))),," + }, + { + "category": "enemies", + "value": "(҂` ロ ´)︻デ═一 \(º □ º l|l)/" + }, + { + "category": "enemies", + "value": "(╯°Д°)╯︵ /(.□ . \)" + }, + { + "category": "enemies", + "value": "(¬_¬'')ԅ( ̄ε ̄ԅ)" + }, + { + "category": "enemies", + "value": "/( .□.)\ ︵╰(°益°)╯︵ /(.□. /)" + }, + { + "category": "enemies", + "value": "(ノ-.-)ノ….((((((((((((●~* ( >_<)" + }, + { + "category": "enemies", + "value": "!!(メ ̄  ̄)_θ☆°0°)/" + }, + { + "category": "enemies", + "value": "(`⌒*)O-(`⌒´Q)" + }, + { + "category": "enemies", + "value": "(((ง’ω’)و三 ง’ω’)ڡ≡ ☆⌒ミ((x_x)" + }, + { + "category": "enemies", + "value": "(งಠ_ಠ)ง σ( •̀ ω •́ σ)" + }, + { + "category": "enemies", + "value": "(っ•﹏•)っ ✴==≡눈٩(`皿´҂)ง" + }, + { + "category": "enemies", + "value": "(「• ω •)「 (⌒ω⌒`)" + }, + { + "category": "enemies", + "value": "( °ᴗ°)~ð (/❛o❛)" + }, + { + "category": "weapons", + "value": "( ・∀・)・・・--------☆" + }, + { + "category": "weapons", + "value": "(/-_・)/D・・・・・------ →" + }, + { + "category": "weapons", + "value": "(^ω^)ノ゙(((((((((●~*" + }, + { + "category": "weapons", + "value": "( -ω-)/占~~~~~" + }, + { + "category": "weapons", + "value": "(/・・)ノ   (( く ((へ" + }, + { + "category": "weapons", + "value": "―⊂|=0ヘ(^^ )" + }, + { + "category": "weapons", + "value": "○∞∞∞∞ヽ(^ー^ )" + }, + { + "category": "weapons", + "value": "(; ・_・)――――C" + }, + { + "category": "weapons", + "value": "(ಠ o ಠ)¤=[]:::::>" + }, + { + "category": "weapons", + "value": "(*^^)/~~~~~~~~~~◎" + }, + { + "category": "weapons", + "value": "¬o( ̄- ̄メ)" + }, + { + "category": "weapons", + "value": "―(T_T)→" + }, + { + "category": "weapons", + "value": "(((  ̄□)_/" + }, + { + "category": "weapons", + "value": "(メ` ロ ´)︻デ═一" + }, + { + "category": "weapons", + "value": "( ´-ω・)︻┻┳══━一" + }, + { + "category": "weapons", + "value": "(メ ̄▽ ̄)︻┳═一" + }, + { + "category": "weapons", + "value": "✴==≡눈٩(`皿´҂)ง" + }, + { + "category": "weapons", + "value": "Q(`⌒´Q)" + }, + { + "category": "magic", + "value": "(ノ ˘_˘)ノ ζ|||ζ ζ|||ζ ζ|||ζ" + }, + { + "category": "magic", + "value": "(ノ≧∀≦)ノ ‥…━━━★" + }, + { + "category": "magic", + "value": "(ノ>ω<)ノ :。・:*:・゚’★,。・:*:・゚’☆" + }, + { + "category": "magic", + "value": "(ノ°∀°)ノ⌒・*:.。. .。.:*・゜゚・*☆" + }, + { + "category": "magic", + "value": "╰( ͡° ͜ʖ ͡° )つ──☆*:・゚" + }, + { + "category": "magic", + "value": "(# ̄□ ̄)o━∈・・━━━━☆" + }, + { + "category": "magic", + "value": "(⊃。•́‿•̀。)⊃━✿✿✿✿✿✿" + }, + { + "category": "magic", + "value": "(∩ᄑ_ᄑ)⊃━☆゚*・。*・:≡( ε:)" + }, + { + "category": "magic", + "value": "(/ ̄ー ̄)/~~☆’.・.・:★’.・.・:☆" + }, + { + "category": "magic", + "value": "(∩` ロ ´)⊃━炎炎炎炎炎" + }, + { + "category": "food", + "value": "(っ˘ڡ˘ς)" + }, + { + "category": "food", + "value": "( o˘◡˘o) ┌iii┐" + }, + { + "category": "food", + "value": "( ’ω’)旦~~" + }, + { + "category": "food", + "value": "( ˘▽˘)っ♨" + }, + { + "category": "food", + "value": "♨o(>_<)o♨" + }, + { + "category": "food", + "value": "( ・ω・)o-{{[〃]}}" + }, + { + "category": "food", + "value": "( ・ω・)⊃-[二二]" + }, + { + "category": "food", + "value": "( ・・)つ―{}@{}@{}-" + }, + { + "category": "food", + "value": "( ・・)つ-●●●" + }, + { + "category": "food", + "value": "(*´ー`)旦 旦( ̄ω ̄*)" + }, + { + "category": "food", + "value": "(*´з`)口゚。゚口(・∀・ )" + }, + { + "category": "food", + "value": "( o^ ^o)且 且(´ω`*)" + }, + { + "category": "food", + "value": "(  ̄▽ ̄)[] [](≧▽≦ )" + }, + { + "category": "food", + "value": "( *^^)o∀*∀o(^^* )" + }, + { + "category": "food", + "value": "( ^^)_旦~~  ~~U_(^^ )" + }, + { + "category": "food", + "value": "(* ̄▽ ̄)旦 且(´∀`*)" + }, + { + "category": "food", + "value": "-●●●-c(・・ )" + }, + { + "category": "food", + "value": "( ・・)つ―●○◎-" + }, + { + "category": "music", + "value": "ヾ(´〇`)ノ♪♪♪" + }, + { + "category": "music", + "value": "ヘ( ̄ω ̄ヘ)" + }, + { + "category": "music", + "value": "(〜 ̄▽ ̄)〜" + }, + { + "category": "music", + "value": "〜( ̄▽ ̄〜)" + }, + { + "category": "music", + "value": "ヽ(o´∀`)ノ♪♬" + }, + { + "category": "music", + "value": "(ノ≧∀≦)ノ" + }, + { + "category": "music", + "value": "♪ヽ(^^ヽ)♪" + }, + { + "category": "music", + "value": "♪(/_ _ )/♪" + }, + { + "category": "music", + "value": "♪♬((d⌒ω⌒b))♬♪" + }, + { + "category": "music", + "value": "└( ̄- ̄└))" + }, + { + "category": "music", + "value": "((┘ ̄ω ̄)┘" + }, + { + "category": "music", + "value": "√( ̄‥ ̄√)" + }, + { + "category": "music", + "value": "└(^^)┐" + }, + { + "category": "music", + "value": "┌(^^)┘" + }, + { + "category": "music", + "value": "\( ̄▽ ̄)\" + }, + { + "category": "music", + "value": "/( ̄▽ ̄)/" + }, + { + "category": "music", + "value": "( ̄▽ ̄)/♫•*¨*•.¸¸♪" + }, + { + "category": "music", + "value": "(^_^♪)" + }, + { + "category": "music", + "value": "(~˘▽˘)~" + }, + { + "category": "music", + "value": "~(˘▽˘~)" + }, + { + "category": "music", + "value": "ヾ(⌐■_■)ノ♪" + }, + { + "category": "music", + "value": "(〜 ̄△ ̄)〜" + }, + { + "category": "music", + "value": "(~‾▽‾)~" + }, + { + "category": "music", + "value": "~(˘▽˘)~" + }, + { + "category": "music", + "value": "乁( • ω •乁)" + }, + { + "category": "music", + "value": "(「• ω •)「" + }, + { + "category": "music", + "value": "⁽⁽◝( • ω • )◜⁾⁾" + }, + { + "category": "music", + "value": "✺◟( • ω • )◞✺" + }, + { + "category": "music", + "value": "♬♫♪◖(● o ●)◗♪♫♬" + }, + { + "category": "music", + "value": "( ˘ ɜ˘) ♬♪♫" + }, + { + "category": "music", + "value": "♪♪♪ ヽ(ˇ∀ˇ )ゞ" + }, + { + "category": "music", + "value": "(ˇ▽ˇ)ノ♪♬♫" + }, + { + "category": "games", + "value": "( ^^)p_____|_o____q(^^ )" + }, + { + "category": "games", + "value": "(/o^)/ °⊥ \(^o\)" + }, + { + "category": "games", + "value": "!(;゚o゚)o/ ̄ ̄ ̄ ̄ ̄ ̄ ̄~ >゚))))彡" + }, + { + "category": "games", + "value": "ヽ(^o^)ρ┳┻┳°σ(^o^)ノ" + }, + { + "category": "games", + "value": "(/_^)/  ● \(^_\)" + }, + { + "category": "games", + "value": "( (≡|≡))_/ \_((≡|≡) )" + }, + { + "category": "games", + "value": "( ノ-_-)ノ゙_□ VS □_ヾ(^-^ヽ)" + }, + { + "category": "games", + "value": "ヽ(;^ ^)ノ゙ ......___〇" + }, + { + "category": "games", + "value": "(=O*_*)=O Q(*_*Q)" + }, + { + "category": "games", + "value": "Ю ○三 \( ̄^ ̄\)" + }, + { + "category": "faces", + "value": "( ͡° ͜ʖ ͡°)" + }, + { + "category": "faces", + "value": "( ͡° ʖ̯ ͡°)" + }, + { + "category": "faces", + "value": "( ͠° ͟ʖ ͡°)" + }, + { + "category": "faces", + "value": "( ͡ᵔ ͜ʖ ͡ᵔ)" + }, + { + "category": "faces", + "value": "( . •́ _ʖ •̀ .)" + }, + { + "category": "faces", + "value": "( ఠ ͟ʖ ఠ)" + }, + { + "category": "faces", + "value": "( ͡ಠ ʖ̯ ͡ಠ)" + }, + { + "category": "faces", + "value": "( ಠ ʖ̯ ಠ)" + }, + { + "category": "faces", + "value": "( ಠ ͜ʖ ಠ)" + }, + { + "category": "faces", + "value": "( ಥ ʖ̯ ಥ)" + }, + { + "category": "faces", + "value": "( ͡• ͜ʖ ͡• )" + }, + { + "category": "faces", + "value": "( ・ิ ͜ʖ ・ิ)" + }, + { + "category": "faces", + "value": "( ͡ ͜ʖ ͡ )" + }, + { + "category": "faces", + "value": "(≖ ͜ʖ≖)" + }, + { + "category": "faces", + "value": "(ʘ ʖ̯ ʘ)" + }, + { + "category": "faces", + "value": "(ʘ ͟ʖ ʘ)" + }, + { + "category": "faces", + "value": "(ʘ ͜ʖ ʘ)" + }, + { + "category": "faces", + "value": "(;´༎ຶٹ༎ຶ`)" + }, + { + "category": "special", + "value": "٩(ˊ〇ˋ*)و" + }, + { + "category": "special", + "value": "( ̄^ ̄)ゞ" + }, + { + "category": "special", + "value": "(-‸ლ)" + }, + { + "category": "special", + "value": "(╯°益°)╯彡┻━┻" + }, + { + "category": "special", + "value": "(╮°-°)╮┳━━┳ ( ╯°□°)╯ ┻━━┻" + }, + { + "category": "special", + "value": "┬─┬ノ( º _ ºノ)" + }, + { + "category": "special", + "value": "(oT-T)尸" + }, + { + "category": "special", + "value": "( ͡° ͜ʖ ͡°)" + }, + { + "category": "special", + "value": "[̲̅$̲̅(̲̅ ͡° ͜ʖ ͡°̲̅)̲̅$̲̅]" + }, + { + "category": "special", + "value": "(ಠ_ಠ)" + }, + { + "category": "special", + "value": "౦0o 。 (‾́。‾́ )y~~" + }, + { + "category": "special", + "value": "( ̄﹃ ̄)" + }, + { + "category": "special", + "value": "(x(x_(x_x(O_o)x_x)_x)x)" + }, + { + "category": "special", + "value": "( ・ω・)☞" + }, + { + "category": "special", + "value": "(⌐■_■)" + }, + { + "category": "special", + "value": "(◕‿◕✿)" + }, + { + "category": "special", + "value": "(  ̄.)o-  【 TV 】" + }, + { + "category": "special", + "value": "`、ヽ`ヽ`、ヽ(ノ><)ノ `、ヽ`☂ヽ`、ヽ" + }, + { + "category": "special", + "value": "‿︵‿︵‿︵‿ヽ(°□° )ノ︵‿︵‿︵‿︵" + }, + { + "category": "special", + "value": "( • )( • )ԅ(≖‿≖ԅ)" + }, + { + "category": "special", + "value": "( ^▽^)っ✂╰⋃╯" + }, + { + "category": "special", + "value": "〜〜(/ ̄▽)/ 〜ф" + }, + { + "category": "special", + "value": "ଘ(੭ˊᵕˋ)੭* ੈ✩‧₊˚" + }, + { + "category": "special", + "value": "ଘ(੭ˊ꒳​ˋ)੭✧" + }, + { + "category": "special", + "value": "_(:3 」∠)_" + }, + { + "category": "special", + "value": "∠( ᐛ 」∠)_" + } + ] +} From 9645c444f65fe3e1d48daa8345b94bb95c804be1 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Tue, 24 Mar 2026 19:10:21 +0530 Subject: [PATCH 02/18] feat: Kaomojis in keyboard --- .../essentials/ui/ime/KaomojiData.kt | 76 ++++++ .../essentials/ui/ime/KaomojiPicker.kt | 249 ++++++++++++++++++ .../essentials/ui/ime/KeyboardInputView.kt | 44 +++- .../drawable/rounded_emoji_language_24.xml | 5 + app/src/main/res/values/strings.xml | 40 +++ 5 files changed, 409 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/com/sameerasw/essentials/ui/ime/KaomojiData.kt create mode 100644 app/src/main/java/com/sameerasw/essentials/ui/ime/KaomojiPicker.kt create mode 100644 app/src/main/res/drawable/rounded_emoji_language_24.xml diff --git a/app/src/main/java/com/sameerasw/essentials/ui/ime/KaomojiData.kt b/app/src/main/java/com/sameerasw/essentials/ui/ime/KaomojiData.kt new file mode 100644 index 000000000..19dbc3848 --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/ui/ime/KaomojiData.kt @@ -0,0 +1,76 @@ +package com.sameerasw.essentials.ui.ime + +import android.content.Context +import android.util.Log +import com.google.gson.Gson +import com.google.gson.annotations.SerializedName +import com.sameerasw.essentials.R +import java.io.InputStreamReader +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.runtime.State +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +data class KaomojiObject( + @SerializedName("category") val category: String, + @SerializedName("value") val value: String +) + +data class KaomojiCategory( + val name: String, + val kaomojis: List +) + +data class KaomojiDataResponse( + @SerializedName("kaomoji") val kaomoji: List +) + +object KaomojiData { + var categories by mutableStateOf>(listOf()) + private var isLoaded = false + + private val _isLoading = mutableStateOf(false) + val isLoading: State = _isLoading + + fun load(context: Context, scope: CoroutineScope) { + if (isLoaded || _isLoading.value) return + _isLoading.value = true + + scope.launch(Dispatchers.IO) { + try { + val inputStream = context.assets.open("kaomoji.json") + val reader = InputStreamReader(inputStream) + val response = Gson().fromJson(reader, KaomojiDataResponse::class.java) + + val grouped = response.kaomoji.groupBy { it.category } + val loadedCategories = grouped.map { (categoryName, list) -> + KaomojiCategory( + name = categoryName, + kaomojis = list + ) + } + + // Sort categories + val finalCategories = loadedCategories.sortedBy { it.name } + + withContext(Dispatchers.Main) { + categories = finalCategories + isLoaded = true + _isLoading.value = false + } + + reader.close() + inputStream.close() + } catch (e: Exception) { + Log.e("KaomojiData", "Error loading kaomojis", e) + withContext(Dispatchers.Main) { + _isLoading.value = false + } + } + } + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/ui/ime/KaomojiPicker.kt b/app/src/main/java/com/sameerasw/essentials/ui/ime/KaomojiPicker.kt new file mode 100644 index 000000000..ed65c6598 --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/ui/ime/KaomojiPicker.kt @@ -0,0 +1,249 @@ +package com.sameerasw.essentials.ui.ime + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsPressedAsState +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.grid.LazyGridState +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.pager.PagerDefaults +import androidx.compose.foundation.pager.VerticalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.foundation.basicMarquee +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.sameerasw.essentials.utils.HapticUtil +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun KaomojiPicker( + modifier: Modifier = Modifier, + keyRoundness: Dp = 24.dp, + isHapticsEnabled: Boolean = true, + hapticStrength: Float = 0.5f, + onKaomojiSelected: (String) -> Unit, + onSwipeDownToExit: () -> Unit = {}, + bottomContentPadding: Dp = 0.dp +) { + val scope = rememberCoroutineScope() + val view = LocalView.current + + if (KaomojiData.categories.isEmpty()) { + Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + CircularProgressIndicator(modifier = Modifier.size(32.dp)) + } + return + } + + val pagerState = rememberPagerState(pageCount = { KaomojiData.categories.size }) + val gridStates = remember { mutableStateMapOf() } + val railScrollState = rememberLazyListState() + + fun performHaptic(strength: Float) { + if (isHapticsEnabled) { + HapticUtil.performCustomHaptic(view, strength) + } + } + + // Sync rail scroll and haptic with pager + LaunchedEffect(pagerState.currentPage) { + if (pagerState.currentPage != -1) { + railScrollState.animateScrollToItem(pagerState.currentPage) + performHaptic(hapticStrength * 0.5f) + } + } + + // Nested Scroll for swipe-down exit gesture + val nestedScrollConnection = remember { + object : NestedScrollConnection { + override fun onPreScroll( + available: Offset, + source: NestedScrollSource + ): Offset { + if (pagerState.currentPage == 0 && available.y > 50f) { + val gridState = gridStates[0] + if (gridState?.firstVisibleItemIndex == 0 && gridState.firstVisibleItemScrollOffset == 0) { + onSwipeDownToExit() + return available + } + } + return Offset.Zero + } + } + } + + Row( + modifier = modifier + .fillMaxSize() + .background(Color.Transparent) + .nestedScroll(nestedScrollConnection) + ) { + // Kaomoji Grid + VerticalPager( + state = pagerState, + modifier = Modifier + .weight(1f) + .fillMaxHeight(), + beyondViewportPageCount = 1, + userScrollEnabled = true, + flingBehavior = PagerDefaults.flingBehavior( + state = pagerState, + snapPositionalThreshold = 0.15f + ) + ) { pageIndex -> + val category = KaomojiData.categories.getOrNull(pageIndex) + if (category != null) { + val gridState = gridStates.getOrPut(pageIndex) { LazyGridState() } + val context = androidx.compose.ui.platform.LocalContext.current + val categoryNameRes = remember(category.name) { + context.resources.getIdentifier("kaomoji_cat_${category.name}", "string", context.packageName) + } + val localizedName = if (categoryNameRes != 0) androidx.compose.ui.res.stringResource(categoryNameRes) else category.name.replaceFirstChar { it.uppercase() } + + Column(modifier = Modifier.fillMaxSize()) { + // Category Header within the page +// Text( +// text = localizedName, +// style = MaterialTheme.typography.labelLarge, +// color = MaterialTheme.colorScheme.primary, +// modifier = Modifier +// .fillMaxWidth() +// .padding(top = 12.dp, bottom = 4.dp, start = 12.dp) +// ) + + LazyVerticalGrid( + state = gridState, + columns = GridCells.Adaptive(minSize = 100.dp), + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 4.dp), + contentPadding = PaddingValues(bottom = bottomContentPadding + 32.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + itemsIndexed( + items = category.kaomojis, + key = { index, it -> "${category.name}_${it.value}_$index" }, + contentType = { _, _ -> "kaomoji" } + ) { index, kaomojiObj -> + val interactionSource = remember { MutableInteractionSource() } + val isPressed by interactionSource.collectIsPressedAsState() + + Box( + modifier = Modifier + .height(48.dp) + .clip(RoundedCornerShape(keyRoundness)) + .background( + if (isPressed) MaterialTheme.colorScheme.surfaceContainerHighest + else MaterialTheme.colorScheme.surfaceContainerLow + ) + .clickable( + interactionSource = interactionSource, + indication = null, + onClick = { + onKaomojiSelected(kaomojiObj.value) + performHaptic(hapticStrength) + } + ) + .padding(horizontal = 8.dp), + contentAlignment = Alignment.Center + ) { + Text( + text = kaomojiObj.value, + fontSize = 16.sp, + maxLines = 1, + textAlign = TextAlign.Center, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurface + ) + } + } + } + } + } + } + + // Vertical Category Rail + LazyColumn( + state = railScrollState, + modifier = Modifier + .width(65.dp) + .fillMaxHeight() + .padding(vertical = 4.dp), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.CenterHorizontally + ) { + items(KaomojiData.categories.size) { index -> + val category = KaomojiData.categories[index] + val isSelected = index == pagerState.currentPage + val interactionSource = remember { MutableInteractionSource() } + + val context = androidx.compose.ui.platform.LocalContext.current + val categoryNameRes = remember(category.name) { + context.resources.getIdentifier("kaomoji_cat_${category.name}", "string", context.packageName) + } + val localizedName = if (categoryNameRes != 0) androidx.compose.ui.res.stringResource(categoryNameRes) else category.name.replaceFirstChar { it.uppercase() } + + Box( + modifier = Modifier + .fillMaxWidth() + .height(44.dp) + .padding(horizontal = 4.dp, vertical = 2.dp) + .clip(RoundedCornerShape(keyRoundness / 2)) + .background( + if (isSelected) MaterialTheme.colorScheme.primary + else MaterialTheme.colorScheme.surfaceContainerHigh + ) + .clickable( + interactionSource = interactionSource, + indication = null, + onClick = { + scope.launch { + gridStates[index]?.scrollToItem(0) + pagerState.animateScrollToPage(index) + } + performHaptic(hapticStrength * 0.8f) + } + ), + contentAlignment = Alignment.Center + ) { + Text( + text = localizedName, + style = MaterialTheme.typography.labelSmall.copy( + fontWeight = if (isSelected) FontWeight.ExtraBold else FontWeight.Medium, + fontSize = 10.sp + ), + color = if (isSelected) MaterialTheme.colorScheme.background + else MaterialTheme.colorScheme.secondary, + maxLines = 1, + modifier = Modifier + .padding(horizontal = 4.dp) + .basicMarquee() + ) + } + } + } + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/ui/ime/KeyboardInputView.kt b/app/src/main/java/com/sameerasw/essentials/ui/ime/KeyboardInputView.kt index f3b3a6b59..2676a42ea 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/ime/KeyboardInputView.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/ime/KeyboardInputView.kt @@ -383,6 +383,7 @@ fun KeyboardInputView( var shiftState by remember { mutableStateOf(ShiftState.OFF) } var isClipboardMode by remember { mutableStateOf(false) } var isEmojiMode by remember { mutableStateOf(false) } + var isKaomojiMode by remember { mutableStateOf(false) } var isSuggestionsCollapsed by remember { mutableStateOf(false) } var currentWord by remember { mutableStateOf("") } @@ -448,7 +449,7 @@ fun KeyboardInputView( // Total Height animation val animatedTotalHeight by animateDpAsState( - targetValue = if (isEmojiMode) keyboardHeight + 120.dp else keyboardHeight, + targetValue = if (isEmojiMode || isKaomojiMode) keyboardHeight + 120.dp else keyboardHeight, animationSpec = spring( dampingRatio = Spring.DampingRatioLowBouncy, stiffness = Spring.StiffnessMedium @@ -462,15 +463,17 @@ fun KeyboardInputView( label = "blur" ) - // Pre-load Emoji data on startup (Background thread) + // Pre-load Emoji & Kaomoji data on startup (Background thread) LaunchedEffect(Unit) { EmojiData.load(view.context, scope) + KaomojiData.load(view.context, scope) } LaunchedEffect(onOpened) { if (onOpened > 0) { isSymbols = false isEmojiMode = false + isKaomojiMode = false isClipboardMode = false isSuggestionsCollapsed = false shiftState = ShiftState.OFF @@ -786,7 +789,10 @@ fun KeyboardInputView( onUndoClick() } else if (desc == "Emoji") { isEmojiMode = !isEmojiMode - if (isEmojiMode) isClipboardMode = false + if (isEmojiMode) { + isClipboardMode = false + isKaomojiMode = false + } } else if (desc == "Backspace") { onKeyPress(android.view.KeyEvent.KEYCODE_DEL) } else if (desc == "Expand") { @@ -803,9 +809,17 @@ fun KeyboardInputView( if (desc == "Backspace") canDelete() else true }, onPress = { performLightHaptic() }, + onLongClick = if (desc == "Emoji") { + { + isKaomojiMode = true + isEmojiMode = false + isClipboardMode = false + performHeavyHaptic() + } + } else null, interactionSource = fnInteraction, - containerColor = if ((desc == "Clipboard" && isClipboardMode) || (desc == "Emoji" && isEmojiMode)) MaterialTheme.colorScheme.primaryContainer else MaterialTheme.colorScheme.surfaceContainerHighest, - contentColor = if ((desc == "Clipboard" && isClipboardMode) || (desc == "Emoji" && isEmojiMode)) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.onSurface, + containerColor = if ((desc == "Clipboard" && isClipboardMode) || (desc == "Emoji" && (isEmojiMode || isKaomojiMode))) MaterialTheme.colorScheme.primaryContainer else MaterialTheme.colorScheme.surfaceContainerHighest, + contentColor = if ((desc == "Clipboard" && isClipboardMode) || (desc == "Emoji" && (isEmojiMode || isKaomojiMode))) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.onSurface, shape = RoundedCornerShape(animatedRadius), modifier = if (desc == "Expand") { Modifier.width(50.dp).fillMaxHeight() @@ -847,6 +861,7 @@ fun KeyboardInputView( val currentMode = when { isEmojiMode -> 1 isClipboardMode && isClipboardEnabled -> 2 + isKaomojiMode -> 3 else -> 0 } @@ -958,6 +973,25 @@ fun KeyboardInputView( ) } + 3 -> { + KaomojiPicker( + modifier = Modifier.fillMaxSize(), + keyRoundness = keyRoundness, + isHapticsEnabled = isHapticsEnabled, + hapticStrength = hapticStrength, + onKaomojiSelected = { kaomoji -> + handleType(kaomoji) + }, + onSwipeDownToExit = { + if (isKaomojiMode) { + isKaomojiMode = false + performHeavyHaptic() + } + }, + bottomContentPadding = bottomPadding + ) + } + else -> { Column( modifier = Modifier.fillMaxSize() diff --git a/app/src/main/res/drawable/rounded_emoji_language_24.xml b/app/src/main/res/drawable/rounded_emoji_language_24.xml new file mode 100644 index 000000000..4f1b76a17 --- /dev/null +++ b/app/src/main/res/drawable/rounded_emoji_language_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5fa06f2c2..1ae5a7ad2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1195,6 +1195,46 @@ Protection ABC ?#/ + Kaomoji + Joy + Love + Embarassment + Sympathy + Dissatisfaction + Anger + Apologizing + Bear + Bird + Cat + Confusion + Dog + Doubt + Enemies + Faces + Fear + Fish + Food + Friends + Games + Greeting + Hiding + Hugging + Indifference + Magic + Music + Nosebleeding + Pain + Pig + Rabbit + Running + Sadness + Sleeping + Special + Spider + Surprise + Weapons + Winking + Writing Oi! You can check updates in app settings, No need to add here XD Export Import From 64d668fc0d008c247dcb8482d2047446d57c43bd Mon Sep 17 00:00:00 2001 From: sameerasw Date: Tue, 24 Mar 2026 20:31:26 +0530 Subject: [PATCH 03/18] feat: #277 in-app lang picker --- app/src/main/AndroidManifest.xml | 9 ++ .../sameerasw/essentials/AppLockActivity.kt | 4 +- .../essentials/AppUpdatesActivity.kt | 4 +- .../essentials/FeatureSettingsActivity.kt | 4 +- .../essentials/LinkPickerActivity.kt | 4 +- .../com/sameerasw/essentials/MainActivity.kt | 4 +- .../sameerasw/essentials/SettingsActivity.kt | 9 +- .../data/repository/SettingsRepository.kt | 1 + .../ui/components/pickers/LanguagePicker.kt | 95 +++++++++++++++++++ .../ui/composables/WelcomeScreen.kt | 15 ++- .../essentials/utils/LanguageUtils.kt | 38 ++++++++ .../essentials/viewmodels/MainViewModel.kt | 17 ++++ app/src/main/res/values-night/themes.xml | 2 +- app/src/main/res/values/strings.xml | 1 + app/src/main/res/values/themes.xml | 2 +- 15 files changed, 195 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/com/sameerasw/essentials/ui/components/pickers/LanguagePicker.kt create mode 100644 app/src/main/java/com/sameerasw/essentials/utils/LanguageUtils.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4c589f3e1..dab2a169d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -728,6 +728,15 @@ android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> + + + + \ No newline at end of file diff --git a/app/src/main/java/com/sameerasw/essentials/AppLockActivity.kt b/app/src/main/java/com/sameerasw/essentials/AppLockActivity.kt index 3a8348fb5..e60bac6ad 100644 --- a/app/src/main/java/com/sameerasw/essentials/AppLockActivity.kt +++ b/app/src/main/java/com/sameerasw/essentials/AppLockActivity.kt @@ -26,12 +26,12 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import androidx.core.content.ContextCompat -import androidx.fragment.app.FragmentActivity +import androidx.appcompat.app.AppCompatActivity import com.sameerasw.essentials.services.tiles.ScreenOffAccessibilityService import com.sameerasw.essentials.ui.theme.EssentialsTheme import java.util.concurrent.Executor -class AppLockActivity : FragmentActivity() { +class AppLockActivity : AppCompatActivity() { private lateinit var executor: Executor private lateinit var biometricPrompt: BiometricPrompt diff --git a/app/src/main/java/com/sameerasw/essentials/AppUpdatesActivity.kt b/app/src/main/java/com/sameerasw/essentials/AppUpdatesActivity.kt index 40c2727be..d6672d4ec 100644 --- a/app/src/main/java/com/sameerasw/essentials/AppUpdatesActivity.kt +++ b/app/src/main/java/com/sameerasw/essentials/AppUpdatesActivity.kt @@ -46,7 +46,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.fragment.app.FragmentActivity +import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.viewmodel.compose.viewModel import com.sameerasw.essentials.ui.components.ReusableTopAppBar import com.sameerasw.essentials.ui.components.cards.TrackedRepoCard @@ -62,7 +62,7 @@ import java.util.Date import java.util.Locale @OptIn(ExperimentalMaterial3Api::class) -class AppUpdatesActivity : FragmentActivity() { +class AppUpdatesActivity : AppCompatActivity() { @OptIn(ExperimentalMaterial3ExpressiveApi::class) override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/com/sameerasw/essentials/FeatureSettingsActivity.kt b/app/src/main/java/com/sameerasw/essentials/FeatureSettingsActivity.kt index dc93e7da3..284f822cf 100644 --- a/app/src/main/java/com/sameerasw/essentials/FeatureSettingsActivity.kt +++ b/app/src/main/java/com/sameerasw/essentials/FeatureSettingsActivity.kt @@ -43,7 +43,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import androidx.core.net.toUri -import androidx.fragment.app.FragmentActivity +import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.viewmodel.compose.viewModel @@ -87,7 +87,7 @@ import com.sameerasw.essentials.viewmodels.WatchViewModel import kotlinx.coroutines.delay @OptIn(ExperimentalMaterial3Api::class) -class FeatureSettingsActivity : FragmentActivity() { +class FeatureSettingsActivity : AppCompatActivity() { @OptIn(ExperimentalMaterial3ExpressiveApi::class) override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/com/sameerasw/essentials/LinkPickerActivity.kt b/app/src/main/java/com/sameerasw/essentials/LinkPickerActivity.kt index d807a406d..c890fd3f1 100644 --- a/app/src/main/java/com/sameerasw/essentials/LinkPickerActivity.kt +++ b/app/src/main/java/com/sameerasw/essentials/LinkPickerActivity.kt @@ -3,7 +3,7 @@ package com.sameerasw.essentials import android.content.Intent import android.net.Uri import android.os.Bundle -import androidx.activity.ComponentActivity +import androidx.appcompat.app.AppCompatActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.fillMaxSize @@ -12,7 +12,7 @@ import androidx.compose.ui.Modifier import com.sameerasw.essentials.ui.components.linkActions.LinkPickerScreen import com.sameerasw.essentials.ui.theme.EssentialsTheme -class LinkPickerActivity : ComponentActivity() { +class LinkPickerActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/sameerasw/essentials/MainActivity.kt b/app/src/main/java/com/sameerasw/essentials/MainActivity.kt index 014adbef1..d82dc982e 100644 --- a/app/src/main/java/com/sameerasw/essentials/MainActivity.kt +++ b/app/src/main/java/com/sameerasw/essentials/MainActivity.kt @@ -70,7 +70,7 @@ import androidx.activity.compose.PredictiveBackHandler import androidx.core.animation.doOnEnd import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.WindowCompat -import androidx.fragment.app.FragmentActivity +import androidx.appcompat.app.AppCompatActivity import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import android.widget.Toast @@ -114,7 +114,7 @@ import com.sameerasw.essentials.viewmodels.MainViewModel import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) -class MainActivity : FragmentActivity() { +class MainActivity : AppCompatActivity() { val viewModel: MainViewModel by viewModels() val updatesViewModel: AppUpdatesViewModel by viewModels() val locationViewModel: LocationReachedViewModel by viewModels() diff --git a/app/src/main/java/com/sameerasw/essentials/SettingsActivity.kt b/app/src/main/java/com/sameerasw/essentials/SettingsActivity.kt index d6d0e8168..a3ba33a14 100644 --- a/app/src/main/java/com/sameerasw/essentials/SettingsActivity.kt +++ b/app/src/main/java/com/sameerasw/essentials/SettingsActivity.kt @@ -10,6 +10,7 @@ import android.os.Bundle import android.provider.Settings import android.widget.Toast import androidx.activity.ComponentActivity +import androidx.appcompat.app.AppCompatActivity import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge @@ -76,6 +77,7 @@ import com.sameerasw.essentials.ui.components.containers.RoundedCardContainer import com.sameerasw.essentials.ui.components.dialogs.AboutSection import com.sameerasw.essentials.ui.components.pickers.CrashReportingPicker import com.sameerasw.essentials.ui.components.pickers.DefaultTabPicker +import com.sameerasw.essentials.ui.components.pickers.LanguagePicker import com.sameerasw.essentials.ui.components.sheets.InstructionsBottomSheet import com.sameerasw.essentials.ui.components.sheets.UpdateBottomSheet import com.sameerasw.essentials.ui.modifiers.BlurDirection @@ -91,7 +93,7 @@ import java.util.Date import java.util.Locale @OptIn(ExperimentalMaterial3Api::class) -class SettingsActivity : ComponentActivity() { +class SettingsActivity : AppCompatActivity() { private val viewModel: MainViewModel by viewModels() @@ -339,6 +341,11 @@ fun SettingsContent( ) RoundedCardContainer { + val appLanguage by viewModel.appLanguage + LanguagePicker( + selectedLanguageCode = appLanguage, + onLanguageSelected = { viewModel.setAppLanguage(it) } + ) IconToggleItem( iconRes = R.drawable.rounded_mobile_vibrate_24, title = "Haptic Feedback", diff --git a/app/src/main/java/com/sameerasw/essentials/data/repository/SettingsRepository.kt b/app/src/main/java/com/sameerasw/essentials/data/repository/SettingsRepository.kt index 32313248e..a10703cec 100644 --- a/app/src/main/java/com/sameerasw/essentials/data/repository/SettingsRepository.kt +++ b/app/src/main/java/com/sameerasw/essentials/data/repository/SettingsRepository.kt @@ -907,5 +907,6 @@ class SettingsRepository(private val context: Context) { fun resetPrivateDnsPresets() { savePrivateDnsPresets(getDefaultDnsPresets()) } + } diff --git a/app/src/main/java/com/sameerasw/essentials/ui/components/pickers/LanguagePicker.kt b/app/src/main/java/com/sameerasw/essentials/ui/components/pickers/LanguagePicker.kt new file mode 100644 index 000000000..baf4b61a4 --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/ui/components/pickers/LanguagePicker.kt @@ -0,0 +1,95 @@ +package com.sameerasw.essentials.ui.components.pickers + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.platform.LocalView +import com.sameerasw.essentials.R +import com.sameerasw.essentials.utils.LanguageUtils +import com.sameerasw.essentials.utils.HapticUtil + +@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class) +@Composable +fun LanguagePicker( + selectedLanguageCode: String, + onLanguageSelected: (String) -> Unit, + modifier: Modifier = Modifier +) { + val view = LocalView.current + var expanded by remember { mutableStateOf(false) } + val languages = LanguageUtils.languages + val selectedLanguage = languages.find { it.code == selectedLanguageCode } ?: languages.first() + + Row( + modifier = modifier + .fillMaxWidth() + .background( + color = MaterialTheme.colorScheme.surfaceBright, + shape = RoundedCornerShape(MaterialTheme.shapes.extraSmall.bottomEnd) + ) + .padding(12.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Spacer(modifier = Modifier.size(0.dp)) + + Icon( + painter = painterResource(id = R.drawable.rounded_globe_24), + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = MaterialTheme.colorScheme.primary + ) + + Column { + Text( + text = stringResource(R.string.label_app_language), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurface + ) + } + + ExposedDropdownMenuBox( + expanded = expanded, + onExpandedChange = { expanded = !expanded }, + // modifier = Modifier.fillMaxWidth() + ) { + OutlinedTextField( + value = "${selectedLanguage.nativeName} (${selectedLanguage.name})", + onValueChange = {}, + readOnly = true, + modifier = Modifier + .fillMaxWidth() + .menuAnchor(MenuAnchorType.PrimaryEditable, true), + trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, + colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(), + shape = RoundedCornerShape(12.dp) + ) + + ExposedDropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + languages.forEach { language -> + DropdownMenuItem( + text = { + Text(text = "${language.nativeName} (${language.name})") + }, + onClick = { + HapticUtil.performVirtualKeyHaptic(view) + onLanguageSelected(language.code) + expanded = false + }, + contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding + ) + } + } + } + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/WelcomeScreen.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/WelcomeScreen.kt index dcc50077f..ef43311a5 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/WelcomeScreen.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/WelcomeScreen.kt @@ -46,6 +46,7 @@ import com.sameerasw.essentials.utils.HapticUtil import com.sameerasw.essentials.utils.DeviceUtils import androidx.compose.ui.unit.sp import com.sameerasw.essentials.ui.components.pickers.CrashReportingPicker +import com.sameerasw.essentials.ui.components.pickers.LanguagePicker import kotlinx.coroutines.launch import kotlin.math.PI import kotlin.math.atan2 @@ -96,6 +97,7 @@ fun WelcomeScreen( when (step) { OnboardingStep.WELCOME -> { WelcomeStepContent( + viewModel = viewModel, rotationAnimatable = rotationAnimatable, center = center, onCenterChanged = { center = it }, @@ -158,6 +160,7 @@ fun WelcomeScreen( @Composable fun WelcomeStepContent( + viewModel: MainViewModel, rotationAnimatable: Animatable, center: Offset, onCenterChanged: (Offset) -> Unit, @@ -323,7 +326,17 @@ fun WelcomeStepContent( ) } - Spacer(modifier = Modifier.weight(0.3f)) + Spacer(modifier = Modifier.height(16.dp)) + + val appLanguage by viewModel.appLanguage + RoundedCardContainer(modifier = Modifier.padding(horizontal = 16.dp)) { + LanguagePicker( + selectedLanguageCode = appLanguage, + onLanguageSelected = { viewModel.setAppLanguage(it) } + ) + } + + Spacer(modifier = Modifier.height(2.dp)) } Button( diff --git a/app/src/main/java/com/sameerasw/essentials/utils/LanguageUtils.kt b/app/src/main/java/com/sameerasw/essentials/utils/LanguageUtils.kt new file mode 100644 index 000000000..8faaeaa62 --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/utils/LanguageUtils.kt @@ -0,0 +1,38 @@ +package com.sameerasw.essentials.utils + +object LanguageUtils { + val languages = listOf( + Language("en", "English", "English"), + Language("si", "Sinhala", "සිංහල"), + Language("ar", "Arabic", "العربية"), + Language("de", "German", "Deutsch"), + Language("es", "Spanish", "Español"), + Language("fr", "French", "Français"), + Language("it", "Italian", "Italiano"), + Language("ja", "Japanese", "日本語"), + Language("ko", "Korean", "한국어"), + Language("pt", "Portuguese", "Português"), + Language("ru", "Russian", "Русский"), + Language("tr", "Turkish", "Türkçe"), + Language("zh", "Chinese", "中文"), + Language("uk", "Ukrainian", "Українська"), + Language("vi", "Vietnamese", "Tiếng Việt"), + Language("pl", "Polish", "Polski"), + Language("no", "Norwegian", "Norsk"), + Language("nl", "Dutch", "Nederlands"), + Language("hu", "Hungarian", "Magyar"), + Language("he", "Hebrew", "עברית"), + Language("fi", "Finnish", "Suomi"), + Language("el", "Greek", "Ελληνικά"), + Language("da", "Danish", "Dansk"), + Language("cs", "Czech", "Čេština"), + Language("ca", "Catalan", "Català"), + Language("af", "Afrikaans", "Afrikaans"), + Language("ach", "Acholi", "Luo"), + Language("sr", "Serbian", "Српски"), + Language("sv", "Swedish", "Svenska"), + Language("ro", "Romanian", "Română") + ) + + data class Language(val code: String, val name: String, val nativeName: String) +} diff --git a/app/src/main/java/com/sameerasw/essentials/viewmodels/MainViewModel.kt b/app/src/main/java/com/sameerasw/essentials/viewmodels/MainViewModel.kt index 835f9b88d..9587c064d 100644 --- a/app/src/main/java/com/sameerasw/essentials/viewmodels/MainViewModel.kt +++ b/app/src/main/java/com/sameerasw/essentials/viewmodels/MainViewModel.kt @@ -19,11 +19,13 @@ import android.os.PowerManager import android.provider.CalendarContract import android.provider.Settings import android.view.inputmethod.InputMethodManager +import androidx.appcompat.app.AppCompatDelegate import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.core.content.ContextCompat +import androidx.core.os.LocaleListCompat import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.work.ExistingPeriodicWorkPolicy @@ -107,6 +109,7 @@ class MainViewModel : ViewModel() { val isFullScreenIntentPermissionGranted = mutableStateOf(false) val isBluetoothPermissionGranted = mutableStateOf(false) val isUsageStatsPermissionGranted = mutableStateOf(false) + val appLanguage = mutableStateOf("en") val isBluetoothDevicesEnabled = mutableStateOf(false) val isCallVibrationsEnabled = mutableStateOf(false) @@ -450,11 +453,25 @@ class MainViewModel : ViewModel() { settingsRepository.putString(SettingsRepository.KEY_SENTRY_REPORT_MODE, mode) } + fun setAppLanguage(languageCode: String) { + appLanguage.value = languageCode + val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags(languageCode) + AppCompatDelegate.setApplicationLocales(appLocale) + } + fun check(context: Context) { appContext = context.applicationContext settingsRepository = SettingsRepository(context) updateRepository = UpdateRepository() + // Sync with system per-app language settings + val currentLocales = AppCompatDelegate.getApplicationLocales() + if (!currentLocales.isEmpty) { + appLanguage.value = currentLocales.get(0)?.language ?: "en" + } else { + appLanguage.value = "en" + } + isAccessibilityEnabled.value = PermissionUtils.isAccessibilityServiceEnabled(context) isWriteSecureSettingsEnabled.value = PermissionUtils.canWriteSecureSettings(context) isShizukuAvailable.value = ShizukuUtils.isShizukuAvailable() diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 4b2300ad9..4448dbd55 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,7 +1,7 @@ -