diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 576ba0a..bbc5c02 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,7 +22,7 @@ android:theme="@style/Theme.WKT9"> diff --git a/app/src/main/assets/en_US/words.txt b/app/src/main/assets/en/words.txt similarity index 99% rename from app/src/main/assets/en_US/words.txt rename to app/src/main/assets/en/words.txt index 3f83d78..180c4e1 100644 --- a/app/src/main/assets/en_US/words.txt +++ b/app/src/main/assets/en/words.txt @@ -1,30 +1,4 @@ -. 20 -, 19 -? 18 -! 17 -' 16 -- 15 -@ 14 -$ 13 -/ 12 -( 11 -) 10 -% 9 -:) 10 -:( 9 -:D -:O -:h -:he -:hea -:hear -❤️ 1 143278 -:k -:ki -:kis -😘 1 15477 -'s -a 50 +a aardvark aardvarks aardvark's @@ -161,7 +135,7 @@ ablative ablator ablaut ablaze -able 4 +able abler ablest abloom @@ -3925,7 +3899,7 @@ ardour's arduous arduously arduousness -are 3 +are area areas area's @@ -4145,7 +4119,7 @@ arsines arsis arson arsonist -art 3 +art arte artefact artefacts @@ -5306,7 +5280,7 @@ azotises azotising azure azurite -b 49 +b baa baas babble @@ -5561,7 +5535,7 @@ baiter baiting baits baize -bake 2 +bake baked baker bakeries @@ -5623,7 +5597,7 @@ balkiness balking balks balky -ball 2 +ball ballad ballade balladeer @@ -5705,7 +5679,7 @@ banana bananas banana's banc -band 4 +band bandage bandaged bandager @@ -5833,7 +5807,7 @@ baptismally baptisms baptism's baptistery -bar 4 +bar barb barbarian barbarianism @@ -6043,7 +6017,7 @@ basally basalt basaltic bascule -base 3 +base baseball baseballs baseball's @@ -6287,7 +6261,7 @@ bazaar's bazooka bazookas bdellium -be 3 +be beach beachcomber beachcombers @@ -6561,7 +6535,7 @@ befogged befogging befogs befool -before 4 +before beforehand beforetime befoul @@ -7221,7 +7195,7 @@ bilked bilker bilking bilks -bill 3 +bill billable billabong billboard @@ -8257,7 +8231,7 @@ boisterously boisterousness bola bolas -bold 4 +bold bolder boldest boldface @@ -8387,7 +8361,7 @@ boodle booger boogie boohoo -book 6 +book bookbinder bookbinders bookbindery @@ -9201,7 +9175,7 @@ brimstone brindle brindled brine -bring 3 +bring bringer bringers bringing @@ -9961,7 +9935,7 @@ bustling bustlingly busts busty -busy 4 +busy busybody busying busyness @@ -10087,7 +10061,7 @@ byways byword bywords byword's -c 48 +c cab cabal cabala @@ -10250,7 +10224,7 @@ cajolery cajoles cajoling cajuput -cake 3 +cake caked cakes cakewalk @@ -10334,7 +10308,7 @@ caliphate caliphs calisaya calk -call 3 +call calla callable callboy @@ -10412,7 +10386,7 @@ cambering cambial cambium cambric -came 5 +came camel camelback cameleer @@ -10473,7 +10447,7 @@ campy cams camshaft camshafts -can 5 +can canal canalisation canalisations @@ -10548,7 +10522,7 @@ candy candyfloss candying candytuft -cane 3 +cane canebrake caned canella @@ -10688,7 +10662,7 @@ canyons canyon's canzone canzonet -cap 3 +cap capabilities capability capability's @@ -10819,7 +10793,7 @@ capuche capuchin caput capybara -car 5 +car carabineer caracal caracara @@ -10895,7 +10869,7 @@ carcinogenic carcinogenicity carcinogens carcinoma -card 5 +card cardamom cardboard cardboards @@ -10930,7 +10904,7 @@ cards card's cardsharp cardsharper -care 4 +care cared careen careened @@ -13385,7 +13359,7 @@ clevis clianthus cliché clichés -click 4 +click clicked clicker clickers @@ -14044,7 +14018,7 @@ coir coital coition coitus -coke 3 +coke cokes coking col @@ -14054,7 +14028,7 @@ colanders colatitudes colcannon colcothar -cold 5 +cold colder coldest coldheartedly @@ -14356,7 +14330,7 @@ combustions combustive combustor combustors -come 2 +come comeback comedian comedians @@ -14403,7 +14377,7 @@ comicality comically comics comic's -coming 3 +coming comings comitia comity @@ -15296,7 +15270,7 @@ conferrers conferrer's conferring confers -confess 3 +confess confessable confessant confessed @@ -15580,7 +15554,7 @@ conjurers conjures conjuring conjuror -conk 3 +conk conked conker conkers @@ -16409,7 +16383,7 @@ convulsively convulsiveness coo cooing -cook 4 +cook cookbook cookbooks cooked @@ -16426,7 +16400,7 @@ cookouts cooks cook's cookware -cool 5 +cool coolant coolants cooled @@ -16944,7 +16918,7 @@ cottony cotyledon cotyledons cotyledon's -couch 5 +couch couchant couched couches @@ -17223,7 +17197,7 @@ covetousness covets covey coveys -covid 2 +covid coving cow cowage @@ -18506,7 +18480,7 @@ cytoplast cytosine czar czardas -d 50 +d dab dabbed dabber @@ -18723,7 +18697,7 @@ darnel darner darning darns -dart 3 +dart d'art dartboard darted @@ -21616,7 +21590,7 @@ dining dinitrobenzene dink dinky -dinner 3 +dinner dinnerless dinners dinner's @@ -23047,7 +23021,7 @@ dodgy dodo dodos dodo's -doe 3 +doe doer doers does @@ -23203,7 +23177,7 @@ donating donation donations donator -done 5 +done doneness dong donga @@ -23238,7 +23212,7 @@ dooms doomsayer doomsday doomster -door 5 +door doorbell doorframe doorjamb @@ -24030,7 +24004,7 @@ dungy dunk dunker dunlin -duo 2 +duo duodecimal duodecimo duodenal @@ -24215,7 +24189,7 @@ dyspnoea dysprosium dystrophic dystrophy -e 49 +e e's each eager @@ -24320,12 +24294,12 @@ easements easement's easer eases -easier 3 +easier easiest easily easiness easing -east 4 +east eastbound Easter easterly @@ -24712,7 +24686,7 @@ eightieth eights eighty einsteinium -either 4 +either ejaculate ejaculated ejaculates @@ -25566,7 +25540,7 @@ encyclopaedic encyclopaedically encyclopaedism encyclopaedist -end 5 +end endanger endangered endangering @@ -28155,7 +28129,7 @@ eyewitness's eying eyrie eyrir -f 48 +f f's fab fable @@ -28182,7 +28156,7 @@ fabulously fabulousness facade facades -face 3 +face faced facedown faceless @@ -28505,7 +28479,7 @@ fantasy fantasy's fantod fanwise -far 3 +far farad faradic faradisation @@ -28565,7 +28539,7 @@ farrow farseeing farsighted farsightedness -fart 2 +fart farther farthermost farthest @@ -28604,7 +28578,7 @@ fashioners fashioning fashionmonger fashions -fast 5 +fast fastback fastball fastballs @@ -28617,7 +28591,7 @@ fasteners fastening fastenings fastens -faster 4 +faster fastest fastidious fastidiously @@ -29118,7 +29092,7 @@ feudatory feudist feuds feud's -fever 5 +fever fevered feverfew fevering @@ -29129,7 +29103,7 @@ feverous feverously fevers feverwort -few 3 +few fewer fewest fewness @@ -29409,14 +29383,14 @@ financier's financing finback finch -find 3 +find findable finder finders finding findings finds -fine 2 +fine fineable fined finely @@ -30366,7 +30340,7 @@ focuser focuses focusing fodder -foe 4 +foe foeman foes foe's @@ -30500,7 +30474,7 @@ font fontal fonts font's -food 4 +food foodless foodlessness foods @@ -30612,7 +30586,7 @@ foppish foppishly foppishness fops -for 5 +for forage foraged forager @@ -31727,7 +31701,7 @@ fulguration fulgurations fulgurous fuliginous -full 3 +full fullback fuller fullest @@ -31767,7 +31741,7 @@ fumigators fuming fumitory fumy -fun 3 +fun funambulist function functional @@ -31965,7 +31939,7 @@ fuzzily fuzziness fuzzy fylfot -g 50 +g g's gab gabardine @@ -32204,7 +32178,7 @@ gantlet gantries gantry gaol -gap 3 +gap gape gaped gaper @@ -32301,7 +32275,7 @@ gartering garters garter's garth -gas 4 +gas gasbag gaseous gaseousness @@ -32415,7 +32389,7 @@ gauzily gauziness gauzing gauzy -gave 2 +gave gavel gavelled gavelling @@ -33310,7 +33284,7 @@ gnostic gnosticism gnu gnus -go 3 +go goad goaded goading @@ -33428,7 +33402,7 @@ gondola gondolas gondolier gondoliers -gone 3 +gone goner gonfalon gong @@ -33438,7 +33412,7 @@ gonococcus gonorrhoea gonorrhoeal goober -good 5 +good goodbye goodbyes goodbye's @@ -33526,7 +33500,7 @@ gossipmongers gossips gossipy gossoon -got 3 +got gotcha gotten gouache @@ -34630,7 +34604,7 @@ gyrostabiliser gyrostat gyrostatic gyrostatics -h 49 +h h's ha habeas @@ -34693,7 +34667,7 @@ hacks hacksaw hacksaws hackwork -had 3 +had haddock haddocks hade @@ -34762,7 +34736,7 @@ hagiolatry hagiology hagioscope hah -haha 2 +haha haiku hail hailed @@ -35076,7 +35050,7 @@ happiest happily happiness happing -happy 3 +happy harangue harangued haranguer @@ -35102,7 +35076,7 @@ harbouring harbourless harbours harbour's -hard 3 +hard hardback hardball hardboard @@ -35257,7 +35231,7 @@ harvesting harvestman harvestmen harvests -has 5 +has hash hashed hasher @@ -35288,7 +35262,7 @@ hastily hastiness hasting hasty -hat 3 +hat hatband hatbox hatch @@ -35318,7 +35292,7 @@ hatefulness hater hates hath -hating 2 +hating hatless hatpin hatred @@ -35352,7 +35326,7 @@ hausfrau hautboy haute hauteur -have 3 +have haven havens haven's @@ -35361,7 +35335,7 @@ haversack haversacks haversack's haves -having 3 +having havoc havocked havocking @@ -35425,7 +35399,7 @@ hazily haziness hazing hazy -he 3 +he head headache headaches @@ -35799,7 +35773,7 @@ helmsman helmsmen helot helotism -help 3 +help helped helper helpers @@ -35920,7 +35894,7 @@ herding herds herdsman herdsmen -here 5 +here hereabout hereabouts hereafter @@ -36069,7 +36043,7 @@ hexangular hexapod hexapody hexastich -hey 3 +hey heyday hi hiatus @@ -36196,7 +36170,7 @@ hilly hilt hilts hilt's -him 3 +him himself hind hindbrain @@ -36255,7 +36229,7 @@ hires hiring hirsute hirsuteness -his 3 +his hispid hiss hissed @@ -36478,7 +36452,7 @@ holytide homage hombre homburg -home 4 +home homebody homebound homebred @@ -36710,7 +36684,7 @@ hoots hoover hooves hop -hope 5 +hope hoped hopeful hopefully @@ -36722,7 +36696,7 @@ hopelessness hoper hopes hophead -hoping 4 +hoping hoplite hopped hopper @@ -36851,7 +36825,7 @@ hoses hose's hosier hosiery -hosing 3 +hosing hospice hospices hospitable @@ -36922,7 +36896,7 @@ hourglass hourly hours hour's -house 3 +house houseboat houseboats housebound @@ -37001,7 +36975,7 @@ hovered hoverer hovering hovers -how 5 +how howbeit howdah howdy @@ -37645,7 +37619,7 @@ hysterical hysterically hysterics hysteron -I 60 +I I'd I'll I'm @@ -37835,7 +37809,7 @@ idyllic idyllically idyllist ie -if 2 +if iffy igloo igloos @@ -38630,7 +38604,7 @@ impute imputed imputes imputing -in 2 +in inabilities inability inaccessibility @@ -41628,7 +41602,7 @@ ivory ivy ivy's ix -j 50 +j j's jab jabbed @@ -41979,7 +41953,7 @@ jogs john johns john's -join 3 +join joinable joined joiner @@ -42283,7 +42257,7 @@ juxtaposed juxtaposes juxtaposing juxtaposition -k 49 +k k's kabob kaka @@ -42355,7 +42329,7 @@ keenest keening keenly keenness -keep 3 +keep keeper keepers keeping @@ -42541,7 +42515,7 @@ kina kinaesthesia kinaesthetic kinaesthetically -kind 5 +kind kinder kindergarten kindergartner @@ -42825,7 +42799,7 @@ kumquat kurtosis kwashiorkor kymograph -l 48 +l l's la lab @@ -43290,7 +43264,7 @@ lassoed lassoer lassoes lass's -last 3 +last lasted lasting lastingly @@ -43861,7 +43835,7 @@ lesbianism lesbians lesion lesions -less 4 +less lessee lessen lessened @@ -43874,7 +43848,7 @@ lessoning lessons lesson's lest -let 4 +let letch letdown letdowns @@ -43912,7 +43886,7 @@ levanter levee levees levee's -level 4 +level levelled leveller levellers @@ -44092,7 +44066,7 @@ lidos lids lid's lie -lied 3 +lied lieder liege liegeman @@ -44106,7 +44080,7 @@ lieutenancy lieutenant lieutenants lieutenant's -life 4 +life lifeblood lifeboat lifeboats @@ -44186,7 +44160,7 @@ lignite lignum ligroin likable -like 3 +like likeable liked likelier @@ -44231,7 +44205,7 @@ limbless limbo limbos limbs -lime 3 +lime limeade limed limekiln @@ -44298,7 +44272,7 @@ linchpin linchpins linchpin's linden -line 4 +line lineage lineages lineal @@ -44537,7 +44511,7 @@ littermate littermates littermate's litters -little 3 +little littleneck littleness littler @@ -44876,13 +44850,13 @@ longspur longstanding longwinded loo -look 3 +look looked lookers looking lookout lookouts -looks 3 +looks lookup lookups lookup's @@ -44962,10 +44936,10 @@ losses loss's lossy lost -lot 5 +lot lotion lotions -lots 4 +lots lot's lotteries lottery @@ -45005,7 +44979,7 @@ louvre louvred lovable lovably -love 5 +love loveable lovebird lovebirds @@ -46361,7 +46335,7 @@ maze mazes maze's mazy -me 2 +me mead meadow meadowland @@ -46448,7 +46422,7 @@ mechanistic mechanistically mechanoreceptor mecum -med 3 +med medal medalled medalling @@ -47414,7 +47388,7 @@ mincemeat minces mincing mincingly -mind 3 +mind minded mindedness minder @@ -47427,7 +47401,7 @@ mindless mindlessly mindlessness minds -mine 4 +mine mined minefield minelayer @@ -48530,7 +48504,7 @@ mordancy mordant mordantly mordent -more 4 +more morel moreover mores @@ -48767,14 +48741,14 @@ mouthwash mouthy movable movably -move 3 +move moved movement movements movement's mover movers -moves 3 +moves movie moviegoer moviemaker @@ -49525,7 +49499,7 @@ necrotic nectar nectarine nee -need 3 +need needed needful needfulness @@ -49547,7 +49521,7 @@ needlewomen needlework needling needn't -needs 3 +needs needy nefarious nefariously @@ -49755,7 +49729,7 @@ never nevermore nevertheless nevus -new 3 +new newborn newborns newcomer @@ -49807,7 +49781,7 @@ newsworthy newsy newt newts -next 4 +next nexus nexuses niacin @@ -49819,7 +49793,7 @@ nibblers nibbles nibbling nibs -nice 5 +nice nicely niceness nicer @@ -49908,7 +49882,7 @@ nimbus nimbuses nimiety nincompoop -nine 2 +nine ninebark ninepin ninepins @@ -49963,7 +49937,7 @@ nixed nixes nixing nm -no 4 +no nobble nobelium nobilities @@ -50140,7 +50114,7 @@ northward northwards northwest northwester -nose 3 +nose nosebag nosebags nosebag's @@ -50166,7 +50140,7 @@ nostrils nostril's nostrum nosy -not 3 +not notability notable notables @@ -50190,7 +50164,7 @@ notch notched notches notching -note 4 +note notebook notebooks notebook's @@ -50198,7 +50172,7 @@ notecase noted notelet notepaper -notes 4 +notes noteworthiness noteworthy nothing @@ -50269,7 +50243,7 @@ novice's novitiate novitiates novocaine -now 5 +now nowadays nowhere nowhither @@ -50773,7 +50747,7 @@ octoroon ocular oculist odalisque -odd 4 +odd oddball oddballs oddball's @@ -50820,8 +50794,8 @@ oestrous oestrus oeuvre oeuvres -of 3 -off 5 +of +off offal offbeat offence @@ -50837,7 +50811,7 @@ offensive offensively offensiveness offensives -offer 2 +offer offered offering offerings @@ -50928,7 +50902,7 @@ oilstone oily ointment ointments -OK 3 +OK okapi okay okays @@ -50998,15 +50972,15 @@ omnisciently omnivore omnivorous omnivorously -on 5 +on once oncology oncoming -one 5 +one oneness onerous onerously -ones 5 +ones one's oneself onetime @@ -51031,7 +51005,7 @@ onside onslaught onslaughts onstage -onto 5 +onto ontogenesis ontogenetic ontogenetically @@ -51215,7 +51189,7 @@ opulent opulently opus opuses -or 3 +or oracle oracles oracle's @@ -51505,7 +51479,7 @@ oust ousted ouster ousting -out 3 +out outage outages outage's @@ -52234,7 +52208,7 @@ oyster's oz ozone ozonosphere -p 50 +p p's pH pa @@ -53173,7 +53147,7 @@ pauses pausing pavane pave -paved 8 +paved pavement pavements pavement's @@ -53198,7 +53172,7 @@ pawnshop pawnshops pawnshop's paws -pay 4 +pay payable payday payee @@ -53220,7 +53194,7 @@ payoff's payola payroll payrolls -pays 4 +pays pea peace peaceable @@ -53245,7 +53219,7 @@ peacocks peacock's peafowl peahen -peak 4 +peak peaked peaking peaks @@ -53346,7 +53320,7 @@ pediment pedometer pedometers pedometer's -pee 3 +pee peek peeked peeking @@ -54464,7 +54438,7 @@ picturesquely picturing piddle piddling -pie 2 +pie piebald piece pieced @@ -55334,7 +55308,7 @@ poisoning poisonous poisonously poisons -poke 3 +poke pokeberry poked poker @@ -55560,7 +55534,7 @@ ponders ponds pondweed pone -pong 3 +pong pongee poniard ponies @@ -55914,7 +55888,7 @@ potlatches potluck potpie potpourri -pots 2 +pots pot's potsherd potstone @@ -55942,14 +55916,14 @@ pounce pounced pounces pouncing -pound 3 +pound poundage poundal pounded pounder pounding pounds -pour 4 +pour pourboire poured pourer @@ -57910,7 +57884,7 @@ purse pursed purser pursers -purses 3 +purses pursier pursiness pursing @@ -58054,7 +58028,7 @@ pyroxene Pyrrhic python pythons -q 49 +q q's qua quack @@ -58417,7 +58391,7 @@ quotidian quotient quotients quoting -r 48 +r r's rRNA rabbet @@ -58891,7 +58865,7 @@ ratcheted ratchets rate rateable -rated 9 +rated ratepayer ratepayers rates @@ -58973,7 +58947,7 @@ ravagers ravages ravaging rave -raved 7 +raved ravel ravelled ravelling @@ -58998,7 +58972,7 @@ ravishes ravishing ravishingly ravishment -raw 2 +raw rawer rawest rawhide @@ -59093,7 +59067,7 @@ reaffirming reaffirms reagent reagents -real 5 +real realest realign realigned @@ -59681,7 +59655,7 @@ recycle recycled recycles recycling -red 4 +red redact redaction redactions @@ -59784,7 +59758,7 @@ redistricting redneck rednecks redness -redo 3 +redo redoing redolence redolent @@ -59886,7 +59860,7 @@ re-examine re-examined re-examines re-examining -ref 2 +ref reface refaced refashion @@ -60823,7 +60797,7 @@ replications replied replier replies -reply 5 +reply replying report reportable @@ -61257,7 +61231,7 @@ responsibly responsive responsively responsiveness -rest 5 +rest restage restart restarted @@ -61866,7 +61840,7 @@ ricocheting ricochets ricotta rictus -rid 3 +rid riddance ridded ridden @@ -61978,7 +61952,7 @@ ringdove ringed ringer ringers -ringing 4 +ringing ringleader ringleaders ringlet @@ -62216,7 +62190,7 @@ roister roistered roisterer roistering -role 4 +role role's roles roll @@ -62349,7 +62323,7 @@ rotate rotated rotates rotating -rotation 4 +rotation rotational rotationally rotations @@ -62404,7 +62378,7 @@ rouging roulade roulette roulettes -round 2 +round roundabout rounded roundedness @@ -62502,7 +62476,7 @@ rubout rubric rubs rubstone -ruby 4 +ruby ruby's ruche ruck @@ -62606,7 +62580,7 @@ rumpling rumps rumpus rumrunner -run 3 +run runabout runabouts runaway @@ -62626,11 +62600,11 @@ runnels runner runner's runners -running 5 -runny 4 +running +runny runoff runoffs -runs 3 +runs runt runtime runts @@ -62695,7 +62669,7 @@ rye rye's ryegrass ryot -s 47 +s s's sabbatical sable @@ -62817,7 +62791,7 @@ sago sags saguaro sahib -said 5 +said sail sailable sailboard @@ -63221,7 +63195,7 @@ savant savants save saveable -saved 10 +saved saver savers saves @@ -63240,7 +63214,7 @@ savoury savoury's savvied savvy -saw 3 +saw sawbones sawboneses sawbuck @@ -63263,10 +63237,10 @@ saxophone saxophone's saxophones saxophonist -say 5 +say saying sayings -says 5 +says scab scabbard scabbard's @@ -63823,7 +63797,7 @@ scud scudded scudding scuds -scuff 6 +scuff scuffed scuffing scuffle @@ -63901,7 +63875,7 @@ seagull seagulls seahorse seakale -seal 3 +seal sealant sealants sealed @@ -64113,7 +64087,7 @@ sedulous sedulously sedulousness sedum -see 5 +see seed seedbed seedbeds @@ -64139,14 +64113,14 @@ seeker seekers seeking seeks -seem 4 +seem seemed seeming seemingly seemlier seemliness seemly -seems 4 +seems seen seep seepage @@ -64594,7 +64568,7 @@ session's sessions sestet sestina -set 5 +set set's seta setaceous @@ -64603,7 +64577,7 @@ setback setbacks setline setoff -sets 5 +sets setscrew setscrews settable @@ -64867,7 +64841,7 @@ shawls shawm shay shays -she 4 +she she'd she'll she's @@ -65213,7 +65187,7 @@ shovelnose shovels shoves shoving -show 3 +show showboat showbread showcase @@ -65599,7 +65573,7 @@ simper simpered simpering simpers -simple 5 +simple simpleminded simplemindedly simpler @@ -65661,7 +65635,7 @@ singeing singer singer's singers -singing 5 +singing single singled singleness @@ -65761,7 +65735,7 @@ situating situation situational situations -six 4 +six sixes sixpence sixpences @@ -66249,7 +66223,7 @@ sloughing sloughs slovenliness slovenly -slow 3 +slow slowcoach slowdown slowed @@ -66259,7 +66233,7 @@ slowing slowly slowness slowpoke -slows 3 +slows slowworm sludge sludgy @@ -66335,7 +66309,7 @@ smallish smallness smallpox smarmy -smart 3 +smart smarted smarten smartened @@ -66394,7 +66368,7 @@ smitten smock smocking smocks -smog 2 +smog smoggier smoggy smoke @@ -66460,8 +66434,8 @@ smuttier smuttiness smutting smutty -snack 3 -snacks 3 +snack +snacks snaffle snaffled snaffling @@ -66630,7 +66604,7 @@ snotty snout snout's snouts -snow 3 +snow snowball snowballed snowballing @@ -66826,7 +66800,7 @@ solaced solacing solar solarium -sold 5 +sold solder soldered soldering @@ -66929,7 +66903,7 @@ somatic somatically sombre sombrero -some 5 +some somebody somebody's someday @@ -66964,7 +66938,7 @@ sonant sonar sonata sonatas -song 4 +song song's songbird songbook @@ -67039,7 +67013,7 @@ sorcery sordid sordidly sordidness -sore 4 +sore sorehead sorely soreness @@ -67059,7 +67033,7 @@ sorrowful sorrowfully sorrowfulness sorrows -sorry 4 +sorry sort sorted sorter @@ -67082,7 +67056,7 @@ soulfully soulless soullessly souls -sound 4 +sound soundboard sounded sounder @@ -67098,14 +67072,14 @@ soundproof soundproofed soundproofing soundproofs -sounds 3 -soup 5 +sounds +soup soup's soupier soups soupspoon soupy -sour 3 +sour source source's sources @@ -68241,7 +68215,7 @@ startled startles startling startlingly -starts 3 +starts starvation starve starved @@ -68321,7 +68295,7 @@ stave staved staves staving -stay 5 +stay stayed staying stays @@ -68588,7 +68562,7 @@ stile stile's stiles stiletto -still 3 +still stillbirth stillbirths stillborn @@ -68773,8 +68747,8 @@ stoneworker stonier stonily stoniness -stoning 3 -stony 3 +stoning +stony stonyhearted stood stooge @@ -68786,7 +68760,7 @@ stoopball stooped stooping stoops -stop 4 +stop stop's stopcock stopcocks @@ -69693,7 +69667,7 @@ succumb succumbed succumbing succumbs -such 3 +such suchlike suck sucked @@ -69889,7 +69863,7 @@ sumptuary sumptuous sumptuously sumptuousness -sums 2 +sums sun sun's sunbaked @@ -69939,8 +69913,8 @@ sunlight sunlit sunned sunnier -sunning 4 -sunny 5 +sunning +sunny sunray sunrise sunrises @@ -70096,7 +70070,7 @@ supervisors supervisory supine supinely -supper 4 +supper supper's suppers supping @@ -70184,7 +70158,7 @@ surcharged surcharges surcharging surd -sure 3 +sure sure-fire surefooted surefootedness @@ -71876,7 +71850,7 @@ texture textured textures texturing -th 2 +th thalamic thalamus thalassic @@ -72866,7 +72840,7 @@ tonsured tonsuring tontine tonus -too 3 +too took tool toolbox @@ -74543,7 +74517,7 @@ uglier ugliest ugliness ugly -uh 3 +uh ukulele ulcer ulcer's @@ -77831,7 +77805,7 @@ wartimes warts warty wary -was 3 +was wash washable washbasin @@ -78010,7 +77984,7 @@ waxwings waxwork waxworks waxy -way 3 +way way's waybill wayfarer @@ -78057,7 +78031,7 @@ weapon's weaponed weaponry weapons -wear 4 +wear wearable wearer wearied @@ -78104,7 +78078,7 @@ web's webbed webbing webfoot -webs 3 +webs wed wedded wedding @@ -79064,7 +79038,7 @@ wontedness wonting wonton woo -wood 3 +wood wood's woodborer woodcarver @@ -79566,7 +79540,7 @@ yea yeah yean yeanling -year 5 +year year's yearbook yearling @@ -79615,8 +79589,8 @@ yeoman yeomanly yeomanry yeomen -yep 2 -yes 3 +yep +yes yeses yeshiva yester @@ -79797,7 +79771,7 @@ zodiacs zombie zombies zonal -zone 2 +zone zoned zones zoning diff --git a/app/src/main/java/net/mezimmah/wkt9/WKT9Interface.kt b/app/src/main/java/net/mezimmah/wkt9/IME.kt similarity index 55% rename from app/src/main/java/net/mezimmah/wkt9/WKT9Interface.kt rename to app/src/main/java/net/mezimmah/wkt9/IME.kt index 45f4a0e..c6ba781 100644 --- a/app/src/main/java/net/mezimmah/wkt9/WKT9Interface.kt +++ b/app/src/main/java/net/mezimmah/wkt9/IME.kt @@ -2,39 +2,36 @@ package net.mezimmah.wkt9 import android.content.Intent import android.view.KeyEvent +import net.mezimmah.wkt9.entity.Word import net.mezimmah.wkt9.inputmode.InputMode -interface WKT9Interface { +interface IME { fun onTriggerKeyEvent(event: KeyEvent) fun onStartIntent(intent: Intent) fun onCandidates( - candidates: List, + candidates: ArrayList, current: Int? = 0 ) - fun onCancelCompose() + fun onWords(words: List) - fun onClearCandidates() + fun onNextWord() - fun onCompose(text: String, rangeStart: Int, rangeEnd: Int) + fun onPreviousWord() - fun onDeleteText(beforeCursor: Int = 0, afterCursor: Int = 0, finishComposing: Boolean) + fun onWordSelected(word: Word) - fun onFinishComposing(cursorPosition: Int) + fun onCommit(text: CharSequence = "", beforeCursor: Int = 0, afterCursor: Int = 0) - fun onGetText(): CharSequence? + fun onCompose(text: CharSequence) + + fun onDeleteText(beforeCursor: Int = 0, afterCursor: Int = 0, finishComposing: Boolean = false) fun onGetTextBeforeCursor(n: Int): CharSequence? - fun onReplaceText(text: String) - fun onSwitchInputHandler(inputMode: InputMode) - fun onRecord(finishComposing: Boolean) - - fun onTranscribe() - fun onUpdateStatusIcon(icon: Int?) } \ No newline at end of file diff --git a/app/src/main/java/net/mezimmah/wkt9/WKT9.kt b/app/src/main/java/net/mezimmah/wkt9/WKT9IME.kt similarity index 52% rename from app/src/main/java/net/mezimmah/wkt9/WKT9.kt rename to app/src/main/java/net/mezimmah/wkt9/WKT9IME.kt index b2922d6..a6bb8e6 100644 --- a/app/src/main/java/net/mezimmah/wkt9/WKT9.kt +++ b/app/src/main/java/net/mezimmah/wkt9/WKT9IME.kt @@ -3,113 +3,110 @@ package net.mezimmah.wkt9 import android.annotation.SuppressLint import android.content.Intent import android.inputmethodservice.InputMethodService -import android.media.MediaRecorder import android.provider.Settings import android.util.Log import android.view.KeyEvent import android.view.View +import android.view.inputmethod.CursorAnchorInfo import android.view.inputmethod.EditorInfo -import android.view.inputmethod.ExtractedTextRequest +import android.view.inputmethod.InputConnection import android.view.inputmethod.InputMethodManager -import net.mezimmah.wkt9.candidates.Candidates -import net.mezimmah.wkt9.inputmode.InputManager +import android.view.inputmethod.InputMethodSubtype +import net.mezimmah.wkt9.entity.Word +import net.mezimmah.wkt9.inputhandler.IdleInputHandler +import net.mezimmah.wkt9.inputhandler.InputHandler +import net.mezimmah.wkt9.inputhandler.LetterInputHandler +import net.mezimmah.wkt9.inputhandler.NumberInputHandler +import net.mezimmah.wkt9.inputhandler.WordInputHandler +import net.mezimmah.wkt9.inputmode.InputModeManager import net.mezimmah.wkt9.inputmode.InputMode import net.mezimmah.wkt9.keypad.Event import net.mezimmah.wkt9.keypad.Key import net.mezimmah.wkt9.keypad.KeyEventStat -import net.mezimmah.wkt9.voice.Whisper +import net.mezimmah.wkt9.layout.Words +import net.mezimmah.wkt9.t9.T9 +import java.util.Locale -class WKT9: WKT9Interface, InputMethodService() { + +class WKT9IME: IME, InputMethodService() { private val tag = "WKT9" - private val longPressTimeout = 400L - private lateinit var inputManager: InputManager + private val inputModeManager = InputModeManager(this) - private var inputView: View? = null + private lateinit var locale: Locale + private var inputHandler: InputHandler? = null - private var composing: Boolean = false - private var cursorPosition: Int = 0 + private var wordsView: Words? = null private val keyDownStats = KeyEventStat(0, 0) private val keyUpStats = KeyEventStat(0, 0) - // Whisper - private lateinit var whisper: Whisper - private var recorder: MediaRecorder? = null + private var composing: Boolean = false + private var selectionStart: Int = 0 + private var selectionEnd: Int = 0 - private lateinit var candidates: Candidates - - override fun onCandidates(candidates: List, current: Int?) { - this.candidates.load(candidates, current) + override fun onCandidates(candidates: ArrayList, current: Int?) { +// this.candidates?.load(candidates, current) } - override fun onCancelCompose() { - cancelCompose() - } - - override fun onClearCandidates() { - candidates.clear() - } - - override fun onCompose(text: String, rangeStart: Int, rangeEnd: Int) { - val selectionEnd = rangeStart + text.length - + override fun onCommit(text: CharSequence, beforeCursor: Int, afterCursor: Int) { currentInputConnection?.run { beginBatchEdit() - setComposingRegion(rangeStart, rangeEnd) - commitText(text, 1) - setSelection(rangeStart, selectionEnd) + setComposingRegion(selectionEnd - beforeCursor, selectionEnd + afterCursor) + setComposingText(text, 1) + finishComposingText() endBatchEdit() } } + override fun onCompose(text: CharSequence) { + currentInputConnection?.run { + if (!composing) setComposingRegion(selectionStart, selectionEnd) + + setComposingText(text, 1) + } + } + + @SuppressLint("InflateParams") override fun onCreate() { Log.d(tag, "Starting WKT9") - inputManager = InputManager(this) + val inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager + val languageTag = inputMethodManager.currentInputMethodSubtype?.languageTag ?: "en-US" + + locale = Locale.forLanguageTag(languageTag) + + initializeDictionary(locale) super.onCreate() } @SuppressLint("InflateParams") override fun onCreateInputView(): View? { - inputView = layoutInflater.inflate(R.layout.suggestions, null).also { - candidates = Candidates(this, inputManager, it) - whisper = Whisper(this, inputManager, it) - } + wordsView = layoutInflater.inflate(R.layout.words, null) as Words - return inputView + return wordsView + } + + override fun onCurrentInputMethodSubtypeChanged(newSubtype: InputMethodSubtype?) { + super.onCurrentInputMethodSubtypeChanged(newSubtype) + + newSubtype?.let { + locale = Locale.forLanguageTag(it.languageTag) + + initializeDictionary(locale) + inputHandler?.onSwitchLocale(locale) + } } override fun onDeleteText(beforeCursor: Int, afterCursor: Int, finishComposing: Boolean) { - if (finishComposing) finishComposingText() - deleteText(beforeCursor, afterCursor) } - override fun onFinishComposing(cursorPosition: Int) { - currentInputConnection?.run { - setSelection(cursorPosition, cursorPosition) - } - } - - override fun onFinishInputView(finishingInput: Boolean) { - super.onFinishInputView(finishingInput) - - onClearCandidates() - } - override fun onGetTextBeforeCursor(n: Int): CharSequence? { return this.currentInputConnection?.getTextBeforeCursor(n, 0) } - override fun onGetText(): CharSequence? { - val request = ExtractedTextRequest() - val text = currentInputConnection.getExtractedText(request, 0) - - return text?.text - } - override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { if (keyDownStats.keyCode != keyCode) { keyDownStats.keyCode = keyCode @@ -124,16 +121,16 @@ class WKT9: WKT9Interface, InputMethodService() { val hasLongDownMapping = key.mappings.hasLongDownMapping(InputMode.Word) val mappings = key.mappings.match( event = if (event.repeatCount > 0) Event.keyDownRepeat else Event.keyDown, - inputMode = inputManager.mode, + inputMode = inputModeManager.currentMode, packageName = currentInputEditorInfo.packageName, - alt = event.isAltPressed, + fn = event.isFunctionPressed, ctrl = event.isCtrlPressed, repeatCount = event.repeatCount ) mappings?.map { mapping -> if (mapping.command != null) { - inputManager.handler?.onRunCommand(mapping.command, key, event, keyDownStats) + inputHandler?.onRunCommand(mapping.command, key, event, keyDownStats) } if (mapping.overrideConsume) consume = mapping.consume @@ -161,17 +158,17 @@ class WKT9: WKT9Interface, InputMethodService() { var consume = key.consume val keyDownMS = event.eventTime - event.downTime val mappings = key.mappings.match( - event = if (keyDownMS >= longPressTimeout) Event.afterLongDown else Event.afterShortDown, - inputMode = inputManager.mode, + event = if (keyDownMS >= 400L) Event.afterLongDown else Event.afterShortDown, + inputMode = inputModeManager.currentMode, packageName = currentInputEditorInfo.packageName, - alt = event.isAltPressed, + fn = event.isFunctionPressed, ctrl = event.isCtrlPressed, repeatCount = event.repeatCount ) mappings?.map { mapping -> if (mapping.command != null) { - inputManager.handler?.onRunCommand(mapping.command, key, event, keyUpStats) + inputHandler?.onRunCommand(mapping.command, key, event, keyUpStats) } if (mapping.overrideConsume) consume = mapping.consume @@ -192,15 +189,15 @@ class WKT9: WKT9Interface, InputMethodService() { var consume = key.consume val mappings = key.mappings.match( event = Event.keyLongDown, - inputMode = inputManager.mode, + inputMode = inputModeManager.currentMode, packageName = currentInputEditorInfo.packageName, - alt = event.isAltPressed, + fn = event.isFunctionPressed, ctrl = event.isCtrlPressed ) mappings?.map { mapping -> if (mapping.command != null) { - inputManager.handler?.onRunCommand(mapping.command, key, event, keyDownStats) + inputHandler?.onRunCommand(mapping.command, key, event, keyDownStats) } if (mapping.overrideConsume) consume = mapping.consume @@ -213,50 +210,19 @@ class WKT9: WKT9Interface, InputMethodService() { } } - override fun onRecord(finishComposing: Boolean) { - if (finishComposing) finishComposingText() - if (!isInputViewShown) requestShowSelf(InputMethodManager.SHOW_IMPLICIT) - - record() - } - - override fun onReplaceText(text: String) { - currentInputConnection?.run { - beginBatchEdit() - setComposingRegion(0, text.length) - commitText(text, 1) - endBatchEdit() - } - } - override fun onShowInputRequested(flags: Int, configChange: Boolean): Boolean { - return (inputManager.mode != InputMode.Number && inputManager.mode != InputMode.Idle) + return ( + inputModeManager.currentMode != InputMode.Number && + inputModeManager.currentMode != InputMode.Idle + ) } override fun onStartInput(editorInfo: EditorInfo?, restarting: Boolean) { - if (editorInfo == null) return + val mode = inputModeManager.selectModeByEditor(editorInfo) - inputManager.selectHandler(editorInfo) - } + currentInputConnection?.requestCursorUpdates(InputConnection.CURSOR_UPDATE_MONITOR) - override fun onUpdateSelection( - oldSelStart: Int, - oldSelEnd: Int, - newSelStart: Int, - newSelEnd: Int, - candidatesStart: Int, - candidatesEnd: Int - ) { - inputManager.handler?.onUpdateCursorPosition(newSelEnd) - - super.onUpdateSelection( - oldSelStart, - oldSelEnd, - newSelStart, - newSelEnd, - candidatesStart, - candidatesEnd - ) + switchInputMode(mode) } override fun onStartIntent(intent: Intent) { @@ -265,12 +231,28 @@ class WKT9: WKT9Interface, InputMethodService() { } } - override fun onSwitchInputHandler(inputMode: InputMode) { - inputManager.switchToHandler(inputMode, cursorPosition) + override fun onUpdateCursorAnchorInfo(cursorAnchorInfo: CursorAnchorInfo?) { + cursorAnchorInfo?.let { + selectionStart = it.selectionStart + selectionEnd = it.selectionEnd + composing = if (it.composingTextStart == -1) { + if (composing) finishComposing() + + false + } else if (it.selectionEnd != (it.composingTextStart + it.composingText.length)) { + onCommit() + + true + } else true + } + + super.onUpdateCursorAnchorInfo(cursorAnchorInfo) } - override fun onTranscribe() { - transcribe() + override fun onSwitchInputHandler(inputMode: InputMode) { + val mode = inputModeManager.switchToMode(inputMode) + + switchInputMode(mode) } override fun onTriggerKeyEvent(event: KeyEvent) { @@ -282,15 +264,21 @@ class WKT9: WKT9Interface, InputMethodService() { else showStatusIcon(icon) } - private fun cancelCompose() { - if (!composing) return + override fun onWords(words: List) { + wordsView?.words = words + } - currentInputConnection?.let { - it.beginBatchEdit() - it.setComposingText("", 1) - it.finishComposingText() - it.endBatchEdit() - } + override fun onWordSelected(word: Word) { + this.onCompose(word.word) + this.inputHandler?.onWordSelected(word) + } + + override fun onNextWord() { + wordsView?.next() + } + + override fun onPreviousWord() { + wordsView?.previous() } private fun deleteText(beforeCursor: Int, afterCursor: Int) { @@ -299,28 +287,23 @@ class WKT9: WKT9Interface, InputMethodService() { } } - private fun finishComposingText() { - if (!composing) return + private fun finishComposing() { + wordsView?.clear() + inputHandler?.onFinishComposing() + } - currentInputConnection?.let { - it.finishComposingText() + private fun initializeDictionary(locale: Locale) { + val t9 = T9(this, locale) - inputManager.handler?.onCommitText() + t9.initializeWords() + } + + private fun switchInputMode(mode: InputMode) { + inputHandler = when(mode) { + InputMode.Word -> WordInputHandler(this, this, locale) + InputMode.Letter -> LetterInputHandler(this, this, locale) + InputMode.Number -> NumberInputHandler(this, this) + else -> IdleInputHandler(this, this) } - - composing = false - } - - private fun record() { - // The recorder must be busy... - if (recorder != null) return - - requestShowSelf(InputMethodManager.SHOW_IMPLICIT) - - whisper.record() - } - - private fun transcribe() { - whisper.transcribe() } } \ No newline at end of file diff --git a/app/src/main/java/net/mezimmah/wkt9/candidates/Candidates.kt b/app/src/main/java/net/mezimmah/wkt9/candidates/Candidates.kt deleted file mode 100644 index a5a0339..0000000 --- a/app/src/main/java/net/mezimmah/wkt9/candidates/Candidates.kt +++ /dev/null @@ -1,70 +0,0 @@ -package net.mezimmah.wkt9.candidates - -import android.view.View -import android.view.inputmethod.InputMethodManager -import android.widget.LinearLayout -import android.widget.TextView -import net.mezimmah.wkt9.R -import net.mezimmah.wkt9.WKT9 -import net.mezimmah.wkt9.inputmode.InputManager - -class Candidates( - private val context: WKT9, - private val inputManager: InputManager, - - ui: View -) { - private val candidatesView: LinearLayout = ui.findViewById(R.id.suggestions) - - fun clear() { - candidatesView.removeAllViews() - } - - fun load(candidates: List, current: Int?) { - clear() - - candidates.forEachIndexed { index, candidate -> - val layout = if (index == current) R.layout.current_suggestion else R.layout.suggestion - val candidateView = context.layoutInflater.inflate(layout, null) - val textView = candidateView.findViewById(R.id.suggestion_text) - - textView.text = candidate - - candidateView.setOnClickListener { view -> - getIndex(view)?.let { index -> - load(candidates, index) - } - } - - candidateView.setOnLongClickListener { - val view = it as LinearLayout - val suggestion = view.findViewById(R.id.suggestion_text) - val text = suggestion.text - - inputManager.handler?.onLongClickCandidate(text.toString()) - - true - } - - if (index == current) { - inputManager.handler?.onCandidateSelected(index) - } - - candidatesView.addView(candidateView) - } - - if (!context.isInputViewShown) { - context.requestShowSelf(InputMethodManager.SHOW_IMPLICIT) - } - } - - private fun getIndex(candidate: View): Int? { - for (i in 0 until candidatesView.childCount) { - val child: View = candidatesView.getChildAt(i) - - if (candidate == child) return i - } - - return null - } -} \ No newline at end of file diff --git a/app/src/main/java/net/mezimmah/wkt9/dao/WordDao.kt b/app/src/main/java/net/mezimmah/wkt9/dao/WordDao.kt index d38551d..5a2731e 100644 --- a/app/src/main/java/net/mezimmah/wkt9/dao/WordDao.kt +++ b/app/src/main/java/net/mezimmah/wkt9/dao/WordDao.kt @@ -14,10 +14,10 @@ interface WordDao { @Query("DELETE FROM word WHERE word = :word AND locale = :locale") fun delete(word: String, locale: String) - @Query("SELECT * FROM word WHERE code LIKE :code || '%' " + + @Query("SELECT * FROM word WHERE code LIKE :code || '%' AND locale = :locale " + "ORDER BY length, weight DESC LIMIT :limit") - suspend fun findCandidates(code: String, limit: Int = 10): List + suspend fun findCandidates(locale: String, code: String, limit: Int = 10): List - @Query("UPDATE word SET weight = weight + 1 WHERE word=:word") - suspend fun increaseWeight(word: String) + @Query("UPDATE word SET weight = weight + 1 WHERE id=:id") + suspend fun increaseWeight(id: Int) } \ No newline at end of file diff --git a/app/src/main/java/net/mezimmah/wkt9/entity/Word.kt b/app/src/main/java/net/mezimmah/wkt9/entity/Word.kt index 42a1fe0..8dfe01b 100644 --- a/app/src/main/java/net/mezimmah/wkt9/entity/Word.kt +++ b/app/src/main/java/net/mezimmah/wkt9/entity/Word.kt @@ -4,7 +4,13 @@ import androidx.room.Entity import androidx.room.Index import androidx.room.PrimaryKey -@Entity(indices = [Index(value = ["word"]), Index(value = ["code"]), Index(value = ["locale"]), Index(value = ["word", "locale"], unique = true)]) +@Entity(indices = [ + Index(value = ["word"]), + Index(value = ["code"]), + Index(value = ["locale"]), + Index(value = ["word", "locale"], unique = true), + Index(value = ["code", "locale"], unique = false) +]) data class Word( var word: String, var code: String, diff --git a/app/src/main/java/net/mezimmah/wkt9/inputmode/IdleInputHandler.kt b/app/src/main/java/net/mezimmah/wkt9/inputmode/IdleInputHandler.kt deleted file mode 100644 index 41cc0f1..0000000 --- a/app/src/main/java/net/mezimmah/wkt9/inputmode/IdleInputHandler.kt +++ /dev/null @@ -1,42 +0,0 @@ -package net.mezimmah.wkt9.inputmode - -import android.content.Intent -import android.net.Uri -import android.util.Log -import android.view.KeyEvent -import net.mezimmah.wkt9.R -import net.mezimmah.wkt9.WKT9 -import net.mezimmah.wkt9.WKT9Interface -import net.mezimmah.wkt9.keypad.Command -import net.mezimmah.wkt9.keypad.Key -import net.mezimmah.wkt9.keypad.KeyEventStat - -class IdleInputHandler(wkt9: WKT9Interface, context: WKT9) : InputHandler(wkt9, context) { - init { - mode = InputMode.Idle - capMode = null - } - - override fun onRunCommand(command: Command, key: Key, event: KeyEvent, stats: KeyEventStat) { - when (command) { - Command.DIAL -> dial() - Command.CAMERA -> triggerKeyEvent(KeyEvent.KEYCODE_CAMERA) - Command.NUMBER -> triggerOriginalKeyEvent(key) - else -> Log.d(tag, "Command not implemented: $command") - } - } - - override fun onStart(typeClass: Int, typeVariations: Int, typeFlags: Int) { - wkt9.onUpdateStatusIcon(R.drawable.idle_en_us_na) - } - - private fun dial() { - val uri = "tel:" - val intent = Intent(Intent.ACTION_VIEW) - - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK - intent.data = Uri.parse(uri) - - wkt9.onStartIntent(intent) - } -} \ No newline at end of file diff --git a/app/src/main/java/net/mezimmah/wkt9/inputmode/InputHandler.kt b/app/src/main/java/net/mezimmah/wkt9/inputmode/InputHandler.kt deleted file mode 100644 index a8a9419..0000000 --- a/app/src/main/java/net/mezimmah/wkt9/inputmode/InputHandler.kt +++ /dev/null @@ -1,117 +0,0 @@ -package net.mezimmah.wkt9.inputmode - -import android.text.InputType -import android.view.KeyEvent -import net.mezimmah.wkt9.WKT9 -import net.mezimmah.wkt9.WKT9Interface -import net.mezimmah.wkt9.keypad.Command -import net.mezimmah.wkt9.keypad.Key -import net.mezimmah.wkt9.keypad.KeyEventStat -import net.mezimmah.wkt9.keypad.KeyLayout -import net.mezimmah.wkt9.keypad.Keypad - -open class InputHandler( - override val wkt9: WKT9Interface, - override val context: WKT9 -): InputHandlerInterface { - protected val tag = "WKT9" - - protected var keypad: Keypad = Keypad(KeyLayout.en_US, KeyLayout.numeric) - - override lateinit var mode: InputMode - protected set - - override var capMode: Int? = null - protected set - - override var cursorPosition: Int = 0 - protected set - - override fun onCandidateSelected(index: Int) {} - - override fun onCommitText() {} - - override fun onComposeText(text: CharSequence, composingTextStart: Int, composingTextEnd: Int) {} - - override fun onFinish() {} - - override fun onInsertText(text: CharSequence) {} - - override fun onLongClickCandidate(text: String) {} - - override fun onRunCommand(command: Command, key: Key, event: KeyEvent, stats: KeyEventStat) {} - - override fun onStart(typeClass: Int, typeVariations: Int, typeFlags: Int) {} - - override fun onUpdateCursorPosition(cursorPosition: Int) { - this.cursorPosition = cursorPosition - } - - protected open fun capMode(key: Key) { - val modes = listOf( - InputType.TYPE_TEXT_FLAG_CAP_SENTENCES, - InputType.TYPE_TEXT_FLAG_CAP_WORDS, - InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS, - null - ) - var index = modes.indexOf(capMode) - - when (key) { - Key.B2 -> { - if (index == 0) index = modes.count() - - index-- - } - else -> index++ - } - - capMode = modes[index % modes.count()] - } - - protected open fun finalizeWordOrSentence(stats: KeyEventStat) { - val candidates = listOf(" ", ". ", "? ", "! ", ", ", ": ", "; ") - - wkt9.onCandidates( - candidates = candidates, - current = stats.repeats % candidates.count() - ) - } - - protected open fun getCursorPositionInfo(text: CharSequence): CursorPositionInfo { - val trimmed = text.trimEnd() - val regex = "[.!?]$".toRegex() - val startSentence = text.isEmpty() || regex.containsMatchIn(trimmed) - val startWord = text.isEmpty() || (startSentence || trimmed.length < text.length) - - return CursorPositionInfo( - startSentence = startSentence, - startWord = startWord - ) - } - - protected fun getDefaultCapMode(typeFlags: Int): Int? { - val modes = listOf( - InputType.TYPE_TEXT_FLAG_CAP_SENTENCES, - InputType.TYPE_TEXT_FLAG_CAP_WORDS, - InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS - ) - - modes.forEach { - if (typeFlags.and(it) == it) return it - } - - return null - } - - protected fun triggerKeyEvent(keyCode: Int) { - val down = KeyEvent(KeyEvent.ACTION_DOWN, keyCode) - val up = KeyEvent(KeyEvent.ACTION_UP, keyCode) - - wkt9.onTriggerKeyEvent(down) - wkt9.onTriggerKeyEvent(up) - } - - protected fun triggerOriginalKeyEvent(key: Key) { - triggerKeyEvent(key.keyCode) - } -} \ No newline at end of file diff --git a/app/src/main/java/net/mezimmah/wkt9/inputmode/InputHandlerInterface.kt b/app/src/main/java/net/mezimmah/wkt9/inputmode/InputHandlerInterface.kt deleted file mode 100644 index cd1a69c..0000000 --- a/app/src/main/java/net/mezimmah/wkt9/inputmode/InputHandlerInterface.kt +++ /dev/null @@ -1,34 +0,0 @@ -package net.mezimmah.wkt9.inputmode - -import android.view.KeyEvent -import net.mezimmah.wkt9.WKT9 -import net.mezimmah.wkt9.WKT9Interface -import net.mezimmah.wkt9.keypad.Command -import net.mezimmah.wkt9.keypad.Key -import net.mezimmah.wkt9.keypad.KeyEventStat - -interface InputHandlerInterface { - val wkt9: WKT9Interface - val context: WKT9 - val mode: InputMode - val capMode: Int? - val cursorPosition: Int? - - fun onCandidateSelected(index: Int) - - fun onComposeText(text: CharSequence, composingTextStart: Int, composingTextEnd: Int) - - fun onCommitText() - - fun onFinish() - - fun onInsertText(text: CharSequence) - - fun onLongClickCandidate(text: String) - - fun onRunCommand(command: Command, key: Key, event: KeyEvent, stats: KeyEventStat) - - fun onStart(typeClass: Int, typeVariations: Int, typeFlags: Int) - - fun onUpdateCursorPosition(cursorPosition: Int) -} \ No newline at end of file diff --git a/app/src/main/java/net/mezimmah/wkt9/inputmode/InputManager.kt b/app/src/main/java/net/mezimmah/wkt9/inputmode/InputManager.kt deleted file mode 100644 index a73c512..0000000 --- a/app/src/main/java/net/mezimmah/wkt9/inputmode/InputManager.kt +++ /dev/null @@ -1,98 +0,0 @@ -package net.mezimmah.wkt9.inputmode - -import android.text.InputType -import android.util.Log -import android.view.inputmethod.EditorInfo -import net.mezimmah.wkt9.R -import net.mezimmah.wkt9.WKT9 - -class InputManager(val context: WKT9) { - private val idleInputHandler: IdleInputHandler = IdleInputHandler(context, context) - private val letterInputHandler: LetterInputHandler = LetterInputHandler(context, context) - private val numberInputHandler: NumberInputHandler = NumberInputHandler(context, context) - private val wordInputHandler: WordInputHandler = WordInputHandler(context, context) - - private val numericClasses = listOf( - InputType.TYPE_CLASS_DATETIME, - InputType.TYPE_CLASS_NUMBER, - InputType.TYPE_CLASS_PHONE - ) - - private val letterVariations = listOf( - InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, - InputType.TYPE_TEXT_VARIATION_URI, - InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS, - InputType.TYPE_TEXT_VARIATION_PASSWORD, - InputType.TYPE_TEXT_VARIATION_FILTER, - InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD, - InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD, - InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS, - InputType.TYPE_TEXT_VARIATION_PERSON_NAME - ) - - private var typeClass: Int = 0 - private var typeVariation: Int = 0 - private var typeFlags: Int = 0 - private var allowSuggestions: Boolean = false - - var handler: InputHandler? = null - private set - - var mode: InputMode = InputMode.Idle - private set - - fun selectHandler(editor: EditorInfo) { - val inputType = editor.inputType - val override = selectOverride(editor.packageName) - - typeClass = inputType.and(InputType.TYPE_MASK_CLASS) - typeVariation = inputType.and(InputType.TYPE_MASK_VARIATION) - typeFlags = inputType.and(InputType.TYPE_MASK_FLAGS) - allowSuggestions = typeFlags != InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS - - if (override != null) return switchToHandler(override, editor.initialSelEnd) - - val mode = if (numericClasses.contains(typeClass)) { - InputMode.Number - } else if (typeClass == InputType.TYPE_CLASS_TEXT) { - if (letterVariations.contains(typeVariation) || mode == InputMode.Letter) { - InputMode.Letter - } else { - InputMode.Word - } - } else { - InputMode.Idle - } - - switchToHandler(mode, editor.initialSelEnd) - } - - fun switchToHandler(inputMode: InputMode, cursorPosition: Int) { - val lastHandler = this.handler - val cursor: Int = lastHandler?.cursorPosition ?: cursorPosition - val newHandler = when (inputMode) { - InputMode.Word -> wordInputHandler - InputMode.Letter -> letterInputHandler - InputMode.Number -> numberInputHandler - else -> idleInputHandler - } - - newHandler.apply { - onStart(typeClass, typeVariation, typeFlags) - onUpdateCursorPosition(cursor) - } - - lastHandler?.onFinish() - - this.mode = inputMode - this.handler = newHandler - } - - private fun selectOverride(packageName: String): InputMode? { - val numeric = context.resources.getStringArray(R.array.input_mode_numeric) - - return if (numeric.contains(packageName)) { - InputMode.Number - } else null - } -} \ No newline at end of file diff --git a/app/src/main/java/net/mezimmah/wkt9/inputmode/InputMode.kt b/app/src/main/java/net/mezimmah/wkt9/inputmode/InputMode.kt index 1f1f3d4..4e031dd 100644 --- a/app/src/main/java/net/mezimmah/wkt9/inputmode/InputMode.kt +++ b/app/src/main/java/net/mezimmah/wkt9/inputmode/InputMode.kt @@ -4,7 +4,7 @@ import net.mezimmah.wkt9.R enum class InputMode(val icon: Int) { Word(R.drawable.word_en_us_cap), - Letter(R.drawable.alpha_en_us_cap), - Number(R.drawable.numeric_en_us_num), + Letter(R.drawable.letter_cap), + Number(R.drawable.number_input), Idle(R.drawable.wkt9) } \ No newline at end of file diff --git a/app/src/main/java/net/mezimmah/wkt9/inputmode/InputModeManager.kt b/app/src/main/java/net/mezimmah/wkt9/inputmode/InputModeManager.kt new file mode 100644 index 0000000..64cb98a --- /dev/null +++ b/app/src/main/java/net/mezimmah/wkt9/inputmode/InputModeManager.kt @@ -0,0 +1,72 @@ +package net.mezimmah.wkt9.inputmode + +import android.text.InputType +import android.view.inputmethod.EditorInfo +import net.mezimmah.wkt9.R +import net.mezimmah.wkt9.WKT9IME + +class InputModeManager(private val context: WKT9IME) { + private val numericClasses = listOf( + InputType.TYPE_CLASS_DATETIME, + InputType.TYPE_CLASS_NUMBER, + InputType.TYPE_CLASS_PHONE + ) + + private val letterVariations = listOf( + InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, + InputType.TYPE_TEXT_VARIATION_URI, + InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS, + InputType.TYPE_TEXT_VARIATION_PASSWORD, + InputType.TYPE_TEXT_VARIATION_FILTER, + InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD, + InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD, + InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS, + InputType.TYPE_TEXT_VARIATION_PERSON_NAME + ) + + var currentMode: InputMode = InputMode.Idle + private set + + private var lastMode: InputMode = InputMode.Idle + + fun selectModeByEditor(editor: EditorInfo?): InputMode { + if (editor == null) return switchToMode(InputMode.Idle) + + val override = getPackageOverride(editor.packageName) + val inputType = editor.inputType + val typeClass = inputType.and(InputType.TYPE_MASK_CLASS) + val typeVariation = inputType.and(InputType.TYPE_MASK_VARIATION) + + if (override != null) return switchToMode(override) + +// allowSuggestions = typeFlags != InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS + + val mode = if (numericClasses.contains(typeClass)) { + InputMode.Number + } else if (typeClass == InputType.TYPE_CLASS_TEXT) { + if (letterVariations.contains(typeVariation) || lastMode == InputMode.Letter) { + InputMode.Letter + } else { + InputMode.Word + } + } else { + InputMode.Idle + } + + return switchToMode(mode) + } + + fun switchToMode(inputMode: InputMode): InputMode { + lastMode = currentMode + currentMode = inputMode + + return inputMode + } + + private fun getPackageOverride(packageName: String): InputMode? { + val numeric = context.resources.getStringArray(R.array.input_mode_numeric) + + return if (numeric.contains(packageName)) InputMode.Number + else null + } +} \ No newline at end of file diff --git a/app/src/main/java/net/mezimmah/wkt9/inputmode/LetterInputHandler.kt b/app/src/main/java/net/mezimmah/wkt9/inputmode/LetterInputHandler.kt deleted file mode 100644 index 4a236b5..0000000 --- a/app/src/main/java/net/mezimmah/wkt9/inputmode/LetterInputHandler.kt +++ /dev/null @@ -1,314 +0,0 @@ -package net.mezimmah.wkt9.inputmode - -import android.content.Context -import android.text.InputType -import android.util.Log -import android.view.KeyEvent -import android.view.textservice.SentenceSuggestionsInfo -import android.view.textservice.SpellCheckerSession -import android.view.textservice.SuggestionsInfo -import android.view.textservice.TextServicesManager -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import net.mezimmah.wkt9.R -import net.mezimmah.wkt9.WKT9 -import net.mezimmah.wkt9.WKT9Interface -import net.mezimmah.wkt9.dao.WordDao -import net.mezimmah.wkt9.db.AppDatabase -import net.mezimmah.wkt9.entity.Word -import net.mezimmah.wkt9.exception.MissingLetterCode -import net.mezimmah.wkt9.keypad.Command -import net.mezimmah.wkt9.keypad.Key -import net.mezimmah.wkt9.keypad.KeyEventStat -import net.mezimmah.wkt9.keypad.KeyLayout -import java.util.Locale - -class LetterInputHandler(wkt9: WKT9Interface, context: WKT9): SpellCheckerSession.SpellCheckerSessionListener, InputHandler(wkt9, context) { - private var db: AppDatabase - private var wordDao: WordDao - private var locale: Locale? = null - private var spellCheckerSession: SpellCheckerSession? = null - - private val candidates = mutableListOf() - private var candidateIndex: Int? = null - private var composeRangeStart: Int? = null - private var composeRangeEnd: Int? = null - - private var sentenceStart: Boolean = false - private var wordStart: Boolean = false - - private val queryScope = CoroutineScope(Dispatchers.Main + SupervisorJob()) - private val timeoutScope = CoroutineScope(Dispatchers.Main + SupervisorJob()) - private var timeoutJob: Job? = null - - private val content = StringBuilder() - - init { - val textServiceManager = context.getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE) as TextServicesManager - mode = InputMode.Letter - capMode = InputType.TYPE_TEXT_FLAG_CAP_SENTENCES - - db = AppDatabase.getInstance(context) - wordDao = db.getWordDao() - - locale = Locale.forLanguageTag("en-US") - spellCheckerSession = textServiceManager.newSpellCheckerSession( - null, - locale, - this, - false - ) - - Log.d(tag, "Started $mode input mode.") - } - - override fun capMode(key: Key) { - super.capMode(key) - - updateIcon() - } - - override fun finalizeWordOrSentence(stats: KeyEventStat) { - if (stats.repeats == 0) { - timeoutJob?.cancel() - - clearCandidates(true) - storeLastWord() - } - - candidates.addAll(listOf(" ", ". ", "? ", "! ", ", ", ": ", "; ")) - wkt9.onCandidates(candidates, stats.repeats % candidates.count()) - } - - override fun onCandidateSelected(index: Int) { - val candidate = candidates[index] - val rangeStart = composeRangeStart ?: cursorPosition - val rangeEnd = composeRangeEnd ?: cursorPosition - - content.replace(rangeStart, rangeEnd, candidate) - - candidateIndex = index - composeRangeStart = rangeStart - composeRangeEnd = rangeStart + candidate.length - - wkt9.onCompose(candidate, rangeStart, rangeEnd) - } - - override fun onFinish() { - timeoutJob?.cancel() - - clearCandidates(true) - - cursorPosition = 0 - sentenceStart = false - wordStart = false - - content.clear() - } - - override fun onGetSuggestions(results: Array?) { - TODO("Not yet implemented") - } - - override fun onGetSentenceSuggestions(results: Array?) { - TODO("Not yet implemented") - } - - override fun onInsertText(text: CharSequence) { - clearCandidates(true) - - content.replace(cursorPosition, cursorPosition, text.toString()) - wkt9.onCompose(text.toString(), cursorPosition, cursorPosition) - wkt9.onFinishComposing(cursorPosition + text.length) - } - - override fun onRunCommand(command: Command, key: Key, event: KeyEvent, stats: KeyEventStat) { - when (command) { - Command.CAP_MODE -> capMode(key) - Command.CHARACTER -> composeCharacter(key, stats) - Command.DELETE -> delete() - Command.INPUT_MODE -> inputMode(key) - Command.MOVE_CURSOR -> moveCursor() - Command.NUMBER -> triggerOriginalKeyEvent(key) - Command.RECORD -> wkt9.onRecord(true) - Command.SPACE -> finalizeWordOrSentence(stats) - Command.TRANSCRIBE -> wkt9.onTranscribe() - else -> Log.d(tag, "Command not implemented: $command") - } - } - - override fun onUpdateCursorPosition(cursorPosition: Int) { - super.onUpdateCursorPosition(cursorPosition) - - try { - val info = getCursorPositionInfo(content.substring(0, cursorPosition)) - - sentenceStart = info.startSentence - wordStart = info.startWord - } catch (e: Exception) { - Log.d(tag, "Cursor position out of range.\nContent: $content\nCursor position: $cursorPosition", e) - } - - updateIcon() - } - - override fun onStart(typeClass: Int, typeVariations: Int, typeFlags: Int) { - capMode = getDefaultCapMode(typeFlags) - - // Get current editor content on start - wkt9.onGetText()?.let { - content.replace(0, content.length, it.toString()) - } ?: content.clear() - } - - private fun composeCharacter(key: Key, stats: KeyEventStat) { - val layout = KeyLayout.en_US[key] ?: return - val capitalize = Capitalize(capMode) - - if (stats.repeats == 0) clearCandidates(true) - - layout.forEach { - candidates.add(capitalize.character(it, sentenceStart, wordStart)) - } - - wkt9.onCandidates( - candidates = candidates, - current = stats.repeats % candidates.count() - ) - - timeoutJob?.cancel() - timeoutJob = timeoutScope.launch { - delay(400) - clearCandidates(true) -// getSuggestions() - } - } - - private fun clearCandidates(finishComposing: Boolean) { - if (finishComposing && candidateIndex != null) { - wkt9.onFinishComposing(cursorPosition) - } - - candidateIndex = null - composeRangeStart = null - composeRangeEnd = null - - candidates.clear() - wkt9.onClearCandidates() - } - - private fun delete() { - if (candidateIndex != null) undoCandidate() - else if (content.isNotEmpty()) { - content.deleteAt(content.length - 1) - wkt9.onDeleteText(1, 0, true) - } - } - - private fun getLastWord(): String { - val beforeCursor = content.substring(0, cursorPosition) - val words = beforeCursor.split("\\s+".toRegex()) - - return words.last() - } - - private fun inputMode(key: Key) { - if (key == Key.B4) wkt9.onSwitchInputHandler(InputMode.Word) - else wkt9.onSwitchInputHandler(InputMode.Number) - } - - private fun moveCursor() { - timeoutJob?.cancel() - clearCandidates(true) - } - - private fun storeLastWord() { - val lastWord = getLastWord() - // We're not storing single char words... - if (lastWord.length < 2) return - - try { - val codeword = keypad.getCodeForWord(lastWord) - val word = Word( - word = lastWord, - code = codeword, - length = lastWord.length, - weight = 1, - locale = "en_US" - ) - - queryScope.launch { - wordDao.insert(word) - } - } catch (e: MissingLetterCode) { - Log.d(tag, "Ignoring word because it contains characters unknown.") - } - } - - private fun undoCandidate() { - val rangeStart = composeRangeStart ?: cursorPosition - val rangeEnd = composeRangeEnd ?: cursorPosition - - // Remove text codeword produced from editor - wkt9.onCompose("", rangeStart, rangeEnd) - - // Remove text codeword produced from content - content.deleteRange(rangeStart, rangeEnd) - - clearCandidates(false) - } - - private fun updateIcon() { - val icon = when (capMode) { - InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS -> R.drawable.alpha_en_us_upper - InputType.TYPE_TEXT_FLAG_CAP_WORDS, - InputType.TYPE_TEXT_FLAG_CAP_SENTENCES -> if (wordStart) R.drawable.alpha_en_us_cap else R.drawable.alpha_en_us_lower - else -> R.drawable.alpha_en_us_lower - } - - wkt9.onUpdateStatusIcon(icon) - } -} -// -// override fun onGetSentenceSuggestions(results: Array?) { -// results?.map { -// val suggestions = it.getSuggestionsInfoAt(0) -// -// for (index in 0 until suggestions.suggestionsCount) { -// val suggestion = suggestions.getSuggestionAt(index) -// -// candidates.add(suggestion) -// } -// } -// -// if (candidates.isEmpty()) return -// -// candidateSource = CandidateSource.Dictionary -// -// wkt9.onCandidates( -// candidates = candidates, -// current = null -// ) -// } - -// private fun getSuggestions() { -// val lastWord = getLastWord() -// -// if (lastWord.length < 3) return -// -// val words = arrayOf( -// TextInfo( -// lastWord.plus("#"), // Add hash to string to get magic performance -// 0, -// lastWord.length + 1, // We added the hash, remember -// 0, -// 0 -// ) -// ) -// -// spellCheckerSession?.getSentenceSuggestions(words, 15) -// } \ No newline at end of file diff --git a/app/src/main/java/net/mezimmah/wkt9/inputmode/NumberInputHandler.kt b/app/src/main/java/net/mezimmah/wkt9/inputmode/NumberInputHandler.kt deleted file mode 100644 index 40623fe..0000000 --- a/app/src/main/java/net/mezimmah/wkt9/inputmode/NumberInputHandler.kt +++ /dev/null @@ -1,42 +0,0 @@ -package net.mezimmah.wkt9.inputmode - -import android.util.Log -import android.view.KeyEvent -import net.mezimmah.wkt9.R -import net.mezimmah.wkt9.WKT9 -import net.mezimmah.wkt9.WKT9Interface -import net.mezimmah.wkt9.keypad.Command -import net.mezimmah.wkt9.keypad.Key -import net.mezimmah.wkt9.keypad.KeyEventStat - -class NumberInputHandler(wkt9: WKT9Interface, context: WKT9) : InputHandler(wkt9, context) { - init { - mode = InputMode.Number - capMode = null - - Log.d(tag, "Started $mode input mode.") - } - - override fun onRunCommand(command: Command, key: Key, event: KeyEvent, stats: KeyEventStat) { - when (command) { - Command.CAP_MODE -> capMode(key) - Command.DELETE -> delete() - Command.INPUT_MODE -> inputMode(key) - Command.SPACE -> finalizeWordOrSentence(stats) - else -> Log.d(tag, "Command not implemented: $command") - } - } - - override fun onStart(typeClass: Int, typeVariations: Int, typeFlags: Int) { - wkt9.onUpdateStatusIcon(R.drawable.numeric_en_us_num) - } - - private fun inputMode(key: Key) { - if (key == Key.B4) wkt9.onSwitchInputHandler(InputMode.Letter) - else wkt9.onSwitchInputHandler(InputMode.Word) - } - - private fun delete() { - wkt9.onDeleteText(1, 0, true) - } -} \ No newline at end of file diff --git a/app/src/main/java/net/mezimmah/wkt9/inputmode/CursorPositionInfo.kt b/app/src/main/java/net/mezimmah/wkt9/inputmode/TextPositionInfo.kt similarity index 75% rename from app/src/main/java/net/mezimmah/wkt9/inputmode/CursorPositionInfo.kt rename to app/src/main/java/net/mezimmah/wkt9/inputmode/TextPositionInfo.kt index b203c70..553c6f5 100644 --- a/app/src/main/java/net/mezimmah/wkt9/inputmode/CursorPositionInfo.kt +++ b/app/src/main/java/net/mezimmah/wkt9/inputmode/TextPositionInfo.kt @@ -1,6 +1,6 @@ package net.mezimmah.wkt9.inputmode -data class CursorPositionInfo( +data class TextPositionInfo( val startWord: Boolean, val startSentence: Boolean ) diff --git a/app/src/main/java/net/mezimmah/wkt9/inputmode/WordInputHandler.kt b/app/src/main/java/net/mezimmah/wkt9/inputmode/WordInputHandler.kt deleted file mode 100644 index 73d60b9..0000000 --- a/app/src/main/java/net/mezimmah/wkt9/inputmode/WordInputHandler.kt +++ /dev/null @@ -1,294 +0,0 @@ -package net.mezimmah.wkt9.inputmode - -import android.text.InputType -import android.util.Log -import android.view.KeyEvent -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.launch -import net.mezimmah.wkt9.R -import net.mezimmah.wkt9.WKT9 -import net.mezimmah.wkt9.WKT9Interface -import net.mezimmah.wkt9.dao.SettingDao -import net.mezimmah.wkt9.dao.WordDao -import net.mezimmah.wkt9.db.AppDatabase -import net.mezimmah.wkt9.keypad.Command -import net.mezimmah.wkt9.keypad.Key -import net.mezimmah.wkt9.keypad.KeyEventStat -import net.mezimmah.wkt9.keypad.KeyLayout -import net.mezimmah.wkt9.t9.T9 - -class WordInputHandler(wkt9: WKT9Interface, context: WKT9) : InputHandler(wkt9, context) { - private val content = StringBuilder() - private val codeword = StringBuilder() - private val candidates = mutableListOf() - private var candidateIndex: Int? = null - private var composeRangeStart: Int? = null - private var composeRangeEnd: Int? = null - - private var db: AppDatabase - private var wordDao: WordDao - private var settingDao: SettingDao - - private var t9: T9 - - private val queryScope = CoroutineScope(Dispatchers.Main + SupervisorJob()) - private val ioScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) - private var queryJob: Job? = null - - private var staleCodeword = false - private var sentenceStart: Boolean = false - private var wordStart: Boolean = false - - init { - mode = InputMode.Word - capMode = InputType.TYPE_TEXT_FLAG_CAP_SENTENCES - - db = AppDatabase.getInstance(context) - wordDao = db.getWordDao() - settingDao = db.getSettingDao() - - t9 = T9(context, keypad, settingDao, wordDao) - - // Todo: Hardcoded language - t9.initializeWords("en_US") - - Log.d(tag, "Started $mode input mode.") - } - - override fun capMode(key: Key) { - super.capMode(key) - - updateIcon() - - if (codeword.isNotEmpty()) handleCodewordChange(codeword) - } - - override fun finalizeWordOrSentence(stats: KeyEventStat) { - if (codeword.isNotEmpty()) commit() - - candidates.addAll(listOf(" ", ". ", "? ", "! ", ", ", ": ", "; ")) - wkt9.onCandidates(candidates, stats.repeats % candidates.count()) - } - - override fun onCandidateSelected(index: Int) { - val candidate = candidates[index] - val rangeStart = composeRangeStart ?: cursorPosition - val rangeEnd = composeRangeEnd ?: cursorPosition - - content.replace(rangeStart, rangeEnd, candidate) - - candidateIndex = index - composeRangeStart = rangeStart - composeRangeEnd = rangeStart + candidate.length - - wkt9.onCompose(candidate, rangeStart, rangeEnd) - } - - override fun onInsertText(text: CharSequence) { - commit() - - content.replace(cursorPosition, cursorPosition, text.toString()) - wkt9.onCompose(text.toString(), cursorPosition, cursorPosition) - wkt9.onFinishComposing(cursorPosition + text.length) - } - - override fun onFinish() { - queryJob?.cancel() - - commit() - - cursorPosition = 0 - sentenceStart = false - wordStart = false - - content.clear() - } - - override fun onLongClickCandidate(text: String) { - ioScope.launch { - wordDao.delete(text, "en_US") - - handleCodewordChange(codeword) - } - } - - override fun onRunCommand(command: Command, key: Key, event: KeyEvent, stats: KeyEventStat) { - when (command) { - Command.CAP_MODE -> capMode(key) - Command.CHARACTER -> buildCodeword(key) - Command.DELETE -> delete() - Command.INPUT_MODE -> inputMode(key) - Command.MOVE_CURSOR -> moveCursor() - Command.NUMBER -> triggerOriginalKeyEvent(key) - Command.RECORD -> record() - Command.SPACE -> finalizeWordOrSentence(stats) - Command.TRANSCRIBE -> wkt9.onTranscribe() - else -> Log.d(tag, "Command not implemented: $command") - } - } - - override fun onStart(typeClass: Int, typeVariations: Int, typeFlags: Int) { - capMode = getDefaultCapMode(typeFlags) - - // Get current editor content on start - wkt9.onGetText()?.let { - content.replace(0, content.length, it.toString()) - } ?: content.clear() - } - - override fun onUpdateCursorPosition(cursorPosition: Int) { - super.onUpdateCursorPosition(cursorPosition) - - try { - val info = getCursorPositionInfo(content.substring(0, cursorPosition)) - - sentenceStart = info.startSentence - wordStart = info.startWord - } catch (e: Exception) { - Log.d(tag, "Cursor position out of range", e) - } - - updateIcon() - } - - private fun buildCodeword(key: Key) { - // Don't build fruitless codeword - if (staleCodeword) return - - if (codeword.isEmpty() && candidateIndex != null) clearCandidates(true) - - val code = KeyLayout.numeric[key] - - codeword.append(code) - - handleCodewordChange(codeword) - } - - private fun clearCandidates(finishComposing: Boolean) { - candidateIndex = null - composeRangeStart = null - composeRangeEnd = null - - candidates.clear() - - wkt9.onClearCandidates() - - if (finishComposing) wkt9.onFinishComposing(cursorPosition) - } - - private fun clearCodeword(increaseWordWeight: Boolean) { - staleCodeword = false - - codeword.clear() - - if (!increaseWordWeight) return - - candidateIndex?.let { - val candidate = candidates[it] - - if (candidate.isNotEmpty()) increaseWordWeight(candidate) - } - } - - private fun commit() { - if (codeword.isNotEmpty()) clearCodeword(true) - if (candidates.isNotEmpty()) clearCandidates(true) - } - - private fun delete() { - staleCodeword = false - - if (codeword.length > 1) reduceCodeword() - else if (codeword.isNotEmpty()) undoCodeword() - else if (candidateIndex != null) undoCandidate() - else if (content.isNotEmpty()) { - content.deleteAt(content.length - 1) - wkt9.onDeleteText(1, 0, false) - } - } - - private fun handleCodewordChange(codeword: StringBuilder) { - queryJob?.cancel() - - queryJob = queryScope.launch { - queryT9Candidates(codeword, 25) - - if (candidates.isEmpty()) staleCodeword = true - else { - wkt9.onCandidates( - candidates = candidates, - current = 0 - ) - } - } - } - - private fun increaseWordWeight(word: String) { - queryScope.launch { - wordDao.increaseWeight(word) - } - } - - private fun inputMode(key: Key) { - if (key == Key.B4) wkt9.onSwitchInputHandler(InputMode.Number) - else wkt9.onSwitchInputHandler(InputMode.Letter) - } - - private fun moveCursor() { - commit() - } - - private fun record() { - commit() - wkt9.onRecord(true) - } - - private fun reduceCodeword() { - codeword.deleteAt(codeword.length - 1) - - handleCodewordChange(codeword) - } - - private fun updateIcon() { - val icon = when (capMode) { - InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS -> R.drawable.word_en_us_upper - InputType.TYPE_TEXT_FLAG_CAP_WORDS, - InputType.TYPE_TEXT_FLAG_CAP_SENTENCES -> if (wordStart) R.drawable.word_en_us_cap else R.drawable.word_en_us_lower - else -> R.drawable.word_en_us_lower - } - - wkt9.onUpdateStatusIcon(icon) - } - - private fun undoCodeword() { - undoCandidate() - clearCodeword(false) - } - - private fun undoCandidate() { - val rangeStart = composeRangeStart ?: cursorPosition - val rangeEnd = composeRangeEnd ?: cursorPosition - - // Remove text codeword produced from editor - wkt9.onCompose("", rangeStart, rangeEnd) - - // Remove text codeword produced from content - content.deleteRange(rangeStart, rangeEnd) - - clearCandidates(false) - } - - private suspend fun queryT9Candidates(codeWord: StringBuilder, limit: Int = 10) { - val results = wordDao.findCandidates(codeWord.toString(), limit) - val capitalize = Capitalize(capMode) - - candidates.clear() - - results.forEach { result -> - candidates.add(capitalize.word(result.word, sentenceStart)) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/net/mezimmah/wkt9/keypad/Command.kt b/app/src/main/java/net/mezimmah/wkt9/keypad/Command.kt index 740e110..3e79ced 100644 --- a/app/src/main/java/net/mezimmah/wkt9/keypad/Command.kt +++ b/app/src/main/java/net/mezimmah/wkt9/keypad/Command.kt @@ -6,6 +6,7 @@ enum class Command { CHARACTER, DELETE, DIAL, + ENTER, INPUT_MODE, MOVE_CURSOR, NUMBER, diff --git a/app/src/main/java/net/mezimmah/wkt9/keypad/CommandMapping.kt b/app/src/main/java/net/mezimmah/wkt9/keypad/CommandMapping.kt index d0a77a6..6b37757 100644 --- a/app/src/main/java/net/mezimmah/wkt9/keypad/CommandMapping.kt +++ b/app/src/main/java/net/mezimmah/wkt9/keypad/CommandMapping.kt @@ -6,7 +6,7 @@ data class CommandMapping( val events: List? = null, val inputModes: List? = null, val packageNames: List? = null, - val alt: Boolean = false, + val fn: Boolean = false, val ctrl: Boolean = false, val repeatCount: Int? = null, val overrideConsume: Boolean = false, diff --git a/app/src/main/java/net/mezimmah/wkt9/keypad/Key.kt b/app/src/main/java/net/mezimmah/wkt9/keypad/Key.kt index 9fa2483..7a0bbfc 100644 --- a/app/src/main/java/net/mezimmah/wkt9/keypad/Key.kt +++ b/app/src/main/java/net/mezimmah/wkt9/keypad/Key.kt @@ -8,6 +8,14 @@ enum class Key( val consume: Boolean?, val mappings: Mappings ) { + FN(KeyEvent.KEYCODE_FUNCTION, consume = false, Mappings( + listOf() + )), + + CTRL_RIGHT(KeyEvent.KEYCODE_CTRL_RIGHT, consume = false, Mappings( + listOf() + )), + B1(KeyEvent.KEYCODE_BUTTON_1, consume = null, Mappings( listOf( CommandMapping( @@ -76,16 +84,22 @@ enum class Key( N0(KeyEvent.KEYCODE_0, consume = true, Mappings( listOf( CommandMapping( - events = listOf(Event.afterShortDown), + events = listOf(Event.keyDown), inputModes = listOf(InputMode.Letter, InputMode.Word), command = Command.SPACE ), CommandMapping( - events = listOf(Event.keyDownRepeat), - inputModes = listOf(InputMode.Letter, InputMode.Word), - command = Command.NUMBER, - repeatCount = 2 + events = listOf(Event.keyLongDown), + inputModes = listOf(InputMode.Letter), + command = Command.NUMBER + ), + + CommandMapping( + events = listOf(Event.keyDown), + inputModes = listOf(InputMode.Word), + fn = true, + command = Command.NUMBER ), CommandMapping( @@ -100,16 +114,22 @@ enum class Key( N1(KeyEvent.KEYCODE_1, consume = true, Mappings( listOf( CommandMapping( - events = listOf(Event.afterShortDown), + events = listOf(Event.keyDown), inputModes = listOf(InputMode.Letter, InputMode.Word), command = Command.CHARACTER ), CommandMapping( - events = listOf(Event.keyDownRepeat), - inputModes = listOf(InputMode.Letter, InputMode.Word), - command = Command.NUMBER, - repeatCount = 2 + events = listOf(Event.keyLongDown), + inputModes = listOf(InputMode.Letter), + command = Command.NUMBER + ), + + CommandMapping( + events = listOf(Event.keyDown), + inputModes = listOf(InputMode.Word), + fn = true, + command = Command.NUMBER ), CommandMapping( @@ -124,16 +144,22 @@ enum class Key( N2(KeyEvent.KEYCODE_2, consume = true, Mappings( listOf( CommandMapping( - events = listOf(Event.afterShortDown), + events = listOf(Event.keyDown), inputModes = listOf(InputMode.Letter, InputMode.Word), command = Command.CHARACTER ), CommandMapping( - events = listOf(Event.keyDownRepeat), - inputModes = listOf(InputMode.Letter, InputMode.Word), - command = Command.NUMBER, - repeatCount = 2 + events = listOf(Event.keyLongDown), + inputModes = listOf(InputMode.Letter), + command = Command.NUMBER + ), + + CommandMapping( + events = listOf(Event.keyDown), + inputModes = listOf(InputMode.Word), + fn = true, + command = Command.NUMBER ), CommandMapping( @@ -148,16 +174,22 @@ enum class Key( N3(KeyEvent.KEYCODE_3, consume = true, Mappings( listOf( CommandMapping( - events = listOf(Event.afterShortDown), + events = listOf(Event.keyDown), inputModes = listOf(InputMode.Letter, InputMode.Word), command = Command.CHARACTER ), CommandMapping( - events = listOf(Event.keyDownRepeat), - inputModes = listOf(InputMode.Letter, InputMode.Word), - command = Command.NUMBER, - repeatCount = 2 + events = listOf(Event.keyLongDown), + inputModes = listOf(InputMode.Letter), + command = Command.NUMBER + ), + + CommandMapping( + events = listOf(Event.keyDown), + inputModes = listOf(InputMode.Word), + fn = true, + command = Command.NUMBER ), CommandMapping( @@ -172,16 +204,22 @@ enum class Key( N4(KeyEvent.KEYCODE_4, consume = true, Mappings( listOf( CommandMapping( - events = listOf(Event.afterShortDown), + events = listOf(Event.keyDown), inputModes = listOf(InputMode.Letter, InputMode.Word), command = Command.CHARACTER ), CommandMapping( - events = listOf(Event.keyDownRepeat), - inputModes = listOf(InputMode.Letter, InputMode.Word), - command = Command.NUMBER, - repeatCount = 2 + events = listOf(Event.keyLongDown), + inputModes = listOf(InputMode.Letter), + command = Command.NUMBER + ), + + CommandMapping( + events = listOf(Event.keyDown), + inputModes = listOf(InputMode.Word), + fn = true, + command = Command.NUMBER ), CommandMapping( @@ -196,16 +234,22 @@ enum class Key( N5(KeyEvent.KEYCODE_5, consume = true, Mappings( listOf( CommandMapping( - events = listOf(Event.afterShortDown), + events = listOf(Event.keyDown), inputModes = listOf(InputMode.Letter, InputMode.Word), command = Command.CHARACTER ), CommandMapping( - events = listOf(Event.keyDownRepeat), - inputModes = listOf(InputMode.Letter, InputMode.Word), - command = Command.NUMBER, - repeatCount = 2 + events = listOf(Event.keyLongDown), + inputModes = listOf(InputMode.Letter), + command = Command.NUMBER + ), + + CommandMapping( + events = listOf(Event.keyDown), + inputModes = listOf(InputMode.Word), + fn = true, + command = Command.NUMBER ), CommandMapping( @@ -220,16 +264,22 @@ enum class Key( N6(KeyEvent.KEYCODE_6, consume = true, Mappings( listOf( CommandMapping( - events = listOf(Event.afterShortDown), + events = listOf(Event.keyDown), inputModes = listOf(InputMode.Letter, InputMode.Word), command = Command.CHARACTER ), CommandMapping( - events = listOf(Event.keyDownRepeat), - inputModes = listOf(InputMode.Letter, InputMode.Word), - command = Command.NUMBER, - repeatCount = 2 + events = listOf(Event.keyLongDown), + inputModes = listOf(InputMode.Letter), + command = Command.NUMBER + ), + + CommandMapping( + events = listOf(Event.keyDown), + inputModes = listOf(InputMode.Word), + fn = true, + command = Command.NUMBER ), CommandMapping( @@ -244,16 +294,22 @@ enum class Key( N7(KeyEvent.KEYCODE_7, consume = true, Mappings( listOf( CommandMapping( - events = listOf(Event.afterShortDown), + events = listOf(Event.keyDown), inputModes = listOf(InputMode.Letter, InputMode.Word), command = Command.CHARACTER ), CommandMapping( - events = listOf(Event.keyDownRepeat), - inputModes = listOf(InputMode.Letter, InputMode.Word), - command = Command.NUMBER, - repeatCount = 2 + events = listOf(Event.keyLongDown), + inputModes = listOf(InputMode.Letter), + command = Command.NUMBER + ), + + CommandMapping( + events = listOf(Event.keyDown), + inputModes = listOf(InputMode.Word), + fn = true, + command = Command.NUMBER ), CommandMapping( @@ -268,16 +324,22 @@ enum class Key( N8(KeyEvent.KEYCODE_8, consume = true, Mappings( listOf( CommandMapping( - events = listOf(Event.afterShortDown), + events = listOf(Event.keyDown), inputModes = listOf(InputMode.Letter, InputMode.Word), command = Command.CHARACTER ), CommandMapping( - events = listOf(Event.keyDownRepeat), - inputModes = listOf(InputMode.Letter, InputMode.Word), - command = Command.NUMBER, - repeatCount = 2 + events = listOf(Event.keyLongDown), + inputModes = listOf(InputMode.Letter), + command = Command.NUMBER + ), + + CommandMapping( + events = listOf(Event.keyDown), + inputModes = listOf(InputMode.Word), + fn = true, + command = Command.NUMBER ), CommandMapping( @@ -292,16 +354,22 @@ enum class Key( N9(KeyEvent.KEYCODE_9, consume = true, Mappings( listOf( CommandMapping( - events = listOf(Event.afterShortDown), + events = listOf(Event.keyDown), inputModes = listOf(InputMode.Letter, InputMode.Word), command = Command.CHARACTER ), CommandMapping( - events = listOf(Event.keyDownRepeat), - inputModes = listOf(InputMode.Letter, InputMode.Word), - command = Command.NUMBER, - repeatCount = 2 + events = listOf(Event.keyLongDown), + inputModes = listOf(InputMode.Letter), + command = Command.NUMBER + ), + + CommandMapping( + events = listOf(Event.keyDown), + inputModes = listOf(InputMode.Word), + fn = true, + command = Command.NUMBER ), CommandMapping( @@ -317,38 +385,34 @@ enum class Key( listOf( CommandMapping( events = listOf(Event.keyDown, Event.keyDownRepeat), - inputModes = listOf(InputMode.Word, InputMode.Letter, InputMode.Number), + inputModes = listOf(InputMode.Word, InputMode.Letter), command = Command.DELETE + ), + + CommandMapping( + inputModes = listOf(InputMode.Number), + overrideConsume = true, + consume = null ) ) )), UP(KeyEvent.KEYCODE_DPAD_UP, consume = null, Mappings( - listOf( - CommandMapping( - events = listOf(Event.afterShortDown, Event.afterLongDown), - inputModes = listOf(InputMode.Letter, InputMode.Word), - command = Command.MOVE_CURSOR - ) - ) + listOf() )), DOWN(KeyEvent.KEYCODE_DPAD_DOWN, consume = null, Mappings( - listOf( - CommandMapping( - events = listOf(Event.afterShortDown, Event.afterLongDown), - inputModes = listOf(InputMode.Letter, InputMode.Word), - command = Command.MOVE_CURSOR - ) - ) + listOf() )), LEFT(KeyEvent.KEYCODE_DPAD_LEFT, consume = null, Mappings( listOf( CommandMapping( - events = listOf(Event.afterShortDown, Event.afterLongDown), - inputModes = listOf(InputMode.Letter, InputMode.Word), - command = Command.MOVE_CURSOR + events = listOf(Event.keyDown, Event.keyDownRepeat), + inputModes = listOf(InputMode.Word), + command = Command.MOVE_CURSOR, + overrideConsume = true, + consume = true ) ) )), @@ -356,22 +420,30 @@ enum class Key( RIGHT(KeyEvent.KEYCODE_DPAD_RIGHT, consume = null, Mappings( listOf( CommandMapping( - events = listOf(Event.afterShortDown, Event.afterLongDown), - inputModes = listOf(InputMode.Letter, InputMode.Word), - command = Command.MOVE_CURSOR + events = listOf(Event.keyDown, Event.keyDownRepeat), + inputModes = listOf(InputMode.Word), + command = Command.MOVE_CURSOR, + overrideConsume = true, + consume = true ) ) )), - ENTER(KeyEvent.KEYCODE_ENTER, consume = null, Mappings( + ENTER(KeyEvent.KEYCODE_ENTER, consume = true, Mappings( listOf( + CommandMapping( + events = listOf(Event.keyDown, Event.keyDownRepeat), + inputModes = listOf(InputMode.Letter, InputMode.Word), + command = Command.ENTER, + overrideConsume = true, + consume = true + ), + CommandMapping( events = listOf(Event.keyDown), inputModes = listOf(InputMode.Idle), packageNames = listOf("com.android.camera2"), - command = Command.CAMERA, - overrideConsume = true, - consume = true + command = Command.CAMERA ) ) )); diff --git a/app/src/main/java/net/mezimmah/wkt9/keypad/KeyLayout.kt b/app/src/main/java/net/mezimmah/wkt9/keypad/KeyLayout.kt index ab33357..c9b1bc8 100644 --- a/app/src/main/java/net/mezimmah/wkt9/keypad/KeyLayout.kt +++ b/app/src/main/java/net/mezimmah/wkt9/keypad/KeyLayout.kt @@ -15,15 +15,15 @@ object KeyLayout { Key.N9 to 9, ) - val en_US = mapOf( - Key.N1 to listOf('.','?','!',',','-','+','=','\'','"','@','$','/','%',':','(',')'), - Key.N2 to listOf('a','b','c','ä','æ','å','à','á','â','ã','ç'), - Key.N3 to listOf('d','e','f','è','é','ê','ë','đ'), - Key.N4 to listOf('g','h','i','ì','í','î','ï'), - Key.N5 to listOf('j','k','l','£'), - Key.N6 to listOf('m','n','o','ö','ø','ò','ó','ô','õ','õ'), - Key.N7 to listOf('p','q','r','s','ß','$'), - Key.N8 to listOf('t','u','v','ù','ú','û','ü'), - Key.N9 to listOf('w','x','y','z','ý','þ') + val chars = mapOf( + Key.N1 to listOf('.','?','!',',','-','+','=','\'','"','@','$','&','/','%',':','(',')','0','1'), + Key.N2 to listOf('a','b','c','ä','æ','å','à','á','â','ã','ç','2','₂','²'), + Key.N3 to listOf('d','e','f','è','é','ê','ë','đ','3','³'), + Key.N4 to listOf('g','h','i','ì','í','î','ï','4'), + Key.N5 to listOf('j','k','l','£','5'), + Key.N6 to listOf('m','n','o','ö','ø','ò','ó','ô','õ','õ','ñ','6'), + Key.N7 to listOf('p','q','r','s','ß','$','7'), + Key.N8 to listOf('t','u','v','ù','ú','û','ü','8'), + Key.N9 to listOf('w','x','y','z','ý','þ','9') ) } \ No newline at end of file diff --git a/app/src/main/java/net/mezimmah/wkt9/keypad/Keypad.kt b/app/src/main/java/net/mezimmah/wkt9/keypad/Keypad.kt index 9e1e0ad..1e47e58 100644 --- a/app/src/main/java/net/mezimmah/wkt9/keypad/Keypad.kt +++ b/app/src/main/java/net/mezimmah/wkt9/keypad/Keypad.kt @@ -3,21 +3,17 @@ package net.mezimmah.wkt9.keypad import net.mezimmah.wkt9.exception.MissingLetterCode import java.lang.StringBuilder -class Keypad( - private val letterLayout: Map>, - - numericLayout: Map -) { +class Keypad { private val letterCodeMap: MutableMap = mutableMapOf() init { - numericLayout.forEach { (key, code) -> + KeyLayout.numeric.forEach { (key, code) -> indexKeyLetters(key, code) } } private fun indexKeyLetters(key: Key, code: Int) { - letterLayout[key]?.map { letter -> + KeyLayout.chars[key]?.map { letter -> letterCodeMap[letter] = code } } diff --git a/app/src/main/java/net/mezimmah/wkt9/keypad/Mappings.kt b/app/src/main/java/net/mezimmah/wkt9/keypad/Mappings.kt index 95564d0..9b92931 100644 --- a/app/src/main/java/net/mezimmah/wkt9/keypad/Mappings.kt +++ b/app/src/main/java/net/mezimmah/wkt9/keypad/Mappings.kt @@ -7,7 +7,7 @@ class Mappings(private val mappings: List) { event: Event, inputMode: InputMode, packageName: String, - alt: Boolean = false, + fn: Boolean = false, ctrl: Boolean = false, repeatCount: Int = 0, ): MutableList? { @@ -18,7 +18,7 @@ class Mappings(private val mappings: List) { ((it.events == null) || it.events.contains(event)) && ((it.inputModes == null) || it.inputModes.contains(inputMode)) && ((it.packageNames == null) || it.packageNames.contains(packageName)) && - (it.alt == alt) && + (it.fn == fn) && (it.ctrl == ctrl) && ((it.repeatCount == null) || (it.repeatCount == repeatCount)) ) commands.add(it) diff --git a/app/src/main/java/net/mezimmah/wkt9/layout/Words.kt b/app/src/main/java/net/mezimmah/wkt9/layout/Words.kt new file mode 100644 index 0000000..b77b654 --- /dev/null +++ b/app/src/main/java/net/mezimmah/wkt9/layout/Words.kt @@ -0,0 +1,114 @@ +package net.mezimmah.wkt9.layout + +import android.annotation.SuppressLint +import android.app.PendingIntent.getActivity +import android.content.Context +import android.util.AttributeSet +import android.util.Log +import android.view.ContextThemeWrapper +import android.view.View +import android.widget.HorizontalScrollView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.content.ContextCompat +import net.mezimmah.wkt9.R +import net.mezimmah.wkt9.WKT9IME +import net.mezimmah.wkt9.entity.Word + +class Words(context: Context, attributeSet: AttributeSet): HorizontalScrollView(context, attributeSet), View.OnClickListener, View.OnLongClickListener { + private val tag = "WKT9" + private var wkt9: WKT9IME + private var wordCount: Int = 0 + private var current: Int = 0 + + var words: List? = null + @SuppressLint("InflateParams") + set (words) { + val wordContainer = clear() ?: return + + wordCount = 0 + + words?.forEach { word -> + val view = wkt9.layoutInflater.inflate(R.layout.word, null) as TextView + + view.text = word.word + + view.setOnClickListener(this) + view.setOnLongClickListener(this) + wordContainer.addView(view) + + wordCount++ + } + + current = 0 + + field = words + + select(current) + } + + init { + wkt9 = if (context is ContextThemeWrapper) { + context.baseContext as WKT9IME + } else { + context as WKT9IME + } + } + + fun clear(): LinearLayout? { + val wordContainer = findViewById(R.id.words) + + wordContainer?.removeAllViews() + + return wordContainer + } + + fun next() { + val next = if (current + 1 >= wordCount) 0 else current + 1 + + select(next) + } + + fun previous() { + val previous = if (current == 0) wordCount - 1 else current - 1 + + select(previous) + } + + override fun onClick(v: View?) { + val words = this.words ?: return + val wordContainer = findViewById(R.id.words) + + for (i in 0 until wordCount) { + val child: View = wordContainer.getChildAt(i) + + if (v != child) { + child.background = null + } else { + child.background = ContextCompat.getDrawable(wkt9, R.drawable.button_radius) + current = i + + wkt9.onWordSelected(words[i]) + } + } + } + + override fun onLongClick(v: View?): Boolean { + Log.d(tag, "We need to delete this word from the db") + + return true + } + + fun select(index: Int) { + val wordContainer = findViewById(R.id.words) + + for (i in 0 until wordCount) { + val child: View = wordContainer.getChildAt(i) + + if (i != index) continue + + onClick(child) + smoothScrollTo(child.left, 0) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/net/mezimmah/wkt9/preferences/PreferencesFragment.kt b/app/src/main/java/net/mezimmah/wkt9/preferences/PreferencesFragment.kt index 3978fdf..718276e 100644 --- a/app/src/main/java/net/mezimmah/wkt9/preferences/PreferencesFragment.kt +++ b/app/src/main/java/net/mezimmah/wkt9/preferences/PreferencesFragment.kt @@ -34,13 +34,13 @@ class PreferencesFragment: PreferenceFragmentCompat(), preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) - findPreference(getString(R.string.overlay_key))?.isChecked = Settings.canDrawOverlays(context) + findPreference(getString(R.string.overlay))?.isChecked = Settings.canDrawOverlays(context) } override fun onStart() { super.onStart() - findPreference(getString(R.string.overlay_key))?.isChecked = Settings.canDrawOverlays(context) + findPreference(getString(R.string.overlay))?.isChecked = Settings.canDrawOverlays(context) } override fun onPause() { @@ -53,7 +53,7 @@ class PreferencesFragment: PreferenceFragmentCompat(), this.key = key when (key) { - getString(R.string.speech_to_text_key) -> { + getString(R.string.speech_to_text) -> { if (findPreference(key)?.isChecked == true) { val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { arrayOf( @@ -66,7 +66,7 @@ class PreferencesFragment: PreferenceFragmentCompat(), } } - getString(R.string.overlay_key) -> { + getString(R.string.overlay) -> { val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION) startActivity(intent) diff --git a/app/src/main/java/net/mezimmah/wkt9/t9/T9.kt b/app/src/main/java/net/mezimmah/wkt9/t9/T9.kt index 52ce569..6bb73a4 100644 --- a/app/src/main/java/net/mezimmah/wkt9/t9/T9.kt +++ b/app/src/main/java/net/mezimmah/wkt9/t9/T9.kt @@ -7,96 +7,100 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import net.mezimmah.wkt9.WKT9 +import net.mezimmah.wkt9.WKT9IME import net.mezimmah.wkt9.dao.SettingDao import net.mezimmah.wkt9.dao.WordDao +import net.mezimmah.wkt9.db.AppDatabase import net.mezimmah.wkt9.entity.Setting import net.mezimmah.wkt9.entity.Word import net.mezimmah.wkt9.exception.MissingLetterCode import net.mezimmah.wkt9.keypad.Keypad +import java.util.Locale class T9 ( - private val context: WKT9, - private val keypad: Keypad, - private val settingDao: SettingDao, - private val wordDao: WordDao + private val context: WKT9IME, + private val locale: Locale, + private val batchSize: Int = 1000 ) { // Debugging private val tag = "WKT9" + private val db: AppDatabase = AppDatabase.getInstance(context) + private val wordDao: WordDao = db.getWordDao() + private val settingDao: SettingDao = db.getSettingDao() + + private val keypad: Keypad = Keypad() + // Coroutines private val job = SupervisorJob() private val scope = CoroutineScope(Dispatchers.IO + job) - fun initializeWords(locale: CharSequence) { + fun initializeWords() { scope.launch { - val setting = settingDao.getByKey("initialized") + val key = locale.language.plus(":word:initialized") + val setting = settingDao.getByKey(key) if (setting == null) { - Log.d(tag,"Initializing word database...") - readWords(locale.toString()) - Setting.set("initialized", "t", settingDao) + Log.d(tag,"Initializing '$locale' word database.") + readWords() + Setting.set(key, "check", settingDao) } else Log.d(tag, "Word database already initialized") } } - private fun getWord(word: String, weight: Int = 1, locale: String) = Word( + private fun getWord(word: String, locale: String) = Word( word = word, code = keypad.getCodeForWord(word), length = word.length, - weight = weight, + weight = 1, locale = locale ) - private fun readWords(locale: String) { - val fileName = "$locale/words.txt" - val batchSize = 1000 - val wordBatch = ArrayList(batchSize) + private fun readBatch(batch: Sequence) { + batch.chunked(batchSize).forEach { + readLines(it) + } + } - context.assets.open(fileName).bufferedReader().useLines { lines -> - for (chunk in lines.chunked(batchSize)) { - var arrayListIndex = 0 + private fun readLine(line: String): Word? { + return try { + getWord( + word = line.trim(), + locale = locale.language + ) + } catch (e: MissingLetterCode) { + Log.d(tag, "Character missing:", e) - wordBatch.clear() + null + } + } - chunk.forEach { line -> - val parts = line.split("\\s".toRegex()) + private fun readLines(lines: List) { + val words = mutableListOf() - try { - wordBatch.add( - arrayListIndex, + lines.forEach { line -> + readLine(line)?.also { + words.add(it) + } + } - when (parts.size) { - // Emoji support - 3 -> Word( - word = parts[0], - code = parts[2], - length = 1, - weight = parts[1].toInt(), - locale = locale - ) - // Regular dictionary words - else -> getWord( - word = parts[0], - weight = if (parts.size > 1) parts[1].toInt() else 0, - locale = locale - ) - } - ) + insertWordBatch(words) + } - arrayListIndex++ - } catch (ex: MissingLetterCode) { - Log.w(tag, "Problem adding ${parts[0]}: " + ex.message) - } - } + private fun readWords() { + val fileName = locale.language.plus("/words.txt") - runBlocking { - try { - wordDao.insert(*wordBatch.toTypedArray()) - } catch (e: SQLiteConstraintException) { - Log.d(tag, "Oh, Brother!") - } - } + context.assets.open(fileName).bufferedReader().useLines { + readBatch(it) + } + } + + private fun insertWordBatch(words: List) { + runBlocking { + try { + wordDao.insert(*words.toTypedArray()) + } catch (e: SQLiteConstraintException) { + Log.d(tag, "Oh, Brother!") } } } diff --git a/app/src/main/java/net/mezimmah/wkt9/voice/Whisper.kt b/app/src/main/java/net/mezimmah/wkt9/voice/Whisper.kt index 9c57bd6..7ab105a 100644 --- a/app/src/main/java/net/mezimmah/wkt9/voice/Whisper.kt +++ b/app/src/main/java/net/mezimmah/wkt9/voice/Whisper.kt @@ -11,8 +11,8 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import net.mezimmah.wkt9.R -import net.mezimmah.wkt9.WKT9 -import net.mezimmah.wkt9.inputmode.InputManager +import net.mezimmah.wkt9.WKT9IME +import net.mezimmah.wkt9.inputhandler.InputHandler import okhttp3.MediaType.Companion.toMediaType import okhttp3.MultipartBody import okhttp3.OkHttpClient @@ -23,8 +23,8 @@ import java.io.IOException import java.util.concurrent.TimeUnit class Whisper( - private val context: WKT9, - private val inputManager: InputManager, + private val context: WKT9IME, + private val inputHandler: InputHandler?, private val ui: View ) { private val tag = "WKT9" @@ -59,7 +59,7 @@ class Whisper( showCandidates() } - inputManager.handler?.onInsertText(transcription.plus(" ")) +// inputHandler?.onInsertText(transcription.plus(" ")) } catch (e: IOException) { Log.d(tag, "A failure occurred in the communication with the speech-to-text server", e) } @@ -89,33 +89,33 @@ class Whisper( } private fun showCandidates() { - val candidatesView = ui.findViewById(R.id.suggestion_container) - val loadingView = ui.findViewById(R.id.loading_container) - val messageView = ui.findViewById(R.id.message_container) - - candidatesView.visibility = View.VISIBLE - loadingView.visibility = View.GONE - messageView.visibility = View.GONE +// val candidatesView = ui.findViewById(R.id.suggestion_container) +// val loadingView = ui.findViewById(R.id.loading_container) +// val messageView = ui.findViewById(R.id.message_container) +// +// candidatesView.visibility = View.VISIBLE +// loadingView.visibility = View.GONE +// messageView.visibility = View.GONE } private fun showMessage() { - val candidatesView = ui.findViewById(R.id.suggestion_container) - val loadingView = ui.findViewById(R.id.loading_container) - val messageView = ui.findViewById(R.id.message_container) - - candidatesView.visibility = View.GONE - loadingView.visibility = View.GONE - messageView.visibility = View.VISIBLE +// val candidatesView = ui.findViewById(R.id.suggestion_container) +// val loadingView = ui.findViewById(R.id.loading_container) +// val messageView = ui.findViewById(R.id.message_container) +// +// candidatesView.visibility = View.GONE +// loadingView.visibility = View.GONE +// messageView.visibility = View.VISIBLE } private fun showTranscribing() { - val candidatesView = ui.findViewById(R.id.suggestion_container) - val loadingView = ui.findViewById(R.id.loading_container) - val messageView = ui.findViewById(R.id.message_container) - - candidatesView.visibility = View.GONE - loadingView.visibility = View.VISIBLE - messageView.visibility = View.GONE +// val candidatesView = ui.findViewById(R.id.suggestion_container) +// val loadingView = ui.findViewById(R.id.loading_container) +// val messageView = ui.findViewById(R.id.message_container) +// +// candidatesView.visibility = View.GONE +// loadingView.visibility = View.VISIBLE +// messageView.visibility = View.GONE } private fun stopRecording() { diff --git a/app/src/main/res/drawable/fn_en_us_na.xml b/app/src/main/res/drawable/fn_en_us_na.xml deleted file mode 100644 index 37f20ef..0000000 --- a/app/src/main/res/drawable/fn_en_us_na.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/idle_en_us_na.xml b/app/src/main/res/drawable/idle_input.xml similarity index 100% rename from app/src/main/res/drawable/idle_en_us_na.xml rename to app/src/main/res/drawable/idle_input.xml diff --git a/app/src/main/res/drawable/alpha_en_us_cap.xml b/app/src/main/res/drawable/letter_cap.xml similarity index 100% rename from app/src/main/res/drawable/alpha_en_us_cap.xml rename to app/src/main/res/drawable/letter_cap.xml diff --git a/app/src/main/res/drawable/alpha_en_us_lower.xml b/app/src/main/res/drawable/letter_lower.xml similarity index 100% rename from app/src/main/res/drawable/alpha_en_us_lower.xml rename to app/src/main/res/drawable/letter_lower.xml diff --git a/app/src/main/res/drawable/alpha_en_us_upper.xml b/app/src/main/res/drawable/letter_upper.xml similarity index 100% rename from app/src/main/res/drawable/alpha_en_us_upper.xml rename to app/src/main/res/drawable/letter_upper.xml diff --git a/app/src/main/res/drawable/number_input.xml b/app/src/main/res/drawable/number_input.xml new file mode 100644 index 0000000..5d5b565 --- /dev/null +++ b/app/src/main/res/drawable/number_input.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/numeric_en_us_num.xml b/app/src/main/res/drawable/numeric_en_us_num.xml deleted file mode 100644 index 8fe0071..0000000 --- a/app/src/main/res/drawable/numeric_en_us_num.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/word_de_de_cap.xml b/app/src/main/res/drawable/word_de_de_cap.xml new file mode 100644 index 0000000..98e16af --- /dev/null +++ b/app/src/main/res/drawable/word_de_de_cap.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/word_de_de_lower.xml b/app/src/main/res/drawable/word_de_de_lower.xml new file mode 100644 index 0000000..0d82a43 --- /dev/null +++ b/app/src/main/res/drawable/word_de_de_lower.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/word_de_de_upper.xml b/app/src/main/res/drawable/word_de_de_upper.xml new file mode 100644 index 0000000..38dd5f2 --- /dev/null +++ b/app/src/main/res/drawable/word_de_de_upper.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/word_en_us_cap.xml b/app/src/main/res/drawable/word_en_us_cap.xml index f98b13f..a08ba2f 100644 --- a/app/src/main/res/drawable/word_en_us_cap.xml +++ b/app/src/main/res/drawable/word_en_us_cap.xml @@ -5,6 +5,5 @@ android:viewportHeight="32"> + android:pathData="M0.42,29.993L0.42,2.007L12.979,2.007L12.979,5.117L4.055,5.117l0,8.682l8.077,0l0,3.029L4.055,16.828l0,10.055l9.571,0L13.626,29.993ZM18.593,9.397q1.494,-0.444 3.231,-0.727 1.736,-0.323 3.19,-0.323 1.575,0 2.867,0.444 1.292,0.404 2.181,1.413 0.929,1.01 1.413,2.746 0.525,1.696 0.525,4.24l0,12.802l-3.473,0l0,-12.519q0,-3.15 -0.808,-4.604 -0.808,-1.454 -3.029,-1.454 -1.171,0 -2.625,0.404L22.066,29.993l-3.473,0z"/> diff --git a/app/src/main/res/drawable/word_en_us_lower.xml b/app/src/main/res/drawable/word_en_us_lower.xml index d0855a7..0970d43 100644 --- a/app/src/main/res/drawable/word_en_us_lower.xml +++ b/app/src/main/res/drawable/word_en_us_lower.xml @@ -5,6 +5,5 @@ android:viewportHeight="32"> + android:pathData="m3.75,17.006q0.039,1.499 0.276,2.762 0.237,1.223 0.75,2.17 0.552,0.907 1.42,1.42 0.907,0.513 2.288,0.513 1.144,0 2.091,-0.316 0.986,-0.355 1.42,-0.592l0.631,2.722q-0.513,0.355 -1.736,0.75 -1.184,0.434 -2.841,0.434 -2.17,0 -3.669,-0.789Q2.922,25.291 1.975,23.871 1.068,22.45 0.673,20.478q-0.395,-1.973 -0.395,-4.34 0,-5.72 1.894,-8.364 1.894,-2.643 5.01,-2.643 3.511,0 4.971,2.643 1.46,2.643 1.46,7.417 0,0.434 0,0.907 0,0.434 -0.039,0.907zM7.183,8.011q-1.696,0 -2.485,1.696Q3.947,11.404 3.79,14.244l6.273,0q0,-2.88 -0.592,-4.537Q8.879,8.011 7.183,8.011ZM18.624,6.236q1.46,-0.434 3.156,-0.71 1.696,-0.316 3.117,-0.316 1.539,0 2.801,0.434 1.262,0.395 2.13,1.381 0.907,0.986 1.381,2.683 0.513,1.657 0.513,4.142l0,12.506l-3.393,0l0,-12.23q0,-3.077 -0.789,-4.497 -0.789,-1.42 -2.959,-1.42 -1.144,0 -2.564,0.395L22.016,26.356L18.624,26.356Z"/> diff --git a/app/src/main/res/drawable/word_en_us_upper.xml b/app/src/main/res/drawable/word_en_us_upper.xml index 1616245..1e136cb 100644 --- a/app/src/main/res/drawable/word_en_us_upper.xml +++ b/app/src/main/res/drawable/word_en_us_upper.xml @@ -5,6 +5,5 @@ android:viewportHeight="32"> + android:pathData="M0.25,28.56L0.25,3.44L11.523,3.44L11.523,6.231L3.512,6.231l0,7.793l7.25,0l0,2.719L3.512,16.743l0,9.026l8.591,0l0,2.791zM29.104,28.56Q28.234,26.422 27.11,23.957 26.023,21.455 24.79,18.954 23.594,16.417 22.325,13.952 21.057,11.451 19.824,9.312L19.824,28.56L16.779,28.56L16.779,3.44l2.719,0q1.377,2.247 2.646,4.604 1.305,2.356 2.465,4.676 1.16,2.284 2.175,4.531 1.051,2.211 1.921,4.205L28.705,3.44l3.045,0L31.75,28.56Z"/> diff --git a/app/src/main/res/drawable/word_es_es_cap.xml b/app/src/main/res/drawable/word_es_es_cap.xml new file mode 100644 index 0000000..c5b410a --- /dev/null +++ b/app/src/main/res/drawable/word_es_es_cap.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/word_es_es_lower.xml b/app/src/main/res/drawable/word_es_es_lower.xml new file mode 100644 index 0000000..94c3e72 --- /dev/null +++ b/app/src/main/res/drawable/word_es_es_lower.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/word_es_es_upper.xml b/app/src/main/res/drawable/word_es_es_upper.xml new file mode 100644 index 0000000..8fb99b1 --- /dev/null +++ b/app/src/main/res/drawable/word_es_es_upper.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/word_nl_nl_cap.xml b/app/src/main/res/drawable/word_nl_nl_cap.xml new file mode 100644 index 0000000..2c0b3d1 --- /dev/null +++ b/app/src/main/res/drawable/word_nl_nl_cap.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/word_nl_nl_lower.xml b/app/src/main/res/drawable/word_nl_nl_lower.xml new file mode 100644 index 0000000..dd818d4 --- /dev/null +++ b/app/src/main/res/drawable/word_nl_nl_lower.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/word_nl_nl_upper.xml b/app/src/main/res/drawable/word_nl_nl_upper.xml new file mode 100644 index 0000000..b9fcdbb --- /dev/null +++ b/app/src/main/res/drawable/word_nl_nl_upper.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/word_pt_pt_cap.xml b/app/src/main/res/drawable/word_pt_pt_cap.xml new file mode 100644 index 0000000..3dac23e --- /dev/null +++ b/app/src/main/res/drawable/word_pt_pt_cap.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/word_pt_pt_lower.xml b/app/src/main/res/drawable/word_pt_pt_lower.xml new file mode 100644 index 0000000..057852a --- /dev/null +++ b/app/src/main/res/drawable/word_pt_pt_lower.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/word_pt_pt_upper.xml b/app/src/main/res/drawable/word_pt_pt_upper.xml new file mode 100644 index 0000000..5bc98d1 --- /dev/null +++ b/app/src/main/res/drawable/word_pt_pt_upper.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/current_suggestion.xml b/app/src/main/res/layout/current_suggestion.xml deleted file mode 100644 index 0006ae0..0000000 --- a/app/src/main/res/layout/current_suggestion.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/suggestions.xml b/app/src/main/res/layout/suggestions.xml deleted file mode 100644 index 443d107..0000000 --- a/app/src/main/res/layout/suggestions.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/symbols.xml b/app/src/main/res/layout/symbols.xml deleted file mode 100644 index 3411258..0000000 --- a/app/src/main/res/layout/symbols.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/word.xml b/app/src/main/res/layout/word.xml new file mode 100644 index 0000000..87fa417 --- /dev/null +++ b/app/src/main/res/layout/word.xml @@ -0,0 +1,10 @@ + + diff --git a/app/src/main/res/layout/words.xml b/app/src/main/res/layout/words.xml new file mode 100644 index 0000000..972d07f --- /dev/null +++ b/app/src/main/res/layout/words.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 78a108d..57e7877 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,21 +3,10 @@ WKT9 Preferences - - Speech to Text - Speech to Text - - speech_to_text - Enable Speech to Text - For this feature to work WKT9 needs permission to show notifications and record audio. You will be asked to grant these permissions if you haven\'t already granted it. - - whisper_url - Whisper Server URL - Provide an URL to the Whisper server. - - Draw over other activities - Draw over activities - Grant WKT9 permission to draw over other applications + speech_to_text + whisper_url + overlay + compose_timeout org.linphone @@ -26,4 +15,20 @@ com.android.camera2 + + + Very short + Short + Medium + Long + Very long + + + + 300 + 400 + 600 + 800 + 900 + \ No newline at end of file diff --git a/app/src/main/res/xml/method.xml b/app/src/main/res/xml/method.xml index 6497853..d936094 100644 --- a/app/src/main/res/xml/method.xml +++ b/app/src/main/res/xml/method.xml @@ -8,4 +8,28 @@ android:languageTag="en-US" android:imeSubtypeMode="keyboard" /> + + + + + + + + diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 0bc2dc7..b93f33d 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -3,26 +3,37 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> + app:title="Speech to text" /> + app:key="@string/speech_to_text" + app:title="Enable speech to text" + app:summary="Grant WKT9 access to the microphone." /> + app:key="@string/whisper_url" + app:title="Whisper server URL" + app:summary="URL of server that transcribes the recording" + app:dependency="speech_to_text" /> + app:title="Start other activities" /> + app:key="@string/overlay" + app:title="Start other activities" + app:summary="Permit WKT9 to start other activities, like, for example, the dialer." /> + + + +