diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
index b268ef3..3bf9052 100644
--- a/.idea/deploymentTargetSelector.xml
+++ b/.idea/deploymentTargetSelector.xml
@@ -4,6 +4,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0ced4fa..b836c59 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,8 +3,8 @@
-
-
+
+
@@ -30,22 +31,26 @@
@@ -63,5 +68,16 @@
android:name="android.view.im"
android:resource="@xml/method" />
+
+
+
+
+
diff --git a/app/src/main/assets/bi_cols.bin b/app/src/main/assets/bi_cols.bin
index 3394ced..f34f23e 100644
Binary files a/app/src/main/assets/bi_cols.bin and b/app/src/main/assets/bi_cols.bin differ
diff --git a/app/src/main/assets/bi_logp.bin b/app/src/main/assets/bi_logp.bin
index 1a76e70..4b71d60 100644
Binary files a/app/src/main/assets/bi_logp.bin and b/app/src/main/assets/bi_logp.bin differ
diff --git a/app/src/main/assets/bi_rowptr.bin b/app/src/main/assets/bi_rowptr.bin
index 113e6e7..288cef2 100644
Binary files a/app/src/main/assets/bi_rowptr.bin and b/app/src/main/assets/bi_rowptr.bin differ
diff --git a/app/src/main/assets/keyboard_themes/default/Key_collapse.png b/app/src/main/assets/keyboard_themes/default/Key_collapse.png
index 6696970..68d5c27 100644
Binary files a/app/src/main/assets/keyboard_themes/default/Key_collapse.png and b/app/src/main/assets/keyboard_themes/default/Key_collapse.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/background.png b/app/src/main/assets/keyboard_themes/default/background.png
index c2a690d..d750125 100644
Binary files a/app/src/main/assets/keyboard_themes/default/background.png and b/app/src/main/assets/keyboard_themes/default/background.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_0.png b/app/src/main/assets/keyboard_themes/default/key_0.png
index 3f50ba7..35a70ea 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_0.png and b/app/src/main/assets/keyboard_themes/default/key_0.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_1.png b/app/src/main/assets/keyboard_themes/default/key_1.png
index a63ddbe..fcb29f1 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_1.png and b/app/src/main/assets/keyboard_themes/default/key_1.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_123.png b/app/src/main/assets/keyboard_themes/default/key_123.png
index 2742137..cc45ff2 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_123.png and b/app/src/main/assets/keyboard_themes/default/key_123.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_2.png b/app/src/main/assets/keyboard_themes/default/key_2.png
index dbc74b9..bdfcf3b 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_2.png and b/app/src/main/assets/keyboard_themes/default/key_2.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_3.png b/app/src/main/assets/keyboard_themes/default/key_3.png
index f38b8a3..1dc9644 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_3.png and b/app/src/main/assets/keyboard_themes/default/key_3.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_4.png b/app/src/main/assets/keyboard_themes/default/key_4.png
index ce0566b..e474d11 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_4.png and b/app/src/main/assets/keyboard_themes/default/key_4.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_5.png b/app/src/main/assets/keyboard_themes/default/key_5.png
index f1aecfe..ba2f53d 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_5.png and b/app/src/main/assets/keyboard_themes/default/key_5.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_6.png b/app/src/main/assets/keyboard_themes/default/key_6.png
index c76f7ac..3bc78a6 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_6.png and b/app/src/main/assets/keyboard_themes/default/key_6.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_7.png b/app/src/main/assets/keyboard_themes/default/key_7.png
index 20be3d8..df9bfe9 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_7.png and b/app/src/main/assets/keyboard_themes/default/key_7.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_8.png b/app/src/main/assets/keyboard_themes/default/key_8.png
index 6ee3dfc..018207a 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_8.png and b/app/src/main/assets/keyboard_themes/default/key_8.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_9.png b/app/src/main/assets/keyboard_themes/default/key_9.png
index 3459117..8b9a922 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_9.png and b/app/src/main/assets/keyboard_themes/default/key_9.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_a.png b/app/src/main/assets/keyboard_themes/default/key_a.png
index 76687e3..42e98b9 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_a.png and b/app/src/main/assets/keyboard_themes/default/key_a.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_a_up.png b/app/src/main/assets/keyboard_themes/default/key_a_up.png
index 2598472..049a9a2 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_a_up.png and b/app/src/main/assets/keyboard_themes/default/key_a_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_abc.png b/app/src/main/assets/keyboard_themes/default/key_abc.png
index a1923a6..e0424ad 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_abc.png and b/app/src/main/assets/keyboard_themes/default/key_abc.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_ai.png b/app/src/main/assets/keyboard_themes/default/key_ai.png
index e3cf382..b9812d7 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_ai.png and b/app/src/main/assets/keyboard_themes/default/key_ai.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_amp.png b/app/src/main/assets/keyboard_themes/default/key_amp.png
index 03538fa..264d14f 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_amp.png and b/app/src/main/assets/keyboard_themes/default/key_amp.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_asterisk.png b/app/src/main/assets/keyboard_themes/default/key_asterisk.png
index 620ce9f..6986979 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_asterisk.png and b/app/src/main/assets/keyboard_themes/default/key_asterisk.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_at.png b/app/src/main/assets/keyboard_themes/default/key_at.png
index 00ca5ed..de261f9 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_at.png and b/app/src/main/assets/keyboard_themes/default/key_at.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_b.png b/app/src/main/assets/keyboard_themes/default/key_b.png
index ef6e890..d13aa06 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_b.png and b/app/src/main/assets/keyboard_themes/default/key_b.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_b_up.png b/app/src/main/assets/keyboard_themes/default/key_b_up.png
index bd5a787..8f470d3 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_b_up.png and b/app/src/main/assets/keyboard_themes/default/key_b_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_backslash.png b/app/src/main/assets/keyboard_themes/default/key_backslash.png
index aa8563c..a97e362 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_backslash.png and b/app/src/main/assets/keyboard_themes/default/key_backslash.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_backspace.png b/app/src/main/assets/keyboard_themes/default/key_backspace.png
deleted file mode 100644
index 129f1af..0000000
Binary files a/app/src/main/assets/keyboard_themes/default/key_backspace.png and /dev/null differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_brace_l.png b/app/src/main/assets/keyboard_themes/default/key_brace_l.png
index 5c28b8a..9338dbf 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_brace_l.png and b/app/src/main/assets/keyboard_themes/default/key_brace_l.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_brace_r.png b/app/src/main/assets/keyboard_themes/default/key_brace_r.png
index b1b2eb1..fb585c8 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_brace_r.png and b/app/src/main/assets/keyboard_themes/default/key_brace_r.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_bracket_l.png b/app/src/main/assets/keyboard_themes/default/key_bracket_l.png
index 1ec5367..dbc4d05 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_bracket_l.png and b/app/src/main/assets/keyboard_themes/default/key_bracket_l.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_bracket_r.png b/app/src/main/assets/keyboard_themes/default/key_bracket_r.png
index 300523d..fa633be 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_bracket_r.png and b/app/src/main/assets/keyboard_themes/default/key_bracket_r.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_bullet.png b/app/src/main/assets/keyboard_themes/default/key_bullet.png
index 3ac5c87..5a9549d 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_bullet.png and b/app/src/main/assets/keyboard_themes/default/key_bullet.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_c.png b/app/src/main/assets/keyboard_themes/default/key_c.png
index 63fe12c..998425b 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_c.png and b/app/src/main/assets/keyboard_themes/default/key_c.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_c_up.png b/app/src/main/assets/keyboard_themes/default/key_c_up.png
index d823d4f..95f2581 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_c_up.png and b/app/src/main/assets/keyboard_themes/default/key_c_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_caret.png b/app/src/main/assets/keyboard_themes/default/key_caret.png
index 073533a..a7d055f 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_caret.png and b/app/src/main/assets/keyboard_themes/default/key_caret.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_colon.png b/app/src/main/assets/keyboard_themes/default/key_colon.png
index 83069a9..07004af 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_colon.png and b/app/src/main/assets/keyboard_themes/default/key_colon.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_comma.png b/app/src/main/assets/keyboard_themes/default/key_comma.png
index 641c7d6..89e8b0f 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_comma.png and b/app/src/main/assets/keyboard_themes/default/key_comma.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_d.png b/app/src/main/assets/keyboard_themes/default/key_d.png
index ec4aac5..73e5f2d 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_d.png and b/app/src/main/assets/keyboard_themes/default/key_d.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_d_up.png b/app/src/main/assets/keyboard_themes/default/key_d_up.png
index 79188a0..fe70bd9 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_d_up.png and b/app/src/main/assets/keyboard_themes/default/key_d_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_del.png b/app/src/main/assets/keyboard_themes/default/key_del.png
index 129f1af..adbf8f7 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_del.png and b/app/src/main/assets/keyboard_themes/default/key_del.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_dollar.png b/app/src/main/assets/keyboard_themes/default/key_dollar.png
index f1ae8b9..e06c81f 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_dollar.png and b/app/src/main/assets/keyboard_themes/default/key_dollar.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_dot.png b/app/src/main/assets/keyboard_themes/default/key_dot.png
index adb7bd2..f26e595 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_dot.png and b/app/src/main/assets/keyboard_themes/default/key_dot.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_e.png b/app/src/main/assets/keyboard_themes/default/key_e.png
index 1266671..22fa162 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_e.png and b/app/src/main/assets/keyboard_themes/default/key_e.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_e_up.png b/app/src/main/assets/keyboard_themes/default/key_e_up.png
index b7ea305..cdc5fcb 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_e_up.png and b/app/src/main/assets/keyboard_themes/default/key_e_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_emoji.png b/app/src/main/assets/keyboard_themes/default/key_emoji.png
new file mode 100644
index 0000000..299ac42
Binary files /dev/null and b/app/src/main/assets/keyboard_themes/default/key_emoji.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_equal.png b/app/src/main/assets/keyboard_themes/default/key_equal.png
index a6df242..fee04e4 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_equal.png and b/app/src/main/assets/keyboard_themes/default/key_equal.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_euro.png b/app/src/main/assets/keyboard_themes/default/key_euro.png
index dd2a727..bc34dd7 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_euro.png and b/app/src/main/assets/keyboard_themes/default/key_euro.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_exclam.png b/app/src/main/assets/keyboard_themes/default/key_exclam.png
index 9b13fe9..24a6266 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_exclam.png and b/app/src/main/assets/keyboard_themes/default/key_exclam.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_f.png b/app/src/main/assets/keyboard_themes/default/key_f.png
index bb41a4b..6be8401 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_f.png and b/app/src/main/assets/keyboard_themes/default/key_f.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_f_up.png b/app/src/main/assets/keyboard_themes/default/key_f_up.png
index 613b05c..8916620 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_f_up.png and b/app/src/main/assets/keyboard_themes/default/key_f_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_g.png b/app/src/main/assets/keyboard_themes/default/key_g.png
index 1e2023b..586b06e 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_g.png and b/app/src/main/assets/keyboard_themes/default/key_g.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_g_up.png b/app/src/main/assets/keyboard_themes/default/key_g_up.png
index 3885062..2fe71a8 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_g_up.png and b/app/src/main/assets/keyboard_themes/default/key_g_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_gt.png b/app/src/main/assets/keyboard_themes/default/key_gt.png
index 6aab0fb..cc10e4d 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_gt.png and b/app/src/main/assets/keyboard_themes/default/key_gt.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_h.png b/app/src/main/assets/keyboard_themes/default/key_h.png
index 97cc234..90076a4 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_h.png and b/app/src/main/assets/keyboard_themes/default/key_h.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_h_up.png b/app/src/main/assets/keyboard_themes/default/key_h_up.png
index 169b11c..ba77f87 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_h_up.png and b/app/src/main/assets/keyboard_themes/default/key_h_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_hash.png b/app/src/main/assets/keyboard_themes/default/key_hash.png
index 2dc2f72..14a1ed1 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_hash.png and b/app/src/main/assets/keyboard_themes/default/key_hash.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_i.png b/app/src/main/assets/keyboard_themes/default/key_i.png
index 77cded8..609bccb 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_i.png and b/app/src/main/assets/keyboard_themes/default/key_i.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_i_up.png b/app/src/main/assets/keyboard_themes/default/key_i_up.png
index c44dd63..4335b19 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_i_up.png and b/app/src/main/assets/keyboard_themes/default/key_i_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_j.png b/app/src/main/assets/keyboard_themes/default/key_j.png
index cf66ae5..c99d93e 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_j.png and b/app/src/main/assets/keyboard_themes/default/key_j.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_j_up.png b/app/src/main/assets/keyboard_themes/default/key_j_up.png
index 15221e9..cc535a2 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_j_up.png and b/app/src/main/assets/keyboard_themes/default/key_j_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_k.png b/app/src/main/assets/keyboard_themes/default/key_k.png
index 4c802e8..35d8693 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_k.png and b/app/src/main/assets/keyboard_themes/default/key_k.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_k_up.png b/app/src/main/assets/keyboard_themes/default/key_k_up.png
index 18b5ef2..2d60615 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_k_up.png and b/app/src/main/assets/keyboard_themes/default/key_k_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_l.png b/app/src/main/assets/keyboard_themes/default/key_l.png
index 3da5812..a3a95d6 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_l.png and b/app/src/main/assets/keyboard_themes/default/key_l.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_l_up.png b/app/src/main/assets/keyboard_themes/default/key_l_up.png
index 48d11fe..f85cbd5 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_l_up.png and b/app/src/main/assets/keyboard_themes/default/key_l_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_lt.png b/app/src/main/assets/keyboard_themes/default/key_lt.png
index 8094090..c97de96 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_lt.png and b/app/src/main/assets/keyboard_themes/default/key_lt.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_m.png b/app/src/main/assets/keyboard_themes/default/key_m.png
index 6b5af2c..7d9e583 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_m.png and b/app/src/main/assets/keyboard_themes/default/key_m.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_m_up.png b/app/src/main/assets/keyboard_themes/default/key_m_up.png
index b84d37c..4838732 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_m_up.png and b/app/src/main/assets/keyboard_themes/default/key_m_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_minus.png b/app/src/main/assets/keyboard_themes/default/key_minus.png
index c1912a3..1d16de5 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_minus.png and b/app/src/main/assets/keyboard_themes/default/key_minus.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_money.png b/app/src/main/assets/keyboard_themes/default/key_money.png
index 42220fe..d2c1d67 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_money.png and b/app/src/main/assets/keyboard_themes/default/key_money.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_n.png b/app/src/main/assets/keyboard_themes/default/key_n.png
index dab1f87..33f9cae 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_n.png and b/app/src/main/assets/keyboard_themes/default/key_n.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_n_up.png b/app/src/main/assets/keyboard_themes/default/key_n_up.png
index 1d88286..2cc14b3 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_n_up.png and b/app/src/main/assets/keyboard_themes/default/key_n_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_o.png b/app/src/main/assets/keyboard_themes/default/key_o.png
index 23cf2b9..c3e4a5b 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_o.png and b/app/src/main/assets/keyboard_themes/default/key_o.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_o_up.png b/app/src/main/assets/keyboard_themes/default/key_o_up.png
index c3231f9..6c26507 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_o_up.png and b/app/src/main/assets/keyboard_themes/default/key_o_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_p.png b/app/src/main/assets/keyboard_themes/default/key_p.png
index e93161b..a76ab9d 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_p.png and b/app/src/main/assets/keyboard_themes/default/key_p.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_p_up.png b/app/src/main/assets/keyboard_themes/default/key_p_up.png
index 70194e2..cbeb0db 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_p_up.png and b/app/src/main/assets/keyboard_themes/default/key_p_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_paren_l.png b/app/src/main/assets/keyboard_themes/default/key_paren_l.png
index 36f3555..7fc553a 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_paren_l.png and b/app/src/main/assets/keyboard_themes/default/key_paren_l.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_paren_r.png b/app/src/main/assets/keyboard_themes/default/key_paren_r.png
index 6ce9365..e4bbda1 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_paren_r.png and b/app/src/main/assets/keyboard_themes/default/key_paren_r.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_percent.png b/app/src/main/assets/keyboard_themes/default/key_percent.png
index a7943e2..aa5f800 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_percent.png and b/app/src/main/assets/keyboard_themes/default/key_percent.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_pipe.png b/app/src/main/assets/keyboard_themes/default/key_pipe.png
index 7025885..b089b4b 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_pipe.png and b/app/src/main/assets/keyboard_themes/default/key_pipe.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_plus.png b/app/src/main/assets/keyboard_themes/default/key_plus.png
index 4257687..8616e59 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_plus.png and b/app/src/main/assets/keyboard_themes/default/key_plus.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_pound.png b/app/src/main/assets/keyboard_themes/default/key_pound.png
index 9218ec7..bc2a560 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_pound.png and b/app/src/main/assets/keyboard_themes/default/key_pound.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_q.png b/app/src/main/assets/keyboard_themes/default/key_q.png
index f19cf50..9bdf87e 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_q.png and b/app/src/main/assets/keyboard_themes/default/key_q.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_q_up.png b/app/src/main/assets/keyboard_themes/default/key_q_up.png
index 5d4df5a..7ae4e97 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_q_up.png and b/app/src/main/assets/keyboard_themes/default/key_q_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_question.png b/app/src/main/assets/keyboard_themes/default/key_question.png
index bd2de25..5648e74 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_question.png and b/app/src/main/assets/keyboard_themes/default/key_question.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_quote.png b/app/src/main/assets/keyboard_themes/default/key_quote.png
index 9812639..357b3e3 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_quote.png and b/app/src/main/assets/keyboard_themes/default/key_quote.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_quote_d.png b/app/src/main/assets/keyboard_themes/default/key_quote_d.png
index ad314c5..b238bc2 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_quote_d.png and b/app/src/main/assets/keyboard_themes/default/key_quote_d.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_r.png b/app/src/main/assets/keyboard_themes/default/key_r.png
index 17eabf6..445c888 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_r.png and b/app/src/main/assets/keyboard_themes/default/key_r.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_r_up.png b/app/src/main/assets/keyboard_themes/default/key_r_up.png
index 8566458..d43c37b 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_r_up.png and b/app/src/main/assets/keyboard_themes/default/key_r_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_revoke.png b/app/src/main/assets/keyboard_themes/default/key_revoke.png
new file mode 100644
index 0000000..664827e
Binary files /dev/null and b/app/src/main/assets/keyboard_themes/default/key_revoke.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_s.png b/app/src/main/assets/keyboard_themes/default/key_s.png
index d585210..5024992 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_s.png and b/app/src/main/assets/keyboard_themes/default/key_s.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_s_up.png b/app/src/main/assets/keyboard_themes/default/key_s_up.png
index 96f8e59..73ca4ca 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_s_up.png and b/app/src/main/assets/keyboard_themes/default/key_s_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_semicolon.png b/app/src/main/assets/keyboard_themes/default/key_semicolon.png
index d154544..91c9351 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_semicolon.png and b/app/src/main/assets/keyboard_themes/default/key_semicolon.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_send.png b/app/src/main/assets/keyboard_themes/default/key_send.png
index f15aae1..36ae642 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_send.png and b/app/src/main/assets/keyboard_themes/default/key_send.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_slash.png b/app/src/main/assets/keyboard_themes/default/key_slash.png
index 8f28257..783deff 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_slash.png and b/app/src/main/assets/keyboard_themes/default/key_slash.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_space.png b/app/src/main/assets/keyboard_themes/default/key_space.png
index 4db597c..7218309 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_space.png and b/app/src/main/assets/keyboard_themes/default/key_space.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_symbols_123.png b/app/src/main/assets/keyboard_themes/default/key_symbols_123.png
index e2e9a5b..ab6dff5 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_symbols_123.png and b/app/src/main/assets/keyboard_themes/default/key_symbols_123.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_symbols_more.png b/app/src/main/assets/keyboard_themes/default/key_symbols_more.png
index a5b40cd..c4eeb19 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_symbols_more.png and b/app/src/main/assets/keyboard_themes/default/key_symbols_more.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_t.png b/app/src/main/assets/keyboard_themes/default/key_t.png
index f94a079..3038975 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_t.png and b/app/src/main/assets/keyboard_themes/default/key_t.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_t_up.png b/app/src/main/assets/keyboard_themes/default/key_t_up.png
index 1bfba2a..5203e0a 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_t_up.png and b/app/src/main/assets/keyboard_themes/default/key_t_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_tilde.png b/app/src/main/assets/keyboard_themes/default/key_tilde.png
index de61625..e7fdcac 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_tilde.png and b/app/src/main/assets/keyboard_themes/default/key_tilde.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_u.png b/app/src/main/assets/keyboard_themes/default/key_u.png
index dc06718..c8e147a 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_u.png and b/app/src/main/assets/keyboard_themes/default/key_u.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_u_up.png b/app/src/main/assets/keyboard_themes/default/key_u_up.png
index 1d2ba3c..8497786 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_u_up.png and b/app/src/main/assets/keyboard_themes/default/key_u_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_underscore.png b/app/src/main/assets/keyboard_themes/default/key_underscore.png
index b88b39d..2f3bcf0 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_underscore.png and b/app/src/main/assets/keyboard_themes/default/key_underscore.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_up.png b/app/src/main/assets/keyboard_themes/default/key_up.png
index 9b825cd..bfd31ed 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_up.png and b/app/src/main/assets/keyboard_themes/default/key_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_up_upper.png b/app/src/main/assets/keyboard_themes/default/key_up_upper.png
index 52cd704..f902943 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_up_upper.png and b/app/src/main/assets/keyboard_themes/default/key_up_upper.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_v.png b/app/src/main/assets/keyboard_themes/default/key_v.png
index b6dba48..c4bdb86 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_v.png and b/app/src/main/assets/keyboard_themes/default/key_v.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_v_up.png b/app/src/main/assets/keyboard_themes/default/key_v_up.png
index a3026fa..eddc674 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_v_up.png and b/app/src/main/assets/keyboard_themes/default/key_v_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_w.png b/app/src/main/assets/keyboard_themes/default/key_w.png
index ca77d4e..3146329 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_w.png and b/app/src/main/assets/keyboard_themes/default/key_w.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_w_up.png b/app/src/main/assets/keyboard_themes/default/key_w_up.png
index 2f37d3b..788ce01 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_w_up.png and b/app/src/main/assets/keyboard_themes/default/key_w_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_x.png b/app/src/main/assets/keyboard_themes/default/key_x.png
index baf8eec..439da77 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_x.png and b/app/src/main/assets/keyboard_themes/default/key_x.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_x_up.png b/app/src/main/assets/keyboard_themes/default/key_x_up.png
index 72d9946..7c80335 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_x_up.png and b/app/src/main/assets/keyboard_themes/default/key_x_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_y.png b/app/src/main/assets/keyboard_themes/default/key_y.png
index 3033c75..431575f 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_y.png and b/app/src/main/assets/keyboard_themes/default/key_y.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_y_up.png b/app/src/main/assets/keyboard_themes/default/key_y_up.png
index 09bd13c..f39e85f 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_y_up.png and b/app/src/main/assets/keyboard_themes/default/key_y_up.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_z.png b/app/src/main/assets/keyboard_themes/default/key_z.png
index e757c11..49e400c 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_z.png and b/app/src/main/assets/keyboard_themes/default/key_z.png differ
diff --git a/app/src/main/assets/keyboard_themes/default/key_z_up.png b/app/src/main/assets/keyboard_themes/default/key_z_up.png
index ae0b2b0..504d06a 100644
Binary files a/app/src/main/assets/keyboard_themes/default/key_z_up.png and b/app/src/main/assets/keyboard_themes/default/key_z_up.png differ
diff --git a/app/src/main/assets/uni_logp.bin b/app/src/main/assets/uni_logp.bin
index 8ad7338..4f838b8 100644
Binary files a/app/src/main/assets/uni_logp.bin and b/app/src/main/assets/uni_logp.bin differ
diff --git a/app/src/main/assets/vocab.txt b/app/src/main/assets/vocab.txt
index 7a7822a..450ecb2 100644
--- a/app/src/main/assets/vocab.txt
+++ b/app/src/main/assets/vocab.txt
@@ -49998,3 +49998,20759 @@ linga
linsleyi
listserv
literati
+litigations
+locket
+loincloth
+loomed
+loons
+looseknit
+lorica
+louvers
+lowdose
+lowerranked
+lowinterest
+luminary
+luteous
+lyceum
+macleayi
+macrocephala
+magazinestyle
+magnolias
+mailers
+mailroom
+mano
+manumission
+mapmaker
+marinum
+massifs
+mata
+matchlock
+matchmakers
+mauled
+mbaqanga
+mech
+mediarelated
+mediatised
+medicalsurgical
+meetups
+megahit
+melas
+memorybased
+mesangial
+mesencephalon
+meshed
+mesopredator
+messageoriented
+metalloprotease
+metonymy
+metropolises
+mezzo
+microarchitectures
+microcar
+microtubuleassociated
+midWales
+middorsal
+midprice
+midwater
+mie
+miliaris
+mindanaonis
+minimallyinvasive
+minis
+minored
+misadventure
+misappropriating
+misidentifications
+mispronounced
+misreading
+mists
+mitogenic
+mn
+mockingbird
+modulations
+moister
+moisturizing
+monetized
+monosodium
+muffin
+muffler
+mufti
+multiarts
+multinucleated
+multiplexers
+multipotent
+multistoried
+multisubunit
+mush
+muslims
+mustread
+mutinous
+myoclonus
+mystique
+nadu
+nakedeye
+naloxone
+narrowwaisted
+nationalfederal
+naturalborn
+naturists
+navarretia
+ndrina
+nearness
+nearsightedness
+needbased
+needless
+neocharismatic
+netrin
+neuroticism
+neverending
+nigropunctata
+ninepiece
+nomdeplume
+nonIndigenous
+nonOrthodox
+nonagricultural
+nonblocking
+noncatalytic
+noncompete
+noncovalently
+noncriminal
+nonenveloped
+nonfilm
+nonfunctioning
+nonidentical
+nonnegative
+nonorganic
+nonrecourse
+nonsporulating
+nonsulfur
+nontribal
+nonviable
+nosocomial
+novelized
+nucleases
+nucleobase
+nuraghe
+nutritionists
+obkom
+obovate
+obstinate
+ocularis
+oedema
+offleash
+oleander
+ommatidia
+oneeyed
+onthespot
+optouts
+orchestrates
+organismal
+organochlorine
+organofluorine
+organum
+orthocerids
+osteosarcoma
+outcompete
+outdo
+outflank
+outofmarket
+outpaced
+outport
+outsized
+ov
+overcharging
+overdraft
+overvoltage
+overzealous
+oviposit
+ownerships
+paclitaxel
+paddlesteamer
+padlocks
+paisa
+paleoconservative
+pales
+palika
+pallidula
+panfried
+panregional
+paraathlete
+parareptiles
+paresis
+parolees
+partygoers
+passant
+passionfruit
+patientreported
+patios
+patrolman
+paytelevision
+pectineal
+penitent
+pentila
+pepsin
+pereiopods
+perfectionist
+periurban
+perpetration
+pervades
+perverting
+pew
+phenological
+phosphatidic
+photobiont
+phytochemical
+phytosaurs
+pierid
+pignut
+pillory
+pinchhit
+pinyon
+pipa
+piscina
+pizzicato
+plantpathogenic
+plantsman
+playmaking
+pluralities
+pluralityatlarge
+podandboom
+poetically
+poi
+pointsbased
+policyoriented
+pollinates
+polychromatic
+polysynthetic
+pompom
+pontiffs
+ponytail
+poop
+popinfluenced
+porthole
+postWWII
+postganglionic
+postprimary
+powertoweight
+powertrains
+pranksters
+preapproved
+preassembled
+precollege
+precooked
+predicaments
+prenylated
+preordering
+preowned
+prepped
+presidium
+pretaped
+pretournament
+prettiest
+principalis
+proNazi
+procrastination
+producta
+prof
+projectionist
+prophesies
+prostigs
+protostar
+psephologist
+pseudosuchian
+pulldown
+puma
+punctum
+purpleblack
+purposedesigned
+purpurata
+pusillum
+putida
+pyridoxal
+pyrrolizidine
+quadriplegia
+quarreling
+queso
+quilter
+rackmount
+raffia
+railyard
+rainstorms
+rampaging
+ranchs
+raptorial
+raytracing
+readopted
+reapportioned
+reattached
+rebut
+receptorinteracting
+reconquer
+recused
+redfaced
+redlining
+redoubts
+redstriped
+reductionist
+reedit
+reexamine
+refillable
+reformat
+regius
+regularized
+rei
+reining
+relapses
+releaser
+remanufactured
+remediate
+remerged
+remotes
+repaint
+resplendens
+restatement
+ret
+rete
+reticulation
+retracts
+retransmits
+retrotransposons
+rhabdomyolysis
+rhizospheric
+rhumba
+ridding
+ridiculing
+rightbank
+rightfully
+rightmost
+ringnecked
+rishi
+riverbeds
+roadkill
+roan
+roasters
+rockpower
+rockpunk
+rockstar
+rolledleaf
+rooks
+rootstocks
+rosinweed
+roundups
+rubberized
+rudiments
+rufousvented
+rulesets
+rumbling
+rupturing
+rutilus
+sabers
+saclike
+saluted
+sapient
+sauropodomorph
+savagery
+scabbard
+scalps
+scalytoed
+scarecrow
+scariest
+scions
+sclerosing
+scorch
+screed
+screensavers
+scrofa
+scutum
+seasonality
+secco
+seconddeadliest
+secondterm
+sed
+seethrough
+sei
+seine
+seismological
+selectmen
+selfactualization
+selfconsciously
+selfcreated
+selfdestruct
+selfidentifying
+selflaunching
+selfpreservation
+selfreflection
+semiempirical
+semihard
+seminarys
+semipublic
+semisimple
+sentimentality
+separata
+sepoys
+ser
+servos
+sett
+sevendigit
+shanks
+sharptailed
+sheetmetal
+shieldtail
+shiners
+shirtless
+shockwave
+shoemaking
+shorn
+shorttempered
+signposts
+silencers
+silhouetted
+silversmiths
+simians
+simulata
+sinew
+sirtuin
+sive
+sixdimensional
+sixterm
+sixtimes
+slacks
+slotting
+slumber
+slurries
+smokebox
+sneaked
+sneeze
+snobbish
+snowmaking
+snowman
+soapy
+sociality
+socialnetworking
+sockeye
+solemnity
+solidity
+somaliensis
+songstress
+sorbitol
+soundcheck
+sourcecode
+sparsa
+spasmodic
+spearfishing
+spectrums
+speedup
+spherically
+spinelike
+splintering
+spuria
+stackbased
+staid
+standardgage
+standpipe
+staphylococcal
+starlight
+steelframe
+steeplechasing
+stepentrance
+stickleback
+stillexisting
+stirs
+stockbroking
+storehouses
+storia
+strainer
+strangler
+streamside
+streetlights
+strenuously
+strictness
+strokeplay
+structureactivity
+stth
+studentwritten
+styrofoam
+subarticle
+subheading
+subjectivism
+subkingdom
+subluxation
+subscale
+subscriptiononly
+subventral
+sudoku
+sumatranus
+superintendence
+supermax
+superstardom
+suppers
+surfacelevel
+suspensethriller
+swabs
+swale
+swish
+sylvaticum
+symbolist
+syncytial
+tacitly
+tacky
+tagteam
+tailgating
+tailorbird
+takaful
+takedowns
+tantamount
+tarda
+tartans
+technetium
+techs
+teleconferencing
+telekinesis
+telepath
+tempeh
+tempestuous
+tenanted
+tenax
+tenpoint
+tenthcentury
+tenuicornis
+terete
+textilis
+thenunreleased
+thermoforming
+thimble
+thirdlongest
+thirdly
+thirdworld
+thongs
+thornbush
+threenight
+threeroom
+thresher
+tieup
+timberline
+timehonored
+timeofflight
+tipple
+titlist
+toasters
+toenail
+tokenization
+tomahawk
+toothbrushes
+tossup
+tote
+totems
+touchsensitive
+trachomatis
+transSaharan
+transacting
+transfected
+transubstantiation
+traversodontid
+trematosaurian
+triadic
+triclinic
+tripunctata
+trophoblast
+trustworthiness
+trypanosomes
+tuberosum
+tumefaciens
+tutu
+tuya
+tvOS
+twelveinch
+twentysomethings
+twiggy
+twinturboprop
+twofer
+twospeed
+twospotted
+typescript
+typesetter
+unabated
+unbundled
+undeciphered
+underaged
+undying
+unencumbered
+unflinching
+unlobed
+unpitched
+uns
+unspoken
+unwillingly
+uprooting
+uptight
+urbane
+ustulata
+uvula
+vaccinia
+vallenato
+valved
+vanduzeei
+variegatum
+varnished
+vassalage
+velutinus
+venerate
+verifiability
+vestibulocochlear
+vestitus
+vexillology
+vicariates
+vicechampion
+vihara
+vilified
+vill
+vin
+virally
+visavis
+viscounts
+vitiligo
+vitripennis
+viziers
+vocalizing
+vulpes
+vwo
+wah
+walkietalkie
+walkup
+wallichii
+warthog
+waterrelated
+weathervane
+wedgetailed
+weep
+wellfunded
+wellhouse
+wellliked
+wellmade
+wept
+westernstyle
+wharfs
+whitecrested
+whiteedged
+whiteheadi
+whitelabel
+whitelined
+whitenecked
+wholebody
+widowers
+windstorms
+wiretap
+wistful
+withering
+woken
+wolfpack
+womenowned
+workgroup
+worthiness
+wriggler
+xxxx
+yaks
+yellowbrowed
+yellowshouldered
+yogic
+zeitgeist
+zelandica
+zeroes
+zeroth
+zooid
+zvezda
+abnormis
+abortifacient
+abounded
+abridgment
+absconding
+absurdities
+academe
+acquit
+acrostic
+acrylamide
+actinocerids
+actormanager
+acuminate
+adat
+adderstongue
+adjoint
+adjourn
+admirabilis
+advertorial
+aerobes
+aerofoil
+afarensis
+affectivity
+afire
+agespecific
+agonizing
+agoraphobia
+airside
+alHadi
+alKabir
+albonotata
+alcalde
+alexandri
+allIndia
+allardi
+allcomers
+alleviates
+alpacas
+amplexicaulis
+amyl
+anaerobically
+anaphylactic
+angering
+anglicisation
+anhydrides
+animates
+animus
+annihilate
+annuum
+anodic
+ansorgei
+anthophorine
+anthracina
+antiTaliban
+anticoagulation
+antifeminist
+antihuman
+antimoney
+antiparkinsonian
+antiphons
+antipruritic
+antivaccination
+applicator
+apposition
+apprehensive
+aprotic
+archpriest
+archways
+aridus
+arsenals
+arthroplasty
+arthroscopy
+artiodactyls
+artrelated
+artrock
+artsrelated
+assemblys
+astro
+atomicity
+attainments
+attired
+attractively
+auctioning
+auks
+aurantius
+aureola
+auspice
+austenitic
+autoclave
+averting
+axing
+ayahuasca
+backhoe
+bahiensis
+ballplayers
+balneario
+bandicoots
+bandshell
+barbarism
+basolateral
+batesii
+batswoman
+baueri
+baumannii
+bayeri
+begotten
+belayer
+belllike
+bergfried
+berlandieri
+betaNacetylglucosaminyltransferase
+bib
+bichir
+bidentatus
+biflora
+biguttatus
+biochemists
+bioinspired
+biozone
+biryani
+bisecting
+bispinosus
+bitcoins
+bitesized
+bitterns
+bitwise
+blackchinned
+blastula
+blenders
+bleu
+blowfly
+blueeye
+bluespotted
+bluntly
+boletes
+boneless
+borohydride
+bosons
+bovid
+boxings
+branchs
+brassiere
+brownthroated
+brucellosis
+brushstrokes
+buffalos
+bulking
+burgeoni
+bused
+buskers
+byelaws
+byword
+cadences
+caffer
+cajun
+calcaratus
+calipers
+caliphs
+calisthenics
+calliostoma
+camelopardalis
+canadien
+candidly
+candpolit
+cantos
+capabilitybased
+capitalistic
+capitate
+captor
+carabid
+caramelized
+carboniferous
+carburettors
+cargos
+carne
+carnifex
+carpio
+carpus
+cartulary
+catastrophically
+catatonia
+categorys
+cattail
+caucused
+causally
+celled
+celltocell
+centavos
+centralism
+centrifuges
+cernua
+cestodes
+chalybeous
+changers
+chap
+charmed
+chasm
+chatbot
+chelate
+chennai
+cherish
+childhoods
+chile
+chisels
+chromaffin
+chronobiology
+churned
+ciders
+cilium
+cingulatus
+ciprofloxacin
+circularly
+circumvents
+citizeninitiated
+citizenships
+citruses
+classs
+claustrophobic
+clavigera
+clubmosses
+clubrooms
+coax
+coconsecrators
+coerulescens
+coeval
+cognitions
+collegiality
+colonelcy
+colonizer
+colorings
+comer
+comforted
+comicsrelated
+commendam
+commenters
+communautaire
+communitybuilding
+compactus
+complanata
+complexed
+comprehending
+conational
+conceptacles
+concur
+conformis
+confuses
+conicus
+conidiophores
+connexin
+consecrating
+consignee
+consonance
+contractility
+conventual
+convergens
+convexity
+cookoff
+corbel
+cordage
+cordicollis
+cordis
+coriaceus
+cornfield
+coscreenwriter
+costipennis
+costulata
+cottagers
+coughs
+counteracted
+counterrotating
+counterstain
+courtesans
+coverups
+cowgirl
+crablike
+crassirostris
+craterlike
+crenellate
+crockery
+crony
+crossreferencing
+crowdfund
+cryptically
+cryptosystems
+cyanipennis
+cyathium
+cystoscopy
+cytochromes
+dAssisi
+dBase
+dacoit
+dado
+dag
+dahi
+dama
+damnation
+dandruff
+dandy
+danieli
+danse
+daysofstatic
+debased
+debentures
+debridement
+decelerate
+decile
+decisionmaker
+decked
+decomposers
+decouple
+decrypting
+deeplevel
+defacement
+defecating
+defiantly
+defibrillation
+deftly
+degeneracy
+dehydratase
+deification
+demurrer
+dephosphorylation
+deportees
+deputations
+deputize
+deserting
+desiccated
+dethrone
+devastate
+dextroamphetamine
+dhrupad
+diacritical
+diagrammatic
+dialectology
+dicastery
+dicta
+digestible
+digestsized
+digitalis
+diketone
+dimple
+dipper
+directoral
+disassemble
+disintegrin
+diskless
+disklike
+dismutase
+disobeying
+dispossession
+dissents
+dissipative
+distancelearning
+diterpenes
+documentations
+dollhouse
+doorbell
+doubletracked
+doxycycline
+dragline
+draping
+drenched
+drudgery
+drunkard
+dsRNA
+dualband
+dualsided
+dugesiid
+dulce
+durum
+dwarfing
+dysautonomia
+dysmorphic
+eXtensible
+eburnea
+echocardiogram
+economys
+editha
+effaced
+eigenvalues
+eighthcentury
+ejournal
+electrodynamic
+elegantissima
+elongates
+elongating
+eluding
+ember
+embrasures
+embroideries
+emery
+eminences
+enchantment
+encyclopedist
+endeared
+endocones
+endophyte
+endowing
+enduros
+enemas
+energize
+engelmannii
+enslave
+entrails
+envelopment
+ephemeris
+epigraphical
+epigraphist
+epithelioid
+epizootic
+equina
+erhu
+eroge
+erythrocephala
+escapologist
+eugenol
+evaporite
+excruciating
+exculpatory
+exempting
+expediency
+expending
+expletives
+explication
+expound
+extenuating
+extorting
+extrafloral
+fainted
+fairmairei
+fallwinter
+falsify
+familia
+fanbased
+fastcasual
+fatsoluble
+feae
+feedwater
+feelgood
+felted
+feltlike
+femorata
+fenestrae
+fenestratus
+feng
+ferritin
+fervida
+fess
+fetishistic
+ff
+fibrinolysis
+fictionadventure
+fieldbased
+fiestas
+fifthseason
+fiftyyear
+filets
+financials
+firedamp
+fivemile
+fixeddose
+fixedpoint
+fixedprice
+flashbased
+flatheads
+flaveola
+flecked
+flesheating
+floccosa
+flocculation
+floodwater
+fluidic
+fluting
+folkcountry
+fontainei
+forbs
+forefather
+forefinger
+foregone
+foreignpolicy
+foreknowledge
+foremast
+foreshadows
+forgave
+forwardfacing
+forwardthinking
+foulmouthed
+fourlegged
+fourlined
+fourtimes
+fowleri
+freelances
+freemasonry
+friaries
+frontiersmen
+frontmounted
+fullblooded
+funerea
+furiously
+fuscum
+gallo
+galvanize
+ganglionic
+garra
+garret
+gasholder
+gatehouses
+gen
+genal
+generale
+geophysicists
+geoscientist
+geraniums
+germinating
+gforce
+ghouls
+ghrelin
+glabrescens
+glibc
+glorifies
+glucosidase
+gmol
+goalposts
+goddaughter
+goldenbacked
+gondolas
+gouramies
+governmentappointed
+grandiflorum
+gratiosa
+grayishgreen
+greatgreatgranddaughter
+greatgreatgreatgrandson
+greyer
+grisealis
+guaranty
+guardhouse
+guerillas
+guillemot
+guitaroriented
+gumbo
+gymnosperm
+hacklegilled
+hafnium
+hagiographic
+haircare
+hairyeyed
+halfday
+halfmoon
+hallux
+hardcorepunk
+hardcovers
+hardyhead
+haustoria
+hawthorns
+headoffice
+hearingimpaired
+heartache
+heartlung
+heartthrob
+heathen
+hellebore
+helmetshrike
+helminths
+hematuria
+herdsmen
+herpetologists
+hesperus
+heterotrophs
+hexapods
+hey
+hiccups
+highbudget
+highestever
+highfloor
+highoctane
+hijackings
+hindlimb
+hiplife
+historiographic
+homeotic
+homevideo
+homeware
+hookups
+hoopoe
+hoprap
+horrordrama
+horseflies
+hospitalacquired
+hostnames
+hugs
+humanitarians
+humile
+humoristic
+humors
+humped
+huttoni
+hybrida
+hybridus
+hydratase
+hydrogenated
+hydrolyzing
+hydroquinone
+hydroxyapatite
+hyperrealistic
+hypersurface
+hypomanic
+hypoxantha
+hyraxes
+iSchool
+iZombie
+iberica
+icefishes
+ign
+imipramine
+impatiens
+imperatives
+impinging
+implementers
+incantations
+incentivize
+incongruent
+indecision
+indent
+indict
+indomitable
+industrialscale
+infringes
+initiatory
+innocently
+inselbergs
+institutionalize
+insurrections
+intensifier
+interKorean
+interconversion
+interdistrict
+interestfree
+interferences
+interlanguage
+interlocutors
+intermountain
+internecine
+internets
+intifada
+intima
+intuitions
+inversus
+iota
+irrationality
+isolationism
+isometry
+isotonic
+itemized
+jacamar
+jacketed
+jae
+jaguars
+jazzpop
+jigger
+jonesii
+jubatus
+jugal
+kanamycin
+kernelbased
+kilobases
+kingfish
+kiteboarding
+kr
+krater
+kudu
+labormanagement
+lacquered
+laevigatum
+lagers
+languid
+largestselling
+laryngitis
+latitudinal
+launchpad
+leachate
+leafeared
+leaguewide
+leitmotif
+leonensis
+leukemic
+leukotrienes
+levodopa
+liars
+lightbulb
+lindae
+lioness
+lipases
+liquified
+litmus
+littlealtered
+livability
+livebearing
+loadcarrying
+localism
+localist
+localitys
+lonelygirl
+longan
+longbill
+longfinned
+longifolium
+longline
+longtoed
+lores
+lowangle
+lowskilled
+lumpy
+lunettes
+mabuya
+magmas
+magnesia
+magnesian
+magnocellular
+mailings
+makuuchi
+maletofemale
+manes
+mangled
+manis
+mantids
+marga
+margaretae
+marinade
+marquetry
+maudlin
+maurus
+mazelike
+mealy
+medialward
+meditated
+mediumterm
+megakaryocytes
+megalith
+megalopolis
+melaleucas
+melanomas
+meningoencephalitis
+mensonly
+mentalis
+mercaptan
+meristems
+meromictic
+merriami
+mesonotum
+messagepassing
+mestizos
+metalprogressive
+metalworker
+metopes
+metrelong
+meyeri
+micrograms
+micropayments
+microprocessorbased
+microsurgery
+midteens
+millimeterwave
+millionseller
+mindorensis
+mindsets
+mineralised
+minicomic
+minimax
+minimi
+minimums
+mirei
+miserly
+mismatches
+misrepresent
+missal
+misspellings
+mixedused
+mollissima
+mona
+monadic
+monocle
+mortician
+mortification
+mountainsides
+mudbrick
+multicenter
+multidisciplined
+multilobed
+multiprotocol
+multitap
+multituberculate
+multiweek
+mumblecore
+musae
+myiasis
+myxosporean
+naga
+naphthenic
+nappy
+natans
+nativism
+naturopathic
+nearfield
+neavei
+needleshaped
+neighbored
+neurone
+neutered
+newscasters
+newswire
+ney
+nibs
+nigrifrons
+nilotica
+nilpotent
+nociception
+nolimit
+nonAsian
+nonactors
+nonautonomous
+noncirculating
+noncooperative
+nondominant
+nongaming
+nonindustrial
+nonmetal
+nonpaying
+nonpenetrative
+nonperiodic
+nonpigmented
+nonredundant
+nonreproductive
+nonsingle
+nonsmallcell
+nonsmoking
+nonsporting
+nonstarter
+nonstick
+northend
+norvegica
+nouvelle
+nucleoplasm
+numb
+nunciature
+nutcracker
+nutritionally
+nutshell
+obiter
+oceanographers
+ochraceus
+odorous
+oesophageal
+officinarum
+offkey
+offstreet
+oilonpanel
+olivebacked
+oliveri
+ora
+orand
+orexin
+organosilicon
+ornithomimosaurs
+osteonecrosis
+ostinato
+outstation
+overblown
+overconfidence
+overeating
+overrunning
+overtimes
+overused
+overwrite
+ovina
+oxeye
+oxidants
+packagers
+paddocks
+padic
+pagodas
+painstakingly
+paleozoic
+palpalis
+pandemocrats
+pantheons
+paperandpencil
+paralog
+paralympics
+parang
+paratriathlete
+pasteurization
+pastorate
+pastpresident
+patricia
+pauli
+payam
+pegging
+penandink
+penetrance
+pentafluoride
+perforata
+perfumed
+pericarp
+peridotite
+perimeters
+peripatetic
+perpetrating
+personatus
+perturbative
+petiolaris
+petiolata
+petticoat
+pharyngitis
+phenomenally
+phenotypical
+phenotyping
+phorbol
+photoreconnaissance
+photosharing
+phrasal
+pigmy
+pilus
+pinups
+pitfall
+pixilation
+placeholders
+plantigrade
+plasticine
+platinumcertified
+platy
+plausibly
+playgroup
+playmate
+playset
+pleiotropic
+pleuralis
+plucks
+plumages
+plumed
+pluto
+pochard
+podcasters
+policewomen
+politely
+polyA
+polyamine
+polyamines
+polyandrous
+polyesters
+polyposis
+portlet
+portly
+positrons
+postconviction
+postmistress
+postsurgery
+potty
+precipice
+predicative
+prefab
+prelaw
+premeditation
+presales
+preth
+prewritten
+priapulid
+pringlei
+probono
+prodemocratic
+proffered
+profilers
+propeptide
+propped
+propyl
+prosauropod
+prostrata
+prostyle
+proteinbinding
+proteus
+pruinosus
+pseudopodia
+psychedelicprogressive
+psychosurgery
+publicizes
+pufferfishes
+pulverulenta
+pupating
+puree
+purist
+purser
+quatrefoil
+quickwitted
+quinoline
+quintessence
+rackmounted
+radiotelegraph
+rainfed
+ramen
+rasp
+rationalists
+rava
+ravage
+rawhide
+reaffirmation
+reaffirms
+rearranges
+reassess
+recension
+recertification
+recombinase
+recompense
+reconnection
+reconvene
+redaction
+redeemable
+redtail
+redwoods
+reemerge
+refilling
+refiner
+reflectometry
+reflow
+reformism
+reframing
+reimagines
+relaxin
+relictual
+relictus
+remapped
+remounted
+renaissancestyle
+reorientation
+repetitious
+replications
+repo
+reptiliomorphs
+rerunning
+resentful
+reserpine
+resettling
+resourcebased
+resourcepoor
+retinoid
+retrofuturistic
+retrogaming
+revalidated
+rheumatologist
+rhombicuboctahedron
+ringlike
+rippling
+riskbased
+rivulets
+roadbuilding
+rockfall
+rockjazz
+roguelikes
+rolebased
+rots
+rotted
+roughcut
+ruficauda
+rufousbellied
+rufulus
+rugicollis
+rugosum
+ryanodine
+sagittata
+sandbags
+sansserifs
+sapphires
+sarcolemma
+sarsaparilla
+sawnwork
+scalding
+scalped
+scatters
+schoolwork
+scitula
+scrapbooks
+screener
+scuttling
+seashell
+secondperson
+secundum
+sedate
+sedatives
+seedeaters
+seismograph
+selfpaced
+selfprotection
+semestered
+semitrailers
+senates
+serpentinite
+sevencylinder
+sevenvolume
+sexrelated
+sextuplets
+sexualized
+shallowdraft
+shaven
+shedroof
+shootoff
+showband
+sibiricus
+silicic
+silicide
+siliconbased
+simplifications
+singleseaters
+singlestage
+sinner
+sixdisk
+sixpointed
+sixthmost
+sixtyeighth
+sixtythird
+skewered
+skillet
+sl
+slatecolored
+sleepwake
+slicer
+smack
+smallbusiness
+smallformat
+smeltwhiting
+snakeweed
+snobbery
+sobriquets
+sociopath
+solanacearum
+solida
+songbooks
+soror
+soundman
+southwestwards
+spaceborne
+spacey
+spamdexing
+spandrel
+sparkplug
+spats
+spatulate
+speakership
+spearing
+spedup
+spillways
+spinifer
+splenomegaly
+splints
+spotlighted
+spouts
+spreaders
+sprinkle
+squalene
+squeaking
+ssh
+standardbearer
+startfinish
+statedesignated
+stereochemical
+stereoscopy
+steroidogenesis
+stilllifes
+storedvalue
+stormtroopers
+strapping
+stressinduced
+stretchy
+strigatus
+strigosus
+stubs
+stung
+sua
+subbranches
+subcomponents
+subdialects
+submerge
+submergence
+submillimeter
+subnets
+subpart
+subperiod
+subsaharan
+subscales
+substellar
+substructures
+subtests
+subtriangular
+succinic
+sulci
+sunscreens
+supercouples
+superheroic
+superioris
+supplicant
+suppository
+suprarenal
+supremo
+surcharges
+surinamensis
+surreptitious
+swaths
+sweetgum
+swingarm
+swingman
+sympathizing
+tabloidstyle
+tabulating
+talkative
+tamil
+tampons
+teapots
+teasel
+technologyoriented
+tegu
+telepathically
+televisionradio
+tenantsinchief
+tendollar
+tentrack
+terpenoid
+terroir
+testtakers
+tetrafluoroborate
+tetrahydrobiopterin
+tetrameric
+tetrapyrrole
+thenSenator
+thiazide
+thintoed
+thirdcentury
+threebedroom
+threebladed
+threespan
+threetower
+thriceweekly
+thrifty
+thrombi
+thrombocytopenic
+thst
+thwaitesii
+timeshifted
+timestamps
+tinkering
+tomographic
+topos
+tourismrelated
+traitorous
+tramline
+transect
+transliterations
+transracial
+traviata
+treatmentresistant
+tribunus
+trickortreating
+tricolored
+trifida
+triloba
+triphenylethylene
+triphosphates
+tripods
+triremes
+triterpenoid
+trivially
+troglobite
+troodontids
+trophoblastic
+truncating
+twang
+twayblade
+twentyminute
+twinkling
+twobladed
+twochannel
+twolined
+typicus
+ulFitr
+umami
+umlaut
+unabashed
+unassailable
+unbanked
+unbilled
+uncertified
+undeliverable
+underscoring
+undoubted
+unfluted
+unicornis
+unipolar
+unpolluted
+unpunished
+unquestioned
+unreactive
+untidy
+untried
+unwind
+upanddown
+upgradeable
+utilis
+vadose
+valgus
+vandalizing
+vaulters
+velcro
+velocimetry
+ventilators
+ventromedial
+vernaculars
+vesical
+vibrancy
+vibrated
+videoclips
+videographers
+vindictive
+vinyls
+virginalis
+virginiae
+viridula
+vitis
+vixen
+vocalistkeyboardist
+volitional
+volunteerdriven
+vomer
+voyageurs
+voyeuristic
+vshaped
+wXw
+walteri
+warmers
+warps
+washedup
+washout
+watchdogs
+waterpower
+watertube
+wavesynthpop
+weatherresistant
+weathers
+wellfounded
+wellintentioned
+wellkept
+wheelbarrow
+wheeling
+whirling
+whirlpools
+whopping
+whydah
+wigeon
+wilsonii
+wipers
+wiretaps
+wombat
+wooddecay
+woofer
+worksheet
+worshiper
+xerophytic
+xi
+xiangqi
+yellowrumped
+zebrina
+zenkeri
+zeylanicum
+zig
+zigzagging
+zodiacal
+zucchini
+zum
+abrogation
+absolved
+abstinenceonly
+acclimatization
+accrual
+acousticelectric
+acrimoniously
+acutifolia
+adagio
+adduction
+adjusters
+adventive
+advisement
+african
+aftershow
+aftertax
+afterthought
+agassizii
+airbrushed
+alArab
+alHakim
+alHassan
+albata
+albidus
+albiflora
+albipuncta
+albomarginata
+alboplagiata
+albosignata
+albuginea
+albumlength
+aldolase
+allinstrumental
+allodial
+allyoucaneat
+alphaketoglutaratedependent
+altaica
+alteregos
+alternatingly
+alternatus
+ama
+amara
+amazement
+amazingly
+amirs
+anachronistically
+anaconda
+anchorite
+ancilla
+angiotensinconverting
+ankylosaurid
+anodes
+anoles
+ans
+anthozoans
+antiApartheid
+antiIsrael
+antigenbinding
+antigonadotropic
+antiinfective
+antiporter
+apheresis
+apologizes
+apotheosis
+appetites
+applicationlevel
+applicators
+appraisers
+arbitrations
+arboriculture
+archerfish
+archeri
+arcus
+argumentum
+armeniaca
+arrester
+arroyos
+artillerymen
+ascendens
+assertiveness
+assize
+aterrimus
+atmospherics
+atricornis
+atripennis
+attentiondeficit
+august
+auricle
+automorphic
+autotransportable
+avec
+aviso
+awardgiving
+backbenches
+backboard
+backwardness
+bakelite
+ballclub
+balteatus
+bancrofti
+bangles
+banishing
+baritones
+baronetage
+barreled
+basking
+basque
+batteryelectric
+batterys
+battlegroup
+beagles
+behead
+belowaverage
+belting
+benga
+benthicola
+betaadrenergic
+beth
+bicinctus
+bicolorata
+bigtime
+bilberry
+bilinear
+bioactivity
+biobank
+biodefense
+bioengineered
+biographic
+biotope
+biplagiata
+birdcage
+birdsofparadise
+biroi
+birthplaces
+birthwort
+bisphenol
+bistatic
+biz
+blacklists
+blacknecked
+blackwood
+blanco
+blemishes
+bloke
+bludgeoned
+bluebanded
+bluescreen
+blushing
+bodhran
+bodyboarder
+bole
+bollards
+bonang
+bonanza
+bonnets
+bonspiels
+bottomlands
+bourse
+bower
+boxcar
+brachialis
+breakdance
+breakpoints
+brigand
+brigsloops
+broadbanded
+broadbilled
+bronchoconstriction
+bronchodilator
+broomlike
+brownbacked
+brutish
+bryanti
+bullfight
+burgher
+butlers
+butternut
+buyback
+cEvin
+calendrical
+callups
+calluses
+camerawork
+candidum
+canopied
+capitalintensive
+cara
+carburetted
+cardstock
+carolinus
+carryout
+cartouches
+carvalhoi
+casta
+catsuit
+causeandeffect
+celeste
+cellmate
+centerhall
+ceramidase
+chaindriven
+chalcedony
+chargeback
+charterer
+chelicerate
+chemistries
+chessplaying
+chiller
+chines
+chiseled
+cholestasis
+chowk
+churchrelated
+circinata
+circumnavigating
+cityoperated
+classy
+clawless
+cleansed
+clicker
+climaxes
+closeby
+cmdexe
+coFounder
+coactivators
+coarctata
+coattails
+cobranding
+cockatoos
+cocreate
+coediting
+coeditorsinchief
+cognitivebehavioral
+cohors
+collarti
+collateralized
+comatus
+combusted
+communityfocused
+competently
+complacent
+composerlyricist
+composita
+compressus
+compta
+concentrators
+conceptualizations
+concordant
+concretely
+congenial
+connectionist
+conquers
+conservatoire
+conservatoires
+conservatorship
+constitutionalism
+consumerist
+contactor
+contiguity
+contortion
+contrastingly
+contravened
+cooccur
+coopers
+coots
+copulatory
+coqui
+cordgrass
+cordoned
+cornstarch
+corpsman
+cos
+cosponsoring
+costsharing
+cowinners
+crabbing
+crackdowns
+cranked
+cranking
+cratering
+cremations
+crewserved
+crimp
+cristal
+croaking
+crocata
+cronies
+crosshead
+crossreference
+crosssector
+crosswalks
+crosswind
+cruck
+cryptids
+cs
+ctenophores
+cubism
+cuboctahedron
+culdesacs
+cunt
+cupbearer
+cupreus
+curculio
+curlytailed
+curtailment
+curtisii
+cusk
+customisation
+cyberterrorism
+cystatin
+dAsti
+dEUS
+dacoits
+dais
+dancedrama
+datacenters
+davisi
+deacetylation
+decedent
+decompressing
+decoratus
+deers
+deferment
+deforms
+degreelevel
+deliquescent
+delos
+demystify
+denouement
+dentifera
+deodorants
+deoxyribonucleic
+depersonalization
+deposittaking
+depress
+dermoid
+desertorum
+desertparsley
+designationKorea
+despairing
+despise
+deters
+dhol
+dialers
+diapsids
+diffractive
+digoxin
+dildo
+directbroadcast
+directedenergy
+disaffiliation
+disagreeing
+disburse
+discalis
+discoidalis
+discouragement
+discoverable
+dismasted
+disorienting
+disparage
+disqualifying
+distillates
+distracts
+distributaries
+distrusted
+diterpenoid
+djent
+documentoriented
+dogstail
+dole
+domainbinding
+dopant
+doris
+dotterel
+doused
+downplay
+doxorubicin
+dreamt
+dropseed
+dropwort
+drosophila
+drownings
+drugresistant
+duallicensed
+duals
+duckweed
+dud
+duff
+dukun
+dullcolored
+duplicity
+dyad
+dzong
+eccentrics
+ecclesiastica
+echinatus
+echinoids
+econometrician
+edule
+eggshells
+eicosapentaenoic
+eightpointed
+eightynine
+elatus
+elderberry
+eloped
+emarginatus
+emboli
+emceed
+emotionless
+endogenously
+endproducts
+enganensis
+enlightening
+enquire
+ensuite
+enterpriseclass
+enumerative
+envied
+eos
+epicyclic
+epidermolysis
+epitaphs
+epoxidation
+equalizers
+equids
+equivocal
+ergo
+ergonomically
+ergosphere
+errorcorrecting
+errorfree
+erythropus
+etcetera
+ethnocentrism
+etiologies
+eusociality
+evaporites
+exacerbating
+exarchate
+exes
+exome
+exostoses
+expansionary
+expletive
+exservice
+exsoldier
+extractors
+exurb
+exurban
+eyeglass
+eyesore
+fa
+factitious
+factorybuilt
+falcatus
+fallaciosa
+falter
+famiglia
+fanfiction
+fasciculi
+fasti
+fatherland
+faulttolerance
+featherlike
+feigning
+felderi
+felids
+fended
+ferch
+fiend
+fifthbest
+filles
+finalization
+finetune
+finlayi
+firebrand
+fireships
+firstcentury
+firsthalf
+firstlook
+fissionable
+fixative
+fixedgear
+fixer
+flagellation
+flamines
+flattery
+flattish
+flavitarsis
+fleurdelis
+flexi
+floodlighting
+floorboards
+floriculture
+flowery
+flowthrough
+flumes
+folkloristics
+folky
+fooling
+forebay
+forestfly
+forfeits
+forgettable
+formant
+formicid
+formyl
+fornication
+fortlets
+fossilfuel
+fossilize
+fourinhand
+fourletter
+fourspotted
+fourtholdest
+foxhound
+framebased
+framebyframe
+fraterculus
+freeflow
+freeskier
+freespace
+freezethaw
+freshener
+fritillaries
+fronton
+fruitfly
+fucose
+fullterm
+fume
+functionals
+funksoul
+funneling
+furling
+furtive
+fuscicornis
+galago
+gallate
+gamebird
+gameboard
+gamelike
+gaon
+garnets
+gastropub
+gaur
+gearequipped
+geeky
+gemellus
+genrebending
+gentlemanly
+germinates
+germplasm
+giudice
+glassenclosed
+glassreinforced
+glens
+globalizing
+gloria
+glycolytic
+gnarled
+goalies
+gobetween
+goldcertified
+gondii
+gorgonopsian
+gouged
+gourmand
+grackle
+graham
+granatum
+grandifolia
+gravitationalwave
+graybacked
+greataunt
+greenishcream
+greenlight
+groupie
+groupthink
+gulfs
+guttulata
+gyrase
+haberdasher
+hackerspace
+hackle
+hackney
+hairtail
+halfmillion
+halfpenny
+halfsiblings
+hallandparlor
+hallparlor
+hamatus
+handouts
+handrail
+haphazardly
+harbourside
+hardwearing
+hart
+hatchbacks
+hauler
+havo
+hazmat
+headfirst
+headscarf
+headup
+heidelbergensis
+helleri
+hemosiderin
+henna
+hermaphroditism
+hermitages
+heterologous
+heteromorph
+higherresolution
+highstreet
+hillstream
+hindmargin
+histiocytosis
+hitches
+hockeys
+hoffmanni
+hollyhock
+homebuilders
+homefront
+homeomorphic
+homestyle
+homodimeric
+hoodies
+hoodoo
+hookeriana
+hornero
+hornpipe
+hostelry
+hotelcasino
+houseplants
+hulks
+hunky
+hydrologist
+hydroxides
+hydroxycinnamic
+hyphy
+hypodermis
+iCal
+iSeries
+ich
+idiots
+idolized
+illumos
+imbedded
+immunogen
+impressionists
+incepted
+incessantly
+incharacter
+indentures
+independentminded
+indevelopment
+indiealternative
+indirection
+indisputable
+indusia
+industrialize
+industryled
+industryrelated
+inear
+inexhaustible
+infilling
+inflates
+infraspecific
+infuse
+inquires
+insourcing
+instinctual
+insulae
+insulindependent
+interactionism
+interbellum
+interdental
+interdepartmental
+intermarriages
+intermezzo
+internetconnected
+interparliamentary
+interrelation
+intersperses
+interstates
+intertrochanteric
+invision
+ironweed
+irresponsibility
+irrevocably
+isolators
+jammers
+jetliner
+jewelcase
+jobber
+jobbing
+judicata
+july
+kPa
+kabbalistic
+kame
+kantele
+kashmirensis
+katakana
+kebeles
+kecap
+kendo
+keypunch
+keyring
+kickstart
+kidfriendly
+kindergarteners
+kirtan
+kitschy
+kmlong
+kris
+kuna
+kungfu
+lacordairei
+lacunae
+laevicollis
+lamas
+laminating
+larder
+largescaled
+lascar
+latenineteenth
+latens
+latipes
+latticework
+lawrelated
+leatherbound
+lefteye
+legislating
+legspinner
+lessors
+leucoptera
+levitate
+lexicographical
+likens
+linearifolia
+linkers
+lipodystrophy
+liposuction
+lipsynced
+listenable
+listenedto
+literals
+liturgically
+lobotomy
+locallyowned
+lockable
+lollipops
+longed
+longiceps
+longulus
+longum
+loon
+loopback
+lotic
+lowerranking
+lowestranked
+lowflying
+lucasi
+lumberjacks
+luzonensis
+lysozyme
+madcap
+madrassa
+maegashira
+mafiosi
+magellanica
+magnetospheric
+majorityblack
+maladies
+mandocello
+mandola
+manhours
+manioc
+mansoni
+manubrium
+marae
+marca
+margaritifera
+marginalia
+marihuana
+maritimum
+marmorea
+marxist
+massless
+maternally
+matric
+matroid
+maura
+meadowrue
+mechanoreceptors
+meditating
+mediumdensity
+mediumlift
+megacephalus
+megacorporation
+megaphone
+megaprojects
+melaleuca
+melanops
+melidectes
+mentalhealth
+merchandisers
+mercurial
+merriment
+mesentery
+metacognitive
+metalpoor
+metaphoric
+micaceous
+microhabitats
+microservices
+microstrip
+midafternoon
+midinfrared
+midnd
+midnineteenthcentury
+midribs
+midvein
+mimeograph
+mirus
+misprinted
+missional
+mistranslated
+mixedmember
+modillions
+moerens
+moistened
+moldy
+molerats
+moluccensis
+moneylaundering
+mongrel
+monofilament
+monomorphic
+monoterpene
+monotonic
+monte
+montivaga
+moonwort
+moorhens
+morel
+morels
+motorable
+mottoes
+mouses
+mousse
+mullioned
+multibattalion
+multicar
+multienzyme
+multipass
+multiphoton
+multitenant
+murinus
+musicaldrama
+musicianproducer
+mutagen
+muting
+mycorrhiza
+mycosis
+nakedtailed
+nam
+nannies
+nanoelectronics
+naphthoylindole
+nasopharyngeal
+natured
+nawabs
+nearvertical
+negates
+neighborly
+neoclassicism
+nepal
+neptunium
+neuromuscularblocking
+neuropathies
+neuropsychiatrist
+neurovascular
+neutralizes
+neutrally
+nigropunctatus
+ninepart
+nitrification
+nitriles
+nitrobenzene
+nl
+noball
+noche
+nofault
+nonNATO
+nonbenzodiazepine
+nonconference
+nondigital
+nonfarm
+nonhomologous
+nonintrusive
+nonlinguistic
+nonperishable
+nonprogrammers
+nonsmokers
+nonsocialist
+nonstarters
+noradrenergic
+notational
+notoungulate
+nowlost
+nowobsolete
+nozze
+nuchalis
+nullifying
+observatorys
+ocarina
+ocellate
+ogre
+ol
+oleifera
+omnipresent
+onematch
+oneness
+oofamily
+openheart
+openwater
+ophiolite
+oppressing
+oratorical
+orientalism
+orofacial
+oropharynx
+oseltamivir
+ossicones
+osteitis
+ostium
+outfitters
+outgunned
+outlast
+ovalifolia
+overextended
+overflights
+overlaeti
+overwork
+ovis
+oxygenate
+pachyphylla
+packhouse
+paleobotany
+panpipes
+papillomas
+parallelus
+parasitizing
+parenchymal
+parenthesis
+parvidens
+parvocellular
+passionflower
+pastoring
+pathfinding
+patricius
+pax
+paymaster
+pcode
+peacemaker
+peatlands
+peddlers
+peduncularis
+pendent
+pendulums
+penicillatus
+penmanship
+pennywort
+pentacle
+pepperweed
+peripheries
+peristyle
+permanganate
+peronii
+peroxisomes
+personam
+personifies
+personify
+petaflops
+petascale
+pharmacogenomics
+phenylpropanoid
+phenytoin
+phonologically
+phototropism
+phthalic
+pickpocketing
+pilsbryi
+pilsner
+pinetorum
+pinks
+pinnule
+pinwheel
+piranha
+plainer
+planifrons
+platelike
+pleases
+pneumatically
+pneumococcal
+poinsettia
+polaris
+policemans
+polyene
+polyglutamine
+polymerize
+polymorph
+pompano
+pontica
+pontis
+popindie
+populating
+porcupinefish
+porgy
+porins
+postIndependence
+postcentral
+postconceptual
+postdoctorate
+potently
+pothole
+potluck
+pratti
+preadolescent
+preceptor
+precut
+predesignated
+predisposes
+predraft
+preempting
+preestablished
+prefixing
+prepartition
+preplanned
+presacral
+presupposition
+priapism
+primatology
+primitively
+probationers
+proclivity
+producersongwriter
+prolate
+pronouncement
+propofol
+proprotein
+provably
+provocatively
+psaltery
+pseudodocumentary
+psycho
+psychopaths
+pupillage
+purer
+purplishred
+pushtotalk
+pushup
+putrefaction
+pyrethroid
+pyrimidines
+quadrupeds
+quango
+quash
+quatrain
+quatrains
+quelling
+quietest
+radicalisation
+raindrops
+ramosissima
+rapae
+ratite
+reanimation
+reapplied
+reassured
+recede
+recensions
+reclassed
+reconnecting
+recordequalling
+recurva
+recurvata
+redbelly
+redecoration
+redhead
+redtipped
+reedgrass
+reefing
+refiners
+refoundation
+refusals
+regality
+registrant
+reinsurer
+reinterprets
+rejuvenating
+relished
+remailer
+remotest
+renegotiation
+repented
+repopulated
+reptilelike
+requisites
+rerum
+resents
+reshuffling
+retraced
+revere
+reversibility
+revises
+ribcage
+ribonucleic
+richardsoni
+richteri
+riggers
+rightangled
+rightsbased
+ringens
+romani
+rosei
+rotoscoping
+ruggedized
+runins
+rushhour
+sabermetrics
+sabino
+sachets
+safeworking
+safflower
+saligna
+saluting
+salvo
+sambo
+sandbanks
+sanddune
+santa
+santoor
+sapling
+sapo
+scammers
+scaphopod
+scaring
+schlegelii
+scopa
+scrapbook
+scriptable
+scuffle
+seabrai
+seagulls
+seamer
+searcher
+seaworthiness
+secondfastest
+seizes
+selectman
+selenocysteine
+selfbuilt
+selfdescribing
+selfstudy
+seltzer
+semesterlong
+semilunaris
+semispinalis
+senatus
+senex
+sequins
+serjeant
+serogroup
+serpentina
+serviceability
+serviceberry
+setigera
+sevenstring
+seventyfifth
+sextant
+shackle
+shapeshift
+sharppointed
+shia
+shifters
+shortfin
+shorting
+shredder
+shrimplike
+shuttled
+sidelong
+sidewalls
+sieboldii
+sifted
+signaller
+signees
+signlanguage
+silane
+silvanid
+silverline
+simpleton
+sinense
+singersong
+singlespeed
+siphonophores
+sixgame
+sixpage
+skapop
+skewers
+skinning
+slaveholding
+slavemaking
+sliceoflife
+slideout
+sloe
+slung
+snRNP
+sniping
+snowi
+snowstorms
+solitons
+solstices
+somalica
+sparkignition
+speakerphone
+specialedition
+specialinterest
+speedrunning
+spicatum
+spinet
+spinicollis
+spiteful
+splines
+spongelike
+sportstalk
+spotty
+sprain
+sprig
+squamosal
+squeaker
+stacker
+stairwells
+stammer
+stealthbased
+steed
+steeplypitched
+stemcell
+stepchild
+stephensi
+stepper
+stereographic
+stifled
+stillactive
+stipes
+stoic
+stoker
+stolon
+stoop
+storeroom
+strategical
+strategize
+stroked
+strollers
+strum
+strutting
+stuarti
+studenttofaculty
+stuntmen
+subassemblies
+subcarriers
+subcomponent
+subdermal
+subframe
+subfund
+subline
+submariner
+submersion
+submucosa
+submunicipality
+subnormal
+subscapularis
+subsectors
+subtended
+subverts
+subvillage
+sues
+suffusion
+sunbirds
+supercooled
+superheroines
+superlightweight
+superstores
+superstructures
+suspiciously
+swamphen
+swash
+sweatshirts
+sweaty
+sylvaticus
+symbolical
+synanthropic
+syndactyly
+syntrophic
+syslog
+tablecloths
+tabletennis
+takeup
+talker
+talkshows
+tamarack
+tapeless
+taunts
+technopop
+teledrama
+telencephalon
+teleprompter
+teleserial
+telethons
+temperaturecontrolled
+tenderloin
+tergum
+terminata
+terminators
+terpenoids
+terricola
+testisspecific
+tetratricopeptide
+thawed
+theists
+thenmayor
+theraphosid
+thethen
+thieving
+thioester
+threebarbeled
+threemovement
+threesome
+threewheelers
+thtier
+thuringiensis
+tightens
+tightest
+tightfitting
+tillers
+timberframe
+timetrial
+titlewinning
+toolmaking
+torpor
+tors
+torta
+touchscreenbased
+touronly
+toxicities
+trabeculae
+traceroute
+tractortrailer
+tradeshow
+tragacanth
+transesterification
+transposing
+trawls
+treadle
+trendsetting
+triaxle
+tricksters
+triennially
+trifolii
+trilateral
+trimarans
+trimethylamine
+trippy
+trumpetshaped
+trunking
+tsetse
+tuneful
+turnstile
+twelvebar
+twosport
+typographers
+udder
+uilleann
+ulmi
+ultrashort
+umbilicata
+unabashedly
+unaffordable
+unblack
+unbridled
+unburned
+unceremoniously
+unconvincing
+uncountable
+uncrewed
+undercooked
+undercroft
+undeserved
+unfocused
+unicode
+uninvited
+unities
+unknowable
+unleavened
+unmask
+unsatisfying
+unselected
+unselfish
+unshared
+unthinkable
+updateThe
+upgradation
+upturn
+uremia
+uremic
+vaccinepreventable
+vacuolar
+vaga
+valedictory
+valency
+valvetrain
+vanillin
+variegation
+vascularized
+vasomotor
+vedette
+vedic
+veld
+venae
+venata
+venters
+vergence
+verifications
+versant
+vertextransitive
+vexillum
+viewport
+viking
+violoncello
+viridipennis
+virility
+vise
+vitriol
+viva
+volans
+volcanically
+voussoirs
+voyaging
+vulcanization
+walkthroughs
+walruses
+wanderers
+warders
+wardrama
+warplanes
+watchOS
+wattleeye
+weaponized
+wearables
+webtoon
+wedging
+welldressed
+wellmarked
+wellplanned
+wellused
+welwitschii
+westernized
+wetness
+wetsuit
+whereafter
+whims
+whitecollared
+whiteface
+whiteflies
+whiteowned
+whitepainted
+wholegenome
+wholeheartedly
+widemouthed
+wireguided
+wishful
+wolflike
+woodruff
+woodturning
+workup
+wreaks
+writeup
+xB
+yachtsmen
+yearbyyear
+yonipitha
+yoyos
+zigzags
+zonatus
+zookeeper
+abolishes
+absconded
+academical
+acceptances
+accosted
+achilid
+acidfast
+acrimony
+actionplatform
+acutangula
+addins
+admonition
+advantaged
+adversarys
+advertisingsupported
+ae
+aerostat
+aestivalis
+affirmatively
+aflatoxin
+aftertouch
+agnostid
+agri
+agronomic
+airfoils
+airguns
+alKarim
+albicosta
+albomaculatus
+allover
+allylic
+almighty
+ambercolored
+ambushing
+amelioration
+amenorrhea
+americium
+aminotransferase
+amity
+amperes
+anagrams
+anamorphs
+anesthetized
+anglicization
+angulifera
+anonymised
+antagonized
+antbirds
+antechamber
+antennatus
+anthracycline
+anthropomorphism
+antiCatholicism
+anticensorship
+antifungals
+antigenantibody
+antithetical
+anxiogenic
+api
+apoplexy
+appendectomy
+apportioning
+aramid
+araucaria
+arbitrate
+arbitrated
+arboreus
+arcana
+areolata
+aroundtheclock
+arsenite
+arterials
+arteriole
+arteritis
+ascidians
+ascomycetous
+asemic
+atrovirens
+attestations
+aucuparia
+auroras
+australasiae
+autobiographic
+autosomes
+autotrophs
+avenger
+avocation
+awakes
+axially
+azureus
+backarc
+backpropagation
+backstretch
+backwardscompatible
+bagpiper
+bailiwick
+bairdi
+ballshaped
+bandcamp
+bandurist
+bap
+barbecuing
+barbeled
+barite
+basketballs
+battens
+bazar
+beaklike
+beaming
+bearish
+beatnik
+becard
+bedchamber
+beddomei
+beefsteak
+beeman
+bellbird
+benchtop
+benefaction
+bengal
+benzoin
+betacatenin
+betalactamase
+biconvex
+bioassay
+bioethical
+biomaterial
+biomolecule
+bioorganic
+bipropellant
+birchbark
+birder
+bishopelect
+bitrates
+bizarrely
+blackcap
+blooper
+bluesmen
+bluetongued
+bollworm
+bolton
+bombmaking
+bombsight
+bookends
+bookmarklet
+bouvieri
+bowtie
+brasiliana
+brazen
+breastfed
+breastfeed
+breastwork
+breedspecific
+brent
+briefcases
+brokendown
+brushy
+budgerigar
+bulkier
+bullatus
+bullous
+burqa
+bushbaby
+bushman
+busk
+busted
+butterscotch
+buttes
+cabriolet
+caddis
+cadenza
+caimans
+caliper
+calorific
+calorimeter
+calorimetry
+calpain
+camerunica
+canaliculi
+canards
+candphilol
+canoekayak
+cantaloupe
+cantankerous
+canted
+capitatus
+carbapenem
+carborundum
+cardinality
+carelessly
+cargopassenger
+carica
+carmakers
+carnivory
+casebooks
+casework
+castello
+casters
+castinplace
+catechin
+cathodic
+cavalcade
+cavalier
+ce
+centralisation
+centralizes
+centurions
+chalked
+chalybea
+chasmogamous
+chicane
+chiefofstaff
+chilies
+chloroquine
+chondrodysplasia
+churchstate
+chylomicrons
+cinchweed
+cineraria
+circulars
+citri
+citrifolia
+cittern
+citybased
+clavatus
+cleat
+cleaver
+climaxed
+clingfish
+clomifene
+closein
+clothesline
+cloverlike
+coalburning
+coasting
+cochineal
+cocitation
+codependency
+coffeetable
+coinages
+coir
+coitus
+collimator
+colorblind
+colostrum
+colourings
+comefrombehind
+comfrey
+commas
+commensals
+commissar
+comorbidities
+comosa
+compactly
+compactor
+compartmentalized
+condensers
+condyles
+conferral
+congratulate
+congratulating
+congratulatory
+congregationalist
+conjunctival
+conjured
+consciousnessraising
+consecrator
+constructional
+consulships
+consumerfacing
+consumerlevel
+contentcontrol
+contextualized
+continuouslyoperating
+contrabassoon
+contravenes
+contrition
+convertases
+coolness
+copiously
+coppers
+copresidents
+coronae
+corrector
+correlative
+corrosionresistant
+corrupts
+corvina
+coshowrunner
+cosmetically
+councilowned
+counterparties
+countersued
+cowbells
+cp
+crankshafts
+crawfish
+creamwhite
+credo
+creepers
+crematory
+crocheting
+crosscommunity
+crossfertilization
+crossselling
+crouching
+crowfoot
+crucifers
+crushers
+cryptanalytic
+cucullatus
+cuirass
+cuneatus
+cupule
+curios
+cuticular
+cyclopentadiene
+cypresses
+cystadenoma
+dArt
+dEtat
+dalli
+datestone
+dawah
+dawning
+dayschool
+deacetylases
+deactivating
+deadmaus
+debilitated
+debugged
+decently
+decidable
+decimation
+deeplying
+deflector
+defusing
+degenerating
+degranulation
+dehumanization
+delimiting
+denitrification
+deoxyribonuclease
+deoxyribonucleotides
+depresses
+depressum
+depute
+deputizing
+derivate
+derivational
+dermatologic
+desertions
+desolation
+despotic
+destitution
+detonates
+devolving
+dhonneur
+diacritic
+diene
+diffusely
+diffusive
+dilating
+diluta
+dilutions
+dimorph
+diphallia
+dipoles
+disallows
+disassembling
+discontinuance
+discors
+discrepans
+dishware
+disinterested
+dispositional
+dissuaded
+distans
+distinctness
+distrustful
+disunity
+dit
+divisor
+docklands
+docusoap
+doghouse
+dossiers
+doubleact
+doubledecked
+doubleentry
+doublehanded
+downsides
+dragonfish
+dragonfishes
+dragonlike
+drapers
+drinkware
+drowns
+dryas
+duc
+dugongs
+dunks
+dustjacket
+dyslipidemia
+earlyseason
+easterncentral
+educationrelated
+eduskunta
+effectual
+effusive
+eicosanoids
+eigenvalue
+eighteenyear
+eighthmost
+eighthplace
+einer
+elSheik
+elastica
+electrohydraulic
+electronicore
+elephantiasis
+ellipses
+ellipticus
+eloquently
+embarrassingly
+embellishing
+emulations
+enantioselective
+endodermis
+endoparasitoids
+engrossed
+enlargements
+enliven
+enlivened
+enrollees
+enterovirus
+entheogen
+entrained
+entrees
+enumerating
+envelops
+eosin
+erasable
+eremita
+erosa
+erythraea
+estrus
+ethicists
+ethnical
+ethnographical
+etv
+eu
+eugeniae
+euglossine
+european
+euskelian
+everetti
+evicting
+exSoviet
+excising
+exerciseinduced
+expressible
+exslaves
+exstudents
+extemporaneous
+externa
+externality
+extrachromosomal
+extremophiles
+facebook
+facile
+faired
+fairings
+fancreated
+fantasyscience
+fasciatum
+fasciolata
+fasttracked
+fathering
+favelas
+fc
+feeforservice
+femaleled
+femoratus
+fermentans
+fey
+fiefdoms
+fifteens
+figureheads
+filebased
+filers
+filmTV
+filmi
+filmrelated
+fils
+financings
+finned
+firehouses
+fireproofing
+firstofitskind
+firstwave
+fisheri
+fivecard
+fivespeed
+flagstaff
+flamethrowers
+flatmate
+flaxseed
+flirted
+floodprone
+floorcrossing
+floored
+fluorouracil
+foeticide
+folkoriented
+followedup
+foodies
+footers
+foraged
+fording
+foreignexchange
+foresees
+forestland
+forgings
+fornix
+fortifying
+fragariae
+framerate
+freckles
+freeflying
+fricatives
+friendlier
+frogmen
+frothy
+fructosebisphosphate
+fullline
+funnier
+furosemide
+futurists
+gTLDs
+gadolinium
+galeata
+gamekeeper
+gamemasters
+gassed
+gauge
+gcc
+geminatus
+genderless
+gendhing
+generelated
+genial
+gentium
+geoengineering
+geoffroyi
+geosynthetics
+gettogethers
+geup
+gharanas
+gibberish
+giftgiving
+gimp
+giorni
+giudicato
+glaber
+glaberrima
+glassblowing
+glasshouse
+glasslike
+glassworks
+gliadin
+globetrotting
+glucosamine
+goaround
+goldenbellied
+goldenbush
+goldeneye
+goldenweed
+goldfield
+golem
+gonophores
+gorget
+gossypii
+goths
+grabbers
+gradations
+grammaticalization
+graptolite
+gratifying
+graythroated
+greeneyed
+gregaria
+gridlike
+griffins
+grin
+grottos
+grownups
+guadalupensis
+guar
+guernseys
+gullible
+gusty
+gynecomastia
+hAlba
+hacktivism
+halfyear
+handblown
+handwashing
+happiest
+haptophytes
+haptor
+harmlessly
+harry
+hast
+hateful
+headbands
+headhouse
+headhunter
+headstander
+heave
+heavymetal
+heightening
+helicinids
+helixturnhelix
+helplines
+hemagglutinin
+hemostatic
+heterodont
+heterophyllus
+heterosexism
+hexameter
+hexane
+hexose
+hiemalis
+hierarchs
+highcaliber
+highestplaced
+hightraffic
+hiked
+hilli
+hirtus
+hispanicized
+hoarse
+hognosed
+holdouts
+holism
+hollowedout
+holoprosencephaly
+homemakers
+homesick
+homestays
+homophile
+hoplite
+hornblende
+hors
+hostbased
+hostguest
+hostname
+http
+huddle
+humbuckers
+humpbacked
+humpless
+hydantoin
+hydrogels
+hydrometer
+hygienist
+hyperparasitoids
+hyperstimulation
+hyperthermophilic
+hypnagogic
+hypoglycemic
+hyrax
+iBook
+iHeartMedias
+ichthyology
+ignobilis
+iguanodontian
+illegitimacy
+illustris
+imberbis
+immobilizing
+immunizations
+immunoassays
+impasto
+impermissible
+implementable
+impregnation
+impressa
+impressment
+incites
+indemnify
+indignant
+inducting
+industrials
+infaunal
+infiltrators
+infirmity
+inflicts
+informality
+informations
+inhalers
+inheritor
+initialize
+injunctive
+innersouthern
+innerwestern
+inseason
+insides
+insincere
+interbred
+intercalation
+intercede
+intercommunal
+interconnectivity
+interferons
+intermarry
+interna
+internodes
+interrogates
+interrogative
+intervarsity
+interweave
+intracytoplasmic
+intrathecal
+intraventricular
+inulin
+inuse
+invariable
+invective
+inventoried
+inviteonly
+invulnerable
+ip
+ironman
+ironmasters
+irrepressible
+isabellae
+ischaemia
+isochronous
+isomerism
+jamesonii
+jammer
+jazzbased
+jird
+joist
+jubilant
+jumbled
+juristic
+kaffir
+kaiju
+kalam
+karri
+kebabs
+keratins
+khagan
+kickstarter
+kiloton
+kilts
+kinder
+kinetochores
+kirbyi
+kneading
+kneelength
+knell
+knits
+knobtailed
+lacertid
+lactam
+ladybirds
+lagomorphs
+lancehead
+landlordtenant
+laparotomy
+lastsecond
+lateonset
+launder
+lawnmower
+leges
+lemmonii
+lemonscented
+lenticularis
+leprechaun
+leucophrys
+leucopterus
+leucosticta
+leucotis
+likenamed
+lilting
+limo
+lineblue
+lineside
+lipoic
+liquidus
+lithified
+livres
+ll
+loancharter
+loansharking
+lobar
+lockedin
+lodgement
+logspace
+lomatium
+longarm
+longcourse
+longicaudatus
+longimanus
+longissimus
+longtrack
+longwinged
+lowcalorie
+lowrider
+loxensis
+loya
+lp
+lucens
+luciferase
+lunules
+lusitanica
+luteal
+luteolus
+luxuriant
+lycophyte
+lyse
+lysophosphatidic
+machineguns
+macrocycle
+macrurus
+magnetohydrodynamics
+maidservant
+maingayi
+majori
+majoritarian
+majoritys
+majoritywhite
+malar
+maleate
+malic
+malocclusion
+malpractices
+mama
+mamma
+mammalogist
+manageability
+mange
+mangle
+manofwar
+manyflowered
+maraschino
+marathoners
+marcher
+marginatum
+marketleading
+marls
+martens
+martensi
+masseter
+masseur
+masterminding
+matador
+maypole
+mediterranean
+megabits
+megacephala
+mei
+melancholia
+melange
+melitensis
+memorializing
+mending
+menhir
+merciful
+merong
+merostomatan
+metalsmith
+metasomatism
+miRa
+microcrystalline
+microdistillery
+microform
+microhouse
+microloans
+micronucleus
+microstructures
+microvascular
+middleblocker
+middlegame
+middlesized
+midtour
+militarythemed
+milkfish
+milkweeds
+millrace
+mimed
+mineralocorticoid
+minutissimus
+mirabile
+misbehaving
+misread
+missense
+missteps
+mitchelli
+mitogen
+mitosporic
+mitten
+mixologist
+mixtus
+modernists
+modica
+modillioned
+modularization
+moisturizer
+monacha
+monetizing
+monocotyledon
+monogenetic
+monolayers
+monopodial
+monotype
+montivagus
+moralizing
+mordax
+mostnominated
+mot
+muay
+mudslide
+multibeam
+multichip
+multidisk
+multiepisode
+multifrequency
+multigames
+multispecies
+multivenue
+multiwavelength
+murrayi
+musichall
+musicmaking
+muskrats
+mustaches
+mutism
+myopic
+myxobacteria
+nForce
+nSpace
+nailing
+nanocrystals
+nanomolar
+nanosatellites
+narcissus
+nasogastric
+nattereri
+naturale
+nay
+ndth
+ndtier
+neckties
+necromancer
+nectarine
+needlefish
+neocaledonica
+neoconservatives
+neonatology
+nephrons
+netballer
+neurocognitive
+neuroectodermal
+neurolinguistic
+neuropharmacology
+neutering
+nevercompleted
+nexin
+nextgen
+nicht
+nigerrimus
+nigritarsis
+ninthyear
+nitrogencontaining
+nitrox
+nodosaurid
+nokill
+nonATPase
+nonAustralian
+nonRussian
+nonTest
+nonathletic
+nonbusiness
+noncash
+noncooperation
+nonfat
+nonlife
+nonmarket
+nonparallel
+nonprofitmaking
+nonprogressive
+nonrecognition
+nonsecure
+nonspecialists
+nontest
+nonthreatening
+nontrinitarian
+nonurban
+nonvisual
+northnortheastern
+nosewheel
+notarized
+nowdiscontinued
+nth
+nuclearencoded
+nucleons
+nullius
+nutraceuticals
+nympha
+oakleaf
+obfuscated
+obligor
+observables
+oceanside
+ochroleuca
+octal
+odontocete
+offandon
+offprice
+offseasons
+ohm
+ohne
+oligochaetes
+oligolectic
+omniscience
+oncogenesis
+oncourt
+onebyone
+oneclick
+onepercenter
+onetrack
+onevote
+ontheground
+opacities
+openloop
+orality
+orangery
+orbiculata
+orgy
+orientis
+origen
+orthogonally
+orthotics
+osteotomy
+outliner
+outofwork
+overarm
+overexploited
+overloads
+overprint
+overrode
+oversea
+overshooting
+packetbased
+painkiller
+palatini
+palawanensis
+paleoclimatology
+palinopsia
+pallor
+palmoplantar
+panacea
+pandanus
+panegyric
+panfish
+pantherina
+papillomatosis
+paraAlpine
+paralogous
+paranasal
+parapatric
+parapodia
+paraprofessional
+paratriathlon
+parceled
+parkinsonism
+parlay
+parotia
+passengeronly
+passphrase
+pasty
+pathognomonic
+pavers
+pectinate
+pedagogic
+peephole
+pensive
+pensylvanica
+pentito
+perakensis
+periapical
+peristaltic
+persecuting
+pesantren
+pg
+pharma
+pharmacodynamic
+phaser
+phenanthrene
+phencyclidine
+phenethylamines
+phenolics
+phenylpiperazine
+phoebe
+phosphatic
+phosphoglycerate
+phrynosomatid
+phyllodes
+phyllosilicate
+phyllosphere
+physic
+physicianassisted
+pianistcomposer
+pictographs
+picturing
+piercer
+pigmentosum
+pilaster
+pileatus
+pillaging
+pinkishpurple
+piratethemed
+piscivorous
+pistachios
+pitchman
+pithy
+placode
+planer
+plasmons
+plasterboard
+platensis
+platyphylla
+ploidy
+plumaged
+plumbeous
+plummeting
+plutino
+podiatric
+podiatrist
+polishes
+polyamorous
+polyatomic
+polycythemia
+polygenic
+polymorphs
+polyneuropathy
+polyol
+pondering
+poojas
+popmusic
+poppier
+pornographer
+porphyrins
+porteri
+postAmerican
+potentiality
+potentiate
+potentiometers
+prasad
+prayerbook
+preamplifiers
+precariously
+preclearance
+precollegiate
+prednisone
+prek
+prenominal
+prepainted
+preparers
+prepublication
+preregistration
+presale
+primality
+priscus
+privatizing
+proConfederate
+proGaddafi
+proa
+probationer
+procedurals
+prodigal
+producerDJ
+programmability
+progrock
+prominences
+prominens
+promotor
+proms
+pronominal
+prospected
+proteobacterium
+provirus
+prowar
+proximally
+pruritic
+psammophila
+pseudoD
+pseudouridine
+psychoses
+pterostigma
+publishable
+puddling
+pugnax
+pulcherrimus
+pulsejet
+pumpedstorage
+punchy
+puppeteered
+purifiers
+purplepink
+purples
+pv
+pylorus
+pyram
+qigong
+quadricolor
+quadrifasciata
+quadriguttata
+quadruped
+quadrupole
+quantifiers
+quashing
+quasicrystals
+quasilegislative
+quasiparticles
+quasisatellite
+quasispecies
+quebracho
+questionably
+quiescence
+radiobiology
+radiographer
+radiographers
+radishes
+railfan
+rajasthan
+ramblers
+rapamycin
+rapture
+ras
+ratites
+rauisuchid
+raving
+realignments
+realitydocumentary
+reapers
+rearmost
+reauthorize
+rebeli
+receptormediated
+recombine
+recombining
+reconciles
+reconnaissancebomber
+recordholding
+reddening
+reddishgreen
+redistributing
+redrew
+redrumped
+redshirting
+reductases
+reedbed
+reformminded
+refried
+reggaedancehall
+reggaeinfluenced
+reimplemented
+rekindling
+renderers
+renovates
+reoccupation
+reopens
+repels
+repetitiveness
+replicative
+reprieved
+reptiliomorph
+rereading
+reroofed
+residuals
+respectably
+resuscitated
+retracing
+retrievable
+retroflex
+retroperitoneal
+rhomboidea
+ribald
+riddims
+rifleman
+rightangle
+rimonabant
+ringback
+roadrail
+roams
+robertsi
+robs
+rockcountry
+rockpsychedelic
+rollerblading
+rondo
+rookielevel
+rootkits
+rootsrock
+rotundatus
+roughscaled
+rufouswinged
+rulebooks
+rune
+rustcolored
+rut
+saboteur
+saccharin
+saddlebags
+safetyrelated
+salvini
+samiti
+sampy
+sander
+sanders
+sandmat
+sandwiching
+sangha
+sannyasa
+sapper
+satraps
+sawmilling
+sawscaled
+sc
+schoollevel
+schoolmate
+scoped
+scow
+scruffy
+scrummaging
+scrupulous
+scrutinizes
+scrutinizing
+scute
+seascape
+seatbelts
+secondever
+secondseeded
+secotioid
+sedanbased
+segregates
+selfdesigned
+selfdeveloped
+selfdisclosure
+selfexamination
+selfloathing
+selfparody
+selfregulated
+selfrighting
+selo
+semirufa
+semisequel
+sen
+seneschal
+sensationally
+serializing
+sericans
+serosa
+serra
+sessilis
+sevengill
+seventeenvolume
+seventerm
+sexratio
+shadowdamsel
+shallowness
+sharpnose
+shearer
+shewolf
+shikimate
+shined
+shins
+shipoftheline
+shortbilled
+shortbread
+shorteared
+shortnosed
+shortseason
+shoulderlaunched
+showgirls
+shrikethrush
+shuffles
+shul
+shun
+sickened
+sideboard
+siderophore
+siglum
+signifer
+silentera
+silicones
+silvicultural
+simpleminded
+singermusician
+singersongwriterproducer
+singleCD
+singleminded
+singleparent
+sitio
+situs
+sixbay
+sixcar
+sixthround
+sixthseason
+sixtyfourth
+skerry
+skintight
+skyrocketing
+slaw
+slott
+slowwave
+smothering
+snaketail
+snowdrop
+snowplow
+snug
+sociability
+sociolect
+socotranus
+solenodon
+soloalbum
+somnolence
+soulinfluenced
+souring
+soursop
+spacesuits
+spacethemed
+spaniels
+spatter
+spatulashaped
+specifier
+speckling
+speedily
+sphenopalatine
+spicier
+spindlework
+spinipes
+spintronics
+spools
+sporeproducing
+sportscasting
+sporulation
+squadbased
+squamates
+squeak
+stablemates
+stackable
+steelpan
+steelworkers
+stejnegeri
+stench
+stenography
+steyermarkii
+stippling
+stockman
+stonefish
+storybased
+stramineus
+strath
+streamflow
+striatula
+strictum
+subarea
+subcommunities
+subdean
+subfasciatus
+sublist
+sublittoral
+subname
+subneighborhood
+subprovincial
+subranges
+subsume
+subterranea
+suburbicarian
+succubus
+sulfinic
+sulfonyl
+sulphureus
+sunbeam
+sunsynchronous
+supercharging
+superimpose
+superposed
+supposing
+surahs
+surfed
+switchbacks
+swordshaped
+symmetrickey
+symptomatology
+syndromic
+syngas
+synthwave
+syriaca
+syrinx
+tabacum
+tandemseat
+teacherstudent
+technetiumm
+technologyfocused
+tectonically
+temperaturedependent
+tempering
+tendril
+tenebrosus
+tenmember
+tensioner
+tenthplace
+tenuissima
+teraflops
+terpene
+thankful
+theist
+thenexisting
+thenincumbent
+theophylline
+thermophilus
+thickens
+thionyl
+thirdbestselling
+thirdstring
+threebanded
+threecornered
+threeengine
+threepronged
+throes
+throughhole
+tics
+timberland
+timeframes
+timezone
+titanosaurian
+titers
+tolllike
+toms
+tong
+topaz
+topgallant
+topgrade
+topside
+torques
+tourmaline
+towerlike
+townsendii
+toxicant
+toxoid
+transPacific
+transcriptomics
+transferases
+translucida
+treadmills
+treatys
+treecovered
+treedwelling
+treetops
+triazine
+trickling
+tricounty
+trifasciatus
+trigone
+trinucleotide
+triplefins
+trisphosphate
+triumvirs
+trochlea
+trombonists
+trucked
+trumped
+truncatipennis
+tumorassociated
+tunebook
+tung
+turbojets
+turcica
+turgor
+turnarounds
+twodivision
+twoinch
+typespecies
+ugandae
+ugliness
+uliginosa
+ulnaris
+umbrina
+unadulterated
+unallocated
+uncirculated
+unconstrained
+underfloor
+underhand
+underrecognized
+underresourced
+understaffed
+undulations
+unenclosed
+unfertilized
+unfused
+unifasciatus
+uninstalled
+unleaded
+unmade
+unmapped
+unpredictably
+unraveled
+unreinforced
+unstressed
+untraceable
+unviable
+unwise
+uralensis
+urologists
+ussuriensis
+vagaries
+validators
+valorem
+vancouverensis
+varicella
+venules
+verum
+victualling
+vilification
+violaceous
+violascens
+vittatum
+vittipennis
+vocalese
+vocalsbass
+voicings
+vrh
+vulpina
+vv
+warranting
+wasis
+watercolourist
+waterproofed
+wavefunction
+webcap
+wedded
+wellarmed
+wellbehaved
+wellloved
+wellremembered
+wetsuits
+whitestriped
+wholemeal
+whore
+wicketkeeping
+widereaching
+wielder
+wilful
+windfarm
+woodchips
+woodfired
+wordlist
+workrate
+wormholes
+woundup
+xylose
+yarrow
+yawning
+yuhina
+zag
+zombiethemed
+zooms
+zygomorphic
+abaca
+abrogate
+abscisic
+abseiling
+absolve
+absorptive
+acacias
+acceptably
+achondrites
+achromatic
+acquis
+actionanimated
+activitybased
+actomyosin
+acutirostris
+adenomatous
+adjudicative
+adlibbed
+administrate
+adversities
+aedes
+aegagrus
+aegypti
+aenescens
+aeri
+aeroengines
+affray
+aftersales
+agnathans
+airforce
+airstable
+alBayt
+alSadiq
+albipennis
+albopunctata
+albumen
+alces
+alexandrae
+aliciae
+alkalis
+alkalitolerant
+allantois
+allcolor
+allene
+allotropes
+allrounders
+alnifolia
+alreadyexisting
+alternations
+amazonicus
+ameiva
+amortized
+anarchosyndicalism
+anatolica
+andrei
+angusticeps
+annalist
+annandalei
+annularity
+annuli
+annus
+anomalus
+anoxia
+ansa
+antidemocratic
+antifouling
+antiparty
+antirheumatic
+antisolar
+antitax
+antituberculosis
+anvils
+apache
+apicata
+aponeurotic
+appointive
+aptera
+ardens
+arenafootball
+arginineserinerich
+armiger
+armillary
+aroa
+arrowshaped
+artfully
+artificiality
+ascariasis
+ascendency
+ascribing
+ashgray
+asis
+aspartyl
+assignee
+assuage
+asterisks
+atlantic
+attitudinal
+attractants
+audacity
+auricollis
+autapomorphies
+authenticates
+authenticator
+autoregulation
+autostereogram
+avidly
+avoidant
+awardwinner
+awnings
+ayurveda
+azygos
+backings
+bacterias
+badgeengineered
+baffled
+baffling
+bailouts
+bandoneon
+bandwagon
+banger
+bani
+banka
+bankroll
+baptizing
+baremetal
+bartering
+baseless
+basidiomycetes
+basilaris
+bello
+belowground
+benchmarked
+bennetti
+bentgrass
+benzylpenicillin
+bespectacled
+bestofsix
+bewildering
+bicolour
+bigamous
+bigleague
+biliverdin
+billowing
+binning
+bioarchaeology
+biocide
+bioidentical
+biomimicry
+bioprinting
+biopsychosocial
+biosimilar
+biotransformation
+birches
+bisporus
+bitches
+bivalent
+bivalved
+bivouac
+blackcollared
+blackmailing
+blacktail
+bladderpod
+bleedingheart
+blemish
+bleomycin
+blooded
+bluecolored
+blueribbon
+blueviolet
+bluffing
+boatbuilder
+bodyshell
+boilermaker
+bolivianus
+bollard
+bonariensis
+bondsman
+bookended
+bookie
+boombox
+booter
+booze
+borderland
+boreale
+boric
+boride
+borings
+bosss
+bovines
+braising
+breuningi
+bridleways
+brightened
+brightening
+bro
+broached
+brocket
+bronchiolitis
+bronchus
+bronzeback
+brothersinlaw
+brushtail
+buffets
+buffing
+bullpens
+bullring
+bungling
+burchellii
+burrito
+bursae
+butchered
+butene
+buttoned
+buttstock
+buxifolia
+buyrate
+caatinga
+caffeic
+calamine
+calamus
+calcicolous
+calibrating
+callable
+calligraphers
+calliope
+canadienne
+canter
+canticles
+cantopop
+capite
+capos
+carbody
+carbonized
+carboxylase
+carburettor
+carob
+carriercapable
+carrierneutral
+carting
+cartridgebased
+castaneum
+casted
+casteism
+catecholamine
+catharsis
+catkin
+catus
+caudalis
+caulking
+cayenne
+ccTLDs
+cds
+ceaseanddesist
+celecoxib
+cellspecific
+cementation
+centralhall
+centrioles
+centripetal
+ceratopsians
+ceratopsid
+chachalaca
+chainlink
+chanterelle
+chatterbot
+chattering
+chelicerates
+chemiluminescence
+chilly
+chloris
+cholangitis
+choli
+chopsticks
+chum
+ciclosporin
+cinnamomeus
+cist
+citadels
+citrinum
+civilizing
+clasped
+clausa
+clenched
+cleverness
+cliffside
+clindamycin
+clinids
+clitellum
+cloacal
+clotted
+clubbers
+clumsiness
+coalbearing
+coauthorship
+coccidiostat
+cocoordinator
+codirect
+cognitivism
+coincidences
+colchicine
+coleaders
+coliform
+colilargo
+collectivism
+collectivities
+collectivization
+collides
+colonias
+colorists
+colossus
+commelinids
+committal
+communions
+commutator
+compressions
+concentrica
+conceptualizes
+condensates
+confound
+conjunto
+conjures
+consanguineous
+consensusbased
+constricts
+constructible
+consulategeneral
+contemptuous
+contented
+contestable
+continuoustime
+contrastive
+convergently
+cooccurring
+coomani
+cordatum
+corf
+costumer
+counterfeiters
+counternarcotics
+cowbell
+cowherds
+crackling
+crass
+crassum
+creameries
+credibly
+crenatus
+cretacea
+crewing
+cribrata
+cribriform
+cricothyroid
+crier
+crimemystery
+crombec
+crossChannel
+crossreferenced
+cruenta
+crumb
+crura
+crutch
+crypta
+cryptica
+cubed
+cucurbit
+cueing
+cultivator
+cunnilingus
+curbed
+curcumin
+currentaffairs
+cursory
+cushionlike
+cutoffs
+cyberculture
+cyclins
+cyclopentadienyl
+cyclopropane
+cylindricollis
+cypsela
+dAragona
+dArtagnan
+dacite
+damning
+dampened
+dampness
+dancebased
+dancelike
+dangle
+datamining
+debarred
+decisis
+defacing
+defamed
+defensa
+deflate
+dehumanizing
+dehydrogenation
+deliciosa
+dellArte
+demandside
+demarcating
+demethylation
+demogroup
+demyelination
+denaturing
+deniers
+denotational
+deontological
+deorbited
+departement
+deplete
+dermatan
+derricks
+desiccant
+despicable
+despot
+detaches
+detections
+deviceindependent
+devolve
+dhoti
+dialogic
+diana
+diarrheal
+dich
+diethylamide
+diisocyanate
+dimensionless
+dimmed
+dingoes
+dinucleotides
+diplomate
+dirigible
+discolouration
+discretetime
+disgraceful
+dishs
+disinhibition
+disintegrates
+disputation
+disruptors
+distills
+diversely
+diwan
+dodgy
+dogfaced
+dolostone
+dolphinariums
+dominicensis
+doodle
+doorbells
+doublecollared
+doubleleaf
+downpour
+dragoon
+drawbar
+dreadnoughts
+dredgers
+dressmaking
+droid
+droit
+drowsy
+druids
+drumkit
+drumline
+dualsport
+ducting
+dudleya
+duple
+dysregulated
+eSport
+earlycareer
+earlymids
+earnestly
+eastfacing
+eastnortheastern
+edX
+editio
+egganddart
+eggersii
+eightieth
+eightstring
+eightyfifth
+eine
+ejaculate
+elbowed
+electrify
+electroencephalogram
+electronbeam
+elephantfish
+eleventime
+eliminationstyle
+ellipticum
+elopement
+emanation
+embeddings
+emigre
+emulsified
+endoparasite
+endopeptidases
+endophytica
+endosiphuncular
+endothermic
+endotoxin
+enewsletter
+enfant
+enshrining
+entailment
+enterpriselevel
+epauletted
+ephedra
+eps
+erections
+erects
+eremomela
+eryngo
+erythropoiesis
+escapist
+esp
+esquire
+ethidium
+ethnocentric
+eugenic
+eulogized
+evodevo
+exaggeratedly
+exaggerates
+exaggerations
+exasperated
+excipient
+excreting
+executivelevel
+exhilarating
+expectancies
+experimentalism
+expungement
+extenders
+extinguishment
+extravasation
+extravascular
+extricate
+extruding
+facelifts
+faceon
+factorlike
+faecalis
+falconer
+falconers
+falx
+familybased
+fancier
+farmtotable
+fasces
+fatherdaughter
+fatherinlaws
+fearlessness
+ferrugineum
+ferulic
+fetid
+feverish
+fez
+fifer
+filifera
+filipes
+fimbry
+firstaid
+fishmongers
+fishtail
+fistulae
+fivealbum
+fivecar
+flanging
+flatbill
+flatrate
+flavicosta
+flavofasciata
+fleshcolored
+flexural
+floorless
+floorspace
+floury
+flowerpot
+fluorinating
+fluoroacetate
+flyway
+foie
+foliate
+folktronica
+foment
+foolishly
+foolproof
+footoperated
+forebear
+forensically
+forestay
+foretell
+forgetfulness
+forsaken
+fortunei
+fourchannel
+fourline
+fourmovement
+fourpetalled
+fourstring
+fourthround
+fourwire
+foxhunting
+framer
+fraudsters
+frayed
+frazioni
+freeaccess
+freeopen
+freeradical
+freethinking
+frequencydivision
+frontages
+frontgabled
+frontrunning
+fruitworm
+fruticosus
+ftp
+fulvipes
+fumarolic
+fumitory
+fungible
+funiculars
+funiculata
+furva
+gaffes
+gaiters
+galactosemia
+gallicus
+gamepads
+gangways
+gars
+garu
+gasphase
+gastroparesis
+gazetteers
+gelatinase
+genealogically
+genoa
+genotoxic
+gentamicin
+geodynamics
+geoid
+germane
+giesberti
+gimlet
+gladiolus
+glaring
+glasswing
+glides
+globule
+glowworms
+gluconate
+glycopeptide
+goldrush
+gracilior
+graffito
+grammy
+grasps
+gravitate
+grayhooded
+graziers
+greenblue
+greenstriped
+grenadine
+grimoires
+grisaille
+grossers
+grotesques
+groundstroke
+growthpromoting
+gueststar
+gueststars
+gulch
+gulper
+gundog
+gunports
+gynoecium
+haberdashery
+haemolymph
+haggis
+halfamile
+halfamillion
+halfbeaks
+halogenation
+halophytes
+halophytic
+handaxes
+handprinted
+hangul
+hardnosed
+harks
+hashed
+haystack
+headpiece
+heartless
+hebe
+heddle
+hedgerow
+heifer
+heirapparent
+heirlooms
+hemi
+hemispingus
+herbacea
+herbrich
+hesitated
+hexachlorocyclohexane
+hexamer
+hexastyle
+hiatuses
+highbypass
+highestincome
+highexplosive
+highlow
+highorder
+highpaying
+hinterlands
+hipsters
+hirsutism
+historyfantasy
+hobbit
+hochstetteri
+hoenei
+hofjes
+hollowbody
+holmium
+holoparasitic
+homebrewing
+homefield
+homeopath
+homespun
+hooklike
+hopedfor
+hornwort
+hostparasite
+howdeni
+humidifiers
+hurtful
+hyanggyo
+hydnoid
+hydriai
+hydrologists
+hydrolysate
+hydrophobicity
+hydroplanes
+hydrops
+hydroxylases
+hypanthium
+hypertrichosis
+hypotensive
+iWork
+ideographs
+idled
+idly
+iliopectineal
+illegitimately
+illiquid
+illtempered
+imago
+imitatrix
+immanent
+immitis
+immunoassay
+immunologically
+imode
+impetigo
+impinge
+importin
+impotent
+impoverishment
+impressionable
+improvisatory
+inXile
+inasmuch
+inaudible
+inauthentic
+incongruity
+incremented
+inculcating
+indenting
+indexer
+indexicality
+inducts
+indulgent
+inequitable
+infernal
+inferno
+informationprocessing
+informationsharing
+infringer
+ing
+inheritable
+initializing
+instagram
+interactor
+interatomic
+interestbased
+interlocutory
+interneuron
+intramembrane
+intronic
+invagination
+iptables
+ireland
+irreligion
+irroratus
+isoflavones
+iterator
+jackknife
+jai
+jamband
+jazzier
+jerkinhead
+jerks
+jilted
+jonesi
+junco
+junipers
+junkie
+justiciable
+justifiably
+juxtapositions
+kelloggii
+keratinized
+khel
+kinescopes
+kinglet
+kitbuilt
+kitplane
+kitty
+kronor
+krypton
+kwela
+laborsaving
+labrisomid
+ladle
+lakeshores
+lampposts
+lanceolatus
+langurs
+lanyards
+largearea
+largeeared
+largesize
+largetooth
+lateness
+lateterm
+latifascia
+latifasciata
+lauding
+launderette
+lea
+leadzinc
+leafmining
+leastaltered
+leche
+lecherous
+legato
+lentic
+lesslethal
+letterbox
+lewisia
+libations
+liberalarts
+limitedproduction
+limitedtime
+limosa
+limped
+linchpin
+lindleyana
+liquidpropellant
+lithia
+lithophytic
+lithospheric
+liturata
+liturgist
+livelier
+loblolly
+longplay
+longshoremen
+longula
+longwhiskered
+lossoffunction
+lotor
+lowmaintenance
+lowslung
+lowwater
+lucifer
+lumpsum
+lunchbox
+lycopene
+lymphangitis
+lymphoproliferative
+lyrata
+lyretail
+mHz
+macadamia
+machadoi
+macroalgae
+macronucleus
+magisterium
+magnificence
+magnificently
+mainshock
+makebelieve
+malabarica
+malapportionment
+malemale
+malkoha
+mammallike
+manipurensis
+maniraptoran
+mannagrass
+mannitol
+mansa
+mantelpiece
+manytomany
+mappers
+marabi
+marcescens
+marquees
+martensite
+mas
+masochism
+matchdays
+matorral
+meandered
+meerkat
+meetinghouses
+megaspores
+melam
+melo
+melongena
+membranespanning
+memoria
+memoriam
+menonly
+menorah
+mercantilism
+meritocracy
+meshwork
+mesonephric
+metacarpals
+metaheuristic
+metaheuristics
+metahuman
+metalbased
+metaleuca
+metatherians
+meterhigh
+metformin
+methanogen
+methemoglobin
+metoclopramide
+metricus
+micra
+microbicides
+microcontinent
+microfabrication
+microfilaments
+microfilms
+micron
+microphthalmia
+microspores
+microstock
+midcourse
+mig
+milkbased
+mima
+mince
+miniepisodes
+minifigures
+minke
+miraguama
+miscreants
+mississippiensis
+mistranslation
+mitra
+moccasins
+mochi
+mockups
+mohawk
+molecularly
+monachus
+monilifera
+monkeypox
+monodrama
+monoid
+mononucleosis
+monorails
+mopane
+mops
+morphometric
+morse
+mortgagor
+mostpopulous
+mote
+motorcars
+mountainbike
+mourner
+mousedeer
+mouseear
+muchloved
+muddled
+mudra
+muds
+mugwort
+multiauthor
+multiband
+multibyte
+multifuel
+multiinstitutional
+multilateralism
+multilocation
+multimodality
+multinucleate
+multipage
+multiplane
+multituberculates
+multitudes
+mundo
+mundus
+muroid
+muscovite
+musicale
+musictheater
+muskeg
+mustang
+mutata
+muticus
+myTouch
+myelitis
+myelofibrosis
+myocyte
+myofibrils
+mystica
+naiad
+nailtail
+naira
+nakedbacked
+naltrexone
+nameboard
+nameday
+nameship
+nanosecond
+nanowire
+nappes
+natalis
+navale
+ndimensional
+nearctic
+necking
+nella
+neomycin
+neurocranium
+neurologically
+neuroses
+nevi
+newel
+ngoni
+nidulans
+nightlight
+nigrofasciata
+nigrolineata
+ninehour
+nipalensis
+nn
+nociceptors
+nonRoman
+noncollegiate
+noncommunist
+nondairy
+nondemocratic
+nonelectric
+nonet
+nonexperts
+nonliteral
+nonmetals
+nonobjective
+nonparole
+nonphotosynthetic
+nonprescription
+nonrevenue
+nonsworn
+nontariff
+noreaster
+northwestwards
+nuptials
+nutcrackers
+oberthueri
+obeys
+occultus
+octamer
+october
+oddson
+oecomys
+offhand
+offhook
+officebased
+offlicense
+offpremises
+offyear
+oito
+oligopoly
+olivary
+onboarding
+oncourse
+oneline
+oneoverone
+onewin
+oneword
+onramp
+onychomycosis
+openmic
+openwork
+ophthalmological
+opima
+opine
+opining
+opportunist
+oppositifolia
+orach
+ordinaries
+organogenesis
+organolithium
+otolaryngologist
+outgassing
+outlays
+outofbody
+outrageously
+outranks
+outsize
+outskirt
+overexposure
+overnights
+overprotective
+overreaching
+overshadow
+overshadowing
+oversimplified
+oviduct
+oxidizers
+oxlip
+oxycodone
+palecolored
+pallasii
+palmatus
+palmettes
+paltry
+panicgrass
+panty
+parasitise
+paraswimmer
+pareddown
+parotoid
+parsimony
+partes
+partiality
+partials
+passivity
+pastern
+patriation
+patrimonial
+pauciflorum
+pdr
+pearling
+peatland
+peddler
+pedigreed
+pellicle
+pelycosaurs
+penitents
+pennames
+pennsylvanicus
+pennyroyal
+pentode
+perfectionism
+perfluorinated
+perforators
+perianths
+periastron
+perishables
+perk
+peruana
+pervaded
+petri
+pfeifferi
+phantoms
+phenom
+phi
+pholidoskepian
+phosphorescent
+phosphotyrosine
+photodiodes
+photophobia
+photorealism
+photosphere
+pic
+pictograph
+pigweed
+pilastered
+pinheyi
+pinholes
+pinkishred
+pinky
+pinnace
+pinniped
+pinpointing
+pisco
+piss
+pittieri
+placidus
+plaice
+planform
+playthrough
+playtime
+plugandplay
+pneumoconiosis
+pocketed
+poeciliids
+pointedarch
+pointofcare
+pol
+polemicist
+polkas
+polluters
+polyolefin
+polyols
+polyomaviruses
+polypores
+polyurethanes
+porosus
+portalThe
+portrayer
+posek
+postandbeam
+potentialities
+potentiometric
+poultice
+pow
+powerlines
+practicalities
+praiseworthy
+prefabrication
+preimplantation
+premedical
+premonition
+prenursery
+preparative
+pres
+presbyter
+presumptively
+prevertebral
+proAmerican
+proanthocyanidins
+processual
+procollagen
+procurators
+prodrop
+productservice
+profeminist
+prologs
+pronounces
+propitiate
+prospering
+proteinases
+proteolytically
+protonation
+pseudepigraphical
+pseudoscorpion
+psyllids
+ptype
+pubescence
+publicdomain
+puffbirds
+pulchrum
+pullover
+pullus
+pulmonic
+pulped
+punctuality
+punctuate
+punctures
+punkmetal
+purinoceptor
+purplespotted
+purpletipped
+pushers
+pustulatus
+putters
+pygmies
+pyrin
+qawwali
+qi
+quadricollis
+qualityoflife
+quanta
+quilted
+quinquennial
+quips
+racecard
+racetam
+racino
+radioligand
+radiotelevision
+railgun
+rajput
+rapax
+rapcore
+ratcheting
+rationalizing
+ravenous
+realityTV
+reappraised
+rearadmiral
+rebased
+recharted
+recherche
+reciprocally
+recommissioning
+recompiling
+reconditioned
+recouped
+redistributable
+redlined
+redpurple
+redspot
+redtoothed
+reducer
+reducers
+reedition
+reflexum
+refloat
+reformists
+refreshingly
+refutes
+regicide
+regrant
+regretting
+reinforcers
+reinterpret
+relievers
+renounces
+reoffending
+replicable
+replying
+repressions
+reptans
+resent
+resupplied
+retcon
+retitling
+retorting
+retrievers
+reuteri
+revaluation
+revascularization
+rhyolitic
+ribonucleotides
+riche
+rightleaning
+rightsholders
+rinds
+ringside
+riprap
+roX
+roadtrip
+rockslide
+romana
+roomed
+ropelike
+roper
+rosecolored
+rotund
+roxburghii
+rsync
+rubiginosus
+rubriceps
+ruderal
+rufifrons
+rufousbacked
+rufum
+sabot
+sabotaging
+saccule
+sachet
+sacris
+saddlebag
+saleable
+salicifolia
+salicina
+salinities
+samegender
+samplereturn
+sanded
+sandwicensis
+sanitization
+sarcomere
+sarcomeres
+sarsen
+sate
+saturata
+saucy
+sawyer
+saxophonistmultiinstrumentalist
+scaleup
+scammer
+scandalized
+scapulae
+scenarist
+scenographer
+scenography
+schooldays
+schoolwide
+scintillans
+scintillating
+scot
+scratchy
+scrawled
+seafoods
+seagull
+secondo
+secondranked
+secondrun
+segmenting
+seiner
+selfdefeating
+selflove
+selfpropagating
+sellouts
+semielliptical
+semienclosed
+semiprofessionally
+semiskilled
+sengi
+senseless
+sensitively
+sensitizing
+septs
+sequelae
+seral
+serendipitously
+seriata
+seriatus
+sericeum
+serow
+servicelevel
+servile
+ses
+sesamoid
+seventhday
+seventhhighest
+seventyseventh
+sexpunctata
+shambles
+shamed
+shareduse
+shareholdings
+sharpest
+sharps
+shawm
+sheetlike
+shi
+shiplap
+shophouses
+shrewlike
+shutoff
+sicula
+sidewheeler
+signmanual
+silverspot
+simultaneity
+simus
+singlelayer
+singlemode
+sinusoids
+sistership
+sixdigit
+sixfigure
+sixperson
+sixtysixth
+sixway
+sketchbooks
+skuas
+skylarks
+slashes
+slatecovered
+slickheads
+slipcased
+slowmotion
+slowworms
+slugger
+smelled
+snaffle
+snares
+snarling
+snatches
+sneaks
+socialcultural
+socialeconomic
+socialis
+sociolinguist
+soggy
+soir
+solemnis
+solum
+solvated
+sonars
+songcraft
+sono
+sortingassociated
+souks
+southsoutheastern
+southsouthwestern
+spacings
+spadeshaped
+sparred
+spayed
+spearmint
+specifiers
+spigot
+spironolactone
+spitz
+splinting
+spongiform
+sporocarps
+spurfowl
+spurius
+squill
+ssRNA
+stagehand
+staking
+stalkeyed
+stammering
+standardsized
+standpoints
+starlets
+stationer
+stearate
+steeping
+steeples
+steinbachi
+stemlike
+stepwells
+stillstanding
+stirrups
+stoked
+stomatal
+stonechat
+stonemasonry
+storable
+stovepipe
+strada
+stragglers
+streamliners
+streptococcus
+strummed
+stubbornly
+studenttoteacher
+studious
+stuffs
+subbituminous
+subcircular
+subclans
+subcoastal
+subcutaneously
+subdominant
+subdorsal
+subjectspecific
+suboccipital
+subprovince
+subsumes
+subtracts
+subtyping
+suffices
+sulfone
+sultanas
+summerhouse
+sunlike
+superbantamweight
+supercarriers
+superluminal
+superresolution
+supersoldier
+superspy
+superstring
+supersymmetric
+superweapon
+supraventricular
+sureties
+suspender
+sutural
+sweetly
+swindler
+symbioses
+sympathize
+sympatrically
+synergistically
+synthetics
+tabling
+taillight
+talon
+tamperresistant
+tankette
+tapas
+tariqa
+tarsiers
+tarts
+tasters
+tautomer
+teachinglearning
+teat
+tecta
+telecommuting
+telemarketers
+teleosts
+telephoned
+telephonic
+telerehabilitation
+televangelism
+tentaculata
+tenweek
+teratomas
+terminer
+terpenes
+terrors
+teshuva
+tessellatus
+testudo
+tetrafluoride
+thalli
+thana
+themebased
+thenformer
+thenyearold
+theodicy
+theorbo
+thinline
+thinners
+thoughtless
+thousandth
+threadleg
+threebody
+threefin
+threeinarow
+threelegged
+threelined
+threesport
+threestrikes
+threewicket
+ththcentury
+tidally
+tigrinus
+timekeepers
+timeshared
+timeshares
+tinder
+tipi
+tirade
+tirthankara
+tis
+titans
+toadfishes
+toasts
+toothpastes
+toric
+tormenting
+tortious
+torturers
+touristoriented
+tourneys
+towered
+toxicologists
+toxinantitoxin
+tracheae
+trachyte
+trackmaker
+tractable
+tradesperson
+trancelike
+transactivation
+transborder
+transcriber
+transgene
+transglutaminase
+translocon
+transphobic
+treasonable
+treasonous
+treed
+triamcinolone
+triandra
+triangulated
+tribus
+tridentate
+triiodothyronine
+trill
+trillions
+tripoints
+trite
+trivittatus
+troopships
+tropicbird
+tropicbirds
+truncus
+tubercules
+tuberculous
+tupelo
+turgida
+turntablists
+turrita
+tussle
+tussocks
+tweens
+twelvetime
+twirler
+twobook
+twobuilding
+twocolored
+tworecord
+twospot
+twotoone
+typecasting
+typein
+typesafe
+typica
+ulMulk
+ulkei
+ultraconservative
+ultrafiltration
+ultramodern
+unavoidably
+unblemished
+uncleared
+uncontacted
+uncoupled
+underclassmen
+undercuts
+underdrawing
+underpaid
+underreporting
+underwoodi
+undyed
+unica
+unicycles
+uninstall
+unjustifiable
+unkempt
+unmanageable
+unmoved
+unobtainable
+unter
+untraditional
+unveils
+unwary
+uptotheminute
+uranyl
+urticating
+userlevel
+usermode
+utilitys
+vaporize
+velutinum
+vendorspecific
+ventilate
+verecunda
+verisimilitude
+vermiculata
+vicargeneral
+vicepresidency
+vicissitudes
+videocassettes
+viruss
+visage
+visors
+vitreus
+viverrini
+vivipara
+vocalinstrumental
+vocalize
+vodkas
+voyaged
+vu
+wahoo
+walkaround
+wapiti
+wartlike
+waterlogging
+waterman
+waterplantain
+waterspouts
+wateruse
+waveinfluenced
+wavelets
+wayfinding
+waypoints
+weighty
+welltolerated
+wellunderstood
+westerlies
+westernization
+wheelbases
+wheelie
+whipworm
+whisk
+whispers
+whitecolored
+whiteonly
+whiter
+wholes
+wicks
+widelyused
+wideopen
+williamsoni
+wily
+windbreak
+winddriven
+windingup
+windpipe
+wingers
+winking
+wintry
+wollastoni
+wombats
+woodbased
+woodrotting
+woogie
+workaholic
+workerowned
+workforces
+workin
+xylanase
+yearslong
+yellowfish
+yellowishorange
+yellowstriped
+yews
+yurts
+zeroknowledge
+zerosum
+ziczac
+zikani
+zincfinger
+zoogeographic
+zoonosis
+zouave
+abaxial
+abducens
+abhorrent
+abiogenesis
+absurdly
+acaciae
+acalyptrate
+accentuating
+accentuation
+accessioned
+acerosa
+acetylacetonate
+acharya
+achondroplasia
+acicularis
+acinar
+acrylate
+actinium
+actionbased
+acupuncturist
+acutissima
+adCenter
+adduced
+adits
+adsorb
+adustus
+adventurepuzzle
+adzes
+aenigma
+aeronomy
+aerotolerant
+aestivum
+aestuarii
+afterburner
+afterparty
+aggravate
+aggravation
+agrili
+agroecology
+airmobile
+airraid
+airsensitive
+aisled
+akLaff
+alGaddafi
+alKhattab
+alQaedas
+alZarqawi
+alb
+albendazole
+albitarsis
+alders
+ali
+alkaliphilus
+alkalosis
+allChristmas
+allacoustic
+allergenic
+allocators
+allophones
+allot
+alphaamylase
+altus
+amacrine
+amanda
+amaretto
+amateurish
+amazoncom
+ambitus
+amination
+amnesic
+amphotericin
+amply
+ancillaries
+andinus
+anecdotally
+anechoic
+anovulation
+anteroposterior
+antheridia
+anthos
+anthracinus
+anticult
+antiepileptic
+antiimperialism
+antimiscegenation
+antimonide
+antiphon
+antiseptics
+aperiodic
+apid
+aplocheilid
+apomorphic
+apomorphies
+apophysis
+apostate
+apotropaic
+appetizing
+applicative
+april
+apsidal
+aptitudes
+aquaponics
+aquaticum
+arbors
+archicortex
+archipelagoes
+arecanut
+arguta
+armors
+armslength
+arpeggiator
+arpeggio
+arrestor
+arrowwood
+arthrogryposis
+ascenders
+ascetical
+ascomata
+ascription
+associativity
+asters
+asthmatic
+asylumseekers
+atkinsoni
+atrazine
+attenuating
+aucklandica
+audiocassette
+augmentative
+augusta
+auras
+auricularia
+aurulenta
+australiana
+authorisations
+auxilia
+bTV
+babirusa
+babu
+babul
+backlighting
+backplanes
+backscattering
+bada
+bailo
+bairro
+bakes
+balearica
+baler
+ballooned
+bambusae
+bane
+bankable
+bara
+barbells
+barebones
+barrelvaulted
+barrierfree
+baryonic
+basionym
+bassplayer
+bather
+bathymetry
+batlike
+bayed
+bazooka
+beachheads
+beatmaker
+befallen
+begat
+belied
+bemoaned
+benghalensis
+benzoyl
+beryl
+bestiary
+bestkept
+bestowal
+betacoronavirus
+betasandwich
+betasheets
+beware
+bezoar
+bifrons
+bifurcates
+biguttata
+bilaterian
+bilinealis
+bilinguals
+binotatus
+bioanalytical
+biomorphic
+biophotonics
+biopolymer
+biotopes
+birefringent
+bisector
+bist
+bitComposer
+blackfin
+blacksnakeroot
+blanched
+blanketed
+bln
+bloodletting
+blowflies
+bluecapped
+blunted
+boastful
+boatswains
+bobby
+bobsleighskeleton
+boissieri
+bologna
+bomblet
+bomblets
+bonebed
+bonito
+bonobo
+bookbinders
+bookers
+bookmobiles
+bootstrapped
+bordello
+boronic
+botfly
+bottlings
+brachytherapy
+brachyurus
+braggadocios
+braunii
+braziliensis
+breadcrumbs
+breading
+brevicaudata
+brokenhearted
+bromodomain
+brotherly
+broths
+bryophyte
+buchneri
+bucki
+bugler
+bugles
+bugloss
+buildout
+bulldogs
+buntlines
+buprestid
+burrfish
+burtoni
+bushcrickets
+bushfrog
+businesslike
+butanol
+buzzword
+buzzy
+cabinetmakers
+cackling
+caecum
+caeni
+caesius
+calcicola
+calicoflower
+caliginosus
+callosa
+campfires
+campuss
+canaliculatus
+cancellous
+cancercausing
+candidatus
+candoecon
+caninum
+cantellation
+canthus
+cantilevers
+capitula
+capitulum
+capixaba
+cappuccino
+carbanion
+carcinoembryonic
+carpel
+carpeted
+carrierborne
+carteri
+carves
+casecontrol
+casemakers
+castmember
+castrato
+catappa
+catechisms
+categorizations
+caudatum
+cavernosum
+celebritys
+cenotaphs
+centenarian
+centralpassage
+centrism
+centrists
+centrosomes
+centrum
+centuryThis
+cervina
+chairback
+chalcogenide
+chalkboard
+chandler
+chapmani
+charango
+charlatan
+chartings
+chatrooms
+checkerspots
+checkup
+chervil
+chestnutcapped
+chestnuttailed
+chevrolati
+chewable
+chikungunya
+childcentered
+chinook
+chironomid
+chloral
+chloro
+chore
+christmas
+chrominance
+chugging
+ciguatera
+cinchona
+cinders
+circuitswitched
+cirrostratus
+citrinus
+citydwellers
+civets
+classicists
+classless
+clavicornis
+cleats
+cleavages
+clevelandii
+clevis
+clickers
+climatologists
+clings
+closedcanopy
+clothe
+clownfish
+clownfishes
+clozapine
+clumpforming
+coachwork
+codevelopment
+coelestis
+cogenerate
+cognizant
+cohesin
+coldness
+collagist
+colliders
+collinsi
+colluding
+cologne
+colonialist
+colonialstyle
+colorization
+combinational
+commonlyused
+communitarian
+communityminded
+communitywide
+compacting
+compensators
+compostable
+computerization
+conclaves
+condensedmatter
+conferment
+congestus
+congoana
+conjoint
+conjugations
+connexa
+conscientiousness
+consignments
+constitutionalist
+constraintbased
+conte
+contemporarily
+contortionists
+contractus
+contravene
+contre
+conundrum
+conures
+convalescence
+convertibility
+convexus
+conveyancer
+coolie
+cooper
+coopt
+copresent
+copyists
+copyprotection
+copyrightable
+coquilletti
+cordite
+cornbread
+corniculata
+cornified
+cornu
+corporals
+corroborate
+corydalis
+cosmologies
+costlier
+counterargument
+counterfeiter
+counterforce
+cowcalf
+cowpox
+crabgrass
+craftsperson
+craniosynostosis
+credulous
+creeps
+crevasses
+cribbage
+cribricollis
+cringe
+crispus
+crock
+crofts
+cronyism
+crosscity
+crossdressers
+crossflow
+crosshairs
+crossindustry
+crosslinguistic
+crossmedia
+crosspromotion
+crunching
+cryogenically
+cryogenics
+cryptid
+cryptographers
+crystallizing
+cubicles
+culturebound
+cumini
+cuneate
+curassows
+curried
+cursors
+curvatures
+cwm
+cyanosis
+cyclamen
+cyclophosphamide
+cypionate
+cyprinids
+cytosines
+dAdda
+dEspoir
+dHuez
+dOrcia
+daguerreotypes
+dalits
+dappled
+darshan
+databank
+datacentric
+datastores
+decapods
+deceives
+decemvir
+decipherment
+decolor
+deconstructing
+deconvolution
+deepbodied
+delightfully
+delocalized
+deltaic
+denarius
+dendrobium
+denigrated
+dents
+depolymerization
+deregulate
+derivates
+dermatomes
+dermatomyositis
+dermatophytes
+describers
+desmosomes
+destino
+detaching
+deubiquitinating
+dev
+dexterous
+dhole
+diagenesis
+diavolo
+dichroism
+dichromatic
+difformis
+difluoride
+digenetic
+digibook
+dihydropyridine
+dilapidation
+dimensionally
+dingo
+dinosaurian
+directcurrent
+dis
+disappoint
+disappointingly
+disbarment
+discectomy
+disconnects
+diskjockey
+dispassionate
+dispensations
+dispersant
+dispersants
+disputable
+dissorophoidean
+distributable
+districtlevel
+divina
+diviner
+documentarians
+documentarymaker
+doe
+dogleg
+dogtooth
+dolphinarium
+domestique
+donovani
+doublebill
+doubleheaders
+doublevinyl
+douc
+dovetails
+downhome
+downlisted
+dpi
+draco
+drafter
+dragsters
+dreampop
+dredges
+drizzle
+droll
+druidic
+drumbeat
+drumheads
+duPontColumbia
+dualfuel
+duespaying
+duetted
+dustbin
+dysostosis
+dysplastic
+dystrophic
+eIFG
+eMule
+eScience
+eXtended
+earlobe
+earnt
+earthcreeper
+eavesdrop
+ebola
+echinus
+ecovillage
+edgetoedge
+editorialist
+editorpublisher
+egregia
+ehrlichiosis
+eightysecond
+eins
+elapse
+electioneering
+electrodiesel
+electropositive
+electrostatics
+electroweak
+elevenday
+elitism
+elliottii
+embassieshigh
+embryologist
+emittance
+employmentrelated
+emptyhanded
+enameling
+endgames
+endoglucanase
+endoscopes
+endows
+endproduct
+enduse
+enfants
+engineerproducer
+enjoin
+enkephalinase
+ensigned
+ensnare
+entactogenic
+entangle
+enterocolitis
+entrenching
+epaper
+epaulet
+epidermidis
+epigrapher
+epigraphs
+epimer
+epineurium
+epitomize
+equalspan
+equid
+erFX
+erinacea
+erratica
+ersatz
+esterases
+ethnologists
+eudicot
+euphemistically
+evanescent
+eventuality
+ewe
+exBritish
+exarata
+exhortations
+exonerate
+exostosis
+exotics
+expandability
+explicitness
+extendedplay
+extensors
+extracellularly
+extraprovincial
+extrastriate
+extrude
+eyepatch
+eyewall
+faber
+facetransitive
+faders
+faerie
+faeries
+fale
+fanfares
+fantasybased
+farwestern
+fatigues
+featherlegged
+fedora
+feedbacks
+feedin
+feldspars
+felonious
+femurs
+fencedin
+ferrea
+ferro
+ferrule
+fetishists
+fetlock
+fibratus
+fibrotic
+fictionist
+fiddleneck
+fides
+fifthhighestgrossing
+figlio
+figurae
+fijiensis
+filicornis
+filius
+fimbria
+finalgame
+finchlike
+finedining
+finergrained
+fingertip
+finitedimensional
+firebombed
+firstseeded
+fishfly
+fishponds
+fiveandahalf
+fivebook
+fivecent
+fivedigit
+fiveround
+flagpoles
+flaked
+flashforward
+flatboat
+flatmates
+flattered
+flavanol
+floodlight
+floortoceiling
+floundered
+flowchart
+fluoxetine
+flyout
+folkpunk
+folkstyle
+fomented
+fomenting
+fontinalis
+fordii
+foreseeing
+formalizes
+formosanus
+formosum
+forthwith
+fossilbearing
+foundermember
+fourdecade
+fournote
+foveolatus
+frailty
+framers
+frangible
+fraternitys
+frenectomy
+fresheners
+freshwaters
+frictionless
+fridge
+fringefingered
+fulgidus
+fumarole
+fumigatus
+functor
+funhouse
+furtiva
+fuscula
+gagged
+gaijin
+gallinaceous
+gallinarum
+gamechanging
+gangliosides
+gantries
+gavel
+gelatine
+gemina
+gemmae
+gemology
+generalists
+geniculatus
+gentiles
+gentis
+gentryi
+genuss
+geodesics
+geotagging
+getrichquick
+getters
+geyeri
+gforces
+ghostwriting
+gilberti
+givenThe
+glassmaker
+globosum
+glucosinolate
+gluons
+glycerine
+glycosylases
+glycosylphosphatidylinositol
+gnutella
+goalline
+goalpost
+godfathers
+goggle
+goldencrowned
+goodhearted
+goons
+gov
+governmentdebt
+gracing
+graminearum
+grammitid
+grandiceps
+grandiflorus
+grandsire
+granodiorite
+granti
+grantinaid
+granulipennis
+granulocytic
+grasswren
+gratuity
+graueri
+gravedigger
+graycrowned
+grazes
+greenbrier
+gridconnected
+gripper
+groins
+groovy
+groping
+groundhog
+guestbook
+guillemots
+guitarbass
+guitaristbassist
+gundi
+gunfighters
+guyanensis
+guyot
+gyroplane
+hadiths
+hadron
+haematopoietic
+haemolytic
+haemorrhoidalis
+hag
+hajj
+haka
+hakea
+haloperidol
+hamiltoni
+hamstrings
+handcranked
+handstamp
+hantaviruses
+hapkido
+hardcoremetal
+harddriving
+hardshelled
+haveli
+hawkmoths
+hawksbeard
+hawksbill
+hayesi
+headlamp
+headlong
+headwords
+healings
+heddles
+helianthi
+heliosphere
+helipads
+heliskiing
+helpfulness
+hematoxylin
+hemiparesis
+heteroatom
+heterogametic
+hideouts
+highconcept
+highestprofile
+highfunctioning
+hippedroof
+hiproof
+hispidula
+histograms
+histoplasmosis
+hocicudo
+homebound
+homeschoolers
+homophone
+homophones
+hongkongensis
+hoodoos
+hornist
+horrendous
+horrormystery
+hort
+hotseat
+housekeepers
+housemaster
+humanely
+hush
+hyaluronidase
+hydrogel
+hyperalgesia
+hyperkeratosis
+hyperkinetic
+hypermutation
+hyperplastic
+hyperspace
+hypnotism
+hypocalcemia
+hypogeous
+hypomania
+hypoparathyroidism
+hypostome
+iCalendar
+iMovie
+iPhoneiPod
+iRobot
+ichnology
+iconoclast
+iconostasis
+icosahedra
+igloo
+immunemediated
+immunosuppressed
+impar
+imploring
+impoundments
+impregnate
+inbrowser
+incanus
+incl
+inclusivity
+incorporators
+incriminate
+indecora
+indigenes
+infidels
+infinitives
+ingests
+innovativeness
+inoculant
+inorder
+inotropic
+inquirenda
+insigne
+insolitus
+inspace
+instantiate
+insulates
+interactional
+intercalating
+intercoastal
+interlinking
+interlobular
+intermingle
+internalizing
+intimated
+intoxicants
+intraperitoneal
+intricatus
+invigorating
+ipso
+irate
+islandica
+iso
+isoenzymes
+isothermal
+issuebased
+italicus
+ivermectin
+jackalope
+jazzoriented
+jeepney
+jenny
+jerking
+jesters
+jockeying
+jolt
+joshi
+jour
+jujitsu
+jujube
+jumpsuits
+junctures
+juniorlevel
+kachina
+kala
+kali
+kameez
+kar
+keelboats
+keratinocyte
+ketal
+kevlar
+kinesis
+kingbird
+kingly
+kininogen
+kinky
+kirkii
+kleine
+koreana
+kumaenorum
+kylix
+labialis
+lability
+labyrinthodont
+lactase
+laminitis
+landscaper
+lanei
+languagelearning
+languagespecific
+langue
+lanigera
+laparoscopy
+lapponicus
+largeflower
+lasiocarpa
+latefasciata
+laticornis
+laughable
+laundered
+lauryl
+lavage
+lawfulness
+leagueleading
+leantos
+lectureships
+lepidopterans
+leptalea
+lettings
+leucophaea
+leva
+lexicographers
+lexicographic
+liber
+lichenicolous
+lidded
+ligata
+lightblue
+lightening
+lightvessels
+lignicola
+lilioid
+limping
+lindheimeri
+lindy
+lineolatus
+linsang
+liocichla
+lionfish
+lipidlowering
+lipsynched
+lis
+loafing
+loathed
+lobatus
+locomotory
+locules
+logbooks
+logperch
+london
+lonesome
+longclawed
+longdelayed
+lounging
+loveable
+loveless
+lowerelevation
+lowturnout
+loyalism
+lubber
+lunaris
+lunatics
+lunule
+lurk
+lysed
+mAh
+mHealth
+mTc
+macerated
+maces
+macrodon
+macropus
+macroscale
+madeforcable
+madetoorder
+madrasahs
+magazinefed
+magnetoencephalography
+magnetoresistance
+mainboard
+maiolica
+maiores
+maison
+maisonettes
+majorityminority
+mako
+malady
+mali
+maltings
+mandarina
+manicures
+mannikin
+mantellid
+mapmaking
+margaritae
+marginalize
+mariana
+marietti
+martingale
+massparticipation
+masterly
+masterworks
+masturbate
+materia
+matinees
+mattered
+mb
+meaningmaking
+meatball
+meatloaf
+medica
+medicolegal
+mediofasciata
+medulloblastoma
+mee
+melanosomes
+melic
+mellower
+melodeon
+menaced
+menorrhagia
+mento
+mephedrone
+meriting
+mesocyclone
+mesolithic
+messes
+messing
+metaethical
+metalled
+metalloid
+metamorphose
+meterlong
+methacrylate
+methanogens
+methodologically
+metronidazole
+metronome
+metropolitans
+microasteroid
+microcephala
+microelectrode
+microsite
+microsomal
+microsurgical
+middleoftheroad
+midsixties
+mille
+milliliter
+mineralocorticoids
+minimalis
+minuet
+misaligned
+miscalculation
+miserably
+misinterpret
+misinterpretations
+misleads
+miso
+misreported
+misshapen
+mitchellii
+mitzvot
+mixedtonegative
+mk
+mockumentaries
+modello
+mohair
+moisturizers
+molybdate
+mon
+monadnock
+moniliformis
+monnei
+monocyte
+monocytic
+monogyna
+monohydrate
+monopolizing
+monosperma
+monstrosa
+montrouzieri
+monzonite
+mooncakes
+moron
+mothertongue
+motorcar
+mouflon
+mtvU
+muckraking
+mucocutaneous
+mucopolysaccharidosis
+mudpuddling
+muhafazah
+multicasting
+multiprocessors
+multisided
+municipium
+murphyi
+mushing
+musiciancomposer
+mustelid
+musty
+myalgia
+mycobacterial
+myelination
+myosins
+myotonic
+myspace
+mysticus
+nanoseconds
+naris
+nasus
+nationalize
+nautilids
+nautilus
+nds
+nebulas
+needlefly
+needlestick
+neoNazism
+neomexicana
+nephrogenic
+neststraw
+netlist
+neuroanatomical
+neuroblasts
+neuroeconomics
+neurogenetics
+neuropteran
+neurotrophin
+newsman
+newwave
+nigerrima
+nightwear
+nigroapicalis
+nite
+nitrides
+nitrogenase
+noblest
+nodulosa
+noirs
+noiseless
+nonCatholics
+nonCongress
+nonGreek
+nonattendance
+noncompulsory
+nonconfrontational
+noncorporate
+nondurable
+noneconomic
+nonfinishers
+nonimport
+nonindependent
+noninterventionism
+nonjudgmental
+nonlegal
+nonliturgical
+nonnational
+nonperformance
+nonplacental
+nonplayers
+nonpotable
+nonpowered
+nonprimary
+nonproteinogenic
+nonresponse
+nonribosomal
+nonscripted
+nonspore
+nontour
+nook
+normalis
+northtosouth
+notifiable
+novads
+novaezealandiae
+novarum
+novena
+nucleobases
+nudism
+nunneries
+nutraceutical
+nylons
+obscurata
+obtusirostris
+obviating
+occiput
+octocorals
+octuplets
+od
+oddest
+offaxis
+offstream
+offtarget
+offthewall
+oldestknown
+oleoresin
+oligarch
+oligarchic
+oligomer
+ommatidium
+omnipresence
+oncoprotein
+oneoffs
+onlinebased
+oogonia
+oospecies
+opercular
+opercularis
+ophthalmia
+ophthalmosaurid
+opposita
+oppress
+orangebreasted
+ordeals
+oreas
+ornithomimosaur
+orthochoanitic
+ostraca
+otagoensis
+outbid
+outgrow
+outliving
+outofcontrol
+outspent
+ouvrages
+overcall
+overhears
+overwater
+oxidations
+oystercatchers
+ozonedepleting
+paa
+padlock
+pageviews
+paidin
+paladin
+palaeography
+paleocortex
+paleoecology
+palmaris
+palmlike
+pandit
+pantanal
+papaverine
+papayas
+papuanus
+paracanoeist
+paraensis
+paramountcy
+paraphilic
+parented
+pargana
+pari
+parquet
+parsons
+particularism
+particularities
+parturition
+parviceps
+passivation
+pat
+patchily
+patency
+pater
+patera
+pathosystem
+pathovar
+patronym
+patruelis
+patulin
+pawang
+payola
+payphone
+peaceable
+peacekeeper
+peaceloving
+pearsoni
+pecel
+peddle
+penetrators
+pennate
+pennsylvanica
+pent
+pentecostal
+pentellated
+pentyl
+peony
+perceptually
+periallocortex
+peristome
+permease
+perry
+persevere
+perspicua
+pervade
+petered
+pf
+phagosome
+pharaonic
+phenylketonuria
+pheochromocytoma
+phosphoenolpyruvate
+phosphorylating
+photobleaching
+photocopies
+photocopy
+photodetector
+photogenic
+photoluminescence
+photolysis
+photosynthesize
+phylogeography
+physiologists
+pianoforte
+pianoled
+piastra
+picnickers
+piebald
+pigeonhole
+piggybacking
+piglike
+piiri
+pinewoods
+pinicola
+pita
+pitchfork
+pixies
+placentation
+plagiarizing
+plantarum
+plantfeeding
+plasmonic
+plateway
+platinumyear
+plumbeus
+pointblank
+polarize
+poliocephalus
+polychoron
+polylepis
+polymerizes
+polypody
+ponder
+postApartheid
+postWar
+postacute
+postconditions
+posteriori
+postmodernity
+postponements
+potentiometer
+powerassisted
+powerboating
+poweron
+powerup
+preEuropean
+preKindergarten
+prebiotics
+prebooked
+precode
+precongregational
+preconstruction
+predawn
+predebut
+predesigned
+predestined
+preglacial
+preissii
+premiumrate
+preppy
+presentable
+presentational
+prespecified
+pressurize
+pressurizing
+presuming
+pricking
+priesthoods
+primroses
+privateequity
+privatizations
+proTrump
+processs
+profitmaking
+proisocortex
+prokaryote
+prolegs
+propers
+prophesy
+propraetor
+proprius
+prorector
+prosthodontics
+prostituted
+proteinaceous
+proteintyrosine
+protruded
+provincelevel
+prudential
+pseudocode
+psychophysiology
+pteron
+pulla
+pulpits
+punctatissima
+punkpop
+punted
+purinergic
+putu
+pyrenaica
+pyridoxine
+quadrillion
+quantile
+quart
+quarterhour
+quartile
+quartos
+quasiindependent
+queenslandica
+quickies
+quicklime
+quizmaster
+quod
+quoining
+quotients
+rRNAs
+rabbinate
+racists
+radiationinduced
+radiochemistry
+ragtag
+railbus
+raincoats
+rajas
+rama
+rambutan
+rancho
+rapidity
+rapturous
+rarefied
+ratedetermining
+readjustment
+reairing
+rears
+reassuring
+rebus
+rebuttable
+recant
+recapitalizations
+reconnects
+redbanded
+redefines
+redlist
+reelin
+refinishing
+refits
+refresher
+regionalisation
+regranted
+regressions
+regularization
+regurgitated
+rehire
+reimagine
+reimbursements
+reinvigorating
+relicta
+religiosa
+relit
+remedying
+remembrances
+remissions
+remover
+renames
+rentfree
+repentant
+replayability
+reposted
+reroute
+resentenced
+restharrow
+restorers
+resubmitted
+reticent
+revisionists
+revocable
+rewarming
+ribonucleoproteins
+ricotta
+ridleyi
+rightward
+rimbachii
+ringette
+ringroad
+riparius
+riven
+roaches
+roberti
+rockhopper
+rockprogressive
+rocksnail
+rolani
+romcom
+roomtemperature
+rootsy
+rosella
+rosenbergii
+rosids
+rostrally
+rotundus
+rubricollis
+rudeness
+rufousheaded
+runcinated
+sabulosus
+sailfish
+salacious
+salat
+salmonella
+saloonkeeper
+saltern
+samoensis
+sandblasting
+sandpipers
+sangam
+sanitizer
+saponin
+sarcosine
+saundersi
+sawtoothed
+saxes
+saxitoxin
+saxophonistflutist
+scala
+scallion
+scalybreasted
+scareware
+scarification
+scaup
+scenthound
+schists
+schizoaffective
+schoolrecord
+scintigraphy
+scitulus
+scoparius
+scorpionweed
+scotoma
+screenbased
+screenprinting
+screwworm
+scrubwren
+scutellatus
+seafarer
+seakeeping
+seashores
+seaters
+secondaryschool
+secondstring
+secondunit
+segreto
+seismometers
+selfarchiving
+selfassembled
+selfconfessed
+selfdiscipline
+selfdual
+selfexplanatory
+selfsustained
+sella
+semaphores
+semihistorical
+semperi
+sendoff
+serena
+serf
+sesterces
+setts
+sevenman
+sevenpoint
+seventhmost
+sh
+shallowly
+shalt
+shalwar
+sharecropper
+sheading
+sheepbreeders
+shenanigans
+shieldshaped
+shingling
+shiprigged
+shipwright
+shortestserving
+shortrodshaped
+shove
+showboat
+showmen
+shunted
+siddur
+sienna
+sigmoidal
+signifera
+signifiers
+silences
+silkscreened
+similaris
+singaporensis
+singledigit
+singlepiece
+singleserving
+singlevehicle
+siphoned
+siphoning
+sisorid
+sisterhood
+sixtyminute
+skateparks
+skerries
+sketchcomedy
+skicross
+skifield
+skirmished
+skool
+skyrocket
+skyway
+slapper
+sleeker
+sleighs
+slimming
+slinky
+slivers
+slowdowns
+slut
+smallbodied
+smallholdings
+smaragdinus
+smartglasses
+smokestacks
+snailkilling
+snorting
+snowfalls
+soiltransmitted
+solenoids
+solifuges
+solipsism
+solus
+somethings
+sonographers
+sororia
+sorter
+sounder
+sourceThe
+sparrowhawks
+spearmen
+specular
+spellers
+spermathecae
+sperms
+sphingomyelin
+sphingosine
+sphinxes
+spiderwort
+spikenard
+splay
+splendidus
+spolia
+spoliation
+sporophylls
+sporophytes
+sportswomen
+spotless
+springparsley
+sprocess
+squarekilometer
+squaring
+squaw
+squelch
+standardbearers
+standins
+stanozolol
+starrs
+startstop
+stashed
+stateapproved
+steamy
+stella
+stellarator
+stenciling
+sterications
+sternites
+sternwheelers
+stickwork
+stigmatizing
+stipitata
+stouter
+stowage
+straggling
+straightforwardly
+straitjacket
+stratiform
+stratovolcanoes
+stratus
+streetscapes
+stressrelated
+strictus
+stringy
+stripetail
+stroller
+structurebased
+stupor
+stylet
+subindex
+subking
+sublet
+sublieutenant
+submittal
+subsists
+substring
+subtaxa
+subthalamic
+sulla
+sunroom
+supercards
+superclusters
+superfight
+supergiants
+superinjunction
+superlatives
+superordinate
+superstes
+supplication
+suprascapular
+supremely
+sural
+sustainer
+swage
+swales
+swashplate
+swathe
+sweepoared
+sweetsmelling
+switchboards
+syllabuses
+syllogistic
+sympodial
+synchronic
+syncretised
+synecdoche
+syrupy
+taillike
+taipan
+tamales
+tamarins
+tamperproof
+tangerines
+tansy
+tapetum
+taproots
+targa
+tarring
+teachertraining
+teahouse
+teamsters
+teatime
+technicolor
+telangiectasia
+telcos
+templeform
+temporality
+tempt
+tenofovir
+tentlike
+teratogenic
+tertius
+testandset
+testate
+thalami
+thalamocortical
+theatergoers
+theatricality
+thenSecretary
+thencontemporary
+thenfuture
+theodolite
+thereunder
+thermocouple
+thgrade
+thickeners
+thicktailed
+thievery
+thirdfastest
+thixotropic
+thminute
+tholos
+thousandths
+threadsnake
+threeawn
+threecent
+threefinger
+threepage
+threescreen
+threespot
+throwin
+thunbergii
+tickers
+tickle
+timedivision
+tlatoani
+tomatobased
+tonsure
+topoisomerases
+tormentors
+tornal
+torquetum
+torrid
+torturous
+totalisator
+tourniquet
+trachoma
+tradeable
+tradenames
+transcriptomic
+transcutaneous
+transduced
+transpire
+travancorica
+trebuchet
+trenching
+tricarboxylic
+triclads
+trie
+trifecta
+trifoliata
+trills
+trimethoprim
+trimmers
+tripinnate
+triquetra
+triticale
+troff
+trompe
+tropic
+trounced
+truncatum
+trutta
+tryst
+tumbled
+tumblers
+tumbles
+turbidites
+turbodiesel
+twelvehour
+twentyfoot
+twocent
+twofamily
+twoliter
+typists
+tyramine
+ubiquitinconjugating
+ufologist
+ugandensis
+ukiyoe
+ulema
+ultraOrthodox
+ultralights
+ultranationalist
+ultrastructure
+ultrazoom
+unafraid
+unbeatable
+uncaring
+uncluttered
+unconformities
+uncoupling
+underbanked
+underbone
+underfoot
+undigested
+undulate
+unearthly
+unelectrified
+unescorted
+unfenced
+unfurled
+unhurt
+uni
+uniaxial
+unicolorous
+unlabeled
+unlit
+unmetered
+unmyelinated
+uno
+unreformed
+unsponsored
+unsuitability
+untamed
+unyielding
+upgradable
+upped
+uppercut
+uppersides
+ur
+ursinus
+userland
+ut
+vCard
+valuepriced
+vaporizes
+vaporizing
+variabile
+vascularization
+vee
+veli
+verandas
+vetchling
+viciously
+victualler
+vignetting
+villainy
+vining
+violenta
+virgatum
+virginea
+virginicus
+viridissima
+viruslike
+viscose
+vivisection
+vocalized
+vocalsguitars
+vocoders
+volleyballist
+vols
+volva
+vor
+vos
+voterapproved
+waiving
+wakerobin
+walkovers
+wallboard
+wallmounted
+wampum
+warder
+wareni
+warmseason
+warred
+warreni
+wastewaters
+watchmen
+waterjet
+watermen
+waterrepellent
+watsonii
+wattage
+wavelike
+wavetable
+weaponbased
+weatherboards
+weaved
+wellbeaten
+wellprepared
+wellwooded
+wheelhouse
+wheelset
+whitestart
+wideeyed
+widowbird
+wigeons
+wildrye
+windlass
+windsurfers
+wingmounted
+wingsuit
+winkle
+womanist
+wondrous
+woodbat
+woodsman
+wooing
+workarounds
+worldbuilding
+wow
+xenoliths
+xinjiangensis
+xs
+xylene
+yaxis
+yearlings
+yellowtipped
+yoked
+youtube
+zIIP
+abate
+abbreviate
+abc
+abductors
+abetted
+abled
+ablest
+abodes
+abortus
+absentminded
+acaulis
+accidentprone
+acclimate
+acclimation
+accreting
+acetates
+acetophenide
+achalasia
+acousmatic
+acquiesced
+acquittals
+acrosomal
+actionmasala
+adamantine
+adaptors
+addenda
+adherens
+adit
+adlib
+aegyptiaca
+aeneum
+aether
+afresh
+afzelii
+ageappropriate
+ageless
+ageng
+agoraphobic
+agricola
+agroindustrial
+airplayonly
+airspaces
+alAziz
+alIslami
+alNuman
+albocincta
+algeriensis
+alii
+allAfrican
+allegorically
+allocator
+allogeneic
+alloriginal
+allyear
+alni
+alphasynuclein
+alternifolia
+aluminate
+alvars
+amanuensis
+aminoacyl
+ammon
+amo
+amoebiasis
+amphoteric
+amylose
+anaesthesiology
+anaglyph
+analogical
+analysers
+anarchosyndicalist
+andean
+andicola
+andrewesi
+andunder
+annealed
+annulatum
+anomalously
+anomodont
+antediluvian
+anthocyanidin
+anthropomorphised
+antiCastro
+antiRoman
+antiWestern
+antiallergic
+antibioticresistant
+anticlines
+antifascism
+antifraud
+antigenspecific
+antimetabolite
+antiperspirant
+antiphishing
+antisocialist
+antithrombotic
+antithyroid
+antiwhaling
+anurans
+anxiolytics
+apartmentstyle
+apertural
+appendiculatus
+applaud
+appraise
+appressed
+aquila
+aquilonia
+arboreta
+archon
+arco
+arcshaped
+areca
+arenosa
+arewere
+argenteum
+aridity
+armigera
+arowana
+arrowgrass
+arrowroot
+artesunate
+artifice
+artistproducer
+arundinacea
+aryepiglottic
+asexuality
+asneeded
+astonishingly
+astra
+astroparticle
+asymmetries
+atlantooccipital
+atomically
+atomization
+atypically
+audax
+audiotape
+audiotapes
+aulica
+auricula
+autocephaly
+autophosphorylation
+autoreactive
+autoxidation
+awarenessraising
+awls
+axil
+axonemal
+bZIP
+baccharis
+backfires
+backtobasics
+backwardcompatible
+badmintons
+baeri
+baileys
+balalaika
+balansae
+bambara
+bandwidths
+bangers
+barbarae
+barbican
+barest
+bargained
+barkeri
+barotrauma
+barrestaurant
+barroom
+baseboard
+baserich
+bastards
+batatas
+bathyal
+batterrunner
+beargrass
+beauxarts
+beggarticks
+behaviorbased
+bellcote
+beluga
+benched
+benefactions
+bereft
+bestreviewed
+beststudied
+betaamyloid
+betacarotene
+bezels
+bicupola
+billfish
+bilobed
+bioacoustics
+biofilter
+biomonitoring
+bionics
+biosystems
+bioweapons
+bipyramids
+birdied
+birdies
+birdsnest
+birdsong
+birthname
+bispecific
+bisphosphonate
+bitting
+blackhead
+blacknose
+blackstriped
+blackwhite
+blairi
+blanketing
+blitzkrieg
+blocklevel
+bloodied
+blowdown
+bluecrowned
+blueflowered
+boardinghouse
+boba
+bocagei
+bogeyman
+bolas
+boldest
+boobies
+booing
+bookmarked
+booties
+boronia
+botanique
+bottae
+bottomless
+boxelder
+boxtobox
+brackishwater
+brahmins
+brandti
+brant
+braves
+brazed
+brazieri
+breakins
+breathalyzer
+breathhold
+bricabrac
+bricklaying
+bridegrooms
+bridles
+briefer
+bristled
+brittlegill
+broach
+broadtailed
+bronchiectasis
+browncapped
+bruneri
+brunneum
+brutalized
+bss
+buccaneers
+buchanani
+bulbuls
+bullish
+buprenorphine
+burdock
+burgdorferi
+burnishing
+burro
+businesspersons
+buteo
+butoh
+butters
+butterwort
+cadetship
+caeca
+cafeteriastyle
+cagelike
+calcinosis
+caldo
+callaloo
+calle
+callout
+candi
+candidus
+candybar
+caniceps
+cannabidiol
+cantonments
+cantorial
+canzone
+capitulate
+capybara
+carbocation
+carbohydratebinding
+carbonyls
+cardiogenic
+carhop
+caribaea
+carinatum
+carloads
+carnosa
+caroli
+carparks
+carrageenan
+carted
+cartoony
+caseworker
+castellanus
+castellum
+castling
+castmate
+catsharks
+caucasian
+caveats
+caymanensis
+cellcycle
+cellulosome
+centriole
+cepa
+cerana
+chaat
+chairmanships
+chakra
+chakras
+chanceries
+chanteuse
+chapelofease
+chapini
+charmer
+charr
+chastised
+chatroom
+chebula
+chelae
+chestnutbreasted
+chews
+chez
+chicha
+chiefship
+chins
+chitinase
+chits
+chlorofluorocarbons
+chocolateflavored
+chorales
+chorizo
+chough
+chow
+chrism
+christophi
+chthonic
+churchmanship
+cincture
+cinque
+citronella
+civile
+clammy
+clamor
+clandestina
+clapboarded
+clarinetists
+clarus
+classing
+claustrum
+clearness
+cleptoparasitic
+clerkship
+clerkships
+clueless
+clung
+clypeatus
+cnidocytes
+coChair
+coarranged
+coaxed
+coccineus
+cochairmen
+cocounsel
+codeword
+coeruleus
+coffered
+cofinancing
+cohousing
+coinfection
+coinventors
+coldweather
+collegebound
+collegeeducated
+colli
+colonially
+comarcas
+comedienne
+comedywestern
+commandery
+commissario
+commotes
+communique
+complicata
+composted
+comprehends
+comstocki
+concedes
+conceptualist
+concord
+condyloid
+confidentially
+conformist
+confounds
+conjugacyclosed
+conjugating
+conjunctive
+conjuring
+consultum
+consumerdriven
+contentions
+contextaware
+contiguously
+conversant
+convoluta
+cooter
+copei
+coprolites
+copyprotected
+corallina
+corallites
+cordate
+cordatus
+cored
+coregency
+coring
+corkscrews
+cornflower
+coronaria
+coronatum
+corporateowned
+corticola
+coruns
+coscripted
+costae
+counterclaims
+counterdrug
+counterproliferation
+cowbird
+cowon
+coxalis
+crampons
+crassidens
+creamyyellow
+creatinine
+creolization
+cribbing
+cribellum
+cricoarytenoid
+crisscrossed
+crocodilelike
+croesus
+crooning
+croplands
+crossbones
+crosscoupling
+cru
+crucifer
+crumbles
+cryptocrystalline
+cryptoprocessor
+crystallites
+culm
+cumingi
+cupping
+curdled
+curvipes
+curvy
+cusickii
+cutlass
+cuvieri
+cyanogenic
+cyanohydrin
+cyanuric
+cybersquatting
+cyclophilin
+cylindrata
+cytochalasin
+cytological
+dArco
+dAulnoy
+dOrange
+daerah
+dahlia
+damask
+dampwood
+damselfishes
+dancerchoreographer
+dar
+darkgreen
+darkwinged
+dasa
+databasedriven
+dayboarding
+daybreak
+daytrippers
+deWilde
+deadball
+deafmute
+debauched
+decanoate
+decemviri
+decimalisation
+decorus
+decouples
+deductibles
+deemphasizing
+defibrillators
+deflationary
+deflectors
+deiodinase
+delist
+demarcates
+democratizing
+denature
+deniability
+denotified
+deodar
+deoxy
+deoxyribose
+deporting
+deprecation
+derogatorily
+deserta
+designee
+deuterated
+dextrin
+diGMP
+diastema
+diceless
+dichotomies
+dichrous
+diethylene
+differentiator
+digitalized
+dilecta
+diluent
+diopside
+dioxinlike
+diphenyl
+dir
+disciplinarian
+discoideus
+discriminant
+disfranchisement
+disinterest
+disomy
+disparagement
+disproving
+disrespected
+dissectum
+distractors
+distros
+disulfidelinked
+dithering
+diuresis
+diversus
+divinorum
+divisors
+divulged
+djinn
+dockworkers
+doctoring
+dodger
+dodsonii
+dogwoods
+dolorosa
+domingensis
+dominica
+donee
+donoradvised
+doodles
+dopants
+dorothea
+dorsoventral
+doublebyte
+doublehulled
+doublemini
+downandout
+downonhisluck
+downplays
+dowsing
+dram
+dramaadventure
+drawstring
+drippings
+drivable
+driveshafts
+drivetrains
+droids
+dromaeosaurids
+dross
+drucei
+drumlins
+drunks
+drypoint
+dualSIM
+dualhatted
+dualregistered
+dualtrack
+dubber
+dubiosa
+ducarmei
+duetting
+dugong
+dumper
+dumpers
+duoprism
+dupe
+duplicatus
+durational
+durophagous
+dussumieri
+dusters
+dv
+dyari
+dynamos
+earmarks
+earpiece
+easeofuse
+easytoread
+eatoniellids
+eau
+ecclesia
+ecclesiae
+echos
+ecolabel
+econophysics
+ecotypes
+ectothermic
+edentulous
+edibles
+efficientmarket
+eggbased
+eicosanoid
+eighthgeneration
+eighthhighest
+eightthousanders
+ejaculatory
+elSisi
+eland
+elatum
+electorally
+electra
+electrochemically
+electromagnetically
+electronicbased
+electrospinning
+elevenminute
+elopes
+eloping
+emanations
+embodiments
+embryophytes
+emesis
+emiliae
+empyema
+emus
+encapsidation
+endblown
+endoparasitic
+endoribonuclease
+endorphins
+enigmatica
+enmeshed
+enoploteuthid
+ensnared
+enstatite
+entablatures
+entactogen
+enthalpy
+entheogenic
+enthralled
+enumerators
+envenomation
+envious
+enzymelinked
+epee
+ephrin
+epipelagic
+equalsized
+equational
+equatorialis
+equi
+equilibration
+erred
+ersguterjunge
+erythrocephalus
+esculentum
+esculentus
+esotropia
+essayists
+estrous
+ethnobotanical
+eubacteria
+euphorbiae
+euthanised
+eutherians
+evaporators
+everpopular
+everted
+evildoers
+evisceration
+exCIA
+exarmy
+excitons
+excitotoxicity
+exedra
+exegete
+exoffenders
+experimentalist
+extendedstay
+externalizing
+extolled
+extraembryonic
+extraversion
+extremal
+extrusions
+eyelets
+faciundis
+factchecking
+fady
+faipule
+falsifiable
+familiarization
+familyoperated
+fannish
+fanrun
+farina
+farsighted
+fasciculatum
+fastacting
+fastenings
+fattest
+federalization
+feedlots
+femaleoriented
+femaletomale
+feminisms
+fendleri
+ferredoxin
+ferrooxidans
+fetes
+feu
+fictionthemed
+ficus
+fieldbus
+fifthplaced
+fiftyminute
+figureofeight
+filiforme
+filosa
+finery
+finless
+finweight
+fireclay
+firefights
+fireteam
+firstdivision
+firstlanguage
+firststage
+fishmonger
+fittest
+fiveacre
+fivesecond
+fivesided
+fixedsize
+flaccida
+flashcut
+flashsideways
+flatscreen
+flavirostris
+flavivirus
+flexes
+fluorination
+fluorocarbon
+fluoropolymers
+flutterer
+flyable
+flytrap
+fob
+fogou
+foley
+foliacea
+foodplant
+foraminiferal
+forcipata
+forded
+forebody
+foreclose
+foreshadow
+forfeitures
+forktailed
+forlorn
+foyers
+franciscanus
+frankness
+frantically
+frat
+fraternization
+freeborn
+freedesktoporg
+freedomfighter
+freephone
+freepiston
+freerange
+frequencydependent
+frequentflyer
+frondosa
+fronttoback
+frugality
+fruited
+fryer
+fulldome
+fullyear
+fulminans
+fumigant
+funereus
+funfairs
+funkrock
+funneleared
+fuoco
+furfural
+furrowed
+fuscomaculata
+fuss
+futurology
+gabapentin
+gaffrigged
+gambled
+gambusia
+gamekeepers
+gameshows
+gametocytes
+gapping
+garagerock
+garfish
+garish
+gasping
+gastrocnemius
+gastrovascular
+gateposts
+gayoriented
+ge
+gegen
+gelded
+gemma
+gemmology
+gendarmes
+gennaker
+geodesist
+geopark
+geoscientific
+gertschi
+gi
+gibber
+gibbicollis
+gilts
+ginkgo
+girlhood
+glabratus
+glimpsed
+gliomas
+glucanohydrolase
+glucosinolates
+glucuronic
+glycerides
+glycoconjugates
+glyoxylate
+gnateater
+goalsetting
+goanna
+goatee
+godeffroyi
+goo
+gorgon
+gorgonian
+gorgonians
+gossiping
+gouldii
+governmentrelated
+gradualism
+grail
+granatensis
+granduncle
+granny
+granulosum
+grappler
+grasscovered
+grassleaved
+grater
+gravimetric
+graving
+graycapped
+greased
+grieves
+gristmills
+groovebased
+grotei
+grottoes
+groundlaunched
+gt
+guacamole
+guardrail
+gubernaculum
+guile
+guitarshaped
+gulai
+gunrunning
+guqin
+gurdwaras
+gusset
+gyrocompass
+hailstorm
+hairiness
+halacha
+halfstory
+halophiles
+han
+handcrafts
+handdug
+handicapper
+handshapes
+handspring
+haram
+harddrive
+hardtoreach
+harmonizes
+harpists
+hassles
+hatchetfish
+hawkish
+headcoach
+hearses
+heatingcooling
+heavyset
+heckling
+hectocotylus
+hectolitres
+heelsplitter
+hegemon
+heiresses
+helicis
+helvetica
+hemiacetal
+hemionus
+hemipterans
+herbology
+heroically
+hesperid
+heterocysts
+heterojunction
+heterostracans
+hexavalent
+hexokinase
+hibernates
+higherpriced
+highestdebuting
+highestquality
+highestrating
+highheeled
+highlift
+highwire
+hijra
+himalayana
+hindrances
+hiphops
+hispidum
+hodgepodge
+holidaying
+holistically
+holsters
+homebuyers
+homestay
+homiletics
+homological
+homonyms
+honeycombs
+honourees
+hoolock
+hornless
+horseless
+horticulturists
+hotshot
+hounded
+housefly
+householder
+howl
+hubbardi
+humaninterest
+humeri
+humi
+hummocks
+hummocky
+hunterkiller
+hurl
+hushed
+hustling
+hyalinus
+hydrofluoric
+hydrogens
+hydrolysable
+hydronic
+hydroxycoumarin
+hydroxylated
+hymenoides
+hymnody
+hypercalcemia
+hypercorrection
+hyperventilation
+hypocaust
+hypovolemia
+iBiquity
+iFM
+iVillage
+ibid
+icefield
+icefish
+ichnospecies
+iheringi
+ilex
+ilk
+illconceived
+imbalanced
+imines
+immunohistochemical
+impactor
+impartially
+impatience
+impatient
+impedances
+impolite
+improbably
+impulsiveness
+inapplicable
+incarnate
+incomers
+inconceivable
+inconcert
+inconclusively
+indefatigable
+independentlyowned
+indiscipline
+indolebased
+inducements
+industrialelectronic
+indwelling
+inescapable
+inexorably
+infidelities
+informationbased
+ingot
+inheritances
+innocents
+inops
+inouei
+instep
+insulana
+insurrectionary
+integrable
+integrally
+intensifiers
+intensional
+intensivecare
+intercollege
+interferometers
+interiomarginal
+interleukins
+intermediation
+intermixing
+interpeduncular
+interzone
+intraplate
+introversion
+intuitionism
+invalids
+inversa
+invertible
+invicta
+involution
+ironore
+isabella
+ischial
+isomorphous
+isoquant
+itemizes
+ivorybilled
+jacaranda
+jackpots
+jackrabbits
+jamborees
+janus
+javascript
+jean
+jelskii
+jetblack
+jibs
+johnstonii
+joiners
+judgemade
+jumpstart
+kN
+kabuki
+kaftan
+kaki
+kalari
+kalimba
+kelvin
+kempyang
+kermadecensis
+kerogen
+ketene
+ketoacidosis
+keyboardistguitarist
+keystream
+khanqah
+khat
+kho
+kinaseassociated
+kindling
+kip
+kiwifruit
+kneels
+knickers
+knobbed
+knowitall
+kokanee
+kraal
+krupuk
+kt
+kurgan
+kw
+lOrdre
+laboratorys
+lacerations
+laidoff
+lambing
+lamprophiid
+lanceleaf
+lancifolia
+landliving
+laneways
+languageindependent
+largecaliber
+largescreen
+lariat
+lashing
+laterialba
+lather
+laths
+latior
+laurae
+lawnmowers
+laxity
+leaden
+leadfree
+leat
+lectotype
+legatine
+legitimization
+legitimizing
+leiomyoma
+leleupi
+lengthens
+lengthier
+lentus
+leotard
+leotards
+lepidopterist
+lessingia
+letzte
+lib
+liberalizing
+librations
+libri
+licensors
+lifealtering
+lifesaver
+lighterweight
+liiga
+liken
+limbal
+linesmen
+liniment
+lipopeptide
+liposome
+lipsync
+lisp
+lithographed
+lithologies
+lithos
+litura
+liveperformance
+livework
+lncRNAs
+loams
+localizer
+locallevel
+longhandled
+longicaudata
+longipalpis
+lontong
+loti
+lowrent
+luciferin
+lucus
+lunchtimes
+lunula
+lusca
+lutzi
+lycanthropy
+lycopersicum
+lysyl
+maars
+mackerels
+madre
+mags
+maguirei
+mailin
+maire
+majalis
+malacological
+mammaliaform
+manakins
+mancipi
+mandapa
+maned
+mangabeys
+mangosteen
+mani
+manna
+manycore
+maquis
+marchese
+marinades
+marketshare
+massaging
+massproducing
+masterfully
+mastership
+mastic
+masturbating
+matchpoint
+matteroffact
+maturities
+mbuna
+mealworms
+mediumship
+meetingplace
+megachurches
+megacity
+megakaryocyte
+megaloblastic
+megaproject
+melanite
+melilot
+melodically
+melzeri
+memberdriven
+membranophones
+menhaden
+menisci
+mensural
+mesodermal
+mesoglea
+mesonephros
+mesoporous
+metabolomic
+metallescens
+metalpower
+metaobject
+metatherian
+methylcytosine
+metonymically
+microbicide
+microclimates
+microfibrils
+microfilariae
+microgranular
+microphyllum
+micros
+microsystems
+midcap
+middleage
+midnorthern
+midtier
+miei
+mightiest
+mike
+mildest
+miliolid
+milked
+millpond
+millworkers
+mimus
+minicamp
+minitournament
+minty
+minutiae
+mis
+misella
+miseries
+mishmash
+mismanaged
+misnamed
+misogynist
+misprint
+moans
+modicum
+mohan
+monazite
+moneymaking
+monooxygenases
+monopropellant
+monopulse
+moonwalk
+mor
+mora
+morgen
+morphinan
+mosh
+mosquitoborne
+mostread
+mostrecent
+motherly
+mothertochild
+motionbased
+motioncapture
+motivators
+motorcross
+mountainbiking
+mowed
+mucronatus
+mudflows
+muesli
+mugged
+mugging
+mullion
+multicurrency
+multigrain
+multihulls
+multilink
+multimeters
+multiphysics
+multipleoutput
+multiproduct
+multisector
+multispan
+muris
+murti
+muscorum
+muskox
+mv
+mycelial
+mycotic
+myelopathy
+myofascial
+myostatin
+mystax
+mythologist
+myxedema
+nOg
+nacional
+nada
+nakedness
+nanometerscale
+nanometres
+nanotechnologies
+nanum
+nasalis
+natricine
+naturae
+naturallyoccurring
+neatness
+neckwear
+nectarines
+nectary
+needlessly
+negroes
+nematocysts
+neoclassic
+neocolonialism
+neosuchian
+networkcentric
+neumanni
+neuroactive
+neurochemical
+neurosteroid
+neverbeforereleased
+newberryi
+newline
+nightshades
+nightspot
+ninetrack
+ninthhighest
+ninthmost
+nip
+nirvana
+nitrated
+nitrites
+nivosa
+nogo
+nonApple
+nonDisney
+nonEuclidean
+nonGMO
+nonNewtonian
+nonUK
+nonaggressive
+nonanimal
+nonconfidence
+nondefense
+nonexpert
+nongraduates
+nonhistoric
+noninstitutional
+nonintervention
+noninterventionist
+nonmalignant
+nonmusic
+nonpassenger
+nonperturbative
+nonphysician
+nonprofitable
+nonpsychoactive
+nonrecurring
+nonregular
+nonrepresentational
+nonrotating
+nonscience
+nonsensemediated
+nonspeech
+nonterminal
+nonthermal
+nontowered
+nonuniformed
+nonunionized
+nonwinning
+noresult
+northerners
+northflowing
+nothingness
+novelistic
+nucleoli
+nucleon
+nuit
+nuragic
+nymphal
+oberthuri
+obligates
+obscuripennis
+observability
+observergunner
+obstetriciangynecologist
+obtrusive
+obviate
+occultations
+oceanarium
+octopods
+odometer
+odoratum
+offreserve
+offthefield
+offtopic
+offtrack
+ohms
+oilcontaminated
+okapi
+oldworld
+oligodendrocyte
+olivieri
+olympiad
+onechild
+oneforone
+onestep
+oneyearold
+ontogenetic
+oozes
+oozing
+opalina
+opendoor
+opentopped
+openweight
+operability
+operationalized
+opisthosoma
+opsin
+orache
+orang
+organophosphates
+organotin
+ornithuromorph
+oro
+orthocerid
+oscillated
+ospreys
+osteology
+osteopath
+ostracon
+otherness
+outpacing
+overallrankings
+overbroad
+overestimation
+overreaction
+overruling
+overstayed
+overunder
+overwhelms
+overworld
+ovipositors
+ovulatory
+owstoni
+oxidiser
+oxychloride
+oxyconic
+oxyrhynchus
+pachycephalosaur
+pacificum
+paged
+pagus
+palaearctic
+palindromes
+pallidipennis
+pamoate
+panela
+pang
+paniculatum
+pantheistic
+papilio
+papyrifera
+paracyclist
+paradoxum
+paraprofessionals
+parasitises
+pareiasaurs
+parley
+parotta
+pascoei
+passengermiles
+pastas
+pastellist
+pastrami
+pasturage
+patellofemoral
+patriciate
+patronize
+patti
+paysites
+peacefulness
+pearlwort
+pedantic
+pedicure
+peltate
+penandpaper
+pencak
+pendens
+penetrans
+penghulu
+penitence
+penknife
+penstemons
+pentalogy
+pentameric
+pentandra
+pepperoni
+peppy
+perf
+perfoliata
+perinuclear
+periodontist
+periodontium
+perithecia
+permittivity
+persimmons
+perstans
+persuasions
+peruanus
+perunit
+petrologist
+phagocyte
+phalangeal
+pharmacognosy
+pharmacologists
+philandering
+philologists
+phlorotannin
+phosphotransferase
+photobooks
+photomasks
+photometer
+photomontage
+phyllostegia
+phylogenic
+phytochemistry
+picipes
+pickpockets
+picric
+pictipes
+pietistic
+pikes
+pilosum
+pimples
+pinnules
+pinworm
+pitvipers
+placemaking
+placers
+planche
+platani
+platethigh
+platformadventure
+playas
+playerdriven
+pledgers
+plena
+plicatus
+pluripotency
+pointbased
+polaritons
+policedrama
+pollute
+polyphase
+polyphemus
+polyphenolic
+polysilicon
+polytetrafluoroethylene
+ponyfish
+popsicle
+populares
+portalHenry
+portlets
+postRevolutionary
+postminimalist
+postpolio
+postprandial
+postshow
+potyvirus
+powerhungry
+pracharak
+pragmatically
+pram
+prams
+prasinus
+preIndependence
+preNorman
+preachings
+preconstructed
+prefigured
+preheat
+prehispanic
+premenopausal
+premiershipdeciding
+preprep
+presaging
+previouslyreleased
+pricefixing
+printouts
+privata
+privateuse
+proboscidean
+proboscideans
+professionalgrade
+progeria
+prolata
+promulgates
+pronouncer
+proofofwork
+propolis
+propriospinal
+prosencephalon
+prostration
+protean
+prounion
+provicechancellor
+provident
+prurigo
+psychophysiological
+publicinterest
+publishereditor
+publishsubscribe
+pullback
+pumilum
+pumper
+puncta
+puppeteering
+pur
+purgative
+purifier
+purplishblack
+purposive
+pusio
+pyralid
+pyriforme
+qua
+quadrinotata
+quadripunctatus
+quam
+quasimilitary
+quasiperiodic
+quicksand
+radiopaque
+radioplay
+radome
+rajahs
+ranchstyle
+rappersinger
+rasterization
+ratifies
+rationibus
+rauisuchian
+rawness
+raze
+reacquire
+reactance
+realitycompetition
+reallocate
+rearfacing
+rearview
+reassign
+reassortment
+reassumed
+rebbes
+recalcitrant
+recapitulates
+recedes
+receiverdefensive
+reciprocate
+reckon
+recoded
+recognizance
+recolored
+recommission
+rectally
+rectangulus
+rectouterine
+rectrices
+recyclers
+redcarpet
+redeal
+redoing
+redshank
+reenlist
+referents
+reflexively
+reflexivity
+refract
+refugium
+regressed
+rehearing
+reheated
+rehouse
+rehoused
+reification
+reignite
+relabeled
+relinquishment
+relives
+remanence
+renegades
+repairer
+repatriating
+reportable
+reputational
+requester
+requestor
+reranked
+reread
+resentencing
+reshuffles
+resinosa
+resistanceassociated
+resoundingly
+restocking
+resuscitate
+retarding
+retested
+retinoids
+retrain
+retroreflective
+reunify
+reverseengineering
+rewiring
+rhetoricians
+rhizosphaerae
+rhombifolia
+ricebased
+rickety
+ridgelines
+righttowork
+ritonavir
+roadracing
+roadrunner
+roared
+rockslides
+rocksynthpop
+romanisation
+rookeries
+rootless
+rosadoi
+rosenbergi
+rossa
+rotogravure
+roundels
+roundly
+roundnecked
+roundtrips
+roused
+routings
+rowboat
+rowboats
+rubblestone
+rubicundus
+rucksack
+rufescent
+rufitarsis
+rufousbreasted
+rufouscrowned
+rufousnaped
+rufousthroated
+rutila
+sagar
+saka
+salinus
+sallfly
+salva
+sanguine
+sanitize
+sanitizing
+saprotrophs
+sarawakensis
+sarda
+sardars
+sarong
+satellitedelivered
+satiation
+saucepan
+savignyi
+scallions
+scantilyclad
+scapulars
+schnapps
+sconces
+scopulorum
+screwdriven
+scrubfowl
+scullery
+sd
+sealift
+seaming
+seawalls
+seconddivision
+secondgrowth
+secondleading
+sedativehypnotic
+seebohmi
+seers
+selfadvocacy
+selfassemble
+selfdeception
+selfexecuting
+selffertile
+selflearning
+selfmonitoring
+selfperpetuating
+selfrun
+semicha
+semicustom
+semioctagonal
+semisoft
+semitone
+semitones
+sequitur
+serendipity
+serpulid
+servlets
+sesquiterpenes
+setaside
+settlors
+sevenbay
+seventeenyear
+seventyeighth
+seventysecond
+severally
+sexspecific
+seychellensis
+shamisen
+shapers
+shareable
+shelfstable
+sherbet
+shim
+shiur
+shockingly
+shoeing
+shoplifter
+shuttering
+sickles
+sideshows
+sideview
+sig
+signora
+sika
+simmer
+simony
+singersongwritermultiinstrumentalist
+singlecarriageway
+singlechannel
+singlecore
+singlemasted
+singlestagetoorbit
+sinkings
+sip
+sirventes
+sisterships
+siteswap
+siva
+sixbook
+sixpoint
+sixthgrade
+sixtyninth
+sixtyseventh
+skylines
+slaked
+sleaze
+sleightofhand
+slicker
+slop
+sluggers
+slurred
+smallcap
+smoothscaled
+snags
+snailfish
+snailfishes
+snipes
+snob
+socialemotional
+sodalite
+soldierfish
+soldiering
+solidi
+solstitialis
+solvation
+somatoform
+sonority
+sonsinlaw
+sorption
+soulmate
+soundbites
+spanner
+sparkle
+sparser
+spatulata
+speciesism
+speedways
+spelaea
+spelaeus
+speleothem
+spermatids
+spermatophore
+spermidine
+spicebush
+spiralshaped
+splat
+splendidly
+spoked
+spooling
+spoonerism
+sprayers
+spraypainted
+springbeauty
+springflies
+squeegee
+src
+stagflation
+stannary
+stardust
+startlingly
+statelessness
+statics
+steakhouses
+steatosis
+steelblue
+stepfamily
+stereospondyl
+stevedore
+stevedores
+stevia
+stickiness
+stictica
+stimulators
+stipendiary
+stipularis
+stnd
+stoats
+stonethrowing
+storerooms
+strafed
+streaky
+streptomycete
+stricture
+striolatus
+strop
+structurefunction
+stylesheet
+stylosa
+subcamp
+subcastes
+subdirectory
+subdwarf
+subheadings
+sublaevis
+sublanguage
+subsector
+subsidiarys
+subsidizes
+subspeciality
+subsuming
+subtask
+subtehsil
+subtler
+subtopic
+subtributary
+suburbanized
+subzones
+sucralose
+sulfation
+sundews
+superbum
+superciliosus
+superheaters
+superheterodyne
+superimposition
+supermodels
+superphylum
+supersaturated
+supershow
+supra
+sus
+swatting
+sweatshops
+sweetest
+sweetgrass
+swerve
+swingometer
+swordlike
+swordsmen
+syllabics
+sylvanus
+synchromesh
+synchronism
+synchronizer
+syncline
+syncs
+syndicators
+syntagmatic
+systemd
+taconite
+tacrolimus
+tafsir
+tailbone
+tailending
+talkers
+taming
+tapa
+tarns
+tarp
+tarpaulin
+tat
+tatei
+taxfunded
+taxol
+teals
+tearooms
+technologys
+teepee
+teff
+teleconference
+televangelists
+tellall
+tennesseensis
+tenpiece
+tenrecs
+tenvolume
+terabyte
+tercentenary
+terrarium
+terre
+tertia
+tesserae
+tessitura
+testaceous
+testretest
+tetani
+tethers
+tetragona
+tetrodotoxin
+thailandica
+theae
+theaterintheround
+thenChief
+thenPremier
+thenVice
+thenchampion
+thendominant
+thenfledgling
+thenmanager
+thenowners
+theosophist
+thereabouts
+thermomechanical
+thermowell
+thgeneration
+thine
+thiopental
+thirdbiggest
+thirdrail
+thistletail
+thoroughness
+thrashers
+threedaylong
+threedomain
+threeheaded
+threemeter
+threepointers
+threewheel
+thrombolytic
+throughcomposed
+tidytips
+tiedown
+timecritical
+timeresolved
+titulus
+toadheaded
+tobogganing
+tobyMac
+togetherness
+toiled
+toiling
+tonguesole
+tonkinea
+tonsillitis
+toothcarp
+toothpicks
+tortfeasor
+tortilis
+tortuosa
+totemic
+toto
+toucanet
+touchline
+towerhouse
+traditionalstyle
+trama
+tramcar
+tranches
+translucency
+transsexuality
+transurethral
+trapezius
+travesty
+treading
+trekked
+tremble
+tremuloides
+trenchant
+trialist
+tribally
+tribunate
+tricarinata
+trifluoromethyl
+trimers
+trimulcast
+tripleA
+trogons
+tropism
+tropomyosin
+troubleshooter
+truncatula
+tuftedtailed
+turmoils
+turnbyturn
+turnings
+tweeter
+twicedaily
+twoandahalfyear
+twobanded
+twohander
+twohundred
+twoline
+twolobed
+twostriped
+twowire
+typologies
+uhleri
+ultima
+ultradistance
+ultramarathoner
+ultraprominent
+umbilici
+unadjusted
+unattributed
+unbranching
+unbundling
+uncommercial
+uncommitted
+uncontaminated
+unconverted
+undatus
+underdiagnosed
+underestimation
+underfloorengined
+underinsured
+underwings
+undressing
+unforeseeable
+unhygienic
+universityowned
+unknowing
+unmarketable
+unmastered
+unmentioned
+unpacking
+unperformed
+unpigmented
+unpressurized
+unrealised
+unrehearsed
+unschooling
+unscored
+unsolvable
+untethered
+upcountry
+uplisted
+upperincome
+upstanding
+urbanites
+urbansuburban
+urease
+urens
+ureteric
+url
+urticae
+usenet
+userdriven
+usurpers
+utters
+vaccae
+vaginitis
+valerate
+valvata
+vancomycinresistant
+vandal
+varicornis
+vectorborne
+vedotin
+veiny
+velomobile
+veneered
+ventricosus
+verapamil
+vesicant
+vetiver
+vici
+vicinal
+vicinus
+victimless
+vies
+vigas
+vinblastine
+vindication
+vingtaines
+virga
+virginiensis
+virtuosos
+vis
+visualizer
+vitrified
+vloggers
+vocalistlyricist
+voce
+volutions
+voxelbased
+vtm
+wad
+wagered
+waistcoats
+wallpaintings
+warbird
+warbirds
+warble
+wardrobes
+warfighter
+wastegate
+waterfronts
+waterweed
+wavyedged
+weavings
+weberbaueri
+weblike
+wellcovered
+welllit
+wellplaced
+wellrepresented
+westernthemed
+westflowing
+wheelchairusing
+whippet
+whitedominated
+whitenaped
+wickhami
+wides
+wiggle
+wilfully
+williams
+willistoni
+winghalf
+winkleri
+winks
+wolffish
+wolfs
+woodnymph
+woolens
+wordprocessing
+workability
+worldchampion
+wracked
+wrapup
+wrathful
+wreaked
+wrecker
+wrighti
+writeoff
+writerdirectorproducer
+wt
+xiphosuran
+xxmember
+xylanolytic
+yangi
+yearending
+yellowhammer
+yeomen
+yogis
+zVM
+zeolite
+zygotes
+abdita
+abditus
+abducts
+abet
+abietis
+ablated
+abraded
+abridgements
+abscission
+absinthium
+abugida
+abugidas
+acanthodians
+acellular
+acetals
+acnes
+acquisitive
+acral
+acromegaly
+acroporid
+acta
+actio
+adamantly
+adamsi
+addictiveness
+adipic
+adnexa
+adsorbents
+advection
+advertisingfree
+aestiva
+aforethought
+afromontane
+afterdeck
+afterload
+agonism
+agonistantagonist
+agoseris
+agroecosystem
+agroecosystems
+airbrakes
+airbrushing
+aiyl
+alAhmar
+alArabi
+alHashimi
+alMutawakkil
+alZubayr
+alas
+alboguttatus
+albomarmorata
+albrechti
+alexia
+alighted
+alkylated
+allay
+allcomposite
+allimportant
+allmusic
+allornothing
+allwheeldrive
+allwomens
+allwood
+aloes
+alps
+alwayson
+amentum
+amethystina
+amica
+amidohydrolase
+aminosteroid
+aminoterminal
+ammunitions
+amnestic
+amnicola
+amphisbaenian
+amplexus
+amputate
+amseli
+amt
+anagenesis
+anaplasmosis
+ancora
+angelshark
+anglerfishes
+anglewings
+angulicollis
+angustifolius
+aniconic
+animalderived
+animationrelated
+animestyle
+ankylosaurian
+anni
+annotates
+anonymization
+antagonizes
+anthesis
+anthologys
+antiCD
+antiLabor
+antiSikh
+antiausterity
+anticapitalism
+anticyclonic
+antidotes
+antidumping
+antimateriel
+antiobesity
+antioquiensis
+antipoaching
+antipodum
+antiprostitution
+antipsychiatry
+antizyme
+antv
+anxiously
+aphelion
+apolipoproteins
+appeasing
+appendicular
+appreciations
+aptamer
+apterous
+aquatilis
+aquifolium
+arachidis
+archaeogenetics
+architraves
+archiveorg
+archosauriforms
+areabased
+areolatus
+aril
+arisan
+arista
+armrests
+arraignment
+arresters
+arteriosclerosis
+articulators
+artpop
+artstyle
+asanas
+asbestosrelated
+ascus
+asocial
+assaying
+assumpsit
+asthenosphere
+astrobiologist
+astronomically
+astroturfing
+atabeg
+atavus
+attenuators
+attila
+attractors
+audiological
+audiologists
+auditable
+augurs
+aurantiacum
+aureum
+auripes
+autogas
+autoloader
+automobilia
+autoreceptors
+autostereoscopic
+avex
+avialans
+awned
+axion
+azhwars
+azoospermia
+azurite
+bachelorette
+backfilled
+backless
+backstop
+bacteriochlorophyll
+bagger
+bailee
+bakso
+balfourii
+baling
+balloonist
+bankrolled
+barbadensis
+barbarous
+barbeque
+bareeyed
+barometers
+bartschi
+basilicum
+basmati
+bast
+beachgoers
+beachwear
+bearskin
+beata
+behaviorally
+belaying
+bellula
+belowgrade
+beltfed
+benandanti
+bender
+benthamii
+benzamide
+berms
+bernardi
+bestial
+betaB
+betaglucosidase
+betapropeller
+bettong
+bevels
+bhaji
+biblically
+bibliometrics
+bicommunal
+bicyclefriendly
+biennale
+bifidus
+bigcity
+bihar
+bijective
+billbug
+bioaccumulation
+bioculata
+biogeochemist
+biomineralization
+bionanotechnology
+bioturbation
+bipedalism
+birdwatcher
+birthweight
+bistort
+blackbrowed
+blackburni
+blackcaps
+blackchested
+bladelike
+blading
+blanca
+blaring
+blastema
+blockchainbased
+blockship
+bloodstain
+blowbackoperated
+blowhole
+bluishwhite
+boatshaped
+bobble
+bock
+bocourti
+bodyboard
+bohemians
+bola
+boliyan
+bombardiers
+booklouse
+bookpublishing
+borane
+boranes
+borg
+borides
+bowfin
+boyar
+brachypterus
+brachyura
+brainspecific
+brainstorm
+breadmaking
+bristlecone
+broadshouldered
+broadwinged
+broilers
+bromelain
+brotherinlaws
+brownishorange
+budgerigars
+buffthroated
+bugeyed
+bupropion
+burbot
+burette
+burlesques
+buttercream
+buxom
+buzzwords
+cDc
+cabooses
+cachexia
+cadenzas
+caduceus
+caelatus
+caesarian
+cajan
+calcifications
+calida
+calmodulinbinding
+camelids
+canastero
+candlelit
+canina
+canvassers
+capitatum
+caprock
+capucinus
+carbolic
+cardioversion
+cardrooms
+carhouse
+carnelian
+carnosus
+carota
+carotene
+carpetlike
+carpometacarpal
+carrack
+carriergrade
+cartoonstyle
+casebased
+cassiterite
+castmates
+casus
+catabolite
+catalepsy
+catapulting
+catechesis
+catenata
+cauliflora
+cauline
+causewayed
+cavea
+celandine
+celata
+celer
+cellulases
+centrale
+centromeric
+cephalometric
+cerebri
+ceruminous
+cerussite
+cervicalis
+chaebol
+chairmans
+chandlers
+characterisations
+chargesheet
+charioteer
+chasuble
+chatbots
+chauffeurs
+chaus
+chav
+cheapness
+cheaters
+chelator
+chemoinformatics
+chemoprevention
+chiasm
+chinch
+chinchillas
+chiptunes
+chiricahuae
+chitosan
+chlorophenol
+chocolatebrown
+cholecystectomy
+chome
+choreographies
+christyi
+chromatophores
+chromogenic
+chronostratigraphic
+chronostratigraphy
+chucks
+churns
+ciliatum
+cinclodes
+cinnabarina
+cinnamic
+citalopram
+citrinin
+cladistically
+classe
+clathratus
+cleome
+clientelism
+clinopyroxene
+cloakroom
+closerange
+closers
+clubman
+cluboriented
+cmyc
+coaccused
+coanchoring
+coatis
+cocaptains
+coccidiosis
+cockatiels
+cocommissioned
+codebooks
+codesign
+codiscovery
+codrove
+coelurosaurian
+coexpressed
+coffeae
+cofrontman
+cogenerates
+coldhardy
+coleads
+colensoi
+collegia
+colorcoding
+colorimetric
+columbarium
+columnoriented
+comedones
+comedyaction
+comedyfantasy
+comity
+commendatory
+commercialresidential
+commissaries
+comorensis
+comosus
+compensations
+componentry
+compulsively
+conceives
+conceptbased
+concessional
+concoctions
+condescending
+condolence
+condominia
+condone
+condylar
+confectioneries
+confessors
+confided
+config
+confiscations
+confluences
+congdonii
+congenitally
+conifera
+conned
+consequentially
+consonantal
+consumerbased
+contactors
+contaminates
+contemptible
+contextualism
+contextualize
+contextualizing
+contortrix
+contraindication
+convolutional
+convolutions
+coofficial
+cookei
+copal
+copastor
+copperhead
+copublishing
+cora
+coreceptors
+coriaceum
+corniculatus
+corrie
+corruptions
+corticobulbar
+corticotropin
+corticotropinreleasing
+coscored
+cosmopolitanism
+costate
+costimacula
+cottonwoods
+coua
+couched
+coumarins
+councilmen
+counterclaim
+countercult
+counterfeited
+counterfeits
+countershading
+countyequivalents
+courageously
+coveralls
+coverart
+covermount
+coxswains
+crepitans
+crescentic
+cretica
+crimeaction
+crispata
+crispum
+croon
+crossbars
+crossbrowser
+crossconnect
+crossexamine
+crossfunctional
+crosshatching
+crossreactivity
+crownofthorns
+cruciferous
+crucis
+crural
+cryotherapy
+cryptologic
+cryptozoological
+cubicle
+cubist
+culls
+curettage
+curtainraiser
+curvata
+cuskeel
+custodianship
+cutgrass
+cyanidin
+cyanotic
+cybersex
+cyclitol
+cyclol
+cyclopentane
+cylindershaped
+cyproterone
+cyrtocones
+cytoprotective
+dAzione
+dBASE
+dElsa
+dHarcourt
+dItalie
+daenggi
+dala
+dalla
+damsels
+dangerousness
+darkveined
+darwiniensis
+daytimer
+dblock
+deLillos
+deaconess
+decamped
+decibel
+deckhouse
+declarers
+declensions
+decoherence
+decussate
+deducing
+dee
+deemphasize
+deepfrying
+deflagration
+deitys
+delator
+deliberating
+delimiter
+dellAquila
+dellintervallo
+deltoides
+deltopectoral
+demountable
+dene
+denervation
+denialist
+denitrifying
+denominate
+densiflorum
+dentipes
+denuded
+deorbit
+depositor
+deprecating
+depthfirst
+dermatome
+desensitized
+desertdandelion
+destructiveness
+devas
+devours
+deworming
+dextran
+dhow
+diable
+diabolica
+diaconal
+dialectics
+dialin
+diaphysis
+dichroa
+diehli
+dieoff
+diffusers
+digitizes
+dilatatus
+dinein
+dioxygen
+diphtheriae
+directtofan
+dirge
+disaccharides
+discophora
+discopop
+discretely
+disembarkation
+disincentive
+disobey
+dispersa
+disruptor
+dissections
+dissorophoid
+distaff
+distinctives
+disulfides
+div
+divinatory
+divisus
+documentaryreality
+doer
+doldrums
+dollhouses
+dolosa
+domiciliary
+dominicanus
+dorsiflexion
+dosed
+dpkg
+dragger
+drinkable
+drinkdriving
+drooling
+droving
+drumset
+dryad
+dryandra
+drywood
+dubiously
+duckling
+dukedoms
+duplicator
+dvinosaurian
+dynamicist
+dysplasias
+dysprosium
+dystrophies
+eastsoutheastern
+ebXML
+ebenus
+eberti
+eccentrically
+echinococcosis
+ecolabels
+economywide
+ecophysiology
+ecosocialist
+educationalists
+ee
+eerily
+effused
+effusions
+egovernance
+eightbar
+eightfoot
+eighthgrade
+eightsong
+ejects
+ejournals
+elastically
+elastics
+electronicarock
+electronwithdrawing
+electrooptic
+elegantulus
+elementarymiddle
+elitelevel
+ellagitannins
+emarginate
+embargoed
+emeryi
+emigrates
+emulsification
+enablers
+enamine
+encrustations
+encumbrances
+ender
+endo
+endocrinologists
+endophthalmitis
+endorsers
+enlargers
+enormity
+enshrine
+entranced
+entrench
+ephemerides
+ephippium
+epiblast
+epicenters
+equivalences
+erases
+erica
+eruv
+erythrothorax
+ethnogenesis
+ethnohistory
+euptera
+ev
+eva
+eventrelated
+everevolving
+exCEO
+exchairman
+excons
+exemployees
+exfootballer
+exguitarist
+exhorting
+exigent
+exiguum
+exopolysaccharide
+exotoxins
+expectorant
+explayers
+extendedrange
+extrabase
+extroverted
+eyepieces
+eyestripe
+fabula
+facetious
+facetiously
+faceup
+factorbinding
+factorybacked
+faecium
+fairtrade
+falconeri
+fandoms
+fansites
+fantasyaction
+farefree
+farfetched
+farinfrared
+farmworker
+fasciculatus
+fasttalking
+fava
+fecaloral
+feebleminded
+feign
+felid
+felting
+femtocell
+fermentable
+fermionic
+fernandezi
+festooned
+fewflowered
+fibrosa
+fidelis
+fifers
+fiftholdest
+fifthround
+filbert
+filibustering
+filum
+finfish
+fipple
+fireresistance
+firethorn
+firmament
+firstpass
+firstpreference
+firstprinciples
+firth
+fistball
+fistfight
+fitzgeraldii
+fivestage
+fixedlength
+fixedrate
+flabellata
+flailing
+flamboyance
+flankers
+flasher
+flatly
+flavolineatus
+flavosignata
+flayed
+flexuous
+florensis
+floridanum
+flowerbeds
+fluorspar
+flutamide
+flyingfish
+fody
+fogs
+folium
+folkinspired
+folksonomy
+footbag
+footballsoccer
+footballthemed
+footstool
+forager
+foraminiferans
+forayed
+forego
+foremen
+forenames
+forestation
+forhire
+fori
+formosensis
+fortuneteller
+forwardline
+fourangled
+fourcounty
+fourinarow
+foveolata
+foxy
+franchiser
+fratelli
+fraying
+freakbeat
+freerider
+fro
+froggatti
+frostfree
+frosti
+fruitpiercing
+fuelwood
+fufu
+fullbacklinebacker
+fullface
+fullon
+fullthickness
+fulminant
+fulvicollis
+fumipennis
+furans
+gadwall
+gagging
+galapagensis
+galaxiid
+gallantly
+galleried
+galvanometer
+gameday
+gammaaminobutyric
+garciai
+garleppi
+gasdischarge
+gastald
+gastroenterologists
+gasturbine
+gat
+gaudichaudii
+geisha
+gemologist
+genderfluid
+genderrelated
+generales
+geneticallyengineered
+gentrifying
+genuineness
+geocaching
+geocast
+geoglyph
+geomatics
+geophytes
+georgei
+geoscientists
+geosocial
+gestalt
+gestroi
+gether
+ghosting
+gigantocellular
+gimbal
+givers
+glasswort
+glidepath
+gloved
+glucosides
+glycobiology
+gnatcatcher
+goa
+goaloriented
+goddesss
+godmani
+goldenmantled
+goodlooking
+goodquality
+gopuram
+gotta
+governmentsanctioned
+governmentsupported
+graciously
+gradesharing
+granddaddy
+granges
+grantfunded
+granum
+graphbased
+graphicsbased
+grappa
+grapples
+gratuities
+gravure
+graywhite
+grazer
+greasewood
+greatgrandfathers
+greenbanded
+greenlighted
+greenscreen
+gregorii
+greyscale
+griffon
+grippers
+griseata
+grommet
+groomers
+grossepunctata
+groundings
+groundup
+groupbased
+groupies
+grunting
+guajava
+guarana
+guestrooms
+guitarheavy
+guitarled
+guitarsvocals
+gulp
+gunplay
+gunsmiths
+gurnard
+guttulatus
+habituated
+haematobium
+haematoxylin
+hailstorms
+hairdo
+hairstreaks
+hairyfooted
+hairytailed
+halfcourt
+halfling
+halfsisters
+hamate
+handbell
+handbills
+handcraft
+handcut
+handguard
+haptics
+harbourfront
+harkens
+harpoons
+hatOLOGY
+hatchets
+hatter
+headboard
+healthpromoting
+heathy
+heatstable
+heattreated
+hecklers
+heeded
+heeled
+hepatomegaly
+heptagonal
+heringi
+hermeneutical
+herpesviruses
+hesperiid
+hessian
+heterodyne
+heteronormative
+heterosis
+heterothallic
+heterotypic
+heterozygosity
+heures
+hexameric
+hh
+hians
+higherperformance
+higherprofile
+higherseeded
+highestresolution
+highfin
+highprotein
+highschools
+hijinks
+himalayensis
+hiragana
+hirasei
+hirtella
+historia
+historiated
+historicalcritical
+historymaking
+hitmakers
+hitmaking
+holdovers
+holdup
+holospira
+homecourt
+homemaking
+homepages
+homer
+hominem
+homme
+homogeneously
+hondurensis
+hoppy
+horsfieldii
+horst
+horticulturalists
+hostesses
+hotkeys
+houraday
+houseboats
+housebuilder
+huachuca
+huegelii
+humanrobot
+humansized
+humantohuman
+humanus
+humifusa
+hundredyear
+hybridity
+hydrangea
+hydrophones
+hydroxamic
+hydroxyzine
+hygroscopicus
+hymenophore
+hyperopia
+hyperplane
+hypertelorism
+hyperythra
+hyphenation
+hypnotics
+hypolipidemic
+iMessage
+iN
+iPlanet
+ibogaine
+ic
+icestrengthened
+icterid
+idempotent
+identifiably
+ideographic
+idiophones
+idli
+idyll
+iguanians
+ileocolic
+illuminance
+illwill
+imaginal
+imbibed
+imbibing
+immaculatus
+immunize
+imp
+impala
+imperialists
+impossibly
+impressus
+impugned
+incentivized
+inclass
+inclusively
+inconveniences
+incrassata
+indazolebased
+indicting
+indiepunk
+indiscretions
+indult
+indumentum
+indurated
+industrializing
+infarct
+infinitedimensional
+inflatables
+inflationadjusted
+infraorders
+infringers
+ingrown
+inhalational
+innominata
+innominate
+inquirybased
+inscribe
+instanton
+interconfederation
+internists
+interorganizational
+interposition
+interstitials
+intertexta
+intragroup
+iodinated
+iriver
+ironbased
+ironbinding
+irrigating
+irritates
+isoelectronic
+isoquinoline
+itThe
+italicum
+itzingeri
+ivories
+iwan
+jackhammer
+jarl
+jaunt
+jaunty
+jetpack
+jetski
+jewelrymaking
+jobbers
+jointer
+joules
+joust
+juggles
+jujutsu
+jumpoff
+jumpstarted
+juno
+juvenilia
+kalos
+kamacite
+kambing
+kampong
+karoo
+kashmir
+kati
+keepsake
+kefir
+kenong
+keratoderma
+kernelmode
+ketupat
+khyal
+kilometres
+kittiwake
+kivas
+kneaded
+knocker
+knowable
+koel
+kombucha
+kookaburra
+kozlovi
+kulit
+kurodai
+kyung
+labral
+lacera
+lacquerware
+lacteata
+laevipes
+lambasted
+lambic
+lamblia
+lamellophone
+landscapers
+laner
+lanolin
+lanternshark
+lappaceum
+lar
+latae
+latecomer
+latemodel
+laterals
+latifolius
+latitudelongitude
+latrans
+laughingthrushes
+lawgiver
+laxum
+leapfrog
+leaseholders
+leastknown
+lefthanders
+leiomyomas
+lensshaped
+lenta
+lepidosaurs
+lepospondyls
+lepton
+lethargic
+letterwriting
+leucocephalus
+leucura
+leukoplakia
+lexeme
+libavcodec
+libertine
+lichenised
+lifeaffirming
+lifesavers
+lifeways
+lignaria
+ligne
+lignins
+liminal
+limosus
+linamarin
+lineare
+linearization
+lineitem
+lingulata
+lipoamide
+lipolysis
+liquidations
+liquidfueled
+literalism
+lithophyte
+liveable
+loantovalue
+lobate
+lobelioid
+lockets
+locution
+logicbased
+logograms
+longclaw
+longforgotten
+longispinus
+longissima
+longitudes
+longspined
+longspur
+lorikeets
+lovegrass
+lowcarbohydrate
+lowfare
+lowfi
+lowmolecularweight
+lowpaid
+lowvalue
+lowveld
+ls
+lubricate
+lubricity
+luctuosus
+lumens
+luminaires
+lunacy
+lunchroom
+lungfishes
+lunular
+lutung
+luxe
+luzonicus
+mR
+mV
+macrocycles
+macropods
+macroptera
+macrostoma
+macroura
+macularis
+madras
+madrasas
+maestros
+magdalenae
+magicJack
+maiming
+majorparty
+majuscula
+makings
+malingering
+malonate
+mandamento
+mangos
+manholes
+mantlet
+marinemollusks
+maroons
+marquisate
+marshloving
+martyrology
+marvels
+masa
+masculinities
+masoni
+masqueraded
+masseteric
+masterplanning
+masterslevel
+mastheads
+matriarchy
+mau
+mauritanica
+maynei
+mayri
+meadowgrass
+mearnsii
+meatbased
+medicalization
+mediumsmall
+meetup
+megacarpa
+megalops
+melania
+melanism
+melanocortin
+melanosis
+melanoxylon
+mellea
+memberschool
+membersupported
+memorability
+memoriae
+memorymapped
+menaces
+mentalities
+menubased
+mercer
+mesencephalic
+mesoregions
+mesothorax
+metacarpophalangeal
+metaloriented
+metamodeling
+metapuzzle
+metonymic
+mgkg
+miasma
+mich
+michaelseni
+microcirculation
+microcline
+microlithiasis
+microphthalmus
+microprobe
+microregions
+microscopist
+microsimulation
+microsomes
+microsporidian
+microstate
+midbody
+midflight
+midlatitudes
+midtwentiethcentury
+millefolium
+mils
+mimula
+mindanensis
+minifootball
+minion
+minorkey
+misattribution
+misfolding
+mislabeling
+mispricing
+misprision
+misquoted
+missionbased
+mitts
+mixdown
+mixedtraffic
+modelmaker
+modernizations
+modillion
+modularized
+molted
+moneyed
+monodon
+monogenic
+monoplacophorans
+monopole
+monsoonal
+moolavar
+morbidly
+morganatic
+motorgliders
+mottes
+mountable
+mouthing
+mps
+mr
+muchbranched
+mucinosis
+mucoid
+mukims
+multiflorum
+multigabled
+multimachine
+multipartisan
+multiprogramming
+multirotor
+multisource
+multisyllabic
+multitiered
+multivesicular
+musicvideo
+mustachioed
+mustcarry
+mutilating
+mutuals
+mynas
+myops
+myrsinites
+mysticete
+mz
+naivety
+nappies
+narrowboat
+narrowmouth
+nasociliary
+nat
+nationallyrecognized
+naumanni
+nearcoastal
+nearpasserine
+necrotrophic
+nectridean
+nemertean
+neoclassicalstyle
+neoromantic
+neritic
+netCDF
+nettop
+neuro
+neurodiversity
+neurotensin
+newgrass
+newlybuilt
+newness
+newtoni
+ngmoco
+nicest
+nickelplated
+nicks
+nictitating
+nielseni
+niente
+nightspots
+nigrans
+nigrofasciatus
+nigromaculatus
+nigrovittata
+nimbus
+nineteenyear
+nobudget
+noctule
+nocturnally
+nohit
+noisemakers
+nolonger
+nonCommunist
+nonFIFA
+nonIBM
+nonLatin
+nonbonding
+noncompetition
+noncrystalline
+nondrafted
+nonhomogeneous
+nonincumbent
+noninternational
+nonlawyer
+nonmoving
+nonneurotoxic
+nonnews
+nonobvious
+nonplanar
+nonreactive
+nonregistered
+nonrelational
+nonrival
+nonscientists
+nonsterile
+nonstudent
+nontextual
+nontraumatic
+nonvascular
+nonviral
+nonword
+noseband
+nosy
+notchback
+nothobranch
+nourishes
+novelised
+novicida
+nowdeceased
+nuclearcapable
+nuclearfree
+nucleogenic
+nulls
+nyctosaurid
+oathtaking
+obliques
+oblita
+obliterate
+obscenities
+obscurely
+obsoleted
+obtusata
+occhi
+odora
+offbalance
+offboard
+offcolor
+offends
+officious
+offisland
+offoffBroadway
+offthegrid
+offworld
+ogee
+ogres
+oinochoai
+okmotu
+olsoni
+oncedaily
+oncepopular
+onduty
+oneacre
+onelevel
+onenightonly
+onenorth
+oneteacher
+onground
+onhand
+onium
+ons
+onside
+ontime
+oops
+oospores
+opals
+ophthalmopathy
+opsis
+optima
+opulenta
+orbweb
+orc
+ordains
+ordinaria
+orebody
+oreophila
+oreophilus
+ornis
+oropendola
+orthopedist
+orthophoto
+osculating
+osseointegration
+osteological
+ostomy
+otoliths
+otorhinolaryngology
+outflanked
+outgroups
+outgrowing
+outmaneuver
+outmaneuvered
+outpointed
+outranked
+outspokenness
+outstations
+overburdened
+overgrazed
+overheat
+overpriced
+overspending
+overstepped
+overstretched
+oversupply
+ovine
+oviraptorid
+oximetry
+oxo
+padi
+palatability
+palates
+palatina
+palazzi
+paleyellow
+palmfly
+pampered
+pandemocracy
+pandering
+panspermia
+papa
+papain
+pappi
+paralogues
+parameterization
+parapsychological
+parasailing
+parched
+parenthetical
+partido
+passwordprotected
+pathogenassociated
+patientspecific
+pauperis
+paychecks
+pe
+peacemakers
+pearlbordered
+pecked
+pectineus
+pedagogues
+peeks
+pemphigus
+penalizing
+penology
+pentagona
+pentane
+pentavalent
+pentellations
+penthouses
+pentoxide
+peopletopeople
+perceiver
+perceivers
+perigynium
+perilymph
+perioral
+perplexed
+pervert
+pes
+petiti
+pharming
+phaselocked
+philanthropies
+phonetician
+phospholipases
+phosphoproteins
+phosphorescence
+phosphoribosyltransferase
+photobased
+phrenology
+phytoremediation
+phytosanitary
+picea
+pigeonholed
+pigtails
+pileipellis
+pining
+pinpointed
+pipiens
+pipped
+piqued
+piratical
+pitman
+pitohui
+pixelated
+pkg
+placodonts
+plaited
+planarity
+planteating
+plantlets
+plastically
+platting
+platysma
+playbook
+playercontrolled
+plcs
+plebejus
+pleiotropy
+pleurae
+plexiglass
+plowman
+pluvialis
+podocytes
+poeciliid
+pointscore
+pointscoring
+politicizing
+polkadot
+pollsters
+poloidal
+polycarbonates
+polyimide
+polymerizations
+polyvalent
+pondered
+popolo
+poppers
+populaire
+porcelaneous
+porin
+porno
+porticus
+posetal
+postbox
+postimpressionist
+postmarked
+postmerger
+postpunknew
+potto
+powerlessness
+ppt
+practicum
+praestans
+praomys
+preCrisis
+precocial
+precomputed
+predatorprey
+predisposing
+preelementary
+prefatory
+preflight
+preheated
+premillennial
+premotor
+prepay
+prerRNA
+prescientific
+preservational
+preservice
+pretax
+preussi
+preventer
+pricklypear
+primatial
+primigenius
+printout
+prizegiving
+producerrapper
+professionalquality
+proffer
+progestogenic
+prohibitionist
+proleptic
+prolixa
+promethium
+pronggilled
+proofreader
+proprietorships
+proscriptions
+proselytism
+prostatespecific
+proteinligand
+protozoal
+provers
+psalters
+pseudanthium
+pseudoacacia
+pseudoautosomal
+pseudosuchians
+psychoacoustic
+psychoeducational
+psychotherapies
+publ
+publiclyfunded
+pubrestaurant
+pulsecode
+puniceus
+punkhardcore
+purism
+puritanical
+purplegreen
+purpletinted
+purpuratus
+purveys
+pushbuttons
+pustulosa
+putti
+pyelogram
+pyelonephritis
+pyramidshell
+pyruvic
+qibla
+quadband
+quadrangularis
+quadrat
+quadratojugal
+quadrifolia
+quercifolia
+quesadillas
+quickrelease
+quicksort
+quinta
+quintile
+rabble
+racemization
+radarguided
+radiolabelled
+radioman
+radiotelephony
+radiotracer
+ragazza
+railbed
+rainmaker
+ramosus
+rancid
+randalli
+ransacking
+ranting
+rasping
+rattus
+rayless
+rc
+realizable
+reaped
+rearprojection
+reasserting
+rebalanced
+rebated
+rebrands
+rebuttals
+receiverlinebacker
+receptorbinding
+recharges
+recirculated
+reclassifying
+reclined
+reconfiguring
+reconvening
+rectilinea
+recto
+redbordered
+redcrowned
+redeposited
+redpolls
+redressed
+redshanks
+redveined
+reeled
+reenters
+refashioned
+referrer
+refinanced
+regionalization
+regulative
+rehashed
+relaxants
+reliving
+remediated
+remount
+renamings
+renminbi
+renominate
+reoccupy
+reparative
+repertories
+repopulate
+reprogram
+reschedule
+rescored
+residentialcommercial
+resister
+resize
+resprouting
+restive
+retable
+retouched
+retranslated
+retroreflector
+returners
+revelator
+revelry
+rewinding
+rhamnose
+rhinovirus
+rhomboidal
+rhombus
+ria
+ribeye
+rideon
+ridgetop
+ridiculously
+rightside
+rigidum
+rimu
+roadhouses
+roadless
+roadshows
+roars
+rockdwelling
+rockelectronic
+rockface
+rockformatted
+rollerskating
+rollup
+ropax
+rostered
+rostratum
+rothi
+roughskinned
+roughwinged
+roundness
+routable
+roux
+rprocess
+rubellus
+ruffed
+rugifrons
+runt
+rupestre
+rushers
+rushreleased
+russula
+rusticana
+rustler
+rytas
+sabbath
+sabertooth
+sabrewing
+saccadic
+sacramentary
+sacredness
+sacrorum
+saddest
+saddleback
+saguaro
+salps
+saltans
+salutatorian
+sanctaecrucis
+sancti
+sandlot
+sanitorium
+saprobes
+saprophyte
+sapwood
+sara
+sarasinorum
+sarmentosa
+saturate
+saundersii
+sauropterygian
+sauros
+savagei
+saxicolous
+scalariformis
+scalpels
+scalping
+scaphopods
+scatterable
+schiedea
+schneideri
+schoolfriend
+schottii
+scintillators
+sclerophylla
+screencasting
+screwpropelled
+scribbled
+scrim
+scriptura
+scuffles
+scuppered
+seagrasses
+seastar
+secondbottom
+secretagogues
+sectorspecific
+secundus
+seedstage
+seeping
+selfcriticism
+selfdefinition
+selfeducation
+selfidentifies
+selfinterested
+selflessness
+selfmotivated
+selfmutilation
+selfowning
+selfpity
+selfunderstanding
+selloff
+semihit
+semipermanently
+semitropical
+sensitizer
+sententiae
+senza
+serendipitous
+seriocomic
+serjeantatlaw
+serpens
+serverless
+servicemember
+sessilifolia
+seung
+sevenbook
+sevencard
+sevenhour
+sevenperson
+seventyfirst
+seventyninth
+sevenwicket
+sexbased
+sexselective
+sexting
+sexualised
+shakti
+shallowest
+shapeless
+shatters
+sheikhs
+shelllike
+shifty
+shikhara
+shims
+shipmates
+shiptoship
+shiurim
+shive
+shola
+shortbarreled
+shortcrust
+shorthold
+shoulderwing
+shoveler
+showjumper
+shreds
+shri
+shrikebabbler
+shriveled
+shuffleboard
+shuttlecraft
+siRNAs
+sickly
+sidecars
+sidepassage
+sidequests
+sidetank
+sidetracked
+sidevalve
+sift
+signallers
+signalosome
+signum
+silverback
+silverygray
+silyl
+sind
+singerproducer
+singlechain
+singleonly
+singlephoton
+singleturreted
+sinicus
+sinter
+sirenians
+sitchensis
+sitedirected
+sitski
+sixinch
+sixpassenger
+skewering
+skilfully
+skillsbased
+skillset
+skua
+skybox
+slanting
+slatted
+sleeplessness
+slights
+sloweddown
+slowmedium
+smacks
+smallangle
+smallness
+smallsize
+smartcards
+smartweed
+smithiana
+smokey
+smudge
+snagged
+snagging
+snark
+sneezeweed
+snowfields
+snowmen
+soapbox
+sobering
+socioeconomics
+sodic
+soffit
+softdrink
+softswitch
+softwareopen
+soiled
+soldi
+soleil
+soloartist
+solomonensis
+solubilize
+sonorensis
+sorghi
+soteriology
+sourcetosource
+southeastnorthwest
+southflowing
+spaceports
+spalling
+spanners
+speakeasies
+speculatively
+speleothems
+spendthrift
+spermatogonia
+spheroids
+spheromak
+spiciness
+splake
+splitfins
+splitlevel
+sporocarp
+sporozoites
+sportsbook
+sportsmanlike
+spotbilled
+spotfin
+spritsail
+spurts
+squabbling
+squamatus
+squamouscell
+squint
+stadsdeel
+stateofthe
+stationkeeping
+std
+stealer
+stepbrothers
+stereocenter
+stereoselective
+stereospondyls
+stevedoring
+stewarded
+stiles
+stillbirths
+stilted
+stingers
+stoat
+stockists
+stonecutters
+stonei
+stooge
+stopovers
+storechain
+stowing
+straightaway
+straightchain
+straightens
+straightsided
+stratagem
+streambed
+streetside
+streetsmart
+strenua
+striatulus
+strikeshortened
+striven
+stroboscopic
+stylomastoid
+subapicalis
+subassembly
+subbase
+subbasins
+subcallosal
+subdiscoidal
+subflava
+subfusca
+subgraphs
+subiculum
+sublists
+submaxillary
+subnetwork
+subnetworks
+suborganizations
+subsect
+subsides
+subspecialists
+substage
+substriatus
+subtopics
+subverted
+sulfatide
+sulfonamides
+sulfonylureas
+suling
+sumichrasti
+sundae
+sunroof
+superalloys
+supercapacitors
+superfeatherweight
+superficiality
+supergun
+superhighway
+superintelligent
+supermajorities
+superpipe
+supertribe
+surcharged
+surfeit
+survivin
+sustentacular
+suteri
+swagman
+swamy
+sward
+swarthy
+sweetscented
+swidden
+swirled
+swordandsandal
+sylvatic
+symptomless
+synaesthesia
+syndicating
+synephrine
+synthbased
+taarab
+tabira
+tachykinin
+tambour
+tapaculos
+tapebased
+taprooted
+taxifolia
+tearaway
+teardropshaped
+teaspoon
+teats
+tejano
+telepaths
+telerecordings
+teleserye
+tendonitis
+tenman
+tenmile
+tentorium
+tenue
+tenuifolium
+teosinte
+terbium
+territoriality
+testdriven
+testtaker
+tetany
+tetraplegic
+tetrazolium
+texanum
+thaler
+thatching
+thenCEO
+thenGov
+thenState
+thenfashionable
+thenhead
+thenpartner
+theorys
+therian
+thermic
+thermochemical
+thermogenesis
+thermophila
+thermophile
+thermostable
+thermotolerant
+thgreatest
+thioether
+thirdbase
+thirddeadliest
+thirdparties
+thither
+thnd
+tho
+thrd
+threadleaf
+threeaxis
+threecolor
+threedecade
+threefoot
+threehundred
+threelane
+threeyearlong
+thrombolysis
+thromboplastin
+throwins
+thylacine
+tiaras
+tibetanus
+tibetica
+tigerfish
+tigre
+tills
+timbering
+tinamous
+tinkling
+titanosauriform
+tobacconist
+toggling
+tomboyish
+tomistomine
+tooled
+toolpaths
+topend
+tornadogenesis
+toro
+torpedoboat
+torrida
+torulosa
+toymaker
+tr
+trackpad
+tradecraft
+tragicomic
+tramtrain
+transcatheter
+transcendentalist
+transcode
+transdenominational
+transputer
+transversefasciata
+travail
+treeliving
+treeshrews
+tremens
+tremula
+trenbolone
+tribulation
+tridactyla
+trifold
+trifunctional
+trimethylsilyl
+trinervia
+trinitarian
+triviality
+trivium
+troublemaking
+truecolor
+trumpetervocalist
+trunkfish
+ts
+tubeless
+tufting
+tuman
+turacos
+turbellarian
+turboelectric
+turningpoint
+turnstone
+twelvepart
+twelvetrack
+twotoed
+typewritten
+udDin
+uddin
+ufologists
+ugliest
+ultrawideband
+uluguruensis
+umbo
+umbratica
+unIslamic
+unarmoured
+unchangeable
+uncoated
+uncoiled
+unconquered
+unconstitutionality
+unconvinced
+uncultured
+underemployment
+underpainting
+underpants
+undersecretaries
+undulated
+uneasiness
+unexposed
+ungainly
+unheralded
+unicity
+unilineata
+unimaginable
+unindicted
+uninformed
+uninspiring
+unipunctata
+unmargined
+unmasking
+unmethylated
+unmolested
+unnavigable
+unobtrusively
+unpacked
+unpatriotic
+unrealistically
+unrepresentative
+unsimulated
+unspecialized
+unspecific
+unstudied
+untelevised
+untreatable
+unum
+unweighted
+upscaling
+upstage
+upto
+urad
+urbanrural
+uroporphyrinogen
+uropygialis
+usambarensis
+ushers
+vachanas
+vaginatum
+vaginismus
+valuer
+valveless
+vang
+vaping
+variablepitch
+varix
+vaunted
+ved
+velia
+velodromes
+ventriloquists
+vermicularis
+vermiculatum
+vermiform
+verticallyintegrated
+verve
+vicechairmen
+vicecounty
+videotelephony
+videotex
+vietnamensis
+villous
+viminalis
+vincristine
+vindicate
+violator
+viride
+viroids
+virtuality
+visaexempt
+viscosum
+visionimpaired
+visuality
+visuallyimpaired
+vitriolic
+vivant
+vocalistrhythm
+voix
+voluntaryaided
+vorax
+vous
+voxels
+wagonway
+walkability
+walkingsticks
+walkouts
+wargamers
+warmhearted
+warthogs
+wasabi
+wat
+watchmakers
+waterholes
+waterparks
+watersaving
+waterwheels
+waystation
+webaccessible
+webshow
+websteri
+weirdest
+weirdness
+welladapted
+wellconserved
+wellconstructed
+westcoast
+westtoeast
+wetlease
+whammy
+wheelbarrows
+wherry
+whisked
+whitebearded
+whitespots
+whitestem
+whitethroats
+wickedly
+widefield
+wierde
+windage
+windproof
+wisest
+woe
+woodswallows
+wordmark
+workroom
+worldsystems
+worldweary
+worrisome
+wrack
+wreckers
+wretched
+wrinkly
+wrongdoer
+wrongness
+wv
+xBase
+xcompatible
+xeroderma
+xerophila
+yampah
+yaws
+yearns
+yellowbelly
+yellowcolored
+yellowwinged
+yokes
+youthbased
+yurt
+zale
+zap
+zeae
+zerofare
+zincdependent
+zipline
+zoanthid
+zooarchaeology
+zoomed
+zoot
+abbandonata
+abbreviates
+abruption
+absolutist
+accentual
+accentus
+acclaims
+acousticbased
+acquaint
+acreages
+acromioclavicular
+actionscience
+activin
+actoractress
+actresss
+aculeatum
+acupressure
+adhesins
+adipogenesis
+adivasi
+adjudications
+adrenocorticotropic
+adunca
+advena
+aediles
+aerate
+aerea
+aerosolized
+aes
+affordances
+aflame
+aftereffect
+afterimage
+ag
+agebased
+agegrade
+agriculturerelated
+agrifood
+ahistorical
+airlifter
+airpark
+ajax
+alAbbas
+alHasakah
+alHouthi
+alKhatib
+alMukhtar
+alQura
+alae
+alberti
+albina
+albopunctatus
+albula
+algebraically
+alkalic
+alkyd
+allEnglish
+alleghaniensis
+allegro
+allo
+allostatic
+alphanumerical
+alpicola
+alpinism
+alterglobalization
+altiplano
+aluminized
+amaniensis
+amaurosis
+ambassadoratlarge
+ameliorating
+amici
+amicorum
+amineassociated
+aminoacids
+amiodarone
+ammoniaoxidizing
+amoebic
+ampersand
+amylopectin
+anatolicus
+andamanicus
+andra
+androcentric
+androgendependent
+angelicus
+anguished
+anguli
+aniconism
+animalistic
+anis
+annatto
+annoyances
+annunciation
+annuus
+anomodonts
+antedated
+antiAmericanism
+antiFascist
+antiFrench
+antiMormon
+antiTreaty
+antiZionism
+antianxiety
+antiarmor
+anticaking
+anticholinergics
+anticodon
+anticolonialist
+anticounterfeiting
+antidiarrheal
+antifascists
+antiinvasion
+antimafia
+antimigraine
+antiparticle
+antirevisionist
+antiserum
+antistate
+antitheft
+antitorpedo
+antitragus
+antivaccine
+antmimicking
+anuran
+apalutamide
+aphanitic
+apo
+aposematism
+applicationbased
+approximans
+approximatively
+aquanaut
+aquaporin
+arRahman
+arachnoidea
+arbiters
+arborescence
+arbuscula
+archangels
+archosauromorphs
+ardently
+arenarium
+arenaviruses
+areolae
+argillite
+argo
+ariki
+arimanni
+armlock
+aromaticity
+arpa
+arsons
+artemisiae
+arthistorical
+articulator
+artscene
+aruensis
+asaphid
+aspergillosis
+assamica
+assemblyline
+assimilationist
+assistantcoach
+associational
+astarte
+asterids
+asthenia
+astrocytoma
+asymptotically
+asynchrony
+ataxic
+atelectasis
+athecate
+atlantis
+atque
+atricapilla
+attenuates
+atypia
+auricularis
+aurifrons
+aurum
+austrina
+autocannons
+autocatalytic
+autoerotic
+autoinjector
+automatics
+autorotation
+avarice
+avocets
+axel
+azathioprine
+azimuthal
+bFM
+babai
+babesiosis
+backchannel
+backformation
+backmasked
+backrest
+baculovirus
+badtempered
+bagh
+bailing
+balk
+ballclubs
+ballfield
+ballista
+balsamic
+baltica
+balticus
+bandh
+bandsaw
+bannerfish
+baptistry
+barnesi
+baroreceptors
+barre
+bartsia
+barycenter
+baryon
+baryons
+baseballonly
+baskettail
+basse
+batandball
+battleax
+battlement
+beatmania
+beaufortia
+beautified
+beeper
+beeping
+beeps
+beetroot
+begonia
+beijingensis
+beisa
+bellydance
+bematistes
+bergeri
+berlepschi
+bermudensis
+bestplaced
+bewitching
+bharatanatyam
+bhutanensis
+biaxial
+bicornuta
+bidirectionally
+biennis
+billable
+bioassays
+bioorthogonal
+biosensor
+bipectinate
+biped
+biphobia
+bipunctalis
+bisignata
+bistate
+bittertasting
+blackedged
+blackhooded
+blackishgray
+blacklight
+blackmails
+blanci
+blesses
+bloat
+blockbased
+blondes
+bloodbath
+bluebird
+bluefaced
+bluesoriented
+boater
+boatswain
+bobblehead
+bodhisattva
+bodysuits
+bogan
+booklice
+boonei
+boorish
+boudoir
+bowllike
+brachiation
+brachydactyly
+brainy
+brandished
+braueri
+bravest
+breakneck
+breams
+breastmilk
+breviary
+brickred
+brickyards
+bridesmaids
+bridetobe
+bridgewire
+brigadelevel
+brightred
+brimmed
+brindled
+briquettes
+brisket
+briskly
+bristlegrass
+brittleness
+broaching
+broadcloth
+bronco
+bronzecolored
+bronzed
+brownstones
+bruchid
+brushwood
+buccopharyngeal
+buddha
+buffetstyle
+bufo
+bugging
+buio
+bullnose
+bullocks
+bumpkin
+bung
+bungalowstyle
+bunghole
+bungled
+bunkering
+burgee
+burgeoned
+burly
+burnings
+bushcraft
+bushfood
+bushmallow
+bushwalkers
+bushwillow
+businessfocused
+bustards
+busters
+butcherbird
+buttonweed
+cAMPdependent
+cDNAs
+cabinetmaking
+cachet
+caco
+caespitosum
+caffeinefree
+calamitous
+calicivirus
+callouts
+calmness
+calvary
+camming
+canaliculatum
+cancan
+cancerassociated
+cantharidin
+cantina
+capacitation
+capense
+capindependent
+capitalis
+capitano
+capitols
+capybaras
+carbamates
+carbamoyl
+carbocyclic
+cardui
+careerlong
+carfentanil
+carillons
+carinae
+carinipennis
+carioca
+carmen
+carnations
+carplet
+carpool
+cascaded
+catechu
+catenifer
+caul
+caulescens
+caulk
+caved
+cavein
+cavemen
+cavifrons
+ceRNA
+cebuensis
+centrallylocated
+centurylong
+cepacia
+cerastes
+cerebelli
+ceres
+ceroid
+cervinus
+chachalacas
+chaetae
+chainring
+chainsmoking
+chaitya
+chalcopyrite
+chalkhill
+chamaeleon
+chansen
+chante
+chapatis
+chathamensis
+checkups
+cheesemaker
+cheilocystidia
+chemoattractant
+chemoheterotrophic
+chestnutcrowned
+chevrotain
+chickadee
+chilis
+chimp
+chinstrap
+chital
+chlorantha
+chloroformate
+choirboys
+choker
+chokeslam
+cholesteryl
+choro
+christenings
+chromaticity
+chromatograph
+chronobiologist
+chuckwalla
+chukar
+churchgoers
+churchwarden
+cinematographed
+cinnamate
+circlet
+circovirus
+circumdata
+cirques
+cirrate
+citrusflavored
+classroombased
+claudin
+clavi
+clavus
+cleansers
+clewlines
+clickthrough
+clockmaking
+closedcell
+cloudhosted
+clubfoot
+clupeid
+coNP
+coagulated
+coagulopathy
+cobwebs
+coccineum
+cockerel
+cockscomb
+cocoyam
+codesharing
+coelomic
+coffeeshop
+cognizable
+coholder
+coleading
+collagraph
+collaterals
+collegian
+colocalizes
+colorado
+colorism
+colorspace
+columellar
+combinedcycle
+comedymusical
+commanderies
+commaseparated
+commemoratives
+commoditized
+communicant
+communicantes
+communityrun
+compartmentalization
+complexation
+comradeship
+concocts
+concomitantly
+concubinage
+condoning
+confederated
+confidants
+conformant
+conidial
+conjugacy
+connate
+conquistadores
+consensually
+considerate
+consigliere
+consolidators
+conspersus
+constabularies
+constrictors
+contextbased
+contortus
+contravening
+contrivance
+conveniens
+conversos
+convivial
+cooccurrence
+cookii
+cookingthemed
+coolies
+cooperators
+coorganizing
+coppernickel
+copulate
+coquina
+cordiformis
+corinthian
+cormous
+cornets
+cornett
+corns
+coronagraph
+corporis
+corrugations
+corrugatus
+corsetry
+corvids
+cosmetologist
+cosmochemistry
+costimulatory
+coterie
+cottongrass
+coulteri
+councilmembers
+countably
+counterattacking
+counterbattery
+countervailing
+countryblues
+countryformatted
+countryinfluenced
+couturiers
+cowbirds
+cownose
+coy
+cradling
+cram
+crania
+crashlanding
+cravat
+crawshayi
+craziest
+creatorexecutive
+creditcard
+credulity
+crematoria
+cribripennis
+crimecomedydrama
+crimesolving
+criminalisation
+crisper
+criteriums
+cropmarks
+crossbowmen
+crossculturally
+crossoverfriendly
+crossstate
+cruciatus
+crucibles
+cruentus
+crypsis
+cryptophytes
+cryptozoologist
+crystallina
+ctenophore
+cultclassic
+cuneifolia
+cuniculus
+curialis
+curlews
+cursorial
+curvaceous
+curvatum
+cutandcover
+cutandpaste
+cutaways
+cutback
+cyanocephala
+cyanogen
+cyclecars
+cycloalkane
+cyclobutane
+cyclohydrolase
+cyclorama
+cyrtochoanitic
+dArtois
+dAutomobiles
+dAuvergne
+dEbre
+dIberville
+dIvry
+dabbles
+dachshund
+dalam
+dalmatic
+damnatio
+danae
+dangereuses
+danseuse
+daphne
+dashiki
+dastardly
+datapath
+datas
+datasheet
+dawsoni
+dayi
+daymark
+db
+deadeyes
+debenture
+debited
+debonair
+decadal
+decadeold
+decaryi
+decelerating
+deciders
+declivis
+decoction
+decolorata
+decongest
+decry
+decurved
+decussation
+deducting
+defers
+defile
+defoliating
+dehalogenase
+dehydroepiandrosterone
+deinstitutionalization
+delamination
+delhi
+dellEmilia
+demerit
+demissionary
+demodulator
+demoralizing
+demote
+denticulatum
+depowered
+deprotection
+derbid
+desa
+descant
+descarpentriesi
+descender
+desertus
+desi
+despoiled
+destructively
+det
+determinacy
+deterministically
+dethroning
+detracting
+detrital
+deula
+devilsbit
+devolves
+devoutly
+dianhydride
+diatomaceous
+dicotyledon
+digenean
+diglycerides
+digna
+dihydroxyacetone
+dimensioning
+dimerize
+dimly
+dinerstyle
+dingbat
+dint
+dioicous
+diphthongs
+diptera
+dipteran
+directdrive
+directionally
+directness
+dirk
+dirtiest
+disciplinespecific
+diseaserelated
+diseasespecific
+disenfranchise
+disfavored
+disheartened
+dishonorably
+dishonored
+disinclined
+disincorporate
+disinfecting
+disown
+disparilis
+distanti
+distillations
+distresses
+distributorship
+districtfree
+disturbingly
+ditzy
+diversities
+dockland
+docureality
+dodecahedra
+dogaressa
+dognini
+dolens
+domenica
+dominical
+dominula
+dongs
+dosedependent
+dosimeters
+dotmatrix
+doublebanded
+doubleender
+doublets
+doula
+downregulate
+downshifting
+drake
+dramahorror
+dramaromance
+drawbridges
+dreamtime
+dregeanum
+dromedary
+drugsmuggling
+drumlin
+drydocks
+dryseason
+dualdegree
+duathlete
+ducalis
+dudleyi
+duomo
+duralumin
+durbar
+dustpan
+dutiful
+dyestuffs
+dynamiclink
+dynamited
+dyrosaurid
+dysphonia
+dysphoric
+dyspraxia
+dysthymia
+eReader
+eSTREAM
+eServer
+earlike
+earlytomidth
+earworm
+eclampsia
+ecotype
+ecrime
+edu
+efflorescence
+eggfly
+egregius
+eightdimensional
+eightperson
+einsteinium
+eke
+eked
+elasmosaurid
+electroactive
+electrodeposition
+electronegative
+electronhole
+electronicmusic
+electrophiles
+electroporation
+electrum
+elevatus
+elevenstory
+elided
+elisabethae
+elisae
+elision
+elixirs
+ella
+ellagic
+elliptocytosis
+elongatum
+eludes
+emodin
+emollients
+emoryi
+emotionality
+empirebuilding
+employmentbased
+emulsifying
+encase
+encroaches
+endocardium
+endod
+endolymph
+endophytes
+energizing
+enfilade
+englynion
+engorgement
+entertainmentrelated
+enticement
+entrust
+enucleation
+environmentfriendly
+ependymal
+epigenome
+epilogs
+episodically
+epitomizing
+equestris
+ergative
+errans
+erythrogaster
+essences
+ethno
+ethnopolitical
+eucalypti
+eugenicist
+eun
+euphratica
+europium
+eutriconodont
+everybodys
+evidencing
+examinee
+exboyfriends
+excavatum
+excavatus
+exclaiming
+excommunicate
+exfiltration
+exgirlfriends
+exhorts
+exocyst
+exopeptidase
+exoribonuclease
+exoskeletal
+exoticism
+expanders
+expartners
+expediting
+expensively
+experiencebased
+experimentalists
+explicate
+explorable
+explorative
+exslave
+exsul
+extranet
+extraterritoriality
+extremophile
+facially
+fag
+fairyfly
+fairylike
+fait
+fallible
+familyrelated
+familystyle
+fanfunded
+fanout
+fanthroated
+fanvoted
+farmhands
+farmrelated
+farnesyltransferase
+farrier
+fasciated
+fasterpaced
+fastjet
+fathead
+fattened
+faun
+faxed
+faxing
+fencedoff
+fera
+fermentative
+fernleaf
+ferociously
+ferruginosa
+fervently
+festa
+ffrench
+fibrate
+fibrillar
+fibrosarcoma
+fiends
+fifthbusiest
+figment
+filamentosus
+filmgoers
+fingered
+fireandforget
+firebomb
+fireprone
+fireside
+firma
+fishbone
+fishnet
+fishpond
+fittingly
+fiveseason
+fiveyears
+fixating
+fixity
+flails
+flammeus
+flashbulb
+flashcard
+flavanonol
+flavomaculatus
+fleeces
+fleshing
+flexors
+flippant
+flotsam
+flumazenil
+fluoridated
+flyaway
+flywheels
+fnumber
+folddown
+folia
+foodie
+foosball
+footballplaying
+footsoldiers
+footstep
+foreignbased
+forewords
+formless
+formylation
+forticornis
+foundereditor
+foundling
+founds
+fourbar
+fourbeat
+fourbladed
+fourfaced
+fourhelix
+fourhorse
+fouronthefloor
+fourpanel
+foursquare
+fourteenstory
+fourtoed
+fourtower
+foxi
+frameless
+freestyling
+freethinker
+freetouse
+frequentative
+freshest
+friable
+friendtofriend
+frivolity
+frogman
+frontoffice
+frosty
+frugivore
+fruitbody
+fruitgrowing
+fulgurata
+fuliginea
+fullseason
+fultoni
+fulvicornis
+fumida
+functionalized
+funesta
+funkacid
+funkdisco
+furfuracea
+fuscicollis
+fuscosignatus
+gadgetry
+gaffe
+gaiety
+gainoffunction
+galagos
+galanin
+galli
+galling
+gametogenesis
+gametying
+gamingrelated
+gangway
+ganja
+gape
+garam
+garri
+gaslift
+gasp
+gayfeather
+gayrights
+gazettes
+gdb
+geayi
+generalis
+generalship
+genetical
+genies
+genredefying
+geodes
+geomembranes
+geomorphologist
+geotagged
+gerardi
+ghatam
+gibbosum
+gigolo
+glassfiber
+glasswork
+globemallow
+glossa
+glyceryl
+glycomics
+glycopeptides
+glycosphingolipids
+glyptodont
+glyptodonts
+gm
+gnathos
+gnoma
+gnosticism
+goalsagainst
+goatantelope
+goatskin
+godwits
+gofasi
+gompa
+goniometer
+gooseberries
+gorgoniantype
+gossypinus
+gothicstyle
+gotomarket
+gounellei
+governmentimposed
+governmentled
+governmentwide
+grabber
+gradelevel
+graftversushost
+grahami
+graminicola
+graminifolium
+granitoids
+grassfed
+greenfinch
+greengrocer
+greenschist
+greenveined
+grieve
+grimy
+griseipennis
+griseola
+groaning
+groovetoothed
+grooving
+groped
+grouchy
+groundskeepers
+growthstage
+grungy
+guarantors
+guesswork
+guesthosted
+guitaristproducer
+gummifera
+gunsmithing
+guttula
+gymnasia
+gynoid
+gyrate
+haciendas
+hadrosaurids
+haemodialysis
+haha
+halfinch
+halfpage
+halfscale
+hallmarked
+haloalkanes
+halve
+hammerless
+handbells
+handeye
+handhold
+handmaiden
+handpowered
+handshakes
+haploinsufficiency
+hardedge
+hardliners
+harewallaby
+harrows
+hartwegii
+hastatus
+hastening
+hauntingly
+havelis
+hawkbit
+headfinal
+headinitial
+headmarking
+headstream
+healthiest
+heartily
+heatsink
+hedgenettle
+heerlijkheid
+heinrichi
+hemangiomas
+hematomas
+hematopathology
+hemiparasite
+hemoglobinuria
+hemophiliacs
+hempnettle
+henrici
+hepaticus
+hepatobiliary
+hercules
+herrerae
+heterodimerizes
+heterodoxa
+heteronormativity
+heterotopia
+hexahydrate
+hexahydroxydiphenic
+hexandra
+hexaploid
+hierarch
+hierba
+highcarbon
+highcontrast
+highdemand
+higherlayer
+highflier
+highspired
+highspirited
+hightide
+highveld
+hilariously
+hilarity
+hillclimbs
+hillslopes
+himselfherself
+hindlegs
+hippodrome
+histopathologic
+hitchhiked
+hitmaker
+hm
+hnRNP
+hoatzin
+hocks
+hoffmannsi
+hollandi
+holosericea
+hombre
+homecountry
+homeground
+homewardbound
+hommes
+homopolymers
+homothallic
+hookah
+hookladen
+hookshaped
+horary
+hormonelike
+hormonesensitive
+hornfels
+hornsnail
+horological
+horrorscience
+horsenettle
+horsewoman
+hosei
+hostagetakers
+hostpathogen
+houseband
+hu
+hubcaps
+huberi
+hunchbacked
+huntingtin
+hussars
+huxleyi
+hyacinthorchid
+hyaluronan
+hybridizing
+hydel
+hydrazone
+hydrochlorothiazide
+hydrogenases
+hydrogenpowered
+hygroma
+hypercarnivorous
+hypergraph
+hyperlinking
+hypermethylation
+hyperpolarization
+hypnotize
+hypocrites
+iAPX
+iCN
+iChat
+iConji
+iKON
+iShine
+iTV
+iTaukei
+iTree
+iboga
+iceplant
+ichthys
+iconographical
+ideologues
+idiotic
+illsuited
+illusionistic
+imageguided
+imatinib
+imbricate
+immaculately
+immanence
+imminently
+immodest
+immunogenetics
+immunosuppressants
+impish
+implementer
+implored
+implores
+imprisons
+ina
+inbody
+incurvata
+incurved
+indazole
+indels
+indexation
+indexers
+indigestible
+individuallevel
+indoctrinated
+indoorarena
+industrialism
+industryfocused
+iners
+infernalis
+inflammations
+infolding
+infra
+inimitable
+injectionmolded
+injuryplagued
+inlining
+innernorthern
+inquinata
+inrush
+inscribing
+insecta
+insets
+insideleft
+insignificance
+insolens
+instructorled
+insulare
+insularum
+insurable
+integrum
+intellectualism
+intensa
+interatrial
+intercaste
+interconvert
+interlayer
+interlinear
+intermediaterange
+intermodulation
+internationallyknown
+internode
+interpretable
+interrelate
+interstices
+interstitialis
+intertextual
+interveniens
+intimates
+intrahepatic
+introducer
+inundating
+inveterate
+invisibly
+ionizes
+ironical
+irrationally
+irreparably
+irrigates
+irritans
+isidia
+isobaric
+isobutyl
+isomerases
+isopropanol
+italica
+iterate
+iterators
+itis
+jacana
+jacobsi
+jag
+jailers
+jamb
+jambs
+jansei
+jarrah
+jazzs
+jeepneys
+jessamine
+jetpropelled
+jigsaws
+jinx
+jogs
+jou
+jugularis
+juju
+juliae
+junebug
+juniperi
+junks
+kalan
+kathak
+kaya
+kempi
+kempul
+kennedyi
+kenyensis
+kephale
+kessleri
+ketuk
+keydependent
+keyloggers
+kiellandi
+kinetically
+kinked
+kinks
+kirkko
+kirkyard
+klapperichi
+klugii
+knifemaker
+knobbly
+knockon
+knowledgeintensive
+knulli
+koehleri
+koinobiont
+kollari
+korean
+krai
+kraussii
+kuwait
+kya
+kyu
+labiatus
+labii
+lackey
+lacrimation
+lacunosa
+lacus
+ladybugs
+laetifica
+laevipennis
+lal
+lam
+lamin
+lamivudine
+lamour
+landraces
+landsale
+lang
+languagerelated
+largeleaved
+largeprint
+largesse
+larrikin
+lass
+lastnamed
+laticauda
+latidens
+laudable
+laudatory
+laurisilva
+lawbreakers
+lawfirm
+layabout
+layia
+leachii
+leadbased
+leadmining
+leaguebest
+leaseback
+lefty
+legalism
+legalities
+legit
+lentiform
+lentigines
+lentiginosa
+leopardus
+lepidota
+leptosporangiate
+lesbianfeminist
+lesbianthemed
+lessdeveloped
+letterboxing
+libation
+liberationists
+librettists
+lienholder
+ligatus
+lightinduced
+lightwave
+lignans
+ligulate
+limber
+limiters
+linebyline
+lini
+linkup
+linocut
+liotia
+lipomas
+liquidfuel
+lirae
+lithification
+litigious
+littleleaf
+liveliness
+loathing
+locoweed
+lodes
+logicians
+logotype
+loiter
+longboarding
+longdead
+longhand
+longispina
+longlegs
+longstay
+lossmaking
+loungewear
+lousewort
+loveteam
+lowball
+lowborn
+lowerleague
+lowerquality
+lowii
+lownoise
+lowsulfur
+loyally
+lube
+luciae
+ludens
+lungwort
+lupa
+lurch
+lusitanicus
+lutosa
+lycioides
+mAb
+mach
+machineries
+macleani
+macquariensis
+macrocarpus
+macroengineering
+macronutrients
+macrophthalmus
+macrosperma
+maculicornis
+maculifrons
+maculiventris
+magellanicus
+magnirostris
+mahal
+mahalla
+mahout
+mainmast
+mainspring
+majlis
+malacologists
+malacostracans
+malapropisms
+malayanus
+malayi
+malepreference
+malleability
+malodorous
+manca
+mancalas
+maneaters
+manhunts
+manicata
+manifestly
+manmachine
+manpowered
+manroot
+mantidflies
+manufacturability
+manybranched
+maoria
+mappa
+mara
+marigolds
+marimbas
+markhor
+marriageable
+martensitic
+martian
+martinsi
+marzban
+masochistic
+massacring
+masterminds
+mastodonsauroid
+matchsticks
+matins
+matzo
+mazurka
+meateating
+mechanistically
+mechs
+medalproducing
+mediabased
+medicinalis
+medlar
+medroxyprogesterone
+megacolon
+megalodon
+megalotis
+melancholicus
+melanoptera
+meliloti
+melisma
+melvilli
+memetics
+meningioma
+mentum
+meperidine
+merlons
+mesylate
+metaldeath
+metalloproteins
+metalsmithing
+metanephric
+metaplot
+metatarsus
+metatheory
+methanotrophs
+methylenedioxy
+microbat
+microfinancing
+microgrids
+microliter
+microsaurs
+middleeastern
+midelevation
+midfifteenth
+midfifties
+midmounted
+midrash
+midthirties
+militaryrelated
+milks
+milliners
+mindinabox
+minks
+minutiflora
+mirages
+misanthropy
+miscredited
+misfeasance
+misjudged
+misl
+missouriensis
+mistrusted
+mittens
+mixedgrass
+mixin
+mjobergi
+moaning
+mobilis
+modestum
+moglie
+monarchic
+monetalis
+mongoliensis
+monkshood
+monnieri
+monoamines
+monoglycerides
+mononymous
+monoplacophoran
+monopoles
+monosomy
+monounsaturated
+monteithi
+moonlights
+moorgrass
+moralist
+morid
+morose
+morpholine
+mosaicist
+mostlistenedto
+mothball
+mothballs
+motionsensing
+motorman
+motorvehicle
+motown
+mouhoti
+mountainbuilding
+mountaintops
+mouthbrooders
+mouthwashes
+mpg
+msDNA
+msnbccom
+mt
+mufflers
+multiPlatinum
+multiaxial
+multicellularity
+multiengined
+multifactor
+multifasciata
+multifield
+multiissue
+multimodel
+multinarrative
+multiphonics
+multipleuse
+multipolar
+multipronged
+multiregional
+multisectoral
+multistriata
+multitenancy
+multiway
+muntins
+muons
+muricatum
+muscularis
+musiciansongwriter
+musing
+mutagens
+mutates
+mutex
+mutualisms
+muziki
+mycoplasma
+mycorrhizae
+mythologized
+nacreous
+naively
+nama
+namechecked
+namechecks
+nanomachines
+naringenin
+narratology
+narrowboats
+natrix
+naturallanguage
+naturopath
+navigability
+nearmonopoly
+neartotal
+nec
+necrolysis
+neediest
+neglectum
+negus
+nematicide
+nematicides
+nemo
+nemorosa
+neoLatin
+neocolonial
+neoformans
+neotype
+nephridia
+nervously
+nettops
+networkconnected
+networth
+neuroethics
+neuromodulators
+neuromorphic
+newbuilt
+newsbased
+nigeriae
+nigeriensis
+nigricauda
+nigrosignata
+nigrovittatus
+niobe
+nipponica
+nitidulus
+nitrogens
+nocturnus
+noirish
+noiserock
+nomological
+nonAnglican
+nonAustronesian
+nonGovernment
+nonItalian
+nonNintendo
+nonabelian
+nonacceptance
+nonadjacent
+nonbuilding
+noncapital
+noncircular
+noncoalition
+noncoercive
+noncollecting
+nonconducting
+noncontroversial
+nonedible
+nonenzymatic
+nonfamily
+nonfermentative
+nonfluorescent
+nongan
+nongraded
+nonimaging
+noninstrumental
+nonjusticiable
+nonliterary
+nonmajor
+nonmoral
+nonmotil
+nonnatives
+nonnatural
+nonneoplastic
+nonoriginal
+nonradioactive
+nonrelated
+nonremovable
+nonresistance
+nonscholarship
+nonsequiturs
+nonsubscribers
+nontheism
+nontherapeutic
+nontournament
+nori
+nosed
+nota
+notarial
+nubicola
+nucleate
+nudisco
+nuke
+nullity
+nyatiti
+oaxacana
+objectorientation
+objets
+obligee
+obliterans
+obscurior
+obviated
+occidental
+ochrea
+octagons
+octomaculata
+octuple
+octyl
+odious
+odoratus
+offbreaks
+offcourse
+offeror
+officialdom
+officiant
+offnetwork
+offroading
+offtrail
+oilbearing
+oiling
+oklahomensis
+okutanii
+oligopeptide
+olingo
+olla
+olympiads
+omnes
+omnipotence
+onagainoffagain
+onca
+oncethriving
+onehundredth
+onepass
+ontheair
+oocysts
+opamps
+openage
+openchain
+openend
+operatingsystem
+opisthoglyphous
+opportunists
+orangetip
+orbitofrontal
+orderings
+oregana
+organochloride
+organoleptic
+originalism
+ornithomimid
+orthomolecular
+orthoses
+osmolarity
+osmoregulation
+otherworld
+otolith
+outlasting
+outofhospital
+outreaches
+outstripping
+overemphasis
+overo
+overrepresentation
+overripe
+overstate
+overstating
+oversteer
+overvalued
+overwatch
+ovotransferrin
+ownerbreeder
+oxidization
+oxoglutarate
+oxygencarrying
+oxymoron
+pachinko
+pacu
+paddleshaped
+padlocked
+padre
+pagodula
+pai
+paidup
+paintwork
+palatalization
+palebellied
+palmitoyltransferase
+palmyra
+panchayaths
+pani
+pannosa
+panoply
+pansori
+panto
+papua
+papyrologist
+paracompact
+paraganglioma
+paragon
+parallelized
+paralympian
+parapeted
+paraphyly
+parapsoriasis
+parasympathomimetic
+paratope
+paraventricular
+parboiled
+pardalina
+pardoning
+parentteacher
+parka
+parterre
+partfunded
+partialism
+parvorder
+paso
+pasticcio
+patel
+paucicostata
+paupercula
+payrolls
+paytoplay
+peaceably
+peakhour
+pearlite
+peda
+pegylated
+pelargonium
+pelea
+pelted
+pemphigoid
+pendentives
+penduliflora
+peneplain
+penstock
+pentachloride
+pentapeptide
+pentaprism
+peopling
+pepe
+peralkaline
+percher
+perchlet
+percolating
+perestroika
+perfective
+performancerelated
+perfunctory
+perfused
+perilla
+perissodactyl
+perissodactyls
+periventricular
+permeation
+pernix
+persephone
+persicum
+personifying
+pertinax
+pertusa
+perversa
+petticoats
+pfefferi
+phalloides
+pharmacopeia
+phasers
+phenacetin
+phenmetrazine
+philippii
+philippina
+philosophyrelated
+phonebook
+phonolite
+phosphodiesterases
+phosphoserine
+photoplay
+photothermal
+phototransduction
+phototypesetting
+phpBB
+phthalates
+phycologist
+physa
+phytopathogenic
+phytoplasma
+phytoplasmas
+pickaxe
+pickoff
+pictipennis
+pictogram
+pieve
+pigmentary
+pigra
+pilota
+pinewood
+pinkpurple
+pinnatum
+pinout
+pipettes
+pisiform
+pitha
+pitlane
+pityriasis
+pkgsrc
+plaits
+planation
+planatus
+planetesimals
+planigale
+planum
+plasmodesmata
+playwrightinresidence
+plexippus
+pliosaurid
+plumipes
+pocketing
+podocarp
+poignantly
+pointcut
+pointspaying
+policybased
+policys
+poling
+politicalmilitary
+poliziotteschi
+pollo
+polluter
+polyelectrolytes
+polygamists
+polymerisation
+polymorphonuclear
+polyolefins
+polysemy
+polysomnography
+pomelo
+poncho
+pont
+ponyfishes
+poplin
+populates
+possessors
+postByzantine
+postReconstruction
+postally
+postmatch
+postrevolution
+postscarcity
+posttreatment
+potestate
+potoroo
+pottage
+pounce
+pourover
+powerlifters
+pr
+praeclara
+pragmatist
+preSocratic
+precalculus
+preceramic
+precomposed
+preconditioning
+precovery
+predacious
+prednisolone
+prefigures
+prefilled
+preheating
+preliminarily
+premarket
+premaxillary
+premorbid
+preorbital
+preposterous
+prepreparatory
+preprints
+presbyters
+preselections
+presss
+presuppositions
+prevalently
+prickle
+primitivism
+primordia
+princebishop
+printrun
+printworks
+prizewinners
+proBrexit
+problemist
+procerus
+processbased
+procreate
+productiondistribution
+progressiveness
+progressiverock
+pronation
+propagandistic
+propagator
+propagules
+propanol
+prophage
+propranolol
+proscribe
+prostacyclin
+protoporphyrin
+prototypic
+provinciale
+psalmody
+pseudomallei
+pseudoplatanus
+pseudosciences
+pseudotuberculosis
+psychoanalytical
+psychologies
+pugilist
+pulmonologist
+pulsates
+pulverea
+puncticeps
+purposemade
+purulent
+pushbacks
+pustaha
+putrescine
+putrid
+pyogenic
+pyrazinamide
+pyrazine
+quadrilineatus
+quadrupleplatinum
+quadrupling
+quae
+qualityadjusted
+quarrymen
+quartic
+quartzites
+quasiparticle
+quasipeak
+quasipublic
+quatrefoils
+queenless
+quicken
+quincunx
+quinic
+quinones
+quinquemaculata
+quinquestriatus
+quip
+quipped
+quitensis
+quoined
+rabbet
+raceethnicity
+racemosum
+racerelated
+racerunner
+racketeers
+radials
+radiocommunication
+radiogram
+radiolucent
+rafted
+ragazzi
+rai
+railmotors
+raincoat
+rainfalls
+raivaru
+rajputs
+ramair
+rambunctious
+rater
+ratfish
+raymondi
+rdeyegirl
+reactivating
+readying
+readytorun
+reair
+reassigning
+reattach
+recapped
+receival
+recognizer
+recommence
+recompile
+reconceptualization
+reconditioning
+reconsidering
+recouping
+rectorate
+redblack
+redder
+reddishyellow
+reddy
+redeploy
+rediscovers
+redpoll
+redundantly
+reencryption
+reflexology
+refound
+refractories
+refractors
+reg
+regidor
+reginae
+regionfree
+reheating
+reified
+reimiro
+reinforcer
+reiteration
+rejoice
+rekeying
+reliquaries
+remarrying
+remodels
+reoriented
+reovirus
+repairable
+repatriates
+reportages
+repurchases
+requestresponse
+reregistration
+rerio
+respelling
+resplendent
+restenosis
+restock
+restorationist
+resung
+retesting
+retouch
+retraces
+retributive
+retroperitoneum
+retropharyngeal
+rhamnogalacturonan
+rheological
+rhizoids
+rhynchocephalian
+riband
+ribboncutting
+rices
+richardsonii
+richeri
+ricinus
+ridable
+ridehailing
+riderless
+rightangles
+rightofcenter
+ringneck
+roachlike
+roadies
+rockery
+rocketassisted
+rocketed
+rockindie
+rockmaster
+rockshelter
+rogenhoferi
+rollingstock
+roofer
+rotatable
+rotundifolium
+roughhewn
+roundoff
+rubberlike
+rubbertired
+rubbertyred
+ruckmen
+runcination
+ruts
+sabermetric
+sackbut
+sailboard
+sailmaker
+sallies
+salmonis
+salsify
+saltation
+saltmarshes
+saltworks
+salutary
+salve
+sanatoriums
+sandtreader
+sandverbena
+sanguinicollis
+saron
+sartorial
+saturatus
+satyagraha
+satyrs
+saussurei
+sauteri
+savants
+sayur
+scarcer
+scavenges
+schlechte
+scission
+sclerite
+sclerophyllous
+sclerotic
+scones
+scotica
+scourged
+scramblase
+screencast
+screwpine
+scripter
+scrobiculata
+scroller
+scrubbed
+sculpturing
+seamens
+seared
+secessionists
+secondclosest
+secretarymanager
+sectionals
+secularists
+securitys
+seeped
+segmentalarched
+segmentata
+seigneur
+sejm
+selenite
+selfacceptance
+selfauthorship
+selfdetermined
+selfeducated
+selfhosted
+selfing
+selflimited
+selfperception
+selfpollinate
+selfpresentation
+selfrenewal
+selfsacrificing
+selfsupported
+semaphorin
+semaphorins
+semialdehyde
+semicrystalline
+semiflava
+semihollow
+semiimprovised
+semimetal
+semiparametric
+semisweet
+sensationalistic
+sentimentalism
+serializable
+serialize
+sericulture
+serology
+serpyllifolia
+serration
+serratum
+serricornis
+sertraline
+serval
+servitudes
+sesquicentenary
+setacea
+setiger
+setpoint
+sette
+sev
+sevenmatch
+sevensegment
+seventythird
+severability
+sexton
+shackled
+shags
+shama
+shantytown
+sharedmemory
+sharpei
+sharpener
+shasum
+sheepherders
+sheeted
+shellcoiling
+shepherded
+shod
+shootemup
+shoreside
+shortnecked
+showtime
+showtunes
+shreddy
+shrieks
+shrublike
+sibogae
+sicca
+sideboards
+sideloading
+sieves
+sifaka
+sightlines
+signee
+signups
+sikh
+silktassel
+silvercolored
+silversmithing
+silverwork
+sinfluenced
+singalongs
+singerpianistsongwriter
+singlebyte
+singlepage
+singleperson
+singlesonly
+singlewinner
+sirolimus
+sissy
+sistersinlaw
+sixdayaweek
+sizzling
+skinnable
+skole
+skybridge
+skyphoi
+skyphos
+skyward
+slater
+slayings
+sleuths
+slitting
+slooprigged
+sloughing
+sluicing
+smallschool
+smalltoothed
+smelly
+snakeeater
+snakeeyed
+snakeskin
+snaking
+snappers
+sneer
+snowfinch
+snowtrout
+snus
+soaks
+sobbing
+sociolinguists
+sociologically
+softlaunched
+softserve
+softshelled
+softwinged
+softwoods
+soi
+solemnized
+soleus
+solium
+soloproject
+somebodys
+somersaults
+songbased
+songwriterguitarist
+songwritingproduction
+soothsayer
+sophist
+soulpop
+soundchecks
+soupy
+sousveillance
+souterrain
+spaceage
+spaceman
+speared
+speciess
+speedcuber
+speeded
+speedthrash
+spellcasting
+spenceri
+spermalege
+spiderorchid
+spinulosus
+spirulina
+spixii
+splashdown
+splendidula
+splenectomy
+sponsons
+sportfishing
+sportrelated
+spouting
+sprawled
+springeri
+sprinted
+spunky
+sputtered
+squalida
+squama
+squamulosa
+squaredoff
+squashed
+squealing
+squirrelfish
+sri
+stackers
+stagnate
+stares
+starflower
+stargazer
+statemandated
+statesmanship
+statism
+steadfastness
+steadicam
+stealthily
+stearic
+steatohepatitis
+stepfathers
+stereocilia
+stereograms
+stewing
+stigmatica
+stirfried
+stockmen
+stoloniferous
+stonecutter
+storeandforward
+stowaway
+straightfour
+straightness
+stressstrain
+striding
+stripebacked
+studium
+stuntwork
+stylesheets
+styx
+subRoman
+subareas
+subclassification
+subcounties
+subgeneric
+subgrade
+subgrouping
+sublicensed
+sublimate
+sublineata
+subornata
+subprior
+subproblems
+subsampling
+subsiding
+subsocial
+subspherical
+subsumption
+subtree
+subungual
+succinea
+sud
+sugarbush
+suggestibility
+sulcicollis
+sulfuryl
+summeronly
+sundries
+supercontinents
+superdelegate
+superheater
+superintend
+supersoldiers
+superteam
+supination
+suprachiasmatic
+suprapubic
+supremacism
+survivalism
+sutra
+swallowwort
+swamplands
+sways
+sweetclover
+symphonys
+syndesmosis
+synodical
+syntypes
+systematization
+tPA
+tableland
+tachycardias
+taffeta
+taggers
+tahr
+tailcoat
+tailing
+taillights
+tailpipe
+taitensis
+talkradio
+tanka
+tankettes
+tanneri
+tapeout
+taphonomy
+tarnishing
+tarpon
+taser
+tasmaniensis
+tattered
+teambuilding
+teardown
+teatree
+technologydriven
+teeing
+tegulae
+telecare
+teleology
+teleomorphic
+teleoperation
+teleosaurid
+telerecorded
+teletypewriter
+televoters
+tempest
+tencent
+tenens
+tensioning
+tenuiflora
+tenuously
+teretifolia
+terming
+termitaria
+terrigenous
+tesla
+tessera
+tessmanni
+testa
+tetrahydrate
+tetrahydrofolate
+tetraplegia
+thcenturies
+thenar
+thenemerging
+thermo
+thesauri
+thespians
+thibetanus
+thicketbird
+thielei
+thiourea
+thirdseeded
+thirteenpart
+threelayered
+threerace
+threering
+throng
+throttles
+thwarts
+thymocytes
+thynnid
+tickseed
+tierra
+tigress
+timbales
+timelessness
+timeshifting
+timpanist
+tinfoil
+tinsmith
+tintype
+toadstool
+toileting
+tollhouse
+tonsured
+toolsets
+topquality
+toprating
+toptobottom
+torc
+torgrass
+touchback
+touchtone
+tourerstyle
+townsites
+tra
+tradeshows
+transduce
+transducing
+transects
+transgenesis
+transhipment
+transitivity
+transmigration
+transmittance
+transposes
+treebased
+treeplanting
+trendsetters
+trespasses
+triAce
+triangulations
+triarylmethane
+tribological
+triceratops
+triclosan
+trifle
+trifluoroacetic
+trigeminothalamic
+trimaculatus
+trimix
+trioval
+triphenylphosphine
+triplestore
+triptychs
+triquetrum
+triune
+troika
+trp
+truant
+truer
+truest
+trumpetfish
+trunnion
+tryptamines
+tryscoring
+tubas
+tuberculosa
+tuckeri
+tuffaceous
+tumidus
+turbidite
+turbocharging
+turbopump
+turnofthethcentury
+tuyas
+twelveteam
+twinspot
+twintron
+twoacre
+twoandonehalf
+twocandidatepreferred
+twoplatform
+tworow
+twoseatsintandem
+twowinged
+twoyears
+txt
+typhimurium
+tyrosinase
+tyrosinebased
+uDraw
+uShaka
+ua
+udDaulah
+ultramarathons
+ultrastructural
+ultraviolent
+umbrata
+unaddressed
+unassociated
+unblock
+underachieving
+underestimating
+underfive
+underframes
+underslung
+underwrites
+underwrote
+undiminished
+undisciplined
+undulatum
+unexcavated
+unexceptional
+unfixed
+unfrozen
+ungrammatical
+unguis
+unholy
+unidentifiable
+uninitialized
+unipuncta
+uniserial
+universalis
+universalist
+unmemorable
+unnoticeable
+unobservable
+unowned
+unprincipled
+unreached
+unrestored
+unseasoned
+unsegmented
+unspent
+unsporting
+unsprung
+unsubstituted
+unwinds
+uomini
+upcurved
+upcycling
+upfronts
+upraised
+upshot
+upslope
+uraeus
+urichi
+urinated
+urokinase
+urothelial
+userbased
+usercontributed
+userfriendliness
+usufruct
+vSphere
+vada
+valens
+vampirelike
+vanpool
+vanquishing
+vara
+variablesweep
+variola
+varyingly
+vasospasm
+veer
+vehiclemounted
+vehicleramming
+veiling
+velata
+velopharyngeal
+vendorindependent
+vento
+veritas
+versechorus
+vespertilio
+vestalis
+vestrymen
+vetches
+vexed
+vicechairperson
+vicechancellors
+vicedean
+vidhansabha
+vihuela
+villagelike
+vino
+violetbacked
+virologists
+viscida
+vitesse
+vitrification
+vivarium
+vividness
+vocative
+volleys
+voltaic
+volunteerled
+vomeronasal
+vorticity
+votegetter
+voto
+vulnerata
+wallichiana
+wallum
+wandoo
+waraffected
+warhorse
+warmtemperate
+warungs
+washouts
+waspfishes
+wasplike
+wassail
+watermarked
+waxbill
+waxwing
+waxworks
+waylaid
+wd
+webenabled
+wef
+weighbridge
+weldability
+welfaretowork
+wellqualified
+wellread
+wellspaced
+wellstocked
+wentletrap
+whaleback
+wheellock
+wherries
+whitened
+whiteyellow
+whiz
+wholewheat
+widowhood
+wie
+wil
+wilaya
+williami
+windpump
+windrows
+wining
+wiregrass
+withstands
+witnesss
+woad
+wolfpacks
+womanowned
+womenled
+wong
+woodcreepers
+woodwardi
+woollystar
+woos
+wordlists
+workbook
+workhorses
+workweek
+wrenching
+wrestlings
+wrinkling
+writerperformer
+writeups
+writtendirected
+wurtzite
+wwwlegislationgovuk
+xCD
+xanthus
+xerographic
+xerography
+xv
+yachtswoman
+yakuza
+yawing
+yea
+yellowbacked
+yelloweared
+yellowishred
+yellowjackets
+yellowwood
+yerburyi
+yeti
+ylangylang
+yolksac
+yoni
+yourselves
+ypsilon
+yu
+zAAP
+zarzuela
+zen
+zenana
+zoek
+zymogen
+abandonware
+abbr
+abbrev
+abductive
+abides
+abietina
+abjad
+abjection
+abjuration
+abominable
+aborting
+abri
+abut
+acaule
+accentuates
+accreditors
+acculturated
+acesulfame
+acetylacetone
+achondrogenesis
+achromatopsia
+ack
+acolytes
+acquirers
+acquitting
+actindependent
+actionpuzzle
+actormusician
+actuate
+actuating
+acutephase
+adamantane
+adenoviruses
+adidas
+adipocyte
+adorable
+adsorptive
+adspersa
+advocation
+aegirine
+aerobically
+aerophone
+aerophones
+africa
+agammaglobulinemia
+agender
+agranulocytosis
+agreeableness
+agricultures
+agua
+aimlessly
+airdried
+airfares
+airfreight
+akathisia
+alAnsar
+alJihad
+alMuayyad
+alMulk
+alQassam
+alQuwain
+alRashid
+alShaykh
+alShibh
+alTabari
+alatus
+albicincta
+albimacula
+albiplaga
+alboguttata
+albolateralis
+albopicta
+albopictus
+albumDVD
+alecto
+allRussian
+allopathic
+allopurinol
+allozymes
+allresidential
+allroad
+allseason
+allstock
+allusive
+alongwith
+alphasialyltransferase
+altimetry
+alunalun
+ambiguum
+amd
+ameliae
+ameliorated
+amieti
+aminoacylation
+amitriptyline
+amniocentesis
+ampakine
+amphibolite
+analogdigital
+anaphoric
+anarchocapitalists
+andalusite
+androecium
+androgyny
+andromeda
+ane
+anemias
+anemic
+angasi
+angeli
+angiogenic
+angiopathy
+anglica
+anguid
+angulated
+angulation
+angustior
+angustissima
+angwantibo
+anhedral
+annulate
+anomeric
+ansorgii
+answerer
+antarctic
+antarcticum
+anteroventral
+anthracene
+anthropoid
+anthropometry
+anthroposophist
+anthroposophy
+antiStalinist
+antibodymediated
+anticlericalism
+anticlinal
+anticommercial
+antifeminism
+antihelix
+antiincumbency
+antiinsurgency
+antimitotic
+antimutant
+antimycobacterial
+antinociceptive
+antioxidative
+antiport
+antiquum
+antireflective
+antiroll
+aolid
+aperitif
+apertus
+apiary
+apiculture
+applicationoriented
+appraising
+approximatus
+apterus
+aquathlon
+aquatint
+arabesques
+arbitrates
+arborist
+archaean
+archaebacteria
+archduke
+architectonic
+archosaurian
+arenaindoor
+argid
+arietinum
+armatum
+armeniacus
+armorer
+arrhythmogenic
+arrowheadshaped
+arrowleaf
+artbased
+arte
+artforms
+arthropathy
+arthropodborne
+arthuri
+artificer
+arty
+asbestosis
+ashen
+ashmaple
+asian
+asteroides
+astrocytomas
+astrodynamics
+atheneum
+atomism
+atrophied
+attainders
+attenuatum
+attributive
+aubergine
+auctioneering
+aupouria
+aurescens
+auricoma
+auriculatus
+auripennis
+aurorae
+austeni
+austerities
+australasica
+authoreditor
+autoconfiguration
+autogenic
+automobilerelated
+automorphisms
+autopilots
+autotomy
+autotransporter
+ave
+avenae
+avenges
+azapirone
+azithromycin
+babysitters
+bacilliform
+backdoors
+backroads
+baclofen
+bactericide
+bagasse
+baguette
+bahamensis
+bailli
+baja
+balancers
+baller
+ballhandling
+barbieri
+barbthroat
+barfi
+barodontalgia
+baronium
+barramundi
+bartered
+barteri
+basepair
+bashed
+basicity
+basidium
+bask
+bassdriven
+bassguitar
+bastarda
+bastide
+bayan
+bayberry
+beachcombers
+beaching
+beanie
+beatem
+beckii
+bedpan
+beguinage
+behaviorists
+belids
+bellflowers
+bellhop
+bemused
+bendahara
+benefactress
+beneficent
+beneficiarys
+bennettii
+bentwing
+benzimidazole
+benzofuran
+benzophenone
+benzoquinone
+berated
+bergii
+beseech
+bestcharting
+besteffort
+betaDfructan
+betaoxidation
+bg
+bhavas
+biarticulated
+bibliophiles
+bibroni
+bicincta
+bicker
+bicolora
+biconica
+bicostata
+biennium
+bigamist
+bilaminar
+bindery
+biobehavioral
+biocatalysis
+biochar
+biocultural
+biodegradability
+bioplastics
+biorefinery
+biotechnologies
+bipinnata
+biplagiatus
+bipustulata
+biretta
+bitesize
+bitterling
+bituberculata
+blackbutt
+blackdeath
+blackthrash
+blackveined
+blakei
+blameless
+blanfordi
+blastomycosis
+blatchleyi
+blaue
+blazar
+bleheri
+blesseds
+bleue
+blip
+blissfully
+blistered
+bloods
+bloomed
+blu
+blueandwhite
+bluebonnet
+bluebreasted
+bluelight
+bluetongue
+bluishblack
+blunttipped
+boatbuilders
+bodystyles
+bol
+bolivari
+bombmaker
+bonafide
+bonefish
+bookies
+bookkeepers
+bookplates
+borbonica
+borndigital
+borneana
+bosuns
+bottlenecked
+bowmen
+brachyphylla
+brachypterous
+bracteatus
+bracted
+bradleyi
+brags
+brauni
+braved
+breakdancer
+breastbone
+breasted
+brevispinus
+brickmaker
+brigadiers
+brimming
+bristletipped
+brit
+brittlegills
+broadcastquality
+bromo
+brooded
+broodmares
+broomstick
+brotula
+brownfields
+brownwater
+brunnealis
+brunneipennis
+brushlegged
+brushturkey
+brusque
+bu
+bubbler
+bubur
+budworm
+buffbellied
+buffbreasted
+buffish
+bugged
+bulbifera
+bulletshaped
+bullettes
+bullfights
+burh
+burmeisteri
+burntout
+bursage
+buruensis
+bushranging
+businessmans
+buttercups
+butterfish
+butterflys
+butyrophenone
+buyside
+byggnadsminne
+cJun
+cadastre
+cadaveric
+cagebird
+calcar
+calcined
+calfskin
+calibrations
+calla
+calmed
+calumnia
+calvaria
+camaldulensis
+camerae
+cameroni
+camptown
+canariense
+canarygrass
+candicans
+canephora
+canna
+canso
+cantabile
+canticle
+capandtrade
+capbinding
+capitalizationweighted
+capitation
+capitularies
+caproate
+capsularis
+capsulatus
+captaincoaching
+capuchins
+caracal
+carbonarius
+cardiotoxic
+cardsized
+cargocarrying
+caridean
+cartesian
+cartographical
+caruncle
+carvel
+caryae
+casebearer
+casei
+cashstrapped
+cassowaries
+castrati
+casuarina
+catadromous
+catafalque
+cataloguer
+catechist
+catecholaminergic
+cath
+catnip
+catuaba
+caucasicus
+cavallo
+cavernicola
+cayennensis
+ceftriaxone
+celatus
+celebica
+cellulosebased
+cellwall
+cena
+centimes
+centimeterlong
+centra
+centre
+centreline
+centroids
+centrosaurines
+cerata
+ceratina
+cercle
+cereale
+cerebellopontine
+certainties
+cest
+chalking
+challengeresponse
+cham
+chancellorship
+chank
+chapaensis
+chapati
+chapelries
+chaplaincies
+chaps
+chard
+charmers
+charring
+chartarum
+charterhouse
+chartreuse
+chateaux
+checkbox
+checkpointing
+checkweighman
+cheesmanae
+chefowner
+cheilitis
+chemoreceptor
+chemosynthesis
+cheni
+chestnutheaded
+chia
+chica
+chicklit
+chihuahua
+childrenoriented
+chiles
+chinese
+chirp
+chlamydospores
+chlordiazepoxide
+cholangiocarcinoma
+cholesterollowering
+chopstick
+chorded
+chording
+chordophones
+choreographs
+christen
+chromeplated
+chromeyellow
+chronozone
+churchwardens
+churchwide
+chyme
+ciliogenesis
+cincinnata
+cinctella
+cinctipes
+cinematheque
+cineplex
+cinereiceps
+cinnabarinus
+circumcincta
+circumnavigates
+cislunar
+citable
+citril
+civilianmilitary
+cladodes
+clansmen
+clarifications
+clasper
+classicallytrained
+classis
+clathrincoated
+clayrich
+cleancut
+cleanshaven
+cleavers
+cleistogamy
+clenching
+cliffhangers
+clinicalstage
+clitic
+clonidine
+closedoff
+clotrimazole
+cloture
+cloudforests
+cloudiness
+clubdance
+clubrush
+clusterlily
+coCEOs
+coachbuilt
+coarctation
+coccusshaped
+coccygeal
+cocoach
+coconsul
+coconvenor
+codefensive
+codexes
+codistributed
+codling
+coelenterates
+coenzymes
+coerces
+coercing
+coercivity
+coeur
+coevolutionary
+coexpression
+coffer
+cogent
+cohabit
+coheadliners
+cojoined
+colas
+colchica
+coldpressed
+coldrain
+coleoids
+collaged
+collard
+collectivist
+collectivized
+colliculi
+collimation
+colloquialisms
+collude
+collybioides
+coloboma
+coltan
+colubrids
+comanagement
+combinatory
+commandandcontrol
+commend
+commentate
+commingled
+comminution
+communitylevel
+commutations
+complementarian
+compositors
+composti
+compressedair
+comprimario
+compulsions
+comuna
+conceptualism
+concessionary
+concordia
+concretion
+conditionals
+condors
+confertus
+configurability
+configures
+conflate
+conglutinate
+congolensis
+connectionism
+connectives
+conservativeleaning
+consignor
+consolidator
+conspicillatus
+constrictive
+containerships
+contemporaryformatted
+contextualization
+continentwide
+continuouslyrunning
+contractionary
+contrada
+contrapposto
+converses
+convoyed
+convulsive
+coorbital
+copilots
+coppersmith
+copyhold
+copywriters
+coquette
+coram
+corax
+cordovan
+corella
+coriaceous
+coronial
+corporatised
+correctors
+corrodes
+coruled
+corulers
+corymbs
+cose
+cosponsorship
+costatum
+costed
+costeffectively
+cosubstrate
+cotoneaster
+cots
+countermanded
+countermelody
+counterprotest
+counterprotesters
+counterweights
+countout
+countybased
+countyequivalent
+courtmartialled
+cowherd
+cr
+craftbased
+crameri
+crassiceps
+crataegi
+cratonic
+craved
+creatorproducer
+crenulatus
+crewmates
+crimerelated
+crippleware
+crispness
+cristae
+critter
+croceus
+crooned
+crosscheck
+crosscontamination
+crosscounty
+crosscuts
+crosseyed
+crosslanguage
+crosswinds
+crouch
+croup
+croupier
+crowdsources
+crowngroup
+cruentata
+cryoelectron
+cryptograms
+cryptologist
+cryptomonads
+cryptovirology
+crystalclear
+crystallinity
+cuddle
+cuddling
+cufflinks
+cuing
+culturalhistorical
+culturespecific
+cupronickel
+curiosa
+curvebilled
+customerpremises
+customizability
+cuticles
+cutthrough
+cwmwd
+cyanus
+cyclases
+cyclopropene
+cyclosporin
+cynically
+cyrtocone
+cysteinyl
+cytoarchitecture
+cytolytic
+dAlger
+dAlsace
+dAnnunzio
+dEtudes
+dOrbigny
+dUrbervilles
+dactylifera
+dagga
+dandelions
+dapple
+dapsone
+databased
+dataprocessing
+datasheets
+dauber
+davits
+dayandnight
+daybook
+deadbeat
+deathmatches
+debile
+debtridden
+decapeptide
+decapping
+decemvirate
+deceptrix
+decidual
+decimals
+declarant
+decompressor
+deconstructionist
+decussatus
+defeasible
+definitional
+degradative
+dehumidifiers
+dehydrating
+deidentification
+deildegast
+delavayi
+deliciosus
+delimits
+dellAdriatico
+dellOpera
+delphinidin
+deltav
+demarzi
+demissa
+demodulate
+demonetized
+demoralize
+demotic
+deniable
+densification
+depauperata
+depletes
+depolymerizing
+deprivations
+deprotonated
+depsipeptide
+dept
+derailments
+deregister
+dermatophyte
+descadre
+desertlike
+deservedly
+designees
+desorptionionization
+despondency
+desulfurization
+determinable
+detracts
+deutschen
+developerpublisher
+devilish
+dham
+dholak
+dia
+diagnostically
+dialectologist
+diarchy
+diatribe
+diborane
+dichotomus
+diclofenac
+didactics
+didst
+dieoffs
+differencing
+digitalization
+digitigrade
+digraph
+dihydrocodeine
+dihydroisocoumarin
+dihydromorphine
+dilatatum
+diminutives
+diomedea
+diopetes
+diplococci
+diptychs
+disaggregated
+disassembler
+discoinfluenced
+disconnector
+discosorid
+discosorids
+diseaseresistant
+disengaging
+disillusion
+disinfected
+disjunctus
+dislocating
+dismantles
+dispersions
+displaystyle
+displeasing
+disproportionality
+disses
+dissimilarity
+districting
+disubstituted
+diviners
+divisioona
+dizzy
+djs
+dm
+dockets
+dodecahedral
+doeuvre
+doggerel
+dogmatics
+dolce
+domelike
+dominicana
+dominus
+dorado
+dorcas
+doric
+dorsata
+doublebarrelled
+doubleclick
+doublecurvature
+doublepen
+doubler
+doubletracking
+downregulates
+downstroke
+draftsmanship
+dragnet
+dramaseries
+draperies
+draughting
+drawl
+dressmakers
+droughtresistant
+drubbing
+drugdealer
+drugdealing
+drugging
+druglike
+dualities
+duallanguage
+dualmember
+dualspecificity
+dualvoltage
+duchesses
+dumbwaiter
+dumetorum
+duncani
+dunked
+dunkeri
+duogroup
+durables
+duskywing
+dvips
+dwarven
+dwm
+dynactin
+dysgalactiae
+dystonic
+dysuria
+eCos
+eHarmony
+eXtreme
+ealdorman
+earlyBaroque
+earlyRenaissance
+earmark
+earmarking
+earring
+eastend
+ecclesiological
+eclectus
+ecofeminist
+ecologic
+ectoparasitoids
+ecuadoriensis
+eczematous
+edematous
+edtech
+eelpouts
+effusa
+efile
+egotism
+egressive
+ehealth
+ei
+eider
+eightacre
+eightcounty
+eighteenhole
+eighths
+eightpoint
+eightyfourth
+eightyseventh
+ekmanii
+elation
+elatior
+electroRB
+electrophile
+electrophysiologist
+electrotherapy
+eleventrack
+elgonensis
+elliptically
+elmeri
+elucidates
+eluvial
+emailbased
+emancipatory
+embanked
+embeddedness
+embezzler
+embolic
+embroiderer
+embryologic
+embryonal
+emertoni
+employments
+empresses
+enV
+enceinte
+encephalomyopathy
+enchantments
+enchiladas
+endochondral
+endodeoxyribonuclease
+endometritis
+endosome
+energyefficiency
+enfeoffed
+engrossing
+engulfs
+enigmas
+enjoining
+enkephalin
+enquired
+enshrinement
+enterocytes
+enteroviruses
+enterprisegrade
+entertainmentbased
+enthralling
+enthused
+entices
+entreaties
+entrenches
+entrustment
+entryways
+enumerations
+enunciate
+enunciation
+enuresis
+env
+enzymecatalyzed
+epigyne
+epilepsies
+epilepticus
+epiphysis
+epipubic
+episiotomy
+epochal
+epoxygenases
+eprocurement
+equalarea
+equalities
+equitans
+erbium
+eremic
+eremicola
+eremitic
+ergaster
+ergometer
+erinaceum
+erlangeri
+erotically
+erraticus
+erythroblasts
+escapology
+escheat
+essaying
+essentialist
+ethoxylation
+eubacterial
+eulogies
+eustatic
+evades
+ewer
+exBlack
+exMuslim
+exboxer
+exemplification
+exfoliating
+exhaling
+exhibitionism
+exigency
+exminister
+exmodel
+exocyclic
+exogenously
+exoplanetary
+expiratory
+explicable
+expolice
+extendedrelease
+extraplanar
+extrapolating
+extrema
+exuviae
+fRoots
+faafafine
+fabricii
+facilis
+factice
+factotum
+fairywrens
+falafel
+falcataria
+falcatum
+familiars
+fanfriendly
+fang
+fangame
+fanless
+fanlights
+fantastically
+farebox
+farranging
+fascicular
+fasciculosa
+fasciolatus
+fastidiosa
+fatalism
+fatherly
+fattening
+fatter
+fatwas
+fe
+federallyrecognized
+felina
+feminized
+ferryman
+ferrys
+fervidus
+festivus
+fetishist
+fiberbased
+fibrinolytic
+fibrocartilaginous
+fibrocystic
+fibromas
+fibromatosis
+fictionpolice
+fieldstones
+fim
+fimbriated
+fingerholes
+finschi
+fireadapted
+firebase
+fireboxes
+firecolored
+firetail
+firstplaced
+fitters
+fiveact
+fiveeighths
+fivenight
+fivereel
+fiveseater
+fixedincome
+fixedwidth
+fka
+flabby
+flamed
+flatfour
+flatroof
+flavoproteins
+flavovirens
+flavovittatus
+fleck
+fledglings
+fleur
+flicked
+floaters
+flocculent
+floes
+floodgate
+florae
+floristry
+floundering
+flowerrich
+flues
+fluidly
+fluorosis
+flyback
+flypast
+fogging
+folklores
+fondue
+foodplants
+foodprocessing
+foolishness
+footwide
+forepart
+foresail
+forestalled
+foretells
+forgetful
+forgoes
+formbased
+forrestii
+forza
+fosbergii
+fossicking
+fosteri
+fou
+foursomes
+fourstep
+fourthhighestgrossing
+foxhole
+frameup
+franchisebased
+francophones
+freakish
+freeforall
+freereed
+freeskiing
+freest
+freezedried
+frenchi
+fricative
+friday
+frizzen
+frolic
+fructus
+fruiteater
+fucata
+fuciformis
+fucked
+fugacity
+fullbore
+fulldepth
+fullmarket
+fullrange
+fullstack
+fumata
+fumetti
+funereal
+funfilled
+fungicidal
+funkjazz
+furca
+furcifer
+furlined
+furred
+gadogado
+gadwalls
+gahani
+galangal
+galba
+gallinae
+gallotannins
+gamertag
+gamigo
+gangeticus
+ganglioside
+gardenia
+gargantuan
+gasgenerator
+gash
+gatra
+gazella
+gelisols
+gellike
+geminus
+genei
+genericised
+genitalium
+genotypic
+geomechanics
+geosphere
+germanus
+gesso
+gettogether
+ghanensis
+ghostlike
+ghoul
+ghoulish
+gibberellin
+gigawatts
+gilva
+gingivalis
+girardi
+giro
+gist
+glabricollis
+glace
+glasshouses
+glassmakers
+glazier
+glaziovii
+glean
+glitz
+glossystarling
+glovebox
+glycines
+glycoalkaloid
+gneisses
+gobiensis
+gobo
+goldbased
+goldbearing
+golgi
+goodfaith
+gooey
+gordonii
+gores
+gouges
+governmentsubsidized
+governorelect
+gracilicornis
+grandly
+grandslam
+grangeri
+granitoid
+granzymes
+grapegrowing
+grapeshot
+graphed
+grappled
+grassbird
+grates
+gravitating
+grayblack
+grayblue
+graycolored
+graying
+greaser
+greatgreatgreat
+grebo
+greenbacks
+greenii
+greenishblue
+greenlet
+greenling
+greenskinned
+greentailed
+greenyellow
+greylag
+greywacke
+grindstones
+groenlandica
+gromwell
+groundfeeding
+grudgingly
+guanylyl
+guestedited
+guesthouses
+guillotined
+guineense
+gular
+gullwing
+gumballs
+guppies
+gusto
+gutsy
+gyroelongating
+gyros
+haLevi
+haat
+habenular
+hackneyed
+hadrosauroid
+hairthin
+halfdemon
+halfman
+halfround
+halfstaff
+halfstep
+halloween
+haloalkane
+halteres
+hammerbeam
+hampers
+handcrafting
+handlettered
+handloading
+handlooms
+handout
+handrolled
+handwired
+hangingfly
+hangouts
+haptoglobin
+harddrinking
+hardtack
+hardtail
+hardwickii
+hardyi
+harterti
+hasta
+hatchlings
+hating
+haughty
+haulers
+havanensis
+headbutt
+headbutting
+headsails
+headshot
+headshunt
+heathens
+heathi
+heatwave
+hebes
+hebridarum
+hecate
+hedged
+heifers
+helds
+hematophagous
+hendersoni
+hentzi
+hepatotoxic
+hepcidin
+herbicidal
+hereinafter
+heritors
+hermanni
+hesperia
+heterochrony
+heteroclinic
+heterostructure
+heterostructures
+heterotetramer
+heterotroph
+heterozygotes
+heydeni
+hg
+highalbedo
+highcalorie
+highcost
+highcurrent
+highlanders
+highmounted
+highrate
+highreliability
+highsensitivity
+highsociety
+highyielding
+hihats
+hildebrandtii
+hilltopping
+hilts
+hiphoprap
+histidines
+histiocytes
+histiocytoma
+histopathological
+hitched
+hitsingle
+hl
+hnRNPs
+hoarseness
+hob
+hogshead
+hogweed
+hollies
+holometabolous
+holomorphic
+homomorphisms
+homopolar
+hon
+honeycreepers
+honeypots
+hookers
+hopRB
+hormonereleasing
+hornbeams
+hornii
+hornshaped
+hornworm
+horrormysteryerotica
+horsemounted
+hospitalist
+hothouse
+hotswappable
+hotwater
+hoverboard
+howls
+hulking
+humanbased
+humanize
+humanizing
+hummock
+hun
+hus
+husbandmen
+huur
+hyaloclastite
+hybridelectric
+hybridoma
+hydatid
+hydrocodone
+hydroformylation
+hydrolyzable
+hyperboreus
+hypergiant
+hypergraphs
+hypersexuality
+hypertonia
+hypochondriac
+hypocotyl
+hypogeal
+hypogean
+hypogeum
+hypostatic
+hypotonic
+iF
+iNews
+icefall
+ices
+icicle
+iconoclasts
+ictal
+idaeus
+ideograms
+idleness
+ignita
+iguanid
+iiNet
+ikat
+ileocecal
+iliopsoas
+iliotibial
+illudens
+illusionists
+imagistic
+imbibition
+immunologists
+impetuous
+implacable
+impositions
+impregnating
+imprisonments
+inattention
+incardinated
+incisus
+incomegenerating
+inconspicuus
+inconstant
+incorporations
+incorruptible
+indents
+indigene
+indigetes
+indistinctly
+indistinctus
+indoles
+indomalayan
+inextricable
+infirmaries
+infix
+inflatum
+influenzalike
+infrahyoid
+infrasonic
+infrasound
+infraspinatus
+infructescence
+ingratiation
+ingressive
+inground
+inheritors
+inholding
+injectables
+inkblot
+inkstand
+innameonly
+innervating
+innotata
+innumeracy
+inorbit
+inordinately
+inquisitors
+inroom
+inscripta
+inscriptional
+insolubility
+instabilis
+instants
+instillation
+instream
+instructionlevel
+instrumentalonly
+insufflation
+insulins
+interapplication
+interceded
+intercolonial
+intercommunication
+intercoms
+intercondylar
+intercropping
+interdicted
+interdictor
+intergiro
+intergrade
+intermediatemass
+internationalize
+internee
+interossei
+interpolating
+intersymbol
+interurbans
+intestacy
+intracardiac
+intraspecies
+intrathoracic
+intuitionistic
+invalidly
+inviolable
+invisa
+iodides
+ionophores
+irinotecan
+irish
+irrefutable
+irrelevance
+irretrievably
+isabellina
+isentropic
+isis
+isoflurane
+isogonal
+isomerisation
+isotretinoin
+isthmica
+ixioides
+jacksonii
+jaegeri
+jailbreaks
+jangling
+javanicum
+jawline
+jazzblues
+jeg
+jejuensis
+jeotgal
+jeune
+jewish
+jitsu
+jobrelated
+jobseekers
+johannis
+johns
+jojoba
+josephinae
+jovian
+joyriding
+jubilees
+judicatory
+judice
+juiced
+jul
+jurisprudential
+jut
+juxta
+juxtapose
+kanjira
+kaolinite
+kapok
+karatedo
+karschi
+karsholti
+kaval
+kcal
+keralensis
+keratan
+keratectomy
+keratoses
+keratotic
+kerk
+kethuk
+ketoprak
+keybindings
+keyensis
+keyserlingi
+kickboxers
+kilim
+killdeer
+kilobits
+kimberleyensis
+kina
+kinetoplast
+kingi
+kingmaker
+kirki
+klossi
+knapping
+knickerbockers
+knickpoint
+knifefishes
+knotty
+kotschyi
+kraits
+krishna
+kulintang
+kumquat
+kund
+kundalini
+kunya
+kynurenine
+lAvenir
+lOntario
+labyrinthodonts
+lacelike
+lacrosseonly
+lactiferous
+lahar
+lamarckii
+lambdoid
+lamberti
+lamellata
+lamia
+laminectomy
+landfalls
+languagespeaking
+languida
+lappet
+larches
+largecell
+largeleaf
+largestcirculation
+largetoothed
+largish
+larseni
+laserassisted
+lasso
+latefasciatus
+latenineteenthcentury
+lateralization
+laters
+latirostris
+latum
+lawrencei
+layardi
+lbf
+leadsingle
+leafflower
+leafroll
+leai
+leastused
+leftism
+leftside
+legge
+leggenda
+legrandi
+lekking
+lemoulti
+lengua
+lentiginous
+leonardi
+leonis
+leptons
+lespedeza
+lesscommon
+lessthantruckload
+leuconota
+levitated
+liaised
+liberator
+libpcap
+libro
+licentiousness
+lichenoides
+liege
+lifelines
+lifesustaining
+lightbrown
+lightdependent
+lightyear
+lijsttrekker
+likelier
+limata
+linda
+lineament
+linemandefensive
+lingers
+linksstyle
+linnet
+lipidomics
+lipreading
+lirica
+liui
+liveactionCGI
+liveactioncomputeranimated
+liveinthestudio
+livemusic
+lividum
+locallyproduced
+localregional
+locationspecific
+lockkeepers
+lockon
+lockouts
+locksmiths
+loggerheads
+loi
+lolita
+lon
+loners
+longbox
+longclaws
+longerestablished
+longfooted
+longisporum
+longplayer
+longshort
+longsnout
+longspine
+loosed
+loris
+lostwax
+loveinterest
+loveridgei
+lovestruck
+lowbypass
+lowermiddleclass
+lowestlevel
+lowestranking
+lowlife
+lowpaying
+lowwinged
+loxP
+lumawigi
+lumbermen
+lumborum
+lupines
+lupulus
+lustful
+luxuriously
+lyallii
+lydekkerianus
+lymphadenitis
+lyssavirus
+machismo
+macrocosm
+macroeconometric
+macrolides
+macrospora
+madagascariense
+maderensis
+madia
+mafiatype
+magmatism
+magnesiumrich
+magnetohydrodynamic
+magnetooptical
+magnetostrictive
+magnoliids
+maguey
+maia
+maim
+mainwheels
+maisonette
+majordomo
+malagasy
+mallows
+malonic
+mammoplasia
+mammy
+manat
+mand
+manhandled
+manicure
+mantidfly
+manu
+manumitted
+manycolored
+mapbased
+mapboards
+mapmakers
+maquette
+marchio
+margaritacea
+margraviate
+mariculture
+marketrate
+markups
+marmalades
+martialarts
+martialed
+masers
+masseuse
+masterclass
+masticatory
+mataiapo
+matchboxes
+matchings
+matricellular
+matrixassociated
+matthewsii
+maven
+meanspirited
+meatless
+mechanician
+mechanotransduction
+mediations
+medico
+medio
+mediumformat
+mediumlength
+mediumscale
+mediumweight
+medullaris
+megalitres
+megastar
+megastructure
+melanistic
+melanopus
+melanosticta
+melomys
+memetic
+mendicants
+menstruating
+meow
+meprobamate
+merchandised
+meri
+meridensis
+meridianus
+meridional
+merlin
+merous
+merrymaking
+merula
+meshless
+mesmerized
+mesocortex
+mesophytic
+mesothelium
+metacognition
+metalinfluenced
+metalliferous
+metalloids
+metalthrash
+metalware
+metaphilosophy
+meteoroid
+methylenetetrahydrofolate
+methylmalonic
+metriorhynchid
+metrosexual
+mettle
+micelle
+michaeli
+micranthum
+microalga
+microbats
+microblogs
+microburst
+microcap
+microdissection
+microdon
+microevolution
+microfilming
+microgames
+microlights
+micromarketing
+microphthalma
+microplates
+microstoma
+midCretaceous
+middecade
+middistance
+midlatitude
+midpaced
+midrd
+midtolates
+mileposts
+milfoil
+millenniums
+mimes
+mimesis
+mineralisation
+minggah
+miniatus
+minidocumentary
+miniflyweight
+minihumbucker
+minisite
+minusculus
+minutissimum
+minyan
+misapplication
+misheard
+misinformed
+misperceptions
+mispronunciation
+mistreating
+miterwort
+mitomycin
+mitred
+mixedNOCs
+mixedvoice
+mixins
+mocha
+modernizes
+modernly
+modiglianii
+modiolus
+modo
+molder
+molesta
+mollymawk
+moluccana
+momenta
+monads
+moneda
+monetarily
+monocrystalline
+monodi
+monohulls
+monolinguals
+mononeuropathy
+monopolist
+monotony
+monotypical
+monozygotic
+monthslong
+moog
+moonlit
+moonseed
+moots
+mopping
+morphometrics
+morrisii
+mortared
+mossi
+mosslike
+mostadded
+mostknown
+mostperformed
+motmots
+mountainaccess
+mourns
+mousy
+mouthfeel
+movierelated
+muchanticipated
+mucida
+mucins
+mucolytic
+muerte
+muffed
+muffled
+mulching
+mullions
+multiangle
+multiasset
+multibody
+multiclass
+multicostata
+multigame
+multigene
+multihop
+multilineata
+multimammate
+multimeter
+multiobjective
+multiplayeronly
+multipleinput
+multiplications
+multipole
+multiround
+multisporting
+multistrategy
+multocida
+mummers
+mura
+muralists
+murdermystery
+mus
+muscled
+museologist
+mushers
+mushroomtongue
+mushy
+mustangs
+musthave
+mutabile
+mutator
+mutilate
+mutuality
+myersi
+mysids
+mysterycrime
+mysterydrama
+mysterysuspense
+myxozoan
+nVidia
+naiads
+naivete
+nameservers
+nannodes
+nanobiotechnology
+nanocomposites
+nanofabrication
+nanophotonics
+nanoscopic
+nanosponges
+napus
+narrowheaded
+nasals
+nationalinternational
+nationalsecurity
+natura
+naturopathy
+navigations
+ndlargest
+ndu
+nearrecord
+nebulosity
+necromancy
+nectars
+needling
+neophyte
+neotenic
+neotropica
+nephrosis
+nereis
+neriifolia
+nesiotes
+netlabels
+neuroendocrinology
+neurokinin
+neuroleptics
+neuropathologist
+neuropil
+neuroprotection
+neuroradiology
+neurostimulation
+nevermanni
+newlyelected
+newsboy
+newsfeed
+newsfeeds
+ngoma
+nicefori
+nif
+nigella
+nightflying
+nightingalethrush
+nightwatchman
+nigripuncta
+ninebark
+nineepisode
+ninemile
+ninetieth
+ninetythree
+nipa
+nitration
+nitre
+nitromethane
+nobiliary
+nociceptin
+noire
+nomadism
+nominalism
+nonAboriginal
+nonIslamic
+nonalignment
+nonanimated
+nonbasic
+nonbiodegradable
+nonblack
+noncelebrity
+nonchalant
+noncognitivism
+noncollagenous
+noncommercially
+noncompilation
+nondrafting
+nonelectrical
+nonextant
+nonfigurative
+nongeographical
+nonhistone
+nonhybrid
+nonleap
+nonlifethreatening
+nonlocality
+nonnetwork
+nonneuronal
+nonnormative
+nonobviousness
+nonpersistent
+nonproportional
+nonreflective
+nonrepudiation
+nonrestricted
+nonsanctioned
+nonscripta
+nonserious
+nonsingular
+nontidal
+nontimber
+norepinephrinedopamine
+normalizes
+normani
+normativity
+norovirus
+nortestosterone
+noster
+nostra
+notehead
+notepad
+nothofagi
+noticeboard
+novellength
+nowcanceled
+nowretired
+npower
+nucleotidegated
+nudists
+nul
+numerator
+nummularia
+nymphaeum
+oC
+oast
+obfuscate
+objectify
+objectivebased
+oblates
+obliterata
+obliterating
+obovatus
+obviousness
+occipitotemporal
+occlude
+occlusive
+occulting
+occultists
+ochraceum
+octogenarian
+officebearer
+offputting
+offthecuff
+oftenused
+ohmic
+oilfilled
+ok
+oldiesclassic
+oleaster
+omental
+omissa
+omnitruncated
+omphalinoid
+onager
+onehorse
+oneliner
+onestring
+onfarm
+openangle
+openwheeled
+ophioglossoides
+ophiolites
+ophthalmoplegia
+opilio
+opimus
+opposable
+opposedpiston
+optimates
+orangespotted
+orbis
+oregona
+organisationally
+organismic
+organizationwide
+organoids
+oribatid
+orientational
+ornatipes
+ornatum
+orographically
+orthometric
+osteopaths
+otaku
+otome
+outlawry
+outoffocus
+outpolled
+outrages
+outrighted
+outsmart
+outspread
+outwits
+ovalifolium
+ovenbirds
+overblowing
+overcurrent
+overfished
+overfitting
+overflight
+overlong
+oversimplification
+overtopping
+ovipennis
+oviraptorosaurs
+ovulate
+owlflies
+owlsclover
+oxaloacetate
+oxoacid
+oxyanion
+oxygenic
+oyer
+pachycephalosaurs
+pacifying
+paddlefish
+paella
+pagana
+painlessly
+palaeographer
+palaestra
+palay
+paleobotanist
+paleography
+palla
+pallasite
+palliatus
+palma
+palpal
+panIndia
+panchromatic
+pandora
+paned
+pangenome
+panhandling
+paniculate
+panniculus
+panniers
+papilledema
+paraaortic
+paraboloid
+paracanoe
+paralimbic
+parasitised
+parastatals
+paratonia
+paratroops
+parchmentlike
+parfait
+parietooccipital
+parliamentarism
+parol
+particleboard
+partita
+partwork
+parviflorus
+parvifolium
+paspalum
+passagerequired
+pata
+paternoster
+patriarchates
+patristics
+patronal
+pattress
+pau
+paume
+pawl
+pawnbrokers
+paygrade
+peacockpheasant
+pearcei
+pebbledash
+pectinase
+peculiaris
+pedata
+pedestrianfriendly
+pedestrianization
+pedestrianonly
+pedipalp
+peeping
+peeress
+pegasus
+pellagra
+pelle
+pellitory
+pemoline
+penciltailed
+penduline
+penetrations
+penpal
+pensioned
+pensylvanicus
+pentamer
+peonage
+percepts
+percolate
+perenne
+performingarts
+perfumers
+pergandei
+periaqueductal
+periderm
+perimenopause
+perineurium
+perm
+permadeath
+permeating
+personalizing
+perspex
+perturb
+pervasiveness
+perverted
+peseta
+petersii
+petrophila
+peyrierasi
+phenethyl
+phenylpropene
+philippiana
+philippinus
+philtrum
+phlebotomy
+phloroglucinol
+phoenicea
+phonebased
+phono
+phosphatidylethanolamine
+phosphocreatine
+photoacoustic
+photogrammetric
+photoionization
+photoperiod
+phycology
+physiochemical
+phytoalexin
+phytogeography
+pianistbandleader
+pici
+picturatus
+picturized
+piedra
+piercers
+pietas
+pietra
+piger
+piha
+pileata
+pilosella
+pinelands
+pinelike
+pinkishwhite
+pinstripe
+pinstriped
+pintle
+piperata
+piperita
+pipetting
+pipework
+piranhas
+piscivorus
+piste
+pitiful
+placegetters
+planers
+planetoid
+planicollis
+planispirally
+planked
+plasmin
+plastering
+platformagnostic
+platformpuzzle
+plats
+plaumanni
+playercreated
+playhouses
+playonwords
+playschool
+playscript
+pleco
+pleinair
+pleopods
+plethysmography
+pleurisy
+plexiform
+poensis
+poetsaint
+pointillism
+pointofpurchase
+pokeweed
+polarities
+poled
+polenta
+policyrelated
+polingi
+pollarded
+pollards
+polyamides
+polychroma
+polycomb
+polyelectrolyte
+polymerbased
+polypectomy
+polypoid
+pompadour
+ponderous
+ponerine
+pooram
+popart
+popcountry
+popera
+poprap
+populariser
+populists
+porcellus
+porphyroblasts
+portalRobert
+portalWilliam
+portent
+postBeatles
+postBritpop
+postKatrina
+postabortion
+postcanine
+postcapitalist
+postcrisis
+postcyberpunk
+postfeminism
+posthole
+postoperatively
+postseasons
+pouchlike
+powwows
+ppi
+praenomina
+preBroadway
+preConfederation
+preamps
+preapproval
+precognitive
+precuneus
+predispositions
+predrilled
+preevent
+preferment
+prehuman
+premRNAs
+premenstrual
+prequalification
+prescriptiononly
+preselector
+presenilin
+presidentCEO
+presolar
+presuppose
+presupposed
+pretest
+prez
+pricei
+primase
+printf
+proPalestinian
+problematica
+problembased
+procommunist
+prodromal
+producercomposer
+proficiencies
+profligate
+progun
+prohormones
+projectional
+prolixus
+proloculus
+propanediol
+propertycasualty
+proplyds
+propodeal
+propodeum
+proselytization
+proteasomes
+proteinDNA
+proteinbased
+protem
+protocarnivorous
+protolanguages
+protolith
+protostars
+protosuchian
+provenience
+provincials
+pry
+przewalskii
+pseudepigraphic
+pseudohistory
+pseudopods
+pseudowire
+psychobiology
+psychographic
+psychokinesis
+psychokinetic
+psycholinguistic
+pu
+publichealth
+publicrelations
+puerile
+puklo
+pullata
+pulloff
+punctifrons
+punctiventris
+punditry
+purifies
+purlins
+purpleglossed
+purplethroated
+purpleveined
+pursuer
+pursuivant
+purveyed
+pushchairs
+pushover
+putamen
+puzzleplatformer
+pyrrolidine
+quadrifidus
+quadrivium
+quadruplets
+quaestors
+quagmire
+quandong
+quantitation
+quarrelsome
+quarterbacked
+quarterlies
+quasilegal
+quasiofficial
+quickservice
+quiero
+quieting
+quitrent
+quizbowl
+quorums
+radiatum
+radioshow
+radular
+raffrayi
+rages
+railguns
+railtour
+railyards
+rainbowcolored
+ramjets
+ramsayi
+ramsons
+ranchera
+rangefinders
+ranita
+rapa
+rapidacting
+rapidresponse
+rappel
+ratcatcher
+ravelin
+ravers
+rayed
+rdlargest
+rdparty
+rds
+reaccreditation
+readapted
+reals
+reaming
+reanimate
+reappoint
+reappropriated
+rearm
+reassignments
+reattributed
+reawakened
+reawakening
+recapitalization
+recasts
+recline
+recliner
+recoiloperated
+recollect
+recollected
+recondita
+reconnoiter
+reconstituting
+reconversion
+recrimination
+rectangularshaped
+recuperative
+recusancy
+recyclables
+recycler
+redbay
+redblue
+redbodied
+redbud
+redchested
+redcrested
+reddelli
+redfooted
+rednecks
+redressing
+reductant
+redviolet
+reefbuilding
+reengage
+reevaluating
+refills
+reframe
+refreshable
+refulgens
+regenerator
+regionalized
+regionbased
+regnum
+regurgitate
+rehabilitates
+rehash
+reimbursable
+reincarnate
+reincorporation
+reinstall
+reiterates
+relabelled
+relapsingremitting
+relativist
+remelting
+remiges
+remits
+remitting
+remunerated
+renews
+replenishes
+replevin
+reprieves
+reprimands
+reprintings
+reregister
+rerigged
+resected
+reseeded
+reshoot
+residentially
+resp
+restate
+restricta
+restructures
+resupplying
+resurveyed
+retakes
+retiarius
+retinas
+retool
+retractions
+retransmitting
+retrofits
+retrotransposon
+returnable
+revenant
+reverent
+reversa
+reverser
+rhatany
+rhineurids
+rhizospheres
+rhombifer
+richardi
+rideshare
+ridgeway
+rightbranching
+rightsholder
+rigsdaler
+rioni
+roadbased
+roadlegal
+roadmaps
+rochet
+rockhard
+rockinspired
+rockpostpunk
+rockreggae
+roled
+rollouts
+romancing
+romantics
+roqueforti
+rosaries
+rotisserie
+rotoscoped
+roughest
+roundeared
+rubriventris
+rubythroat
+ruffles
+rufousfronted
+rufoussided
+rugulosa
+rumah
+runandgun
+russelli
+sTLD
+sabbaticals
+sabulosa
+saccades
+saccharolytic
+saccharum
+sachalinensis
+sacristan
+saddlery
+saddleshaped
+safehouse
+sagitta
+saillike
+sailmakers
+salebrosus
+salicylates
+salinarum
+salivarius
+salmonellosis
+salmonfly
+saltwort
+salvia
+samizdat
+sandflies
+sandplains
+sandspit
+sanidine
+sants
+saponification
+sargentii
+satchel
+savez
+sawedoff
+scaffoldin
+scaleless
+scalene
+scalywinged
+scarecrows
+scarps
+schaeferi
+scheffleri
+schemer
+schiffornis
+schilbid
+schistacea
+schistosomes
+schizoid
+schwannomas
+schweinfurthii
+sci
+sciatica
+scienceoriented
+scififantasy
+sclerotherapy
+scolding
+scone
+scoparium
+scorekeeping
+scrambler
+scratchoff
+screamers
+screeners
+screenprinted
+screeves
+scribed
+scribing
+scrimshaw
+scrivener
+scrummager
+scud
+scudderi
+scullers
+sculptilis
+sculpturatus
+scythes
+seaice
+sealskin
+seasnake
+seborrheic
+secessions
+sechellarum
+secondconsecutive
+secreta
+seductress
+seedbed
+segueing
+sein
+seismometer
+selfabsorption
+selfacting
+selfassessed
+selfconfident
+selfdoubt
+selfenhancement
+selfextracting
+selfheal
+selfinjury
+selflocking
+selfmanaging
+selfportraiture
+selfpublishes
+selfreflective
+selfreports
+selfrestraint
+selfsealing
+selfselected
+selfsupport
+selfsynchronizing
+selftrained
+sellata
+sellside
+semiabstract
+semibrunnea
+semicircles
+semiconservative
+semidry
+semifast
+seminigra
+semireality
+semiregularly
+semitractor
+sensitisation
+sensus
+sentinels
+septostomy
+seraph
+seraphim
+serenata
+serinethreoninespecific
+setal
+setoff
+setulosa
+sevenacre
+sevencounty
+sevenseat
+sevenseater
+seventysixth
+sexier
+sexpunctatus
+seychellarum
+shafferi
+shameless
+sharable
+sharpnosed
+shastra
+shavers
+shawarma
+shea
+sheave
+shebeens
+shelducks
+shepherdess
+shied
+shikimic
+shipwrights
+shogunate
+shortduration
+shorthead
+shortlasting
+shortsighted
+shorttime
+shovelers
+shrewrat
+shruti
+shura
+sibia
+sibiricum
+siddha
+sidebars
+sideburns
+sidechains
+sidegill
+sidemounted
+sidestick
+sidewinder
+sieberi
+sightseers
+signalmen
+signore
+signsoff
+silene
+siliciclastic
+silicosis
+siliques
+sillimanite
+silvermedal
+sima
+similarlytitled
+similes
+simulationbased
+singlefield
+singlegender
+singleleaf
+singlepurpose
+singlestep
+singlewalled
+sinistra
+sinuate
+siphonophore
+sis
+sistergroup
+sisterstation
+situating
+sixacre
+sixalbum
+sixcarbon
+sixspotted
+sixthhighest
+skacore
+skeptically
+skiathlon
+skittles
+skydive
+slabsided
+slatybacked
+slaving
+sledging
+sleepaway
+slewing
+slimmest
+slipcover
+slowburning
+slowtempo
+smallcaliber
+smallmarket
+smallscaled
+smiled
+smug
+smuts
+snappy
+snelleni
+snowcats
+snowdrops
+snowplows
+sobria
+socioecological
+sociolegal
+socken
+sodalis
+softwareonly
+sogno
+sojae
+solaris
+soloed
+somalicus
+somatotropin
+somites
+songanddance
+sop
+sophia
+sorely
+sorters
+sortes
+sotol
+soubrette
+souldisco
+soundcard
+soundeffects
+sourness
+souschef
+southeasterncentral
+spacegrant
+spacerock
+sparrowsized
+spectroscopically
+spectroscopist
+speedier
+speleology
+spermatophores
+spermicide
+sphaerica
+sphaericus
+sphingolipids
+spiculum
+spiderlily
+spiker
+spiketail
+spinART
+spinae
+spinetipped
+spinigera
+spinneret
+spinsters
+spirometry
+spittlebugs
+splitreel
+spongiosum
+sportsdrama
+sprawls
+spurwinged
+spymaster
+squadra
+squib
+stagnating
+stairstep
+stalkless
+stampings
+stan
+starched
+starfield
+starfighter
+stargazers
+statebred
+staybehind
+stearothermophilus
+steelreinforced
+steelworker
+steeplechasers
+steerage
+stegosaur
+stellatum
+stenographers
+stenopetala
+stenotype
+stephani
+stepparent
+stercoralis
+sterilis
+sternoclavicular
+stewarding
+stickball
+stillextant
+stingy
+stipitatum
+stlargest
+stoa
+stockier
+stoke
+stoptime
+storksbill
+stovetop
+stowaways
+straightbilled
+straighteight
+straightsnouted
+straightwing
+stranka
+streetfighter
+strenuus
+stresemanni
+striatella
+strippedback
+stroboscope
+strongpoints
+studentbased
+studentdirected
+studioalbum
+studiooffice
+stunningly
+sturgeons
+stygia
+subLOGIC
+subaquatic
+subaqueous
+subband
+subbrands
+subcosmopolitan
+subdirector
+subentity
+subgen
+subhuman
+subjunctive
+subleased
+sublingua
+submarket
+submillimetre
+subpost
+subscript
+subscripts
+subsisted
+subspaces
+substantiation
+subthalamus
+subtleties
+suburbanites
+subvillages
+subwavelength
+succubi
+sudanensis
+suduirauti
+sugaring
+sulcatum
+summiting
+summitted
+sumptuosa
+superblock
+supercup
+superego
+superfluidity
+superfluids
+superimposes
+superimposing
+supermoon
+supernaturally
+supertanker
+superyachts
+supracondylar
+supralibros
+supratrochlear
+surfacesupplied
+surficial
+surfperches
+swaggering
+swainsonii
+swindles
+swinhoei
+swiped
+swiss
+switchback
+swooping
+swordfighting
+swordgrass
+sympatry
+syncretistic
+syncretized
+synovitis
+synthdriven
+synthheavy
+syriacus
+systemspecific
+tabloidsize
+tabulations
+taczanowskii
+tahitensis
+tailshield
+talbotii
+talismanic
+tambourines
+tamsi
+tangleweb
+taphonomic
+taskspecific
+tastefully
+tattooist
+technocracy
+technoscience
+tectus
+tegument
+tela
+teleported
+televisionfilm
+telugu
+temperaturesensitive
+tenable
+tenellum
+tenesmus
+tenfoot
+tensong
+tera
+teramachii
+terga
+terminologyoriented
+termly
+ternata
+terraformed
+terreus
+testability
+testaceipes
+testaceum
+testbench
+tetrads
+tetrahydrofuran
+tetramers
+textmessaging
+textually
+thenBritish
+thenMinister
+thencontroversial
+thenknown
+thenongoing
+thensister
+theobromae
+thermalis
+thermite
+thermochromic
+thermography
+thermohaline
+theyyam
+thhighestgrossing
+thiazole
+thimerosal
+thinskinned
+thirdever
+thirdfloor
+thirdranked
+thirdsmallest
+thirdtallest
+threadsafe
+threecolored
+threeline
+threenote
+threeposition
+threeseater
+threshers
+thrifts
+throb
+thromboembolic
+throttled
+thymosin
+tilling
+tiltwing
+timberlands
+timbral
+tintinnabulum
+tipoff
+titania
+titillation
+titleThe
+tody
+togoensis
+tollways
+tondo
+toneddown
+tonsillar
+toolpath
+toon
+topdressing
+topmounted
+topspin
+torchbearers
+tornadic
+tornados
+torogan
+tou
+toulgoeti
+townscape
+toxopei
+toying
+tp
+tracheobronchial
+trackandfield
+trackwork
+tradein
+trademarking
+tramlines
+tranquilizers
+transcendentalism
+transferral
+transfused
+transhuman
+transhumance
+transience
+translatable
+transliterating
+transload
+transloading
+translocates
+translunar
+transmissive
+transnationally
+transpacific
+transpires
+transplantations
+transverses
+transvestism
+trashy
+trax
+treader
+trebles
+treefrogs
+treehunter
+treering
+treetop
+trehalose
+trellises
+triakis
+triaugmented
+tribosphenic
+tribunician
+trichloroethane
+trichomoniasis
+tricorne
+trifolia
+trilateration
+trilling
+trilobata
+trinitrate
+triphenylchloroethylene
+tripwires
+triteleia
+tritone
+trochaic
+trochanteric
+troglobites
+troublemakers
+trustfund
+truthvalue
+tryoni
+trypsinlike
+tsars
+tserkvas
+tsugae
+tubericollis
+tucking
+tugged
+tularemia
+tuneup
+tunicle
+turanica
+turbinata
+turbot
+turnbowi
+tutta
+tvs
+tweeters
+twelveepisode
+twelveminute
+twentyfirstcentury
+twinrotor
+twinturbo
+twistedpair
+twitter
+twobarred
+twoblock
+twocandidate
+twodecade
+twofaced
+twofifths
+twoforone
+twoleg
+twolight
+twospan
+twospirit
+twostoried
+twostringed
+twothree
+twoweeklong
+twowheeler
+twoword
+tyer
+typeA
+typifying
+typos
+tyrannosaur
+tyrannosauroid
+tyrannosaurs
+ulcerated
+ulei
+ultrasoundguided
+ultrasounds
+umbellatum
+umbilicate
+umbraculum
+unachievable
+unbalance
+unbanned
+unbleached
+unblocked
+unbroadcast
+unclassifiable
+unclothed
+undemanding
+underbrush
+underlings
+undernourished
+undershot
+understatement
+understeer
+understudying
+undervalue
+undosa
+unformed
+unfortified
+unicolored
+unicyclist
+unifascia
+uniflorum
+unionizing
+unitized
+universityaffiliated
+unlistenable
+unmonitored
+unnerving
+unornamented
+unpasteurised
+unpronounceable
+unrolling
+unsaturation
+unscented
+unsorted
+unsurprising
+untalented
+untangle
+untaxed
+untoothed
+unwholesome
+uomo
+updatable
+upended
+upfield
+upperbody
+upscaled
+upselling
+uptick
+urethritis
+uropeltid
+uropods
+userselected
+ute
+vaccinating
+vaccinology
+vacuumchannel
+vacuumtube
+vaginally
+vallecula
+valuesbased
+vampirethemed
+vanquish
+variata
+variates
+varipes
+varus
+vasodilators
+vectorized
+vel
+venipuncture
+ventilatory
+verismo
+vermiculatus
+vermiculite
+vernalgrass
+verrilli
+versioned
+vervet
+vesicatoria
+vespid
+vetus
+vetusta
+vexans
+vexillological
+vibrans
+viceroyalty
+videodisk
+vigintiviri
+viharas
+vilis
+villosum
+vimana
+vinaigrette
+vinegars
+vinelike
+viscosus
+vitrinite
+vittigera
+vivesi
+viviparity
+vivir
+vocalistsongwriter
+vocalskeyboards
+voi
+voiceactivated
+voicetracked
+voir
+volleyballer
+volubilis
+vomited
+voyagers
+voyeur
+vulgarism
+vulval
+wada
+waddling
+wale
+walkietalkies
+wallaroo
+walsinghami
+warmups
+warmweather
+washable
+waterhousei
+watertable
+wavering
+waxcaps
+weaponsgrade
+weatherboarding
+weathercaster
+webMethods
+webtoons
+webzines
+wedgeleaf
+wellproportioned
+wellwatered
+werejaguar
+westerner
+wetware
+whitecapped
+whitechinned
+whitings
+wholegrain
+whoopee
+wicking
+wilfordii
+windbreaks
+windpollinated
+windpowered
+windsurf
+windtunnel
+winerys
+wingbars
+wireworm
+wisteria
+wkndstv
+woodcarvings
+woodchip
+woodcraft
+woodfordi
+woodi
+woodlot
+woodlots
+woodswallow
+wooed
+wools
+woolstore
+workgroups
+worksite
+wormeating
+wows
+writeonce
+writeractor
+wrongdoers
+wryly
+ws
+wunderkind
+wurdackii
+wurrung
+xaxis
+xenarthrans
+xenos
+xenotransplantation
+xyloglucan
+yellowcress
+yellowfooted
+yellowishgray
+yellowthroat
+yi
+yn
+yodel
+yodeler
+yogurts
+yuca
+yunnana
+yuppie
+zari
+zealandica
+zealot
+zerocarbon
+zeroemission
+zerotolerance
+zeteki
+zhejiangensis
+ziemia
+zimmermanni
+zircons
+zombified
+zonalis
+zonation
+zorro
+ztl
diff --git a/app/src/main/java/com/example/myapplication/AppContext.kt b/app/src/main/java/com/example/myapplication/AppContext.kt
new file mode 100644
index 0000000..ab11a18
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/AppContext.kt
@@ -0,0 +1,12 @@
+package com.example.myapplication
+
+import android.content.Context
+
+object AppContext {
+ lateinit var context: Context
+ private set
+
+ fun init(ctx: Context) {
+ context = ctx.applicationContext
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/GuideActivity.kt b/app/src/main/java/com/example/myapplication/GuideActivity.kt
index 97c1a22..af4aa1f 100644
--- a/app/src/main/java/com/example/myapplication/GuideActivity.kt
+++ b/app/src/main/java/com/example/myapplication/GuideActivity.kt
@@ -24,6 +24,7 @@ import androidx.core.content.ContextCompat
import android.widget.ImageView
import android.text.TextWatcher
import android.text.Editable
+import com.example.myapplication.network.BehaviorReporter
class GuideActivity : AppCompatActivity() {
@@ -91,12 +92,22 @@ class GuideActivity : AppCompatActivity() {
}
// 情话复制
findViewById(R.id.love_words_1).setOnClickListener {
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "guide",
+ "element_id" to "copy_example_1",
+ )
val text = it as TextView
val clipboard = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setPrimaryClip(ClipData.newPlainText("text", text.text))
Toast.makeText(this, "Copy successfully", Toast.LENGTH_SHORT).show()
}
findViewById(R.id.love_words_2).setOnClickListener {
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "guide",
+ "element_id" to "copy_example_2",
+ )
val text = it as TextView
val clipboard = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setPrimaryClip(ClipData.newPlainText("text", text.text))
diff --git a/app/src/main/java/com/example/myapplication/ImeGuideActivity.kt b/app/src/main/java/com/example/myapplication/ImeGuideActivity.kt
index c54095e..ab1afd5 100644
--- a/app/src/main/java/com/example/myapplication/ImeGuideActivity.kt
+++ b/app/src/main/java/com/example/myapplication/ImeGuideActivity.kt
@@ -12,6 +12,7 @@ import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
+import com.example.myapplication.network.BehaviorReporter
class ImeGuideActivity : AppCompatActivity() {
@@ -33,6 +34,11 @@ class ImeGuideActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_ime_guide)
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "keyboard_permission_guide",
+ )
+
Log.d(TAG, "onCreate")
btnEnable = findViewById(R.id.enabled) // btn启用输入法
@@ -69,6 +75,15 @@ class ImeGuideActivity : AppCompatActivity() {
}
}
+ override fun onBackPressed() {
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "keyboard_permission_guide",
+ "element_id" to "close_btn",
+ )
+ super.onBackPressed()
+ }
+
private fun registerImeObserver() {
if (imeObserver != null) return
@@ -153,6 +168,11 @@ class ImeGuideActivity : AppCompatActivity() {
selectLayout.setOnClickListener {
val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
imm.showInputMethodPicker()
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "keyboard_permission_guide",
+ "element_id" to "open_settings_btn",
+ )
}
}
@@ -192,6 +212,11 @@ class ImeGuideActivity : AppCompatActivity() {
selectText.setTextColor(Color.parseColor("#A1A1A1"))
step1.text = "Completed"
step2.text = "You have completed the relevant Settings"
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "keyboard_permission_guide",
+ "element_id" to "close_btn",
+ )
Toast.makeText(this, "The input method is all set!", Toast.LENGTH_SHORT).show()
try {
startActivity(Intent(this, GuideActivity::class.java))
diff --git a/app/src/main/java/com/example/myapplication/MainActivity.kt b/app/src/main/java/com/example/myapplication/MainActivity.kt
index 9afd912..d47bb31 100644
--- a/app/src/main/java/com/example/myapplication/MainActivity.kt
+++ b/app/src/main/java/com/example/myapplication/MainActivity.kt
@@ -1,6 +1,7 @@
package com.example.myapplication
import android.os.Bundle
+import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
@@ -10,6 +11,7 @@ import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import com.example.myapplication.network.AuthEvent
import com.example.myapplication.network.AuthEventBus
+import com.example.myapplication.network.BehaviorReporter
import com.example.myapplication.utils.EncryptedSharedPreferencesUtil
import com.google.android.material.bottomnavigation.BottomNavigationView
import kotlinx.coroutines.flow.collectLatest
@@ -59,6 +61,45 @@ class MainActivity : AppCompatActivity() {
private val globalNavController: NavController
get() = globalHost.navController
+ // =======================
+ // 全局路由埋点:新增字段
+ // =======================
+ private val ROUTE_TAG = "RouteReport"
+
+ private var lastHomeDestIdForReport: Int? = null
+ private var lastShopDestIdForReport: Int? = null
+ private var lastMineDestIdForReport: Int? = null
+ private var lastGlobalDestIdForReport: Int? = null
+
+ // 统一 listener,方便 add/remove
+ private val homeRouteListener =
+ NavController.OnDestinationChangedListener { _, dest, _ ->
+ if (lastHomeDestIdForReport == dest.id) return@OnDestinationChangedListener
+ lastHomeDestIdForReport = dest.id
+ reportPageView(source = "home_tab", destId = dest.id)
+ }
+
+ private val shopRouteListener =
+ NavController.OnDestinationChangedListener { _, dest, _ ->
+ if (lastShopDestIdForReport == dest.id) return@OnDestinationChangedListener
+ lastShopDestIdForReport = dest.id
+ reportPageView(source = "shop_tab", destId = dest.id)
+ }
+
+ private val mineRouteListener =
+ NavController.OnDestinationChangedListener { _, dest, _ ->
+ if (lastMineDestIdForReport == dest.id) return@OnDestinationChangedListener
+ lastMineDestIdForReport = dest.id
+ reportPageView(source = "mine_tab", destId = dest.id)
+ }
+
+ private val globalRouteListener =
+ NavController.OnDestinationChangedListener { _, dest, _ ->
+ if (lastGlobalDestIdForReport == dest.id) return@OnDestinationChangedListener
+ lastGlobalDestIdForReport = dest.id
+ reportPageView(source = "global_overlay", destId = dest.id)
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
@@ -105,9 +146,11 @@ class MainActivity : AppCompatActivity() {
openGlobal(R.id.loginFragment)
}
}
+
is AuthEvent.GenericError -> {
- Toast.makeText(this@MainActivity, event.message, Toast.LENGTH_SHORT).show()
+ Toast.makeText(this@MainActivity, event.message, Toast.LENGTH_LONG).show()
}
+
// 登录成功事件处理
is AuthEvent.LoginSuccess -> {
// 关闭 global overlay:回到 empty
@@ -123,24 +166,46 @@ class MainActivity : AppCompatActivity() {
}
}
pendingTabAfterLogin = null
+
+ // 处理intent跳转目标页
+ if (pendingNavigationAfterLogin == "recharge_fragment") {
+ openGlobal(R.id.rechargeFragment)
+ pendingNavigationAfterLogin = null
+ }
+
+ // ✅ 登录成功后也刷新一次
+ bottomNav.post { updateBottomNavVisibility() }
}
+
// 登出事件处理
is AuthEvent.Logout -> {
pendingTabAfterLogin = event.returnTabTag
-
+
// ✅ 用户没登录按返回,应回首页,所以先切到首页
switchTab(TAB_HOME, force = true)
-
+
bottomNav.post {
bottomNav.selectedItemId = R.id.home_graph
openGlobal(R.id.loginFragment) // ✅ 退出登录后立刻打开登录页
}
}
+
// 打开全局页面事件处理
is AuthEvent.OpenGlobalPage -> {
- // 打开指定的全局页面
openGlobal(event.destinationId, event.bundle)
}
+
+ is AuthEvent.UserUpdated -> {
+ // 不需要处理
+ }
+
+ is AuthEvent.CharacterDeleted -> {
+ // 不需要处理
+ }
+
+ is AuthEvent.CharacterAdded -> {
+ // 不需要处理,由HomeFragment处理
+ }
}
}
}
@@ -158,9 +223,27 @@ class MainActivity : AppCompatActivity() {
TAB_MINE -> R.id.mine_graph
else -> R.id.home_graph
}
+ updateBottomNavVisibility()
}
}
+ override fun onResume() {
+ super.onResume()
+ // ✅ 最终兜底:从后台回来 / 某些场景没触发 listener,也能恢复底栏
+ bottomNav.post { updateBottomNavVisibility() }
+ }
+
+ override fun onDestroy() {
+ // ✅ 防泄漏:移除路由监听(Activity 销毁时)
+ runCatching {
+ homeHost.navController.removeOnDestinationChangedListener(homeRouteListener)
+ shopHost.navController.removeOnDestinationChangedListener(shopRouteListener)
+ mineHost.navController.removeOnDestinationChangedListener(mineRouteListener)
+ globalHost.navController.removeOnDestinationChangedListener(globalRouteListener)
+ }
+ super.onDestroy()
+ }
+
private fun initHosts() {
val fm = supportFragmentManager
@@ -196,22 +279,59 @@ class MainActivity : AppCompatActivity() {
// 绑定全局导航可见性监听
bindGlobalVisibility()
-
+
// 绑定底部导航栏可见性监听
bindBottomNavVisibilityForTabs()
+
+ // ✅ 全局路由埋点监听(每次导航变化上报)
+ bindGlobalRouteReporting()
+
+ bottomNav.post { updateBottomNavVisibility() }
+ }
+
+ /**
+ * 这些页面需要隐藏底部导航栏:你按需加/减
+ */
+ private fun shouldHideBottomNav(destId: Int): Boolean {
+ return destId in setOf(
+ R.id.searchFragment,
+ R.id.searchResultFragment,
+ R.id.MySkin,
+ R.id.notificationFragment,
+ R.id.feedbackFragment,
+ R.id.MyKeyboard,
+ R.id.PersonalSettings,
+ )
+ }
+
+ /**
+ * ✅ 统一底栏显隐逻辑:任何地方状态变化都调用它
+ */
+ private fun updateBottomNavVisibility() {
+ // ✅ 只要 global overlay 不在 empty,底栏必须隐藏(用 NavController 判断,别用 View.visibility)
+ if (isGlobalVisible()) {
+ bottomNav.visibility = View.GONE
+ return
+ }
+
+ // 否则按“当前可见 tab 的当前目的地”判断
+ val destId = currentTabNavController.currentDestination?.id
+ bottomNav.visibility =
+ if (destId != null && shouldHideBottomNav(destId)) View.GONE else View.VISIBLE
}
private fun bindGlobalVisibility() {
globalNavController.addOnDestinationChangedListener { _, dest, _ ->
val isEmpty = dest.id == R.id.globalEmptyFragment
-
- findViewById(R.id.global_container).visibility =
- if (isEmpty) View.GONE else View.VISIBLE
- bottomNav.visibility =
- if (isEmpty) View.VISIBLE else View.GONE
- // ✅ 只在"刚从某个全局页关闭回 empty"时触发回退逻辑
- val justClosedOverlay = (dest.id == R.id.globalEmptyFragment && lastGlobalDestId != R.id.globalEmptyFragment)
+ findViewById(R.id.global_container).visibility =
+ if (isEmpty) View.GONE else View.VISIBLE
+
+ // ✅ 底栏统一走 update
+ updateBottomNavVisibility()
+
+ val justClosedOverlay =
+ (dest.id == R.id.globalEmptyFragment && lastGlobalDestId != R.id.globalEmptyFragment)
lastGlobalDestId = dest.id
if (justClosedOverlay) {
@@ -220,16 +340,17 @@ class MainActivity : AppCompatActivity() {
TAB_MINE -> R.id.mine_graph
else -> R.id.home_graph
}
- // 未登录且当前处在受保护tab:强制回首页
+
if (!isLoggedIn() && currentTabGraphId in protectedTabs) {
switchTab(TAB_HOME, force = true)
bottomNav.selectedItemId = R.id.home_graph
}
- // ✅ 只有"没登录就关闭登录页"才清 pending
if (!isLoggedIn()) {
pendingTabAfterLogin = null
}
+
+ bottomNav.post { updateBottomNavVisibility() }
}
}
}
@@ -238,7 +359,7 @@ class MainActivity : AppCompatActivity() {
if (!force && targetTag == currentTabTag) return
val fm = supportFragmentManager
- if (fm.isStateSaved) return // ✅ 防崩:stateSaved 时不做事务
+ if (fm.isStateSaved) return
currentTabTag = targetTag
@@ -255,57 +376,80 @@ class MainActivity : AppCompatActivity() {
}
}
.commit()
+
+ // ✅ 关键:hide/show 切 tab 不会触发 destinationChanged,所以手动刷新
+ bottomNav.post { updateBottomNavVisibility() }
+
+ // ✅ 新增:切 tab 后补一次路由上报(不改变其它逻辑)
+ if (!force) {
+ currentTabNavController.currentDestination?.id?.let { destId ->
+ reportPageView(source = "switch_tab", destId = destId)
+ }
+ }
}
/** 打开全局页(login/recharge等) */
private fun openGlobal(destId: Int, bundle: Bundle? = null) {
val fm = supportFragmentManager
- if (fm.isStateSaved) return // ✅ 防崩
+ if (fm.isStateSaved) return
try {
- if (bundle != null) {
- globalNavController.navigate(destId, bundle)
- } else {
- globalNavController.navigate(destId)
- }
+ if (bundle != null) globalNavController.navigate(destId, bundle)
+ else globalNavController.navigate(destId)
} catch (e: IllegalArgumentException) {
- // 可选:防止偶发重复 navigate 崩溃
e.printStackTrace()
}
+
+ bottomNav.post { updateBottomNavVisibility() }
}
- /** 关闭全局页:pop到 empty */
+ /** Tab 内页面变化时刷新底栏显隐 */
private fun bindBottomNavVisibilityForTabs() {
- fun shouldHideBottomNav(destId: Int): Boolean {
- return destId in setOf(
- R.id.searchFragment,
- R.id.searchResultFragment,
- R.id.MySkin
- // 你还有其他需要全屏的页,也加在这里
- )
+ val listener = NavController.OnDestinationChangedListener { _, _, _ ->
+ updateBottomNavVisibility()
}
- val listener = NavController.OnDestinationChangedListener { _, dest, _ ->
- // 只要 global overlay 打开了,仍然以 overlay 为准(你已有逻辑)
- if (isGlobalVisible()) return@OnDestinationChangedListener
- bottomNav.visibility = if (shouldHideBottomNav(dest.id)) View.GONE else View.VISIBLE
- }
-
homeHost.navController.addOnDestinationChangedListener(listener)
shopHost.navController.addOnDestinationChangedListener(listener)
mineHost.navController.addOnDestinationChangedListener(listener)
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "home",
+ )
+ }
+
+ // ✅ 绑定全局路由埋点(四个 NavController)
+ private fun bindGlobalRouteReporting() {
+ homeHost.navController.addOnDestinationChangedListener(homeRouteListener)
+ shopHost.navController.addOnDestinationChangedListener(shopRouteListener)
+ mineHost.navController.addOnDestinationChangedListener(mineRouteListener)
+ globalHost.navController.addOnDestinationChangedListener(globalRouteListener)
+
+ // ✅ 删除:初始化手动上报(否则启动时会重复上报)
+ // runCatching {
+ // currentTabNavController.currentDestination?.id?.let { reportPageView("init_current_tab", it) }
+ // globalNavController.currentDestination?.id?.let { reportPageView("init_global", it) }
+ // }
}
private fun closeGlobalIfPossible(): Boolean {
if (!isGlobalVisible()) return false
val popped = globalNavController.popBackStack()
- val stillVisible = globalNavController.currentDestination?.id != R.id.globalEmptyFragment
- return popped || stillVisible
+
+ // ✅ pop 后刷新一次(注意:currentDestination 可能要等一帧才更新,所以 post)
+ bottomNav.post { updateBottomNavVisibility() }
+
+ // popped = true 表示确实 pop 了;即使 popped=false 也可能已经在 empty 了
+ return popped || !isGlobalVisible()
}
+ /**
+ * ✅ 改这里:不要再用 View.visibility 判断 overlay
+ * 以 NavController 的目的地为准
+ */
private fun isGlobalVisible(): Boolean {
- return findViewById(R.id.global_container).visibility == View.VISIBLE
+ return globalNavController.currentDestination?.id != R.id.globalEmptyFragment
}
private fun setupBackPress() {
@@ -316,14 +460,15 @@ class MainActivity : AppCompatActivity() {
// 2) 再 pop 当前tab
val popped = currentTabNavController.popBackStack()
- if (popped) return
+ if (popped) {
+ bottomNav.post { updateBottomNavVisibility() }
+ return
+ }
// 3) 当前tab到根了:如果不是home,切回home;否则退出
- if (currentTabTag != TAB_HOME) {
- bottomNav.post {
- bottomNav.selectedItemId = R.id.home_graph
- }
- switchTab(TAB_HOME)
+ if (currentTabTag != TAB_HOME) {
+ bottomNav.post { bottomNav.selectedItemId = R.id.home_graph }
+ switchTab(TAB_HOME)
} else {
finish()
}
@@ -331,17 +476,25 @@ class MainActivity : AppCompatActivity() {
})
}
+ private var pendingNavigationAfterLogin: String? = null
+
private fun handleNavigationFromIntent() {
val navigateTo = intent.getStringExtra("navigate_to")
if (navigateTo == "recharge_fragment") {
bottomNav.post {
if (!isLoggedIn()) {
+ pendingNavigationAfterLogin = navigateTo
openGlobal(R.id.loginFragment)
- return@post
+ return@post
}
openGlobal(R.id.rechargeFragment)
}
}
+ if (navigateTo == "login_fragment") {
+ bottomNav.post {
+ openGlobal(R.id.loginFragment)
+ }
+ }
}
private fun isLoggedIn(): Boolean {
@@ -352,4 +505,68 @@ class MainActivity : AppCompatActivity() {
outState.putString("current_tab_tag", currentTabTag)
super.onSaveInstanceState(outState)
}
-}
\ No newline at end of file
+
+ // =======================
+ // 全局路由埋点:page_id 映射 + 上报
+ // =======================
+
+ private fun pageIdForDest(destId: Int): String {
+ return when (destId) {
+
+ /** ==================== 首页 Home ==================== */
+ R.id.homeFragment -> "home_main" // 首页-主页面
+ R.id.keyboardDetailFragment -> "skin_detail" // 键盘详情页
+ R.id.MyKeyboard -> "my_keyboard" // 键盘设置
+
+ /** ==================== 商城 Shop ==================== */
+ R.id.shopFragment -> "shop" // 商城首页
+ R.id.searchFragment -> "search" // 搜索页
+ R.id.searchResultFragment -> "search_result" // 搜索结果页
+ R.id.MySkin -> "my_skin" // 我的皮肤
+
+ /** ==================== 我的 Mine ==================== */
+ R.id.mineFragment -> "my" // 我的-首页
+ R.id.PersonalSettings -> "person_info" // 个人设置
+ R.id.notificationFragment -> "notice" // 消息通知
+ R.id.feedbackFragment -> "feedback" // 意见反馈
+ R.id.consumptionRecordFragment -> "consumption_record" // 消费记录
+
+ /** ==================== 登录 & 注册 ==================== */
+ R.id.loginFragment -> "login" // 登录页
+ R.id.registerFragment -> "register_email" // 注册页
+ R.id.registerVerifyFragment -> "register_verify_email" // 注册验证码
+ R.id.forgetPasswordEmailFragment -> "forgot_password_email" // 忘记密码-邮箱
+ R.id.forgetPasswordVerifyFragment -> "forgot_password_verify" // 忘记密码-验证码
+ R.id.forgetPasswordResetFragment -> "forgot_password_newpwd" // 忘记密码-重置密码
+
+ /** ==================== 充值相关 ==================== */
+ R.id.rechargeFragment -> "vip_pay" // 充值首页
+ R.id.goldCoinRechargeFragment -> "points_recharge" // 金币充值
+
+ /** ==================== 全局 / 占位 ==================== */
+ R.id.globalEmptyFragment -> "global_empty" // 全局占位页(兜底)
+
+ /** ==================== 兜底处理 ==================== */
+ else -> "unknown_$destId" // 未配置的页面,方便排查遗漏
+ }
+ }
+
+ private fun reportPageView(source: String, destId: Int) {
+ val pageId = pageIdForDest(destId)
+ if (destId == R.id.globalEmptyFragment) return
+ if (destId == R.id.loginFragment || destId == R.id.registerFragment){
+ BehaviorReporter.report(
+ isNewUser = true,
+ "page_id" to pageId,
+ )
+ return
+ }
+
+ Log.d(ROUTE_TAG, "route: source=$source destId=$destId page_id=$pageId")
+
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to pageId,
+ )
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/MyApp.kt b/app/src/main/java/com/example/myapplication/MyApp.kt
index 8df53e8..ac6c030 100644
--- a/app/src/main/java/com/example/myapplication/MyApp.kt
+++ b/app/src/main/java/com/example/myapplication/MyApp.kt
@@ -2,13 +2,16 @@ package com.example.myapplication
import android.app.Application
import com.example.myapplication.network.RetrofitClient
+import com.example.myapplication.network.NetworkClient
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
- // 初始化 RetrofitClient,传入 ApplicationContext
+ AppContext.init(this) // ✅ 新增:全局 Application Context
+
RetrofitClient.init(this)
+ NetworkClient.init(this) // ✅ SSE 用(带 token/签名拦截器)
}
}
diff --git a/app/src/main/java/com/example/myapplication/MyInputMethodService.kt b/app/src/main/java/com/example/myapplication/MyInputMethodService.kt
index 6530edb..6832e27 100644
--- a/app/src/main/java/com/example/myapplication/MyInputMethodService.kt
+++ b/app/src/main/java/com/example/myapplication/MyInputMethodService.kt
@@ -46,6 +46,8 @@ import android.graphics.drawable.GradientDrawable
import kotlin.math.abs
import java.text.BreakIterator
import android.widget.EditText
+import android.content.res.Configuration
+import androidx.constraintlayout.widget.ConstraintLayout
class MyInputMethodService : InputMethodService(), KeyboardEnvironment {
@@ -264,23 +266,6 @@ class MyInputMethodService : InputMethodService(), KeyboardEnvironment {
createNotificationChannelIfNeeded()
tryStartForegroundSafe()
-
- // 监听认证事件
- // CoroutineScope(Dispatchers.Main).launch {
- // AuthEventBus.events.collectLatest { event ->
- // if (event is AuthEvent.TokenExpired) {
- // // 启动 MainActivity 并跳转到登录页面
- // val intent = Intent(this@MyInputMethodService, MainActivity::class.java).apply {
- // flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
- // putExtra("navigate_to", "loginFragment")
- // }
- // startActivity(intent)
- // } else if (event is AuthEvent.GenericError) {
- // // 显示错误提示
- // android.widget.Toast.makeText(this@MyInputMethodService, "请求失败: ${event.message}", android.widget.Toast.LENGTH_SHORT).show()
- // }
- // }
- // }
}
// 输入法状态变化
@@ -319,6 +304,7 @@ class MyInputMethodService : InputMethodService(), KeyboardEnvironment {
val keyboard = ensureMainKeyboard()
currentKeyboardView = keyboard.rootView
mainKeyboardView = keyboard.rootView
+ (keyboard.rootView.parent as? ViewGroup)?.removeView(keyboard.rootView)
return keyboard.rootView
}
@@ -405,7 +391,7 @@ class MyInputMethodService : InputMethodService(), KeyboardEnvironment {
// 初始状态:隐藏联想条,显示控制面板
mainKeyboardView
- ?.findViewById(R.id.completion_scroll)
+ ?.findViewById(R.id.completion_scroll)
?.visibility = View.GONE
mainKeyboardView
@@ -604,15 +590,19 @@ class MyInputMethodService : InputMethodService(), KeyboardEnvironment {
if (aiKeyboard == null) {
aiKeyboard = AiKeyboard(this)
aiKeyboardView = aiKeyboard!!.rootView
+
+ // ✅ AI 键盘 Delete 按钮也绑定“长按连删 + 上滑清空”
+ val delId = resources.getIdentifier("keyboard_button_Delete", "id", packageName)
+ aiKeyboardView?.findViewById(delId)?.let { attachRepeatDeleteInternal(it) }
}
return aiKeyboard!!
}
-
+
override fun showMainKeyboard() {
clearEditorState()
val kb = ensureMainKeyboard()
currentKeyboardView = kb.rootView
- setInputView(kb.rootView)
+ setInputViewSafely(kb.rootView)
kb.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
}
@@ -620,7 +610,7 @@ class MyInputMethodService : InputMethodService(), KeyboardEnvironment {
clearEditorState()
val kb = ensureNumberKeyboard()
currentKeyboardView = kb.rootView
- setInputView(kb.rootView)
+ setInputViewSafely(kb.rootView)
kb.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
}
@@ -628,7 +618,7 @@ class MyInputMethodService : InputMethodService(), KeyboardEnvironment {
clearEditorState()
val kb = ensureSymbolKeyboard()
currentKeyboardView = kb.rootView
- setInputView(kb.rootView)
+ setInputViewSafely(kb.rootView)
kb.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
}
@@ -636,18 +626,48 @@ class MyInputMethodService : InputMethodService(), KeyboardEnvironment {
clearEditorState()
val kb = ensureAiKeyboard()
currentKeyboardView = kb.rootView
- setInputView(kb.rootView)
+ setInputViewSafely(kb.rootView)
kb.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
+ kb.refreshPersonas()
}
override fun showEmojiKeyboard() {
clearEditorState()
val kb = ensureEmojiKeyboard()
currentKeyboardView = kb.rootView
- setInputView(kb.rootView)
+ setInputViewSafely(kb.rootView)
kb.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
}
+ override fun associateClose() {
+ clearEditorState()
+ val kb = ensureEmojiKeyboard()
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ // 先清理缓存,避免复用旧 View
+ currentKeyboardView = null
+
+ mainKeyboardView = null
+ numberKeyboardView = null
+ symbolKeyboardView = null
+ aiKeyboardView = null
+ emojiKeyboardView = null
+
+ mainKeyboard = null
+ numberKeyboard = null
+ symbolKeyboard = null
+ aiKeyboard = null
+ emojiKeyboard = null
+
+ super.onConfigurationChanged(newConfig)
+ }
+
+ private fun setInputViewSafely(v: View) {
+ (v.parent as? ViewGroup)?.removeView(v)
+ super.setInputView(v)
+ }
+
// Emoji 键盘
private fun ensureEmojiKeyboard(): com.example.myapplication.keyboard.EmojiKeyboard {
if (emojiKeyboard == null) {
@@ -943,7 +963,7 @@ class MyInputMethodService : InputMethodService(), KeyboardEnvironment {
// 新增:联想滚动条 & 控制面板
val completionScroll =
- mainKeyboardView?.findViewById(R.id.completion_scroll)
+ mainKeyboardView?.findViewById(R.id.completion_scroll)
val controlLayout =
mainKeyboardView?.findViewById(R.id.control_layout)
@@ -1006,7 +1026,7 @@ class MyInputMethodService : InputMethodService(), KeyboardEnvironment {
// 自动滚回到最左边
private fun scrollSuggestionsToStart() {
- val sv = mainKeyboardView?.findViewById(R.id.completion_scroll)
+ val sv = mainKeyboardView?.findViewById(R.id.completion_HorizontalScrollView)
sv?.post { sv.fullScroll(View.FOCUS_LEFT) }
}
@@ -1402,7 +1422,7 @@ class MyInputMethodService : InputMethodService(), KeyboardEnvironment {
// 4. UI:联想条隐藏 & 控制面板显示
mainHandler.post {
val completionScroll =
- mainKeyboardView?.findViewById(R.id.completion_scroll)
+ mainKeyboardView?.findViewById(R.id.completion_scroll)
val controlLayout =
mainKeyboardView?.findViewById(R.id.control_layout)
diff --git a/app/src/main/java/com/example/myapplication/OnboardingActivity.kt b/app/src/main/java/com/example/myapplication/OnboardingActivity.kt
index b56863f..5bb2dc3 100644
--- a/app/src/main/java/com/example/myapplication/OnboardingActivity.kt
+++ b/app/src/main/java/com/example/myapplication/OnboardingActivity.kt
@@ -3,16 +3,64 @@ package com.example.myapplication
import android.content.Intent
import android.os.Bundle
import android.widget.Button
+import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
+import com.example.myapplication.utils.EncryptedSharedPreferencesUtil
+import android.view.ViewGroup
+import android.widget.Toast
+import com.example.myapplication.ui.common.LoadingOverlay
+import android.os.Handler
+import android.os.Looper
class OnboardingActivity : AppCompatActivity() {
+ private var selectedGender = -1 // 0: male, 1: female, 2: third
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_onboarding)
val btnStart = findViewById(R.id.tv_skip)
+ val maleLayout = findViewById(R.id.male_layout)
+ val femaleLayout = findViewById(R.id.female_layout)
+ val thirdLayout = findViewById(R.id.third_layout)
+ val tvDescription = findViewById(R.id.tv_description)
+
+ // 设置性别选择点击事件
+ maleLayout.setOnClickListener {
+ resetAllLayouts()
+ maleLayout.setBackgroundResource(R.drawable.gender_background_select)
+ selectedGender = 0
+ }
+
+ femaleLayout.setOnClickListener {
+ resetAllLayouts()
+ femaleLayout.setBackgroundResource(R.drawable.gender_background_select)
+ selectedGender = 1
+ }
+
+ thirdLayout.setOnClickListener {
+ resetAllLayouts()
+ thirdLayout.setBackgroundResource(R.drawable.gender_background_select)
+ selectedGender = 2
+ }
+
+ tvDescription.setOnClickListener {
+ if (selectedGender != -1) {
+ // 这里可以获取selectedGender的值(0,1,2)
+ // 标记已经不是第一次启动了
+ val prefs = getSharedPreferences("app_prefs", MODE_PRIVATE)
+ prefs.edit().putBoolean("is_first_launch", false).apply()
+ EncryptedSharedPreferencesUtil.save(this, "gender", selectedGender.toString())
+ // 跳转到主界面
+ val rootView = window.decorView.findViewById(android.R.id.content)
+ LoadingOverlay.attach(rootView).show()
+ startActivity(Intent(this, MainActivity::class.java))
+ finish()
+ }else{
+ Toast.makeText(this, "Please select your gender.", Toast.LENGTH_SHORT).show()
+ }
+ }
btnStart.setOnClickListener {
// 标记已经不是第一次启动了
@@ -20,8 +68,16 @@ class OnboardingActivity : AppCompatActivity() {
prefs.edit().putBoolean("is_first_launch", false).apply()
// 跳转到主界面
+ val rootView = window.decorView.findViewById(android.R.id.content)
+ LoadingOverlay.attach(rootView).show()
startActivity(Intent(this, MainActivity::class.java))
finish()
}
}
+
+ private fun resetAllLayouts() {
+ findViewById(R.id.male_layout).setBackgroundResource(R.drawable.gender_background)
+ findViewById(R.id.female_layout).setBackgroundResource(R.drawable.gender_background)
+ findViewById(R.id.third_layout).setBackgroundResource(R.drawable.gender_background)
+ }
}
diff --git a/app/src/main/java/com/example/myapplication/SplashActivity.kt b/app/src/main/java/com/example/myapplication/SplashActivity.kt
index ebc24d2..b23f6c9 100644
--- a/app/src/main/java/com/example/myapplication/SplashActivity.kt
+++ b/app/src/main/java/com/example/myapplication/SplashActivity.kt
@@ -2,23 +2,54 @@ package com.example.myapplication
import android.content.Intent
import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+// import android.widget.ProgressBar
import androidx.appcompat.app.AppCompatActivity
+import com.example.myapplication.network.BehaviorReporter
class SplashActivity : AppCompatActivity() {
+ // private lateinit var progressBar: ProgressBar
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_splash)
- val prefs = getSharedPreferences("app_prefs", MODE_PRIVATE)
- val isFirstLaunch = prefs.getBoolean("is_first_launch", true)
- if (isFirstLaunch) {
- // 第一次启动 → 进入引导页
- startActivity(Intent(this, OnboardingActivity::class.java))
- } else {
- // 不是第一次 → 直接进入主界面
- startActivity(Intent(this, MainActivity::class.java))
- }
- finish()
+ // progressBar = findViewById(R.id.progressBar)
+
+ Handler(Looper.getMainLooper()).postDelayed({
+ val prefs = getSharedPreferences("app_prefs", MODE_PRIVATE)
+ val isFirstLaunch = prefs.getBoolean("is_first_launch", true)
+ if(isFirstLaunch){
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "sex_select",
+ )
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "guide",
+ )
+ }
+
+ val targetIntent = if (isFirstLaunch) {
+ // 第一次启动 → 进入引导页
+ prefs.edit().putBoolean("is_first_launch", false).apply()
+ Intent(this, OnboardingActivity::class.java)
+ } else {
+ // 不是第一次 → 进入主界面,携带原始intent的参数
+ Intent(this, MainActivity::class.java).apply {
+ intent.extras?.let { putExtras(it) }
+ }
+ }
+ startActivity(targetIntent)
+ finish()
+ }, 1000) // 0.5秒延迟,确保初始化
+ }
+
+ override fun onDestroy() {
+ // progressBar.clearAnimation()
+ super.onDestroy()
}
}
diff --git a/app/src/main/java/com/example/myapplication/keyboard/AiKeyboard.kt b/app/src/main/java/com/example/myapplication/keyboard/AiKeyboard.kt
index fa80317..8430982 100644
--- a/app/src/main/java/com/example/myapplication/keyboard/AiKeyboard.kt
+++ b/app/src/main/java/com/example/myapplication/keyboard/AiKeyboard.kt
@@ -1,24 +1,46 @@
package com.example.myapplication.keyboard
+import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.Typeface
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
+import android.os.Handler
+import android.os.Looper
import android.util.TypedValue
import android.view.Gravity
import android.view.View
+import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
-import android.widget.TextView
-import com.example.myapplication.MainActivity
-import com.example.myapplication.theme.ThemeManager
-import android.os.Handler
-import android.os.Looper
import android.widget.ScrollView
-import com.example.myapplication.network.NetworkClient
+import android.widget.TextView
+import android.content.ClipboardManager
+import android.util.Log
+import android.widget.Toast
+import android.view.inputmethod.ExtractedTextRequest
+
+import com.example.myapplication.SplashActivity
import com.example.myapplication.network.LlmStreamCallback
+import com.example.myapplication.network.ListByUserWithNot
+import com.example.myapplication.network.ApiResponse
+import com.example.myapplication.network.NetworkClient
+import com.example.myapplication.network.RetrofitClient
+import com.example.myapplication.theme.ThemeManager
+import com.google.android.flexbox.FlexboxLayout
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import okhttp3.Call
+import kotlin.math.min
+import com.example.myapplication.network.BehaviorReporter
class AiKeyboard(
env: KeyboardEnvironment
@@ -26,7 +48,18 @@ class AiKeyboard(
private var currentStreamCall: Call? = null
private val mainHandler = Handler(Looper.getMainLooper())
+ private val uiScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
+ // ✅ 卡片选中的 characterId(SSE 参数)
+ private var selectedCharacterId: Int? = null
+
+ // ✅ Paste 得到的内容(SSE message)
+ private var lastPastedText: String? = null
+
+ // ✅ 记录“上次从卡片填入到输入框”的文本,用于覆盖
+ private var lastFilledText: String? = null
+
+ // 输出容器
private val messagesContainer: LinearLayout by lazy {
val res = env.ctx.resources
val id = res.getIdentifier("container_messages", "id", env.ctx.packageName)
@@ -39,15 +72,13 @@ class AiKeyboard(
rootView.findViewById(id)
}
- // 当前正在流式更新的那一个 AI 文本
private var currentAssistantTextView: TextView? = null
-
- // 用来处理 的缓冲
private val streamBuffer = StringBuilder()
-
- //新建一条 AI 消息(空内容),返回里面的 TextView 用来后续流式更新
-
+ /**
+ * ✅ inflate 一条消息(item_ai_message.xml)
+ * ✅ 点击卡片:把内容填入输入框,并覆盖上次填入内容
+ */
private fun addAssistantMessage(initialText: String = ""): TextView {
val inflater = env.layoutInflater
val res = env.ctx.resources
@@ -58,160 +89,440 @@ class AiKeyboard(
res.getIdentifier("tv_content", "id", env.ctx.packageName)
)
tv.text = initialText
- messagesContainer.addView(itemView)
+ // ✅ 点击整张卡片:把当前卡片内容填入输入框,并覆盖上次填入内容
+ itemView.setOnClickListener {
+ val text = tv.text?.toString().orEmpty()
+ if (text.isNotBlank()) {
+ fillToEditorOverwriteLast(text)
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "keyboard_StreamTextView",
+ "handle_label" to "handle_label",
+ "send_text" to text,
+ )
+ }
+ }
+
+ messagesContainer.addView(itemView)
scrollToBottom()
return tv
}
- /**
- * (可选)如果你也想显示用户提问
- */
- private fun addUserMessage(text: String) {
- // 简单写:复用同一个 item 布局
- val tv = addAssistantMessage(text)
- // 这里可以改成设置 gravity、背景区分用户/AI 等
- }
-
private fun scrollToBottom() {
- // 延迟一点点执行,保证 addView 完成后再滚动
messagesScrollView.post {
messagesScrollView.fullScroll(View.FOCUS_DOWN)
}
}
- //后端每来一个 llm_chunk 的 data,就调用一次这个方法
private fun onLlmChunk(data: String) {
- // 丢掉 data=":\n\n" 这条
if (data == ":\n\n") return
- // 确保在主线程更新 UI
mainHandler.post {
- // 如果还没有正在流式的 TextView,就新建一条 AI 消息
if (currentAssistantTextView == null) {
currentAssistantTextView = addAssistantMessage("")
streamBuffer.clear()
}
- // 累积到缓冲区
streamBuffer.append(data)
-
- // 先整体把 ":\n\n" 删掉(以防万一有别的地方混进来)
var text = streamBuffer.toString().replace(":\n\n", "")
- // 处理 :代表下一句/下一条消息
val splitTag = ""
var index = text.indexOf(splitTag)
while (index != -1) {
- // split 前面这一段是上一条消息的最终内容
val before = text.substring(0, index)
currentAssistantTextView?.text = before
scrollToBottom()
- // 开启下一条 AI 消息
currentAssistantTextView = addAssistantMessage("")
-
- // 剩下的留给下一轮
text = text.substring(index + splitTag.length)
index = text.indexOf(splitTag)
}
- // 循环结束后 text 就是「当前这条消息的未完成尾巴」
currentAssistantTextView?.text = text
scrollToBottom()
- // 缓冲区只保留尾巴(避免无限变长)
streamBuffer.clear()
streamBuffer.append(text)
}
}
-
- // 收到 type="done" 时调用,表示这一轮回答结束
private fun onLlmDone() {
mainHandler.post {
- // 这里目前不需要做太多事,必要的话可以清掉 buffer
streamBuffer.clear()
currentAssistantTextView = null
}
}
-
- // 开始一次新的 AI 回答流式请求
- fun startAiStream(userQuestion: String) {
- // 可选:先把用户问题显示出来
- addUserMessage(userQuestion)
+ // ✅ 关键:调用 POST /chat/talk 的 SSE,并渲染到输出区
+ private fun startTalkStream(characterId: Int, message: String) {
+ // 每次发起新对话:清空输出区(你如果想保留历史,把这行删掉)
+ messagesContainer.removeAllViews()
- // 如果之前有没结束的流,先取消
+ // 取消旧流
currentStreamCall?.cancel()
- currentStreamCall = NetworkClient.startLlmStream(
- question = userQuestion,
+ currentStreamCall = NetworkClient.startChatTalkStream(
+ characterId = characterId,
+ message = message,
callback = object : LlmStreamCallback {
override fun onEvent(type: String, data: String?) {
- when (type) {
- "llm_chunk" -> {
- if (data != null) {
- onLlmChunk(data) // 这里就是之前写的流式 UI 更新
+ guard("SSE.onEvent(type=$type)") {
+ when (type) {
+ "llm_chunk" -> if (data != null) onLlmChunk(data)
+ "done" -> onLlmDone()
+ else -> {
+ Log.d("AI_KB", "unknown event type=$type data=${data?.take(200)}")
}
}
- "done" -> {
- onLlmDone() // 一轮结束
- }
- "search_result" -> {
- }
}
}
override fun onError(t: Throwable) {
- addAssistantMessage("出错了:${t.message}")
+ // 尝试解析JSON错误响应
+ val errorResponse = try {
+ val errorJson = t.message?.let {
+ org.json.JSONObject(it)
+ }
+ if (errorJson != null) {
+ ApiResponse(
+ code = errorJson.optInt("code", 500),
+ message = errorJson.optString("message", "Unknown error"),
+ data = null
+ )
+ } else {
+ ApiResponse(
+ code = 500,
+ message = t.message ?: "Unknown error",
+ data = null
+ )
+ }
+ } catch (e: Exception) {
+ ApiResponse(
+ code = 500,
+ message = t.message ?: "Unknown error",
+ data = null
+ )
+ }
+
+ // SSE错误处理(如没有vip)
+ if (errorResponse.code == 50022) {
+ mainHandler.post {
+ Toast.makeText(env.ctx, errorResponse.message, Toast.LENGTH_LONG).show()
+ }
+ } else {
+ showErrorOnUi(errorResponse.message)
+ }
}
}
)
}
- // 比如键盘关闭时可以调用一次,避免内存泄漏 / 多余请求
fun cancelAiStream() {
currentStreamCall?.cancel()
currentStreamCall = null
}
-
-
- // 以下是 BaseKeyboard 的实现
override val rootView: View = run {
val res = env.ctx.resources
val layoutId = res.getIdentifier("ai_keyboard", "layout", env.ctx.packageName)
if (layoutId != 0) {
env.layoutInflater.inflate(layoutId, null) as View
} else {
- // 如果找不到布局,创建一个默认的View
LinearLayout(env.ctx).apply {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER
- addView(TextView(env.ctx).apply {
- text = "AI Keyboard"
- })
+ addView(TextView(env.ctx).apply { text = "AI Keyboard" })
}
}
}
init {
- applyKeyBackground(rootView, "background")
- applyTheme(
- env.currentTextColor,
- env.currentBorderColor,
- env.currentBackgroundColor
- )
- setupListeners()
+ guard("AiKeyboard.init") {
+ applyKeyBackground(rootView, "background")
+ applyTheme(env.currentTextColor, env.currentBorderColor, env.currentBackgroundColor)
+ setupListeners()
+ loadPersonasAndRender()
+ }
}
-
- private fun applyKeyBackground(
- root: View,
- viewIdName: String,
- drawableName: String? = null
- ) {
+
+ // ========= UI 绑定 =========
+
+ private fun setupListeners() {
+ val res = env.ctx.resources
+ val pkg = env.ctx.packageName
+
+ val aiPersonaId = res.getIdentifier("ai_persona", "id", pkg)
+ val aiOutputId = res.getIdentifier("ai_output", "id", pkg)
+ val aiPersonaView = if (aiPersonaId != 0) rootView.findViewById(aiPersonaId) else null
+ val aiOutputView = if (aiOutputId != 0) rootView.findViewById(aiOutputId) else null
+
+ aiPersonaView?.visibility = View.VISIBLE
+ aiOutputView?.visibility = View.GONE
+
+ // 返回主键盘
+ val backId = res.getIdentifier("key_abc", "id", pkg)
+ if (backId != 0) {
+ rootView.findViewById(backId)?.setOnClickListener {
+ env.showMainKeyboard()
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "keyboard_main_panel",
+ )
+ }
+ }
+
+ // VIP
+ val vipButtonId = res.getIdentifier("key_vip", "id", pkg)
+ if (vipButtonId != 0) {
+ rootView.findViewById(vipButtonId)?.setOnClickListener {
+ navigateToRechargeFragment()
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "keyboard_subscription_panel",
+ )
+ }
+ }
+
+ // Return:输出区 -> 人设区
+ val returnButtonId = res.getIdentifier("Return_keyboard", "id", pkg)
+ if (returnButtonId != 0) {
+ rootView.findViewById(returnButtonId)?.setOnClickListener {
+ aiOutputView?.animate()?.alpha(0f)?.setDuration(150)?.withEndAction {
+ aiOutputView.visibility = View.GONE
+ aiPersonaView?.visibility = View.VISIBLE
+ aiPersonaView?.alpha = 0f
+ aiPersonaView?.animate()?.alpha(1f)?.setDuration(150)
+ }
+ }
+ }
+
+ // Paste:读取剪贴板第一条 -> 保存 lastPastedText,并显示到 completion_text
+ val pasteBtnId = res.getIdentifier("keyboard_button_Paste", "id", pkg)
+ val completionTextId = res.getIdentifier("completion_text", "id", pkg)
+ if (pasteBtnId != 0) {
+ rootView.findViewById(pasteBtnId)?.setOnClickListener {
+ val text = readClipboardFirstText()
+ lastPastedText = text
+
+ if (completionTextId != 0) {
+ rootView.findViewById(completionTextId)?.text =
+ if (text.isNullOrBlank()) "" else text
+ }
+ }
+ }
+
+ // HorizontalScrollView:点击事件与Paste按钮相同
+ val scrollViewId = res.getIdentifier("completion_container", "id", pkg)
+ if (scrollViewId != 0) {
+ rootView.findViewById(scrollViewId)?.setOnClickListener {
+ val text = readClipboardFirstText()
+ lastPastedText = text
+
+ if (completionTextId != 0) {
+ rootView.findViewById(completionTextId)?.text =
+ if (text.isNullOrBlank()) "" else text
+ }
+ }
+ }
+
+ // ✅ Delete:沿用 MyInputMethodService.deleteOne()
+ val deleteBtnId = res.getIdentifier("keyboard_button_Delete", "id", pkg)
+ if (deleteBtnId != 0) {
+ rootView.findViewById(deleteBtnId)?.setOnClickListener {
+ env.deleteOne() // 就是 MyInputMethodService.deleteOne()
+ lastFilledText = null // 可选:防止覆盖逻辑误删
+ }
+ }
+
+ // ✅ Send:沿用 MyInputMethodService.performSendAction()
+ val sendBtnId = res.getIdentifier("keyboard_button_Send", "id", pkg)
+ if (sendBtnId != 0) {
+ rootView.findViewById(sendBtnId)?.setOnClickListener {
+ env.performSendAction() // 就是 MyInputMethodService.performSendAction()
+ lastFilledText = null // 发送后不再尝试覆盖
+ }
+ }
+
+ // ✅ Clear:清空输入框
+ val clearBtnId = res.getIdentifier("keyboard_button_Clear", "id", pkg)
+ if (clearBtnId != 0) {
+ rootView.findViewById(clearBtnId)?.setOnClickListener {
+ clearEditorAll()
+ lastFilledText = null
+ }
+ }
+ }
+
+ // ========= 人设卡片:listByUser 渲染 =========
+
+ fun refreshPersonas() {
+ loadPersonasAndRender()
+ }
+
+ private fun loadPersonasAndRender() {
+ val res = env.ctx.resources
+ val pkg = env.ctx.packageName
+ val containerId = res.getIdentifier("persona_container", "id", pkg)
+ val container = if (containerId != 0) rootView.findViewById(containerId) else null
+ if (container == null) return
+
+ uiScope.launch {
+ try {
+ val resp = withContext(Dispatchers.IO) { RetrofitClient.apiService.listByUser() }
+
+ Log.d("1314520-AI_KB", "listByUser response: $resp")
+ if (resp.code == 0) {
+ val list = resp.data ?: emptyList()
+ renderPersonaCards(container, list)
+ } else if (resp.code == 40102) {
+ Toast.makeText(env.ctx, "You need to log in to use this function.", Toast.LENGTH_LONG).show()
+ } else {
+ Toast.makeText(env.ctx, resp.message, Toast.LENGTH_LONG).show()
+ }
+ } catch (_: Throwable) {
+ container.removeAllViews()
+ }
+ }
+ }
+
+ private fun renderPersonaCards(container: FlexboxLayout, list: List) {
+ container.removeAllViews()
+
+ val inflater = env.layoutInflater
+
+ list.forEach { item ->
+ val v = inflater.inflate(
+ com.example.myapplication.R.layout.item_ai_persona_card,
+ container,
+ false
+ )
+
+ val avatar = v.findViewById(
+ com.example.myapplication.R.id.avatar
+ )
+ val nameTv = v.findViewById(com.example.myapplication.R.id.name)
+
+ nameTv.text = item.characterName
+
+ val sizePx = (env.ctx.resources.displayMetrics.density * 20f).toInt()
+ avatar.setImageBitmap(emojiToBitmap(item.emoji, sizePx))
+
+ v.setOnClickListener {
+
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "keyboard_function_panel",
+ "element_id" to "renshe_item",
+ "id" to item.characterId,
+ "name" to item.characterName,
+ )
+ // ✅ SSE 要的是 characterId,不是 id
+ selectedCharacterId = item.characterId
+
+ val res = env.ctx.resources
+ val pkg = env.ctx.packageName
+ val aiPersonaId = res.getIdentifier("ai_persona", "id", pkg)
+ val aiOutputId = res.getIdentifier("ai_output", "id", pkg)
+ val aiPersonaView = if (aiPersonaId != 0) rootView.findViewById(aiPersonaId) else null
+ val aiOutputView = if (aiOutputId != 0) rootView.findViewById(aiOutputId) else null
+
+ // 获取消息内容:优先使用lastPastedText,其次读取剪贴板
+ val message = lastPastedText ?: readClipboardFirstText() ?: run {
+ addAssistantMessage("Please Paste the content of the clipboard first or make sure the clipboard is not empty")
+ return@setOnClickListener
+ }
+
+ aiPersonaView?.animate()?.alpha(0f)?.setDuration(150)?.withEndAction {
+ aiPersonaView.visibility = View.GONE
+ aiOutputView?.visibility = View.VISIBLE
+ aiOutputView?.alpha = 0f
+ aiOutputView?.animate()?.alpha(1f)?.setDuration(150)
+ }
+
+ // 发送SSE请求
+ startTalkStream(item.characterId, message)
+ }
+
+ container.addView(v)
+ }
+ }
+
+ private fun emojiToBitmap(emoji: String, sizePx: Int): Bitmap {
+ val safeSize = min(sizePx.coerceAtLeast(16), 128)
+ val bmp = Bitmap.createBitmap(safeSize, safeSize, Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(bmp)
+
+ val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
+ textAlign = Paint.Align.CENTER
+ typeface = Typeface.DEFAULT
+ textSize = safeSize * 0.8f
+ }
+ val fm = paint.fontMetrics
+ val x = safeSize / 2f
+ val y = safeSize / 2f - (fm.ascent + fm.descent) / 2f
+
+ canvas.drawText(emoji, x, y, paint)
+ return bmp
+ }
+
+ private fun readClipboardFirstText(): String? {
+ val cm = env.ctx.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager ?: return null
+ val clip = cm.primaryClip ?: return null
+ if (clip.itemCount <= 0) return null
+ return clip.getItemAt(0).coerceToText(env.ctx)?.toString()
+ }
+
+ /**
+ * ✅ 将卡片文本填入宿主输入框,并覆盖上一次“卡片填入”的内容
+ * 覆盖策略(安全版):
+ * - 仅当光标前的文本 == lastFilledText 时,才删除那段并覆盖
+ * - 否则就直接插入(避免误删用户手动输入的内容)
+ */
+ private fun fillToEditorOverwriteLast(newText: String) {
+ val ic = env.getInputConnection() ?: return
+
+ ic.beginBatchEdit()
+ try {
+ val prev = lastFilledText
+ if (!prev.isNullOrEmpty()) {
+ val before = ic.getTextBeforeCursor(prev.length, 0)?.toString()
+ if (before == prev) {
+ ic.deleteSurroundingText(prev.length, 0)
+ }
+ }
+ ic.commitText(newText, 1)
+ lastFilledText = newText
+ } finally {
+ ic.endBatchEdit()
+ }
+ }
+
+ /** ✅ 清空宿主输入框(当前编辑框) */
+ private fun clearEditorAll() {
+ val ic = env.getInputConnection() ?: return
+
+ val et = try {
+ ic.getExtractedText(ExtractedTextRequest(), 0)
+ } catch (_: Throwable) {
+ null
+ }
+ val full = et?.text?.toString().orEmpty()
+ if (full.isEmpty()) return
+
+ ic.beginBatchEdit()
+ try {
+ ic.setSelection(0, full.length)
+ ic.commitText("", 1)
+ } finally {
+ ic.endBatchEdit()
+ }
+ }
+
+ // ========= 换肤相关(保留你原有逻辑) =========
+
+ private fun applyKeyBackground(root: View, viewIdName: String, drawableName: String? = null) {
val res = env.ctx.resources
val viewId = res.getIdentifier(viewIdName, "id", env.ctx.packageName)
if (viewId == 0) return
@@ -250,125 +561,51 @@ class AiKeyboard(
}
}
-
- private fun setupListeners() {
- val res = env.ctx.resources
- val pkg = env.ctx.packageName
-
- // 获取ai_persona和ai_output视图引用
- val aiPersonaId = res.getIdentifier("ai_persona", "id", pkg)
- val aiOutputId = res.getIdentifier("ai_output", "id", pkg)
-
- val aiPersonaView = if (aiPersonaId != 0) rootView.findViewById(aiPersonaId) else null
- val aiOutputView = if (aiOutputId != 0) rootView.findViewById(aiOutputId) else null
-
- // 初始化显示状态:显示ai_persona,隐藏ai_output
- aiPersonaView?.visibility = View.VISIBLE
- aiOutputView?.visibility = View.GONE
-
- // 如果 ai_keyboard.xml 里有 “返回主键盘” 的按钮,比如 key_abc,就绑定一下
- val backId = res.getIdentifier("key_abc", "id", pkg)
- if (backId != 0) {
- rootView.findViewById(backId)?.setOnClickListener {
- env.showMainKeyboard()
- }
- }
-
- // 绑定 VIP 按钮点击事件,跳转到充值页面
- val vipButtonId = res.getIdentifier("key_vip", "id", pkg)
- if (vipButtonId != 0) {
- rootView.findViewById(vipButtonId)?.setOnClickListener {
- navigateToRechargeFragment()
- }
- }
-
- //显示切换
- val returnButtonId = res.getIdentifier("Return_keyboard", "id", pkg)
- if (returnButtonId != 0) {
- rootView.findViewById(returnButtonId)?.let { returnButton ->
- // 确保按钮可点击且可获得焦点,防止事件穿透
- returnButton.isClickable = true
- returnButton.isFocusable = true
- returnButton.setOnClickListener {
- // 点击Return_keyboard:先隐藏ai_output,再显示ai_persona(顺序动画)
- aiOutputView?.animate()?.alpha(0f)?.setDuration(150)?.withEndAction {
- aiOutputView?.visibility = View.GONE
- // 等ai_output完全隐藏后再显示ai_persona
- aiPersonaView?.visibility = View.VISIBLE
- aiPersonaView?.alpha = 0f
- aiPersonaView?.animate()?.alpha(1f)?.setDuration(150)
- }
- }
- }
- }
-
- val cardButtonId = res.getIdentifier("card", "id", pkg)
- if (cardButtonId != 0) {
- rootView.findViewById(cardButtonId)?.setOnClickListener {
- // 点击card:先隐藏ai_persona,再显示ai_output(顺序动画)
- aiPersonaView?.animate()?.alpha(0f)?.setDuration(150)?.withEndAction {
- aiPersonaView?.visibility = View.GONE
- // 等ai_persona完全隐藏后再显示ai_output
- aiOutputView?.visibility = View.VISIBLE
- aiOutputView?.alpha = 0f
- aiOutputView?.animate()?.alpha(1f)?.setDuration(150)
- }
- }
- }
-
-
-
-
-
- // // 假设 ai_keyboard.xml 里有一个发送按钮 key_send
- // val sendId = res.getIdentifier("key_send", "id", pkg)
- // val inputId = res.getIdentifier("et_prompt", "id", pkg) // 假设这是你的输入框 id
-
- // if (sendId != 0 && inputId != 0) {
- // val inputView = rootView.findViewById(inputId)
-
- // rootView.findViewById(sendId)?.setOnClickListener {
- // val question = inputView?.text?.toString()?.trim().orEmpty()
- // if (question.isNotEmpty()) {
- // startAiStream(question)
- // }
- // }
- // }
- }
-
private fun navigateToRechargeFragment() {
try {
- val intent = Intent(env.ctx, MainActivity::class.java).apply {
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
- putExtra("navigate_to", "recharge_fragment")
- }
+ val intent = Intent(env.ctx, SplashActivity::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ intent.putExtra("navigate_to", "recharge_fragment")
env.ctx.startActivity(intent)
} catch (e: Exception) {
- // 如果启动失败,记录错误日志
- android.util.Log.e("AiKeyboard", "Failed to navigate to recharge fragment", e)
+ Log.e("AiKeyboard", "Failed to navigate to recharge fragment", e)
}
}
- override fun applyTheme(
- textColor: ColorStateList,
- borderColor: ColorStateList,
- backgroundColor: ColorStateList
- ) {
+ override fun applyTheme(textColor: ColorStateList, borderColor: ColorStateList, backgroundColor: ColorStateList) {
applyKeyBackgroundsForTheme()
}
- // ==============================刷新主题==================================
+
override fun applyKeyBackgroundsForTheme() {
- // 背景
applyKeyBackground(rootView, "background")
-
- // // AI 键盘上的功能键(按你现有 layout 里出现过的 id 来列)
- // val others = listOf(
- // "key_abc", // 返回主键盘
- // "key_vip", // VIP
- // "Return_keyboard", // 返回 persona 页
- // "card" // 切换到 output 页
- // // 如果后续 ai_keyboard.xml 里还有其它需要换肤的 key id,继续往这里加
- // )
- // others.forEach { applyKeyBackground(rootView, it) }
}
-}
\ No newline at end of file
+
+ private fun showErrorOnUi(title: String, t: Throwable? = null) {
+ val msg = if (t == null) title else "$title:${t.message ?: t.toString()}"
+ Log.e("AI_KB", msg, t)
+
+ mainHandler.post {
+ runCatching {
+ val res = env.ctx.resources
+ val pkg = env.ctx.packageName
+ val aiPersonaId = res.getIdentifier("ai_persona", "id", pkg)
+ val aiOutputId = res.getIdentifier("ai_output", "id", pkg)
+ val aiPersonaView = if (aiPersonaId != 0) rootView.findViewById(aiPersonaId) else null
+ val aiOutputView = if (aiOutputId != 0) rootView.findViewById(aiOutputId) else null
+ aiPersonaView?.visibility = View.GONE
+ aiOutputView?.visibility = View.VISIBLE
+
+ addAssistantMessage(msg)
+ }
+ }
+ }
+
+ /** 包一层 try-catch,避免任何地方异常直接白屏 */
+ private inline fun guard(tag: String, block: () -> Unit) {
+ try {
+ block()
+ } catch (t: Throwable) {
+ showErrorOnUi("发生异常($tag)", t)
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/keyboard/BaseKeyboard.kt b/app/src/main/java/com/example/myapplication/keyboard/BaseKeyboard.kt
index 174ba82..2ff5c5c 100644
--- a/app/src/main/java/com/example/myapplication/keyboard/BaseKeyboard.kt
+++ b/app/src/main/java/com/example/myapplication/keyboard/BaseKeyboard.kt
@@ -65,8 +65,8 @@ abstract class BaseKeyboard(
protected fun applyBorderToAllKeyViews(root: View?) {
if (root == null) return
- val keyMarginPx = 1.dpToPx()
- val keyPaddingH = 6.dpToPx()
+ val keyMarginPx = 2.dpToPx()
+ val keyPaddingH = 8.dpToPx()
// 忽略 suggestion_0..20(联想栏)
val ignoredIds = HashSet().apply {
@@ -112,6 +112,12 @@ abstract class BaseKeyboard(
return (this * density + 0.5f).toInt()
}
+ /** dp -> px (float version) */
+ protected fun Float.dpToPx(): Int {
+ val density = env.ctx.resources.displayMetrics.density
+ return (this * density + 0.5f).toInt()
+ }
+
/** 按键震动 */
protected fun vibrateKey() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
diff --git a/app/src/main/java/com/example/myapplication/keyboard/KeyboardEnvironment.kt b/app/src/main/java/com/example/myapplication/keyboard/KeyboardEnvironment.kt
index cd6f98c..df64ef9 100644
--- a/app/src/main/java/com/example/myapplication/keyboard/KeyboardEnvironment.kt
+++ b/app/src/main/java/com/example/myapplication/keyboard/KeyboardEnvironment.kt
@@ -39,6 +39,8 @@ interface KeyboardEnvironment {
fun showAiKeyboard()
//emoji键盘
fun showEmojiKeyboard()
+ // 关闭联想
+ fun associateClose()
// 音效
fun playKeyClick()
diff --git a/app/src/main/java/com/example/myapplication/keyboard/MainKeyboard.kt b/app/src/main/java/com/example/myapplication/keyboard/MainKeyboard.kt
index 74d2be0..e129254 100644
--- a/app/src/main/java/com/example/myapplication/keyboard/MainKeyboard.kt
+++ b/app/src/main/java/com/example/myapplication/keyboard/MainKeyboard.kt
@@ -15,7 +15,9 @@ import android.view.MotionEvent
import android.view.View
import android.widget.PopupWindow
import android.widget.TextView
+import android.widget.LinearLayout
import com.example.myapplication.theme.ThemeManager
+import com.example.myapplication.network.BehaviorReporter
class MainKeyboard(
env: KeyboardEnvironment,
@@ -145,6 +147,10 @@ class MainKeyboard(
view.findViewById(res.getIdentifier("key_ai", "id", pkg))?.setOnClickListener {
vibrateKey(); env.showAiKeyboard()
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "keyboard_function_panel",
+ )
}
view.findViewById(res.getIdentifier("key_send", "id", pkg))?.setOnClickListener {
@@ -159,6 +165,10 @@ class MainKeyboard(
updateRevokeButtonVisibility(view, res, pkg)
}
+ view.findViewById(res.getIdentifier("associate_close", "id", pkg))?.setOnClickListener {
+ vibrateKey();env.associateClose()
+ }
+
view.findViewById(res.getIdentifier("key_emoji", "id", pkg))?.setOnClickListener {
vibrateKey(); env.showEmojiKeyboard()
}
diff --git a/app/src/main/java/com/example/myapplication/network/ApiService.kt b/app/src/main/java/com/example/myapplication/network/ApiService.kt
index 03895e1..34e67a9 100644
--- a/app/src/main/java/com/example/myapplication/network/ApiService.kt
+++ b/app/src/main/java/com/example/myapplication/network/ApiService.kt
@@ -1,6 +1,7 @@
// 请求方法
package com.example.myapplication.network
+import okhttp3.MultipartBody
import okhttp3.ResponseBody
import retrofit2.Response
import retrofit2.http.*
@@ -65,9 +66,42 @@ interface ApiService {
@POST("user/updateInfo")
suspend fun updateUserInfo(
@Body body: updateInfoRequest
- ): ApiResponse
+ ): ApiResponse
+
+ //分页查询钱包交易记录
+ @POST("wallet/transactions")
+ suspend fun transactions(
+ @Body body: transactionsRequest
+ ): ApiResponse
+ //用户人设列表
+ @GET("character/listByUser")
+ suspend fun listByUser(
+ ): ApiResponse>
+
+ //更新用户人设排序
+ @POST("character/updateUserCharacterSort")
+ suspend fun updateUserCharacterSort(
+ @Body body: updateUserCharacterSortRequest
+ ): ApiResponse
+
+ // 删除用户人设
+ @GET("character/delUserCharacter")
+ suspend fun delUserCharacter(
+ @Query("id") id: Int
+ ): ApiResponse
+
+ //提交反馈
+ @POST("user/feedback")
+ suspend fun feedback(
+ @Body body: feedbackRequest
+ ): ApiResponse
+
+ //查询邀请码
+ @GET("user/inviteCode")
+ suspend fun inviteCode(
+ ): ApiResponse
//===========================================首页=================================
// 标签列表
@GET("tag/list")
@@ -102,17 +136,11 @@ interface ApiService {
@Query("id") id: Int
): ApiResponse
- //删除用户人设
- @GET("character/delUserCharacter")
- suspend fun delUserCharacter(
- @Query("id") id: Int
- ): ApiResponse
-
//添加用户人设
@POST("character/addUserCharacter")
suspend fun addUserCharacter(
@Body body: AddPersonaClick
- ): ApiResponse
+ ): ApiResponse
//==========================================商城===========================================
@@ -188,4 +216,18 @@ interface ApiService {
suspend fun downloadZipFromUrl(
@Url url: String // 完整的下载 URL
): Response
+
+
+}
+
+/**
+ * 文件上传服务接口
+ */
+interface FileUploadService {
+ @Multipart
+ @POST("file/upload")
+ suspend fun uploadFile(
+ @Query("file") fileQuery: String,
+ @Part file: MultipartBody.Part
+ ): ApiResponse
}
diff --git a/app/src/main/java/com/example/myapplication/network/AuthEventBus.kt b/app/src/main/java/com/example/myapplication/network/AuthEventBus.kt
index d051603..bfadeae 100644
--- a/app/src/main/java/com/example/myapplication/network/AuthEventBus.kt
+++ b/app/src/main/java/com/example/myapplication/network/AuthEventBus.kt
@@ -6,7 +6,7 @@ import kotlinx.coroutines.flow.SharedFlow
object AuthEventBus {
- // replay=0:不缓存历史事件;extraBufferCapacity:避免瞬时丢事件
+ // replay=1:缓存最近一次事件;extraBufferCapacity=64:增加缓冲区防止瞬时事件丢失
private val _events = MutableSharedFlow(
replay = 0,
extraBufferCapacity = 1
@@ -21,8 +21,11 @@ object AuthEventBus {
sealed class AuthEvent {
data class TokenExpired(val message: String? = null) : AuthEvent()
+ data class CharacterAdded(val personaId: Int, val newAdded: Boolean = false) : AuthEvent()
data class GenericError(val message: String) : AuthEvent()
object LoginSuccess : AuthEvent()
data class Logout(val returnTabTag: String) : AuthEvent()
data class OpenGlobalPage(val destinationId: Int, val bundle: Bundle? = null) : AuthEvent()
-}
+ object UserUpdated : AuthEvent()
+ data class CharacterDeleted(val characterId: Int) : AuthEvent()
+ }
diff --git a/app/src/main/java/com/example/myapplication/network/BehaviorApiService.kt b/app/src/main/java/com/example/myapplication/network/BehaviorApiService.kt
new file mode 100644
index 0000000..d5604d7
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/network/BehaviorApiService.kt
@@ -0,0 +1,21 @@
+package com.example.myapplication.network
+
+import com.google.gson.JsonObject
+import retrofit2.Response
+import retrofit2.http.Body
+import retrofit2.http.POST
+
+interface BehaviorApiService {
+
+ // 新用户
+ @POST("newAccount")
+ suspend fun reportNewUserBehavior(
+ @Body body: JsonObject
+ ): Response
+
+ // 老用户
+ @POST("genericData")
+ suspend fun reportGenericUserBehavior(
+ @Body body: JsonObject
+ ): Response
+}
diff --git a/app/src/main/java/com/example/myapplication/network/BehaviorHttpClient.kt b/app/src/main/java/com/example/myapplication/network/BehaviorHttpClient.kt
new file mode 100644
index 0000000..1f66c3b
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/network/BehaviorHttpClient.kt
@@ -0,0 +1,84 @@
+package com.example.myapplication.network
+
+import android.util.Log
+import okhttp3.Interceptor
+import okhttp3.OkHttpClient
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import java.util.concurrent.TimeUnit
+
+object BehaviorHttpClient {
+
+ private const val TAG = "BehaviorHttp"
+
+ // TODO:改成你的行为服务 baseUrl(必须以 / 结尾)
+ private const val BASE_URL = "http://192.168.2.21:35310/api/"
+
+ /**
+ * 请求拦截器:打印请求信息
+ */
+ private val requestInterceptor = Interceptor { chain ->
+ val request = chain.request()
+
+ val bodyStr = request.body?.let { body ->
+ val buffer = okio.Buffer()
+ body.writeTo(buffer)
+ buffer.readUtf8()
+ }
+
+ Log.d(TAG, "201314-请求")
+ Log.d(TAG, "201314-URL: ${request.url}")
+ Log.d(TAG, "201314-Method: ${request.method}")
+ if (!bodyStr.isNullOrBlank()) {
+ Log.d(TAG, "201314-Body: $bodyStr")
+ }
+
+ chain.proceed(request)
+ }
+
+ /**
+ * 响应拦截器:打印响应信息
+ * ⚠️ response.body 只能读一次,这里使用 clone()
+ */
+ private val responseInterceptor = Interceptor { chain ->
+ val response = chain.proceed(chain.request())
+
+ val responseBody = response.body
+ val bodyStr = responseBody?.source()?.let { source ->
+ source.request(Long.MAX_VALUE)
+ source.buffer.clone().readUtf8()
+ }
+
+ Log.d(TAG, "201314-响应")
+ Log.d(TAG, "201314-URL: ${response.request.url}")
+ Log.d(TAG, "201314-Code: ${response.code}")
+ if (!bodyStr.isNullOrBlank()) {
+ Log.d(TAG, "201314-Body: $bodyStr")
+ }
+
+ response
+ }
+
+ private val okHttpClient: OkHttpClient by lazy {
+ OkHttpClient.Builder()
+ .connectTimeout(10, TimeUnit.SECONDS)
+ .readTimeout(10, TimeUnit.SECONDS)
+ .writeTimeout(10, TimeUnit.SECONDS)
+ .retryOnConnectionFailure(true)
+ .addInterceptor(requestInterceptor)
+ .addInterceptor(responseInterceptor)
+ .build()
+ }
+
+ private val retrofit: Retrofit by lazy {
+ Retrofit.Builder()
+ .baseUrl(BASE_URL)
+ .client(okHttpClient)
+ .addConverterFactory(GsonConverterFactory.create())
+ .build()
+ }
+
+ val service: BehaviorApiService by lazy {
+ retrofit.create(BehaviorApiService::class.java)
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/network/BehaviorReporter.kt b/app/src/main/java/com/example/myapplication/network/BehaviorReporter.kt
new file mode 100644
index 0000000..b2c24d2
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/network/BehaviorReporter.kt
@@ -0,0 +1,111 @@
+package com.example.myapplication.network
+
+import android.util.Log
+import com.example.myapplication.AppContext
+import com.example.myapplication.utils.EncryptedSharedPreferencesUtil
+import com.google.gson.Gson
+import com.google.gson.JsonArray
+import com.google.gson.JsonElement
+import com.google.gson.JsonNull
+import com.google.gson.JsonObject
+import com.google.gson.JsonPrimitive
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.launch
+
+object BehaviorReporter {
+
+ private const val TAG = "BehaviorHttp"
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
+ private val gson = Gson()
+
+ /**
+ * 你只管调用这个方法即可:
+ *
+ * BehaviorReporter.report(
+ * isNewUser = true,
+ * "event" to "register_success",
+ * "page" to "home",
+ * "key" to "A",
+ * "count" to 1,
+ * "obj" to mapOf("a" to 1)
+ * )
+ *
+ * @param isNewUser true = newAccount,false = genericData
+ * @param extra 你想上报的任意字段,null/空字符串/空集合会被过滤掉
+ */
+ fun report(
+ isNewUser: Boolean,
+ vararg extra: Pair
+ ) {
+ scope.launch {
+ runCatching {
+
+ val user = EncryptedSharedPreferencesUtil.get(
+ AppContext.context,
+ "user",
+ LoginResponse::class.java
+ )
+ val token = user?.token.orEmpty()
+
+ // ✅ 用 JsonObject,避免 Retrofit 的 Map, ?> wildcard 报错
+ val body = JsonObject()
+
+ // token:非空才带(放 body 内)
+ if (token.isNotBlank()) body.addProperty("token", token)
+
+ // extra:你传什么都行,自动过滤无效值,并转成 JsonElement
+ extra.forEach { (k, v) ->
+ val element = v.toJsonElementOrNull() ?: return@forEach
+ body.add(k, element)
+ }
+
+ // 根据新/老用户走不同接口
+ if (isNewUser) {
+ BehaviorHttpClient.service.reportNewUserBehavior(body)
+ } else {
+ BehaviorHttpClient.service.reportGenericUserBehavior(body)
+ }
+ }.onFailure { e ->
+ Log.e(TAG, "201314-report failed: ${e.message}", e)
+ }
+ }
+ }
+
+ /**
+ * 把 Any? 转 JsonElement,并过滤掉你不想传的“空值”
+ * 支持:String/Number/Boolean/Char/Map/List/Set/以及其他对象(尽量 gson.toJsonTree)
+ */
+ private fun Any?.toJsonElementOrNull(): JsonElement? {
+ return when (this) {
+ null -> null
+
+ is String -> if (this.isBlank()) null else JsonPrimitive(this)
+ is Number -> JsonPrimitive(this)
+ is Boolean -> JsonPrimitive(this)
+ is Char -> JsonPrimitive(this.toString())
+
+ is Map<*, *> -> {
+ if (this.isEmpty()) return null
+ gson.toJsonTree(this).takeIf { it != JsonNull.INSTANCE }
+ }
+
+ is List<*> -> {
+ if (this.isEmpty()) return null
+ gson.toJsonTree(this).takeIf { it != JsonNull.INSTANCE } ?: JsonArray()
+ }
+
+ is Set<*> -> {
+ if (this.isEmpty()) return null
+ gson.toJsonTree(this).takeIf { it != JsonNull.INSTANCE }
+ }
+
+ else -> {
+ // 兜底:尽量序列化;失败就丢弃,避免影响主流程
+ runCatching { gson.toJsonTree(this) }.getOrNull()
+ ?.takeIf { it != JsonNull.INSTANCE }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/network/HttpInterceptors.kt b/app/src/main/java/com/example/myapplication/network/HttpInterceptors.kt
index 8e88907..b3bf2a9 100644
--- a/app/src/main/java/com/example/myapplication/network/HttpInterceptors.kt
+++ b/app/src/main/java/com/example/myapplication/network/HttpInterceptors.kt
@@ -9,7 +9,16 @@ import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import com.example.myapplication.utils.EncryptedSharedPreferencesUtil
import android.content.Context
-
+import com.example.myapplication.network.security.BodyParamsExtractor
+import com.example.myapplication.network.security.NonceUtils
+import com.example.myapplication.network.security.SignUtils
+import com.google.gson.JsonElement
+import com.google.gson.JsonParser
+import okio.Buffer
+import java.net.URLDecoder
+import java.net.URLEncoder
+import javax.crypto.Mac
+import javax.crypto.spec.SecretKeySpec
// * 不需要登录的接口路径(相对完整路径)
// * 只写 /api/ 后面的部分
@@ -18,59 +27,95 @@ import android.content.Context
private val NO_LOGIN_REQUIRED_PATHS = setOf(
"/themes/listByStyle",
"/wallet/balance",
+ "/character/listByUser",
)
-private fun noLoginRequired(url: HttpUrl): Boolean {
- val path = url.encodedPath // 例:/api/home/banner
+private val NO_SIGN_REQUIRED_PATHS = setOf(
+ "/auth/login",
+)
- // 统一裁掉 /api 前缀
- val apiPath = path.substringAfter("/api", path)
-
- return NO_LOGIN_REQUIRED_PATHS.contains(apiPath)
+private fun apiPath(url: HttpUrl): String {
+ val path = url.encodedPath
+ return path.substringAfter("/api", path)
}
+private fun noLoginRequired(url: HttpUrl): Boolean =
+ NO_LOGIN_REQUIRED_PATHS.contains(apiPath(url))
+
+private fun noSignRequired(url: HttpUrl): Boolean =
+ NO_SIGN_REQUIRED_PATHS.contains(apiPath(url))
+
/**
* 请求拦截器:统一加 Header、token 等
*/
fun requestInterceptor(appContext: Context) = Interceptor { chain ->
val original = chain.request()
+ val url = original.url
val user = EncryptedSharedPreferencesUtil.get(appContext, "user", LoginResponse::class.java)
val token = user?.token.orEmpty()
- val newRequest = original.newBuilder()
+ val builder = original.newBuilder()
.apply {
- if (token.isNotBlank()) {
- addHeader("auth-token", "$token")
- }
+ if (token.isNotBlank()) addHeader("auth-token", token)
}
.addHeader("Accept-Language", "lang")
- .build()
- // ===== 打印请求信息 =====
- val request = newRequest
- val url = request.url
+ // ======= ✅ 按你规则加签名(header + query + body)=======
+ if (!noSignRequired(url)) {
+ val appId = "loveKeyboard"
+ val secret = "kZJM39HYvhxwbJkG1fmquQRVkQiLAh2H" // TODO 正式环境建议下发/混淆/NDK
+ val timestamp = (System.currentTimeMillis() / 1000).toString()
+ val nonce = java.util.UUID.randomUUID().toString().replace("-", "").take(16)
+
+ // 1) 合并成 Map(去掉 sign 本身)
+ val params = linkedMapOf()
+ params["appId"] = appId
+ params["timestamp"] = timestamp
+ params["nonce"] = nonce
+
+ // 2) query 参数
+ for (i in 0 until url.querySize) {
+ params[url.queryParameterName(i)] = url.queryParameterValue(i).orEmpty()
+ }
+
+ // 3) body 参数(json / form)
+ params.putAll(extractBodyParams(original))
+
+ // 4) 生成 sign
+ val sign = calcSign(params, secret)
+
+ builder
+ .addHeader("X-App-Id", appId)
+ .addHeader("X-Timestamp", timestamp)
+ .addHeader("X-Nonce", nonce)
+ .addHeader("X-Sign", sign)
+ }
+
+ val request = builder.build()
+
+ // ===== 打印请求信息(保留你原来的)=====
val sb = StringBuilder()
sb.append("\n======== HTTP Request ========\n")
sb.append("Method: ${request.method}\n")
- sb.append("URL: $url\n")
+ sb.append("URL: ${request.url}\n")
sb.append("Headers:\n")
for (name in request.headers.names()) {
sb.append(" $name: ${request.header(name)}\n")
}
- if (url.querySize > 0) {
+ if (request.url.querySize > 0) {
sb.append("Query Params:\n")
- for (i in 0 until url.querySize) {
- sb.append(" ${url.queryParameterName(i)} = ${url.queryParameterValue(i)}\n")
+ for (i in 0 until request.url.querySize) {
+ sb.append(" ${request.url.queryParameterName(i)} = ${request.url.queryParameterValue(i)}\n")
}
}
val requestBody = request.body
if (requestBody != null) {
- val buffer = okio.Buffer()
+ val buffer = Buffer()
requestBody.writeTo(buffer)
sb.append("Body:\n")
sb.append(buffer.readUtf8())
@@ -83,6 +128,132 @@ fun requestInterceptor(appContext: Context) = Interceptor { chain ->
chain.proceed(request)
}
+//
+// ================== 签名工具(严格按你描述规则) ==================
+//
+
+private fun calcSign(params: Map, secret: String): String {
+ // 去空值 + 去 sign
+ val filtered = params
+ .filter { (k, v) -> v.isNotBlank() && !k.equals("sign", ignoreCase = true) }
+
+ // 按 key 字典序排序
+ val sorted = filtered.toSortedMap()
+
+ // 拼接:k=v&...&secret=xxx(value 统一做 URL encode 防止 & = 破坏结构)
+ val sb = StringBuilder()
+ sorted.forEach { (k, v) ->
+ if (sb.isNotEmpty()) sb.append("&")
+ sb.append(k).append("=").append(urlEncode(v))
+ }
+ sb.append("&secret=").append(urlEncode(secret))
+
+ // HMAC-SHA256 -> hex小写
+ return hmacSha256Hex(sb.toString(), secret)
+}
+
+private fun hmacSha256Hex(data: String, secret: String): String {
+ val mac = Mac.getInstance("HmacSHA256")
+ val keySpec = SecretKeySpec(secret.toByteArray(Charsets.UTF_8), "HmacSHA256")
+ mac.init(keySpec)
+ val bytes = mac.doFinal(data.toByteArray(Charsets.UTF_8))
+ return bytes.joinToString("") { "%02x".format(it) }
+}
+
+private fun urlEncode(v: String): String =
+ URLEncoder.encode(v, "UTF-8")
+
+//
+// ================== Body 参数提取:json / form ==================
+//
+
+private fun extractBodyParams(request: okhttp3.Request): Map {
+ val body = request.body ?: return emptyMap()
+ val ct = body.contentType()?.toString()?.lowercase().orEmpty()
+
+ return when {
+ ct.contains("application/json") -> extractJsonBody(body)
+ ct.contains("application/x-www-form-urlencoded") -> extractFormBody(body)
+ else -> emptyMap() // multipart / stream 等默认不签 body(如需可再扩展)
+ }
+}
+
+private fun extractJsonBody(body: okhttp3.RequestBody): Map {
+ val raw = bodyToString(body).trim()
+ if (raw.isBlank()) return emptyMap()
+
+ return try {
+ val root: JsonElement = JsonParser.parseString(raw)
+ val out = linkedMapOf()
+ flattenJson(root, "", out)
+ out
+ } catch (_: Exception) {
+ emptyMap()
+ }
+}
+
+private fun extractFormBody(body: okhttp3.RequestBody): Map {
+ val raw = bodyToString(body)
+ if (raw.isBlank()) return emptyMap()
+
+ val map = linkedMapOf()
+ raw.split("&")
+ .filter { it.isNotBlank() }
+ .forEach { pair ->
+ val idx = pair.indexOf("=")
+ if (idx > 0) {
+ val k = pair.substring(0, idx)
+ val v = pair.substring(idx + 1)
+ // form 这里解码回“原值”,后续签名阶段再统一 encode
+ map[k] = URLDecoder.decode(v, "UTF-8")
+ } else {
+ map[pair] = ""
+ }
+ }
+ return map
+}
+
+private fun bodyToString(body: okhttp3.RequestBody): String {
+ return try {
+ val buffer = Buffer()
+ body.writeTo(buffer)
+ val charset = body.contentType()?.charset(Charsets.UTF_8) ?: Charsets.UTF_8
+ buffer.readString(charset)
+ } catch (_: Exception) {
+ ""
+ }
+}
+
+/**
+ * JSON 扁平化规则:
+ * object: a.b.c
+ * array : items[0].id
+ */
+private fun flattenJson(elem: JsonElement, prefix: String, out: MutableMap) {
+ when {
+ elem.isJsonNull -> {
+ // null 不参与签名(服务端也要一致)
+ }
+ elem.isJsonPrimitive -> {
+ if (prefix.isNotBlank()) out[prefix] = elem.asJsonPrimitive.toString().trim('"')
+ }
+ elem.isJsonObject -> {
+ val obj = elem.asJsonObject
+ for ((k, v) in obj.entrySet()) {
+ val newKey = if (prefix.isBlank()) k else "$prefix.$k"
+ flattenJson(v, newKey, out)
+ }
+ }
+ elem.isJsonArray -> {
+ val arr = elem.asJsonArray
+ for (i in 0 until arr.size()) {
+ val newKey = "$prefix[$i]"
+ flattenJson(arr[i], newKey, out)
+ }
+ }
+ }
+}
+
/**
* 响应拦截器:统一打印日志、做一些简单的错误处理
@@ -121,7 +292,7 @@ val responseInterceptor = Interceptor { chain ->
val gson = Gson()
val errorResponse = gson.fromJson(bodyString, ErrorResponse::class.java)
- if (errorResponse.code == 40102) {
+ if (errorResponse.code == 40102|| errorResponse.code == 40103) {
val isNoLoginApi = noLoginRequired(request.url)
Log.w(
@@ -134,14 +305,14 @@ val responseInterceptor = Interceptor { chain ->
AuthEventBus.emit(AuthEvent.TokenExpired(errorResponse.message))
}
- return@Interceptor response.newBuilder()
- .code(401)
- .message(
- if (isNoLoginApi) response.message
- else "Login required: ${errorResponse.message}"
- )
- .body(bodyString.toResponseBody(mediaType))
- .build()
+ // return@Interceptor response.newBuilder()
+ // .code(401)
+ // .message(
+ // if (isNoLoginApi) response.message
+ // else "Login required: ${errorResponse.message}"
+ // )
+ // .body(bodyString.toResponseBody(mediaType))
+ // .build()
}
// 其他非0的错误码,通过事件总线发送错误信息
else if (errorResponse.code!= 0) {
diff --git a/app/src/main/java/com/example/myapplication/network/Models.kt b/app/src/main/java/com/example/myapplication/network/Models.kt
index 1c2a916..be6e683 100644
--- a/app/src/main/java/com/example/myapplication/network/Models.kt
+++ b/app/src/main/java/com/example/myapplication/network/Models.kt
@@ -71,18 +71,77 @@ data class User(
val email: String,
val emailVerified: Boolean,
val isVip: Boolean,
- val vipExpiry: String,
- val token: String
+ val vipExpiry: String?,
+ val token: String?,
)
//更新用户
data class updateInfoRequest(
- val uid: Long,
- val nickName: String,
- val gender: Int,
- val avatarUrl: String?,
+ val uid: Long? = null,
+ val nickName: String? = null,
+ val gender: Int? = null,
+ val avatarUrl: String? = null,
)
+//分页查询钱包交易记录
+data class transactionsRequest(
+ val pageNum: Int,
+ val pageSize: Int,
+)
+
+//分页查询钱包交易记录响应
+data class transactionsResponse(
+ val records: List,
+ val total: Int,
+ val size: Int,
+ val current: Int,
+ val pages: Int
+)
+
+//分页查询钱包交易记录响应
+data class TransactionRecord(
+ val id: Long,
+ val type: Int,
+ val amount: Number,
+ val beforeBalance: Number,
+ val afterBalance: Number,
+ val description: String,
+ val createdAt: String,
+)
+
+//用户人设列表响应
+data class ListByUserWithNot(
+ val id: Int,
+ val characterName: String,
+ val emoji: String,
+ val characterId: Int,
+)
+
+//更新用户人设排序
+data class updateUserCharacterSortRequest(
+ val sort: List
+)
+
+//提交反馈
+data class feedbackRequest(
+ val content: String,
+)
+
+//分享响应
+data class ShareResponse(
+ val code: String,
+ val status: Int,
+ val usedCount: Int,
+ val maxUses: Int,
+ val expiresAt: String,
+ val h5Link: String,
+)
+
+data class FreeTrialQuota(
+ val effectiveQuota: Int,
+ val nacosQuota: Int,
+ val source: Int,
+)
// =======================================首页======================================
//标签列表
data class Tag(
@@ -115,7 +174,7 @@ data class listByTagWithNotLogin(
// 人设详情响应
data class CharacterDetailResponse(
- val id: Long? = null,
+ val id: Int? = null,
val characterName: String? = null,
val characterBackground: String? = null,
val avatarUrl: String? = null,
@@ -189,4 +248,4 @@ data class deleteThemeRequest(
//购买主题
data class purchaseThemeRequest(
val themeId: Int,
-)
\ No newline at end of file
+)
diff --git a/app/src/main/java/com/example/myapplication/network/NetworkClient.kt b/app/src/main/java/com/example/myapplication/network/NetworkClient.kt
index 97fae86..d2d25f9 100644
--- a/app/src/main/java/com/example/myapplication/network/NetworkClient.kt
+++ b/app/src/main/java/com/example/myapplication/network/NetworkClient.kt
@@ -1,116 +1,406 @@
package com.example.myapplication.network
+import android.content.Context
+import android.util.Log
import okhttp3.*
+import okio.BufferedSource
import okio.Buffer
import org.json.JSONObject
import java.io.IOException
+import java.net.URLDecoder
+import java.net.URLEncoder
+import java.util.UUID
import java.util.concurrent.TimeUnit
+import javax.crypto.Mac
+import javax.crypto.spec.SecretKeySpec
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
+import com.example.myapplication.utils.EncryptedSharedPreferencesUtil
object NetworkClient {
- // 你自己后端的 base url
private const val BASE_URL = "http://192.168.2.21:7529/api"
+ private const val TAG = "999-SSE_TALK"
+
+ // ====== 按你给的规则固定值 ======
+ private const val APP_ID = "loveKeyboard"
+ private const val SECRET = "kZJM39HYvhxwbJkG1fmquQRVkQiLAh2H"
+
+ @Volatile
+ private lateinit var appContext: Context
+
+ fun init(context: Context) {
+ appContext = context.applicationContext
+ }
+
+ private fun debugLoggingInterceptor(): Interceptor = Interceptor { chain ->
+ val req = chain.request()
+ Log.d("999-HTTP", ">>> ${req.method} ${req.url}")
+ Log.d("999-HTTP", ">>> headers:\n${req.headers}")
+ val resp = chain.proceed(req)
+ Log.d("999-HTTP", "<<< code=${resp.code} ct=${resp.header("Content-Type")} len=${resp.header("Content-Length")}")
+ Log.d("999-HTTP", "<<< headers:\n${resp.headers}")
+ resp
+ }
- // 专门用于 SSE 的 OkHttpClient:readTimeout = 0 代表不超时,一直保持连接
private val sseClient: OkHttpClient by lazy {
+ check(::appContext.isInitialized) {
+ "NetworkClient not initialized. Call NetworkClient.init(context) first."
+ }
+
OkHttpClient.Builder()
- .readTimeout(0, TimeUnit.MILLISECONDS) // SSE 必须不能有读超时
+ .readTimeout(0, TimeUnit.MILLISECONDS)
+ .addInterceptor(debugLoggingInterceptor())
.build()
}
- /**
- * 启动一次 SSE 流式请求
- * @param question 用户问题(你要传给后端的)
- * @return Call,可用于取消(比如用户关闭键盘时)
- */
- fun startLlmStream(
- question: String,
+ fun startChatTalkStream(
+ characterId: Int,
+ message: String,
callback: LlmStreamCallback
): Call {
- // 根据你后端的接口改:是 POST 还是 GET,参数格式是什么
+ Log.d(TAG, "POST /chat/talk send -> characterId=$characterId, message=${message.take(200)}")
+
val json = JSONObject().apply {
- put("query", question) // 假设你后端字段叫 query
+ put("characterId", characterId)
+ put("message", message)
}
val requestBody = json.toString()
.toRequestBody("application/json; charset=utf-8".toMediaType())
- val request = Request.Builder()
- .url("$BASE_URL/llm/stream") // TODO: 换成你真实的 SSE 路径
+ val baseRequest = Request.Builder()
+ .url("$BASE_URL/chat/talk")
.post(requestBody)
- // 有些 SSE 接口会要求 Accept
- .addHeader("Accept", "text/event-stream")
.build()
- val call = sseClient.newCall(request)
+ // ✅ 在这里:按你提供的规则生成签名并加 header
+ val signedRequest = signRequest(baseRequest)
+
+ Log.d(TAG, "before newCall")
+ val call = sseClient.newCall(signedRequest)
+ Log.d(TAG, "after newCall -> enqueue")
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
- if (call.isCanceled()) return // 被主动取消就不用回调错误了
+ if (call.isCanceled()) {
+ Log.w(TAG, "canceled: $call")
+ return
+ }
+ Log.e(TAG, "onFailure: ${e.javaClass.name}: ${e.message}", e)
callback.onError(e)
}
override fun onResponse(call: Call, response: Response) {
+ Log.d(TAG, "onResponse -> $response")
+ Log.d(TAG, "resp headers -> ${response.headers}")
+
+ val body = response.body
+ val contentType = body?.contentType()?.toString().orEmpty()
+ val isSse = contentType.contains("text/event-stream", ignoreCase = true)
+
if (!response.isSuccessful) {
- callback.onError(IOException("SSE failed: ${response.code}"))
+ val err = peekOrReadBody(response)
+ Log.e(TAG, "HTTP failed: code=${response.code}, contentType=$contentType, body=$err")
+ callback.onError(IOException(err))
response.close()
return
}
- val body = response.body ?: run {
+ if (!isSse) {
+ val text = peekOrReadBody(response)
+ Log.w(TAG, "Not SSE response: contentType=$contentType, body=$text")
+ callback.onError(IOException(text))
+ response.close()
+ return
+ }
+
+ if (body == null) {
callback.onError(IOException("Empty body"))
return
}
- // 长连接读取:一行一行读,直到服务器关闭或我们取消
body.use { b ->
val source = b.source()
try {
- while (!source.exhausted() && !call.isCanceled()) {
- val line = source.readUtf8Line() ?: break
- if (line.isBlank()) {
- // SSE 中空行代表一个 event 结束,这里可以忽略
- continue
- }
-
- // 兼容两种格式:
- // 1) 标准 SSE: "data: { ... }"
- // 2) 服务器直接一行一个 JSON: "{ ... }"
- val payload = if (line.startsWith("data:")) {
- line.substringAfter("data:").trim()
- } else {
- line.trim()
- }
-
- // 你日志里是:
- // {"type":"llm_chunk","data":"Her"}
- // {"type":"done","data":null}
- try {
- val jsonObj = JSONObject(payload)
- val type = jsonObj.optString("type")
- val data =
- if (jsonObj.has("data") && !jsonObj.isNull("data"))
- jsonObj.getString("data")
- else
- null
-
- callback.onEvent(type, data)
- } catch (e: Exception) {
- // 解析失败就忽略这一行(或者你可以打印下日志)
- // Log.e("NetworkClient", "Bad SSE line: $payload", e)
- }
- }
+ readSseStream(source, call, callback)
} catch (ioe: IOException) {
if (!call.isCanceled()) {
+ Log.e(TAG, "read error: ${ioe.message}", ioe)
callback.onError(ioe)
}
+ } catch (t: Throwable) {
+ Log.e(TAG, "unexpected error: ${t.message}", t)
+ callback.onError(t)
}
}
}
})
+ Log.d(TAG, "after enqueue")
return call
}
+
+ /**
+ * ✅ 按你给的规则生成:
+ * - timestamp = 秒
+ * - nonce = UUID 去 '-' 后取 16 位
+ * - params = appId/timestamp/nonce + query + body(flatten)
+ * - sign = calcSign(params, secret)
+ * - headers: X-App-Id / X-Timestamp / X-Nonce / X-Sign
+ * - token: auth-token(如果有)
+ */
+ private fun signRequest(original: Request): Request {
+ val url = original.url
+
+ // 你原代码里 token 从 EncryptedSharedPreferencesUtil.get(...) 取
+ // 这里保持一致:如果你项目里有这工具类就用它;没有就把 token 获取替换成你自己的方式
+ val token = try {
+ val user = EncryptedSharedPreferencesUtil.get(appContext, "user", LoginResponse::class.java)
+ user?.token.orEmpty()
+ } catch (_: Throwable) {
+ ""
+ }
+
+ val timestamp = (System.currentTimeMillis() / 1000).toString()
+ val nonce = UUID.randomUUID().toString().replace("-", "").take(16)
+
+ val params = linkedMapOf()
+ params["appId"] = APP_ID
+ params["timestamp"] = timestamp
+ params["nonce"] = nonce
+
+ // query 参数
+ for (i in 0 until url.querySize) {
+ params[url.queryParameterName(i)] = url.queryParameterValue(i).orEmpty()
+ }
+
+ // body 参数(json / form)
+ params.putAll(extractBodyParams(original))
+
+ val sign = calcSign(params, SECRET)
+
+ val builder = original.newBuilder()
+ .addHeader("Accept-Language", "lang")
+ .addHeader("X-App-Id", APP_ID)
+ .addHeader("X-Timestamp", timestamp)
+ .addHeader("X-Nonce", nonce)
+ .addHeader("X-Sign", sign)
+ .apply {
+ if (token.isNotBlank()) addHeader("auth-token", token)
+ }
+
+ val request = builder.build()
+
+ // ✅ 打印签名相关(避免泄露:sign/secret别全量打印到线上)
+ Log.d(TAG, "signed -> X-App-Id=$APP_ID X-Timestamp=$timestamp X-Nonce=$nonce X-Sign=${sign.take(16)}... tokenPresent=${token.isNotBlank()}")
+ return request
+ }
+
+ // ==================== 签名计算(严格按你给的规则) ====================
+
+ private fun calcSign(params: Map, secret: String): String {
+ val filtered = params
+ .filter { (k, v) -> v.isNotBlank() && !k.equals("sign", ignoreCase = true) }
+
+ val sorted = filtered.toSortedMap()
+
+ val sb = StringBuilder()
+ sorted.forEach { (k, v) ->
+ if (sb.isNotEmpty()) sb.append("&")
+ sb.append(k).append("=").append(urlEncode(v))
+ }
+ sb.append("&secret=").append(urlEncode(secret))
+
+ return hmacSha256Hex(sb.toString(), secret)
+ }
+
+ private fun hmacSha256Hex(data: String, secret: String): String {
+ val mac = Mac.getInstance("HmacSHA256")
+ val keySpec = SecretKeySpec(secret.toByteArray(Charsets.UTF_8), "HmacSHA256")
+ mac.init(keySpec)
+ val bytes = mac.doFinal(data.toByteArray(Charsets.UTF_8))
+ return bytes.joinToString("") { "%02x".format(it) }
+ }
+
+ private fun urlEncode(v: String): String =
+ URLEncoder.encode(v, "UTF-8")
+
+ // ==================== body 参数提取:json / form ====================
+
+ private fun extractBodyParams(request: Request): Map {
+ val body = request.body ?: return emptyMap()
+ val ct = body.contentType()?.toString()?.lowercase().orEmpty()
+
+ return when {
+ ct.contains("application/json") -> extractJsonBody(body)
+ ct.contains("application/x-www-form-urlencoded") -> extractFormBody(body)
+ else -> emptyMap()
+ }
+ }
+
+ private fun extractJsonBody(body: RequestBody): Map {
+ val raw = bodyToString(body).trim()
+ if (raw.isBlank()) return emptyMap()
+
+ return try {
+ // 这里只支持 JSON object/array 的扁平化;primitive 忽略
+ val root = org.json.JSONTokener(raw).nextValue()
+ val out = linkedMapOf()
+ flattenJsonAny(root, "", out)
+ out
+ } catch (_: Throwable) {
+ emptyMap()
+ }
+ }
+
+ private fun extractFormBody(body: RequestBody): Map {
+ val raw = bodyToString(body)
+ if (raw.isBlank()) return emptyMap()
+
+ val map = linkedMapOf()
+ raw.split("&")
+ .filter { it.isNotBlank() }
+ .forEach { pair ->
+ val idx = pair.indexOf("=")
+ if (idx > 0) {
+ val k = pair.substring(0, idx)
+ val v = pair.substring(idx + 1)
+ map[k] = URLDecoder.decode(v, "UTF-8")
+ } else {
+ map[pair] = ""
+ }
+ }
+ return map
+ }
+
+ private fun bodyToString(body: RequestBody): String {
+ return try {
+ val buffer = Buffer()
+ body.writeTo(buffer)
+ val charset = body.contentType()?.charset(Charsets.UTF_8) ?: Charsets.UTF_8
+ buffer.readString(charset)
+ } catch (_: Throwable) {
+ ""
+ }
+ }
+
+ /**
+ * JSON 扁平化规则(尽量与你给的 gson flatten 保持一致):
+ * object: a.b.c
+ * array : items[0].id
+ */
+ private fun flattenJsonAny(any: Any?, prefix: String, out: MutableMap) {
+ when (any) {
+ null -> Unit
+ is JSONObject -> {
+ val keys = any.keys()
+ while (keys.hasNext()) {
+ val k = keys.next()
+ val newKey = if (prefix.isBlank()) k else "$prefix.$k"
+ flattenJsonAny(any.opt(k), newKey, out)
+ }
+ }
+ is org.json.JSONArray -> {
+ for (i in 0 until any.length()) {
+ val newKey = "$prefix[$i]"
+ flattenJsonAny(any.opt(i), newKey, out)
+ }
+ }
+ is Boolean, is Int, is Long, is Double, is Float -> {
+ if (prefix.isNotBlank()) out[prefix] = any.toString()
+ }
+ is String -> {
+ if (prefix.isNotBlank()) out[prefix] = any
+ }
+ else -> {
+ // 其它类型忽略
+ }
+ }
+ }
+
+ // ==================== SSE 读取:JSON / 纯文本 chunk ====================
+
+ private fun readSseStream(
+ source: BufferedSource,
+ call: Call,
+ callback: LlmStreamCallback
+ ) {
+ var eventName: String? = null
+ val dataLines = mutableListOf()
+
+ fun dispatch() {
+ if (eventName == null && dataLines.isEmpty()) return
+
+ val rawData = dataLines.joinToString("\n")
+ Log.d("999-SSE_TALK-event", "event=${eventName ?: "(null)"} rawData=[${rawData.take(500)}]")
+
+ if (rawData.isNotEmpty()) {
+ handlePayload(eventName, rawData, callback)
+ }
+
+ eventName = null
+ dataLines.clear()
+ }
+
+ while (!source.exhausted() && !call.isCanceled()) {
+ val line = source.readUtf8Line() ?: break
+ Log.d("999-SSE_TALK-raw", "raw: [$line]")
+
+ if (line.isEmpty()) {
+ dispatch()
+ continue
+ }
+ if (line.startsWith(":")) continue
+
+ val idx = line.indexOf(':')
+ val field = if (idx == -1) line else line.substring(0, idx)
+
+ val rawValue = if (idx == -1) "" else line.substring(idx + 1)
+ val value = if (rawValue.startsWith(" ")) rawValue.substring(1) else rawValue
+
+ when (field) {
+ "event" -> eventName = value
+ "data" -> dataLines.add(value)
+ else -> Unit
+ }
+ }
+
+ dispatch()
+ }
+
+ private fun handlePayload(eventName: String?, rawData: String, callback: LlmStreamCallback) {
+ val trimmed = rawData.trim()
+ val looksLikeJson = trimmed.startsWith("{") && trimmed.endsWith("}")
+
+ if (looksLikeJson) {
+ try {
+ val obj = JSONObject(trimmed)
+ val type = obj.optString("type", "")
+ val dataValue = if (obj.has("data") && !obj.isNull("data")) obj.opt("data") else null
+ val dataStr = dataValue?.toString()
+
+ if (type.isNotBlank()) {
+ callback.onEvent(type, dataStr)
+ return
+ }
+ } catch (_: Throwable) {
+ // fallthrough to text
+ }
+ }
+
+ callback.onEvent(eventName ?: "text_chunk", rawData)
+ }
+
+ private fun peekOrReadBody(response: Response): String {
+ return try {
+ response.peekBody(1024 * 1024).string()
+ } catch (_: Throwable) {
+ try {
+ response.body?.string().orEmpty()
+ } catch (t2: Throwable) {
+ "read body failed: ${t2.message}"
+ }
+ }
+ }
}
diff --git a/app/src/main/java/com/example/myapplication/network/RetrofitClient.kt b/app/src/main/java/com/example/myapplication/network/RetrofitClient.kt
index dc599bd..8812eac 100644
--- a/app/src/main/java/com/example/myapplication/network/RetrofitClient.kt
+++ b/app/src/main/java/com/example/myapplication/network/RetrofitClient.kt
@@ -6,6 +6,7 @@ import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
+import com.example.myapplication.network.FileUploadService
object RetrofitClient {
@@ -50,6 +51,13 @@ object RetrofitClient {
retrofit.create(ApiService::class.java)
}
+ /**
+ * 创建文件上传服务
+ */
+ fun createFileUploadService(): FileUploadService {
+ return retrofit.create(FileUploadService::class.java)
+ }
+
/**
* 创建支持完整 URL 下载的 Retrofit 实例
* @param baseUrl 完整的下载 URL
diff --git a/app/src/main/java/com/example/myapplication/network/security/BodyParamsExtractor.kt b/app/src/main/java/com/example/myapplication/network/security/BodyParamsExtractor.kt
new file mode 100644
index 0000000..0f47d75
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/network/security/BodyParamsExtractor.kt
@@ -0,0 +1,137 @@
+package com.example.myapplication.network.security
+
+import com.google.gson.JsonElement
+import com.google.gson.JsonParser
+import okhttp3.MultipartBody
+import okhttp3.Request
+import okhttp3.RequestBody
+import okio.Buffer
+import java.nio.charset.Charset
+
+object BodyParamsExtractor {
+
+ /**
+ * 抽取 Request 的 body 参数到 map
+ * - JSON: 扁平化(a.b[0].c)
+ * - Form: 直接 key=value
+ * - Multipart: 只签文本字段(文件字段跳过)
+ */
+ fun extractBodyParams(request: Request): Map {
+ val body = request.body ?: return emptyMap()
+ val contentType = body.contentType()?.toString()?.lowercase().orEmpty()
+
+ return when {
+ contentType.contains("application/json") -> extractJsonBody(body)
+ contentType.contains("application/x-www-form-urlencoded") -> extractFormBody(body)
+ contentType.contains("multipart/form-data") -> extractMultipartBody(body)
+ else -> {
+ // 其他类型(例如 stream、protobuf、octet-stream)
+ // 建议不签或签一个摘要(需要服务端同样实现)
+ emptyMap()
+ }
+ }
+ }
+
+ private fun extractJsonBody(body: RequestBody): Map {
+ val json = bodyToString(body).trim()
+ if (json.isBlank()) return emptyMap()
+
+ return try {
+ val root: JsonElement = JsonParser.parseString(json)
+ val out = linkedMapOf()
+ flattenJson(root, "", out)
+ out
+ } catch (_: Exception) {
+ // JSON 解析失败就不签 body(也可以选择直接把原文作为 bodyRaw 参与签名)
+ emptyMap()
+ }
+ }
+
+ private fun extractFormBody(body: RequestBody): Map {
+ // x-www-form-urlencoded 本质就是 querystring:a=1&b=2
+ val raw = bodyToString(body)
+ if (raw.isBlank()) return emptyMap()
+
+ val map = linkedMapOf()
+ raw.split("&")
+ .filter { it.isNotBlank() }
+ .forEach { pair ->
+ val idx = pair.indexOf("=")
+ if (idx > 0) {
+ val k = pair.substring(0, idx)
+ val v = pair.substring(idx + 1)
+ // 注意:这里 raw 是已经 urlencoded 的内容
+ // 为了与服务端一致,推荐:服务端拿到 form 参数的“解码后值”再参与签名
+ // 客户端这里可以不 decode,改为后续签名阶段统一 encode(SignUtils 做了 encode)
+ map[k] = java.net.URLDecoder.decode(v, "UTF-8")
+ } else {
+ map[pair] = ""
+ }
+ }
+ return map
+ }
+
+ private fun extractMultipartBody(body: RequestBody): Map {
+ if (body !is MultipartBody) return emptyMap()
+
+ val map = linkedMapOf()
+ for (i in 0 until body.parts.size) {
+ val part = body.parts[i]
+ val headers = part.headers
+ val disp = headers?.get("Content-Disposition").orEmpty()
+
+ // 取 name="xxx"
+ val name = Regex("""name="([^"]+)"""").find(disp)?.groupValues?.getOrNull(1) ?: continue
+ val filename = Regex("""filename="([^"]+)"""").find(disp)?.groupValues?.getOrNull(1)
+
+ // 有 filename 认为是文件字段:默认不签(避免读文件流/超大)
+ if (!filename.isNullOrBlank()) continue
+
+ // 文本字段:读出内容
+ val value = bodyToString(part.body).trim()
+ map[name] = value
+ }
+ return map
+ }
+
+ private fun bodyToString(body: RequestBody): String {
+ return try {
+ val buffer = Buffer()
+ body.writeTo(buffer)
+ val charset: Charset = body.contentType()?.charset(Charsets.UTF_8) ?: Charsets.UTF_8
+ buffer.readString(charset)
+ } catch (_: Exception) {
+ ""
+ }
+ }
+
+ /**
+ * JSON 扁平化规则:
+ * object: a.b.c
+ * array: items[0].id
+ */
+ private fun flattenJson(elem: JsonElement, prefix: String, out: MutableMap) {
+ when {
+ elem.isJsonNull -> {
+ // null 不参与(也可以 out[prefix] = "null" 但需要服务端一致)
+ }
+ elem.isJsonPrimitive -> {
+ if (prefix.isNotBlank()) out[prefix] = elem.asJsonPrimitive.toString().trim('"')
+ }
+ elem.isJsonObject -> {
+ val obj = elem.asJsonObject
+ for ((k, v) in obj.entrySet()) {
+ val newKey = if (prefix.isBlank()) k else "$prefix.$k"
+ flattenJson(v, newKey, out)
+ }
+ }
+ elem.isJsonArray -> {
+ val arr = elem.asJsonArray
+ for (i in 0 until arr.size()) {
+ val newKey = "$prefix[$i]"
+ flattenJson(arr[i], newKey, out)
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/network/security/NonceUtils.kt b/app/src/main/java/com/example/myapplication/network/security/NonceUtils.kt
new file mode 100644
index 0000000..8167499
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/network/security/NonceUtils.kt
@@ -0,0 +1,11 @@
+package com.example.myapplication.network.security
+
+import java.util.UUID
+
+object NonceUtils {
+ fun genNonce(): String =
+ UUID.randomUUID().toString().replace("-", "").take(16)
+
+ fun genTimestampSeconds(): String =
+ (System.currentTimeMillis() / 1000).toString()
+}
diff --git a/app/src/main/java/com/example/myapplication/network/security/SignUtils.kt b/app/src/main/java/com/example/myapplication/network/security/SignUtils.kt
new file mode 100644
index 0000000..35bfb1b
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/network/security/SignUtils.kt
@@ -0,0 +1,38 @@
+package com.example.myapplication.network.security
+
+import java.net.URLEncoder
+import javax.crypto.Mac
+import javax.crypto.spec.SecretKeySpec
+
+object SignUtils {
+
+ fun calcSign(params: Map, secret: String): String {
+ val signStr = buildSignString(params, secret)
+ return hmacSha256Hex(signStr, secret)
+ }
+
+ fun buildSignString(params: Map, secret: String): String {
+ val filtered = params
+ .filter { (k, v) -> v.isNotBlank() && !k.equals("sign", ignoreCase = true) }
+ .toSortedMap()
+
+ val sb = StringBuilder()
+ filtered.forEach { (k, v) ->
+ if (sb.isNotEmpty()) sb.append("&")
+ sb.append(k).append("=").append(urlEncode(v))
+ }
+ sb.append("&secret=").append(urlEncode(secret))
+ return sb.toString()
+ }
+
+ private fun hmacSha256Hex(data: String, secret: String): String {
+ val mac = Mac.getInstance("HmacSHA256")
+ val keySpec = SecretKeySpec(secret.toByteArray(Charsets.UTF_8), "HmacSHA256")
+ mac.init(keySpec)
+ val bytes = mac.doFinal(data.toByteArray(Charsets.UTF_8))
+ return bytes.joinToString("") { "%02x".format(it) }
+ }
+
+ private fun urlEncode(v: String): String =
+ URLEncoder.encode(v, "UTF-8")
+}
diff --git a/app/src/main/java/com/example/myapplication/ui/circle/CircleFragment.kt b/app/src/main/java/com/example/myapplication/ui/circle/CircleFragment.kt
deleted file mode 100644
index 7df92d3..0000000
--- a/app/src/main/java/com/example/myapplication/ui/circle/CircleFragment.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.example.myapplication.ui.circle
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.fragment.app.Fragment
-import com.example.myapplication.R
-
-class CircleFragment : Fragment() {
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- return inflater.inflate(R.layout.fragment_circle, container, false)
- }
-}
diff --git a/app/src/main/java/com/example/myapplication/ui/common/LoadingOverlay.kt b/app/src/main/java/com/example/myapplication/ui/common/LoadingOverlay.kt
index c5f562f..65e789a 100644
--- a/app/src/main/java/com/example/myapplication/ui/common/LoadingOverlay.kt
+++ b/app/src/main/java/com/example/myapplication/ui/common/LoadingOverlay.kt
@@ -1,5 +1,7 @@
package com.example.myapplication.ui.common
+import android.os.Looper
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -12,20 +14,33 @@ class LoadingOverlay private constructor(
companion object {
fun attach(parent: ViewGroup): LoadingOverlay {
val overlay = LayoutInflater.from(parent.context)
- .inflate(R.layout.view_fullscreen_loading, parent, false)
+ .inflate(R.layout.view_fullscreen_loading, parent, false).apply {
+ visibility = View.GONE
+ bringToFront()
+ elevation = 100f // 确保在其它视图之上
+ }
- overlay.visibility = View.GONE
- parent.addView(overlay) // 加到最上层(最后添加的在最上面)
+ parent.addView(overlay)
return LoadingOverlay(parent, overlay)
}
}
fun show() {
- overlay.visibility = View.VISIBLE
+ if (Looper.getMainLooper().thread == Thread.currentThread()) {
+ overlay.visibility = View.VISIBLE
+ } else {
+ overlay.post { overlay.visibility = View.VISIBLE }
+ }
+ Log.d("LoadingOverlay", "Show loading")
}
fun hide() {
- overlay.visibility = View.GONE
+ if (Looper.getMainLooper().thread == Thread.currentThread()) {
+ overlay.visibility = View.GONE
+ } else {
+ overlay.post { overlay.visibility = View.GONE }
+ }
+ Log.d("LoadingOverlay", "Hide loading")
}
fun remove() {
diff --git a/app/src/main/java/com/example/myapplication/ui/home/HomeFragment.kt b/app/src/main/java/com/example/myapplication/ui/home/HomeFragment.kt
index 6de4209..3650d53 100644
--- a/app/src/main/java/com/example/myapplication/ui/home/HomeFragment.kt
+++ b/app/src/main/java/com/example/myapplication/ui/home/HomeFragment.kt
@@ -25,13 +25,21 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.example.myapplication.ImeGuideActivity
+import com.example.myapplication.ui.common.LoadingOverlay
import com.example.myapplication.R
-import com.example.myapplication.network.*
+import com.example.myapplication.network.AddPersonaClick
+import com.example.myapplication.network.AuthEvent
+import com.example.myapplication.network.AuthEventBus
+import com.example.myapplication.network.BehaviorReporter
+import com.example.myapplication.network.RetrofitClient
+import com.example.myapplication.network.listByTagWithNotLogin
import com.example.myapplication.utils.EncryptedSharedPreferencesUtil
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.card.MaterialCardView
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
+import com.example.myapplication.network.PersonaClick
+import com.example.myapplication.ui.home.PersonaAdapter
import kotlin.math.abs
class HomeFragment : Fragment() {
@@ -47,6 +55,7 @@ class HomeFragment : Fragment() {
private lateinit var tabList2: TextView
private lateinit var backgroundImage: ImageView
private var lastList1RenderKey: String? = null
+ private lateinit var loadingOverlay: LoadingOverlay
private var preloadJob: Job? = null
private var allPersonaCache: List = emptyList()
@@ -85,6 +94,7 @@ class HomeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
AuthEventBus.events.collect { event ->
@@ -95,13 +105,13 @@ class HomeFragment : Fragment() {
personaCache.clear()
allPersonaCache = emptyList()
lastList1RenderKey = null
-
+
// 2) 重新拉列表1(登录态接口会变)
viewLifecycleOwner.lifecycleScope.launch {
allPersonaCache = fetchAllPersonaList()
notifyPageChangedOnMain(0)
}
-
+
// 3) 如果当前在某个 tag 页,也建议重新拉当前页数据
val pos = viewPager.currentItem
if (pos > 0) {
@@ -114,21 +124,100 @@ class HomeFragment : Fragment() {
}
}
}
+
+ is AuthEvent.CharacterAdded -> {
+ viewLifecycleOwner.lifecycleScope.launch {
+ loadingOverlay.show()
+ try {
+ // 1) 列表一:重新拉
+ allPersonaCache = fetchAllPersonaList()
+ lastList1RenderKey = null
+ notifyPageChangedOnMain(0)
+
+ // 2) 列表二:清缓存(最可靠,避免“删除后还显示旧数据”)
+ personaCache.clear()
+
+ // 3) 如果当前就在某个 tag 页:让它立刻更新(显示 loading -> 拉取 -> 刷新)
+ val pos = viewPager.currentItem
+ if (pos > 0) {
+ val tagId = tags.getOrNull(pos - 1)?.id
+ if (tagId != null) {
+ // 先刷新一次,让页面进入 loading(因为缓存被清了)
+ notifyPageChangedOnMain(pos)
+
+ // 再拉当前 tag 的新数据
+ val list = fetchPersonaByTag(tagId)
+ personaCache[tagId] = list
+ notifyPageChangedOnMain(pos)
+ }
+ }
+
+ // 4) 可选:你如果希望“删除后列表二也立刻全量更新”,就顺手再预加载一遍
+ startPreloadAllTagsFillCacheOnly()
+ } finally {
+ loadingOverlay.hide()
+ }
+ }
+ }
+
+ is AuthEvent.CharacterDeleted -> {
+ viewLifecycleOwner.lifecycleScope.launch {
+ loadingOverlay.show()
+ try {
+ // 1) 列表一:重新拉
+ allPersonaCache = fetchAllPersonaList()
+ lastList1RenderKey = null
+ notifyPageChangedOnMain(0)
+
+ // 2) 列表二:清缓存(最可靠,避免“删除后还显示旧数据”)
+ personaCache.clear()
+
+ // 3) 如果当前就在某个 tag 页:让它立刻更新(显示 loading -> 拉取 -> 刷新)
+ val pos = viewPager.currentItem
+ if (pos > 0) {
+ val tagId = tags.getOrNull(pos - 1)?.id
+ if (tagId != null) {
+ // 先刷新一次,让页面进入 loading(因为缓存被清了)
+ notifyPageChangedOnMain(pos)
+
+ // 再拉当前 tag 的新数据
+ val list = fetchPersonaByTag(tagId)
+ personaCache[tagId] = list
+ notifyPageChangedOnMain(pos)
+ }
+ }
+
+ // 4) 可选:你如果希望“删除后列表二也立刻全量更新”,就顺手再预加载一遍
+ startPreloadAllTagsFillCacheOnly()
+ } finally {
+ loadingOverlay.hide()
+ }
+ }
+ }
else -> Unit
}
}
}
}
-
// 充值按钮点击 - 使用事件总线打开全局页面
view.findViewById(R.id.rechargeButton).setOnClickListener {
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "home_main",
+ "element_id" to "buy_vip_btn",
+ )
AuthEventBus.emit(AuthEvent.OpenGlobalPage(R.id.rechargeFragment))
}
// 输入法激活跳转
view.findViewById(R.id.floatingImage).setOnClickListener {
if (!isAdded) return@setOnClickListener
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "home_main",
+ "element_id" to "permission_float_btn",
+ )
startActivity(Intent(requireActivity(), ImeGuideActivity::class.java))
}
@@ -141,11 +230,13 @@ class HomeFragment : Fragment() {
tabList2 = view.findViewById(R.id.tab_list2)
viewPager = view.findViewById(R.id.viewPager)
viewPager.isSaveEnabled = false
- viewPager.offscreenPageLimit = 2
+ viewPager.offscreenPageLimit = 2
backgroundImage = bottomSheet.findViewById(R.id.backgroundImage)
val root = view.findViewById(R.id.rootCoordinator)
val floatingImage = view.findViewById(R.id.floatingImage)
+ loadingOverlay = LoadingOverlay.attach(root)
+ Log.d("HomeFragment", "LoadingOverlay initialized")
root.post {
if (!isAdded) return@post
@@ -169,22 +260,26 @@ class HomeFragment : Fragment() {
// 加载列表一
viewLifecycleOwner.lifecycleScope.launch {
+ loadingOverlay.show()
try {
val list = fetchAllPersonaList()
if (!isAdded) return@launch
allPersonaCache = list
-
+
// ✅ 关键:数据变了就清 renderKey,允许重建一次 UI
lastList1RenderKey = null
-
+
notifyPageChangedOnMain(0)
} catch (e: Exception) {
Log.e("1314520-HomeFragment", "获取列表一失败", e)
+ } finally {
+ loadingOverlay.hide()
}
}
// 拉标签 + 预加载
viewLifecycleOwner.lifecycleScope.launch {
+ loadingOverlay.show()
try {
val response = RetrofitClient.apiService.tagList()
if (!isAdded) return@launch
@@ -204,11 +299,13 @@ class HomeFragment : Fragment() {
startPreloadAllTagsFillCacheOnly()
} catch (e: Exception) {
Log.e("1314520-HomeFragment", "获取标签失败", e)
+ } finally {
+ loadingOverlay.hide()
}
}
}
- // ================== 你要求的核心优化:setupViewPager 只初始化一次 ==================
+ // ================== 核心:setupViewPager 只初始化一次 ==================
private fun setupViewPagerOnce() {
if (sheetAdapter != null) return
@@ -223,7 +320,7 @@ class HomeFragment : Fragment() {
override fun onPageSelected(position: Int) {
if (!isAdded) return
updateTabsAndTags(position)
-
+
// ✅ 修复:当切换到标签页且缓存已有数据时,强制刷新UI
if (position > 0) {
val tagIndex = position - 1
@@ -245,6 +342,52 @@ class HomeFragment : Fragment() {
}
}
+ // ---------------- 方案A:成功后“造新数据(copy)替换缓存”并刷新 ----------------
+
+ private fun applyAddedToggle(personaId: Int, newAdded: Boolean) {
+ // 1) 更新列表一缓存
+ run {
+ val oldAll = allPersonaCache
+ val idxAll = oldAll.indexOfFirst { it.id == personaId }
+ if (idxAll >= 0) {
+ val newList = oldAll.toMutableList()
+ val oldItem = newList[idxAll]
+ newList[idxAll] = oldItem.copy(added = newAdded)
+ allPersonaCache = newList
+
+ // renderList1 有 renderKey,必须清一下
+ lastList1RenderKey = null
+ notifyPageChangedOnMain(0)
+ }
+ }
+
+ // 2) 更新所有 tag 缓存(personaCache)
+ val keys = personaCache.keys.toList()
+ var changedCurrentTagPage = false
+
+ for (tagId in keys) {
+ val old = personaCache[tagId] ?: continue
+ val idx = old.indexOfFirst { it.id == personaId }
+ if (idx >= 0) {
+ val newList = old.toMutableList()
+ val oldItem = newList[idx]
+ newList[idx] = oldItem.copy(added = newAdded)
+ personaCache[tagId] = newList
+
+ // 如果当前就在这个 tag 页,标记需要刷新
+ val pos = viewPager.currentItem
+ val currentTagId = tags.getOrNull(pos - 1)?.id
+ if (pos > 0 && currentTagId == tagId) {
+ changedCurrentTagPage = true
+ }
+ }
+ }
+
+ if (changedCurrentTagPage) {
+ notifyPageChangedOnMain(viewPager.currentItem)
+ }
+ }
+
// ---------------- 拖拽效果 ----------------
private fun initDrag(target: View, parent: ViewGroup) {
@@ -328,62 +471,52 @@ class HomeFragment : Fragment() {
private fun setupBottomSheet(root: View) {
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
-
+
bottomSheetBehavior.isDraggable = true
bottomSheetBehavior.isHideable = false
bottomSheetBehavior.isFitToContents = false
bottomSheetBehavior.halfExpandedRatio = 0.7f
-
- root.post {
- if (!isAdded) return@post
- val coordinatorHeight = root.height - 40
- val button = root.findViewById(R.id.rechargeButton)
- val peek = (coordinatorHeight - button.bottom).coerceAtLeast(200)
- bottomSheetBehavior.peekHeight = peek
- bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
- }
-
+
+ // ✅ 固定初始高度,避免每次计算导致跳动
+ bottomSheetBehavior.peekHeight = dpToPx(260) // 你想要多少就写多少
+ bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
+
bottomSheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
scrim.isVisible = newState != BottomSheetBehavior.STATE_COLLAPSED
}
-
override fun onSlide(bottomSheet: View, slideOffset: Float) {
if (slideOffset >= 0f) scrim.alpha = slideOffset.coerceIn(0f, 1f)
}
})
-
- scrim.setOnClickListener {
- bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
- }
-
- scrim.setOnTouchListener { _, event ->
- if (event.action == MotionEvent.ACTION_MOVE) {
- bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
- true
- } else false
- }
-
- header.setOnClickListener {
- when (bottomSheetBehavior.state) {
- BottomSheetBehavior.STATE_COLLAPSED ->
- bottomSheetBehavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED
-
- BottomSheetBehavior.STATE_HALF_EXPANDED,
- BottomSheetBehavior.STATE_EXPANDED ->
- bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
-
- else -> {}
- }
- }
+ }
+
+ private fun dpToPx(dp: Int): Int {
+ return (dp * resources.displayMetrics.density).toInt()
}
// ---------------- Tabs ----------------
private fun setupTopTabs() {
- tabList1.setOnClickListener { viewPager.currentItem = 0 }
+ tabList1.setOnClickListener {
+ viewPager.currentItem = 0
+
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "home_rank",
+ )
+
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "home_rank_content",
+ )
+ }
tabList2.setOnClickListener {
if (tags.isNotEmpty()) viewPager.currentItem = 1
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "home_hot",
+ )
}
}
@@ -488,28 +621,28 @@ class HomeFragment : Fragment() {
private fun startPreloadAllTagsFillCacheOnly() {
preloadJob?.cancel()
-
+
val semaphore = kotlinx.coroutines.sync.Semaphore(permits = 2)
-
+
preloadJob = viewLifecycleOwner.lifecycleScope.launch {
if (tags.isEmpty()) return@launch
-
+
tags.forEach { tag ->
if (personaCache.containsKey(tag.id)) return@forEach
-
+
launch {
semaphore.acquire()
try {
val list = fetchPersonaByTag(tag.id)
personaCache[tag.id] = list
-
+
// ✅ 只在用户正在看的页时刷新一次(不算乱刷 UI)
val idx = tags.indexOfFirst { it.id == tag.id }
val thisPos = 1 + idx
if (idx >= 0 && viewPager.currentItem == thisPos) {
notifyPageChangedOnMain(thisPos)
}
-
+
} catch (e: Exception) {
Log.e("HomeFragment", "preload tag=${tag.id} fail", e)
} finally {
@@ -519,7 +652,6 @@ class HomeFragment : Fragment() {
}
}
}
-
// ---------------- ViewPager Adapter ----------------
@@ -531,10 +663,10 @@ class HomeFragment : Fragment() {
fun updatePageCount(newCount: Int) {
if (newCount == pageCount) return
-
+
val old = pageCount
pageCount = newCount
-
+
if (newCount > old) {
notifyItemRangeInserted(old, newCount - old)
} else {
@@ -564,10 +696,7 @@ class HomeFragment : Fragment() {
val loadingView = root.findViewById(R.id.loadingView)
rv2.setHasFixedSize(true)
-
- // ✅ 禁止 itemAnimator(减少 layout 抖动)
rv2.itemAnimator = null
-
rv2.isNestedScrollingEnabled = false
var adapter = rv2.adapter as? PersonaAdapter
@@ -584,22 +713,31 @@ class HomeFragment : Fragment() {
is PersonaClick.Add -> {
viewLifecycleOwner.lifecycleScope.launch {
+ val personaId = click.persona.id?: return@launch
+ val oldAdded = click.persona.added
+ val newAdded = !oldAdded
try {
- if (click.persona.added == true) {
- click.persona.id?.let { id ->
- RetrofitClient.apiService.delUserCharacter(id.toInt())
- }
- } else {
+ if (oldAdded) {
+ // RetrofitClient.apiService.delUserCharacter(personaId)
+ // // ✅ 成功后替换缓存并刷新
+ // applyAddedToggle(personaId, newAdded)
+ }
+ else {
+ Log.d("1314520-HomeFragment", "add persona id=${click.persona.id}")
val req = AddPersonaClick(
- characterId = click.persona.id?.toInt() ?: 0,
+ characterId = personaId,
emoji = click.persona.emoji ?: ""
)
RetrofitClient.apiService.addUserCharacter(req)
+ // ✅ 成功后替换缓存并刷新
+ applyAddedToggle(personaId, newAdded)
}
- } catch (_: Exception) {
+ } catch (e: Exception) {
+ Log.e("1314520-HomeFragment", "grid toggle add failed id=$personaId", e)
}
}
}
+ else -> Unit
}
}
rv2.layoutManager = GridLayoutManager(root.context, 2)
@@ -629,7 +767,7 @@ class HomeFragment : Fragment() {
override fun getItemCount(): Int = pageCount
}
- // ---------------- 列表一渲染(原逻辑不动) ----------------
+ // ---------------- 列表一渲染 ----------------
private fun renderList1(root: View, list: List) {
val key = buildString {
@@ -641,6 +779,7 @@ class HomeFragment : Fragment() {
}
if (key == lastList1RenderKey) return
lastList1RenderKey = key
+
val sorted = list.sortedBy { it.rank ?: Int.MAX_VALUE }
val top3 = sorted.take(3)
val others = if (sorted.size > 3) sorted.drop(3) else emptyList()
@@ -650,6 +789,7 @@ class HomeFragment : Fragment() {
avatarId = R.id.avatar_first,
nameId = R.id.name_first,
addBtnId = R.id.btn_add_first,
+ addBtnIcon = R.id.add_first_icon,
containerId = R.id.container_first,
item = top3.getOrNull(0)
)
@@ -659,6 +799,7 @@ class HomeFragment : Fragment() {
avatarId = R.id.avatar_second,
nameId = R.id.name_second,
addBtnId = R.id.btn_add_second,
+ addBtnIcon = R.id.add_second_icon,
containerId = R.id.container_second,
item = top3.getOrNull(1)
)
@@ -668,6 +809,7 @@ class HomeFragment : Fragment() {
avatarId = R.id.avatar_third,
nameId = R.id.name_third,
addBtnId = R.id.btn_add_third,
+ addBtnIcon = R.id.add_third_icon,
containerId = R.id.container_third,
item = top3.getOrNull(2)
)
@@ -686,6 +828,62 @@ class HomeFragment : Fragment() {
val iv = itemView.findViewById(R.id.iv_avatar)
com.bumptech.glide.Glide.with(iv).load(p.avatarUrl).into(iv)
+ // ---------------- add 按钮(失败回滚 + 防连点) ----------------
+ val addBtn = itemView.findViewById(R.id.btn_add)
+ val addIcon = itemView.findViewById(R.id.add_icon)
+
+ val originBg = addBtn.background
+ val originIcon = addIcon.drawable
+
+ fun renderAddState(added: Boolean) {
+ if (added) {
+ addBtn.setBackgroundResource(R.drawable.round_bg_others_already)
+ addIcon.setImageResource(R.drawable.ime_guide_activity_btn_completed_img)
+ } else {
+ addBtn.background = originBg
+ addIcon.setImageDrawable(originIcon)
+ }
+ }
+
+ // ✅ 首次渲染
+ renderAddState(p.added == true)
+
+ addBtn.setOnClickListener {
+ if (!addBtn.isEnabled) return@setOnClickListener
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ val personaId = p.id?: return@launch
+ val oldAdded = p.added
+ val newAdded = !oldAdded
+
+ addBtn.isEnabled = false
+ renderAddState(newAdded)
+ try {
+ if (oldAdded) {
+ // RetrofitClient.apiService.delUserCharacter(personaId)
+ // // ✅ 只有成功才更新缓存 + 更新UI(失败则保持原样)
+ // applyAddedToggle(personaId, newAdded)
+ }
+ else {
+ Log.d("1314520-HomeFragment", "add persona id=${p.id}")
+ val req = AddPersonaClick(
+ characterId = personaId,
+ emoji = p.emoji ?: ""
+ )
+ RetrofitClient.apiService.addUserCharacter(req)
+ // ✅ 只有成功才更新缓存 + 更新UI(失败则保持原样)
+ applyAddedToggle(personaId, newAdded)
+ }
+ } catch (e: Exception) {
+ Log.e("1314520-HomeFragment", "others toggle add failed id=$personaId", e)
+ renderAddState(oldAdded)
+ } finally {
+ addBtn.isEnabled = true
+ }
+ }
+ }
+
+ // ---------------- item 点击 ----------------
itemView.setOnClickListener {
if (!isAdded || childFragmentManager.isStateSaved) return@setOnClickListener
PersonaDetailDialogFragment
@@ -693,23 +891,6 @@ class HomeFragment : Fragment() {
.show(childFragmentManager, "persona_detail")
}
- itemView.findViewById(R.id.btn_add).setOnClickListener {
- viewLifecycleOwner.lifecycleScope.launch {
- try {
- if (p.added == true) {
- p.id?.let { id -> RetrofitClient.apiService.delUserCharacter(id.toInt()) }
- } else {
- val req = AddPersonaClick(
- characterId = p.id?.toInt() ?: 0,
- emoji = p.emoji ?: ""
- )
- RetrofitClient.apiService.addUserCharacter(req)
- }
- } catch (_: Exception) {
- }
- }
- }
-
container.addView(itemView)
}
}
@@ -719,12 +900,14 @@ class HomeFragment : Fragment() {
avatarId: Int,
nameId: Int,
addBtnId: Int,
+ addBtnIcon: Int,
containerId: Int,
item: listByTagWithNotLogin?
) {
val avatar = root.findViewById(avatarId)
val name = root.findViewById(nameId)
- val addBtn = root.findViewById(addBtnId)
+ val addBtn = root.findViewById(addBtnId)
+ val addIcon = root.findViewById(addBtnIcon)
val container = root.findViewById(containerId)
if (item == null) {
@@ -739,19 +922,60 @@ class HomeFragment : Fragment() {
name.text = item.characterName ?: ""
com.bumptech.glide.Glide.with(avatar).load(item.avatarUrl).into(avatar)
+ // ✅ 记录“原始背景/原始icon”,用于 added=false 时恢复
+ val originBg = addBtn.background
+ val originIconRes = when (addBtnId) {
+ R.id.btn_add_first -> R.drawable.first_add
+ R.id.btn_add_second -> R.drawable.second_add
+ R.id.btn_add_third -> R.drawable.third_add
+ else -> 0
+ }
+
+ fun renderAddState(added: Boolean) {
+ if (added) {
+ addBtn.setBackgroundResource(R.drawable.round_bg_others_already)
+ addIcon.setImageResource(R.drawable.ime_guide_activity_btn_completed_img)
+ } else {
+ addBtn.background = originBg
+ if (originIconRes != 0) addIcon.setImageResource(originIconRes)
+ }
+ }
+
+ // ✅ 首次渲染
+ renderAddState(item.added == true)
+
+ // ✅ 点击:失败回滚 + 防连点(请求中禁用按钮)
addBtn.setOnClickListener {
+ if (!addBtn.isEnabled) return@setOnClickListener
+
viewLifecycleOwner.lifecycleScope.launch {
+ val personaId = item.id?: return@launch
+ val oldAdded = item.added
+ val newAdded = !oldAdded
+
+ addBtn.isEnabled = false
+ renderAddState(newAdded)
try {
- if (item.added == true) {
- item.id?.let { id -> RetrofitClient.apiService.delUserCharacter(id.toInt()) }
- } else {
+ if (oldAdded) {
+ // RetrofitClient.apiService.delUserCharacter(personaId)
+ // // ✅ 只有成功才更新缓存 + 更新UI
+ // applyAddedToggle(personaId, newAdded)
+ }
+ else {
+ Log.d("1314520-HomeFragment", "add persona id=${item.id}")
val req = AddPersonaClick(
- characterId = item.id?.toInt() ?: 0,
+ characterId = personaId,
emoji = item.emoji ?: ""
)
RetrofitClient.apiService.addUserCharacter(req)
+ // ✅ 只有成功才更新缓存 + 更新UI
+ applyAddedToggle(personaId, newAdded)
}
- } catch (_: Exception) {
+ } catch (e: Exception) {
+ Log.e("1314520-HomeFragment", "others toggle add failed id=$personaId", e)
+ renderAddState(oldAdded)
+ } finally {
+ addBtn.isEnabled = true
}
}
}
diff --git a/app/src/main/java/com/example/myapplication/ui/home/PersonaAdapter.kt b/app/src/main/java/com/example/myapplication/ui/home/PersonaAdapter.kt
index 1a479f6..d2a0f2e 100644
--- a/app/src/main/java/com/example/myapplication/ui/home/PersonaAdapter.kt
+++ b/app/src/main/java/com/example/myapplication/ui/home/PersonaAdapter.kt
@@ -3,14 +3,15 @@ package com.example.myapplication.ui.home
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.example.myapplication.R
-import com.example.myapplication.network.listByTagWithNotLogin
import com.example.myapplication.network.PersonaClick
+import com.example.myapplication.network.listByTagWithNotLogin
import de.hdodenhof.circleimageview.CircleImageView
-import android.util.Log
class PersonaAdapter(
private val onClick: (PersonaClick) -> Unit
@@ -26,35 +27,37 @@ class PersonaAdapter(
inner class VH(itemView: View) : RecyclerView.ViewHolder(itemView) {
- val ivAvatar: CircleImageView = itemView.findViewById(R.id.ivAvatar)
- val tvName: TextView = itemView.findViewById(R.id.tvName)
- val characterBackground: TextView =
- itemView.findViewById(R.id.characterBackground)
- val download: TextView = itemView.findViewById(R.id.download)
- val operation: TextView = itemView.findViewById(R.id.operation)
+ private val ivAvatar: CircleImageView = itemView.findViewById(R.id.ivAvatar)
+ private val tvName: TextView = itemView.findViewById(R.id.tvName)
+ private val characterBackground: TextView = itemView.findViewById(R.id.characterBackground)
+ private val download: TextView = itemView.findViewById(R.id.download)
+ private val operation: LinearLayout = itemView.findViewById(R.id.operation)
+ private val operationIcon: ImageView = itemView.findViewById(R.id.operation_add_icon)
- /** ✅ 统一绑定 + 点击逻辑 */
fun bind(item: listByTagWithNotLogin) {
-
tvName.text = item.characterName
characterBackground.text = item.characterBackground
download.text = item.download
-
+
Glide.with(itemView.context)
.load(item.avatarUrl)
.placeholder(R.drawable.default_avatar)
.error(R.drawable.default_avatar)
.into(ivAvatar)
-
- // ✅ 整个 item:跳详情
- itemView.setOnClickListener {
- onClick(PersonaClick.Item(item))
- }
-
- // ✅ 添加 / 下载按钮
- operation.setOnClickListener {
- onClick(PersonaClick.Add(item))
- }
+
+ val isAdded = item.added
+
+ // ✅ 背景改 operation(外层容器)
+ operation.setBackgroundResource(
+ if (isAdded) R.drawable.list_two_bg_already else R.drawable.list_two_bg
+ )
+
+ // ✅ 图标改 operationIcon(中间图)
+ operationIcon.setImageResource(
+ if (isAdded) R.drawable.ime_guide_activity_btn_completed_img else R.drawable.operation_add
+ )
+ itemView.setOnClickListener { onClick(PersonaClick.Item(item)) }
+ operation.setOnClickListener { onClick(PersonaClick.Add(item)) }
}
}
diff --git a/app/src/main/java/com/example/myapplication/ui/home/PersonaDetailDialogFragment.kt b/app/src/main/java/com/example/myapplication/ui/home/PersonaDetailDialogFragment.kt
index b8a2663..6482429 100644
--- a/app/src/main/java/com/example/myapplication/ui/home/PersonaDetailDialogFragment.kt
+++ b/app/src/main/java/com/example/myapplication/ui/home/PersonaDetailDialogFragment.kt
@@ -15,6 +15,9 @@ import com.example.myapplication.network.RetrofitClient
import com.example.myapplication.network.CharacterDetailResponse
import kotlinx.coroutines.launch
import com.example.myapplication.network.AddPersonaClick
+import android.util.Log
+import com.example.myapplication.network.AuthEvent
+import com.example.myapplication.network.AuthEventBus
class PersonaDetailDialogFragment : DialogFragment() {
@@ -59,8 +62,9 @@ class PersonaDetailDialogFragment : DialogFragment() {
tvName.text = data.characterName ?: ""
download.text = data.download ?: ""
tvBackground.text = data.characterBackground ?: ""
- btnAdd.text = data.added?.let { "Added" } ?: "Add"
- btnAdd.setBackgroundResource(data.added?.let { R.drawable.ic_added } ?: R.drawable.keyboard_ettings)
+ btnAdd.text = if (data.added == true) "Added" else "Add"
+ btnAdd.setBackgroundResource(if (data.added == true) R.drawable.ic_added else R.drawable.keyboard_ettings)
+ val newAdded = !(data.added ?: false)
Glide.with(requireContext())
.load(data.avatarUrl)
@@ -72,13 +76,13 @@ class PersonaDetailDialogFragment : DialogFragment() {
lifecycleScope.launch {
if(data.added == true){
//取消收藏
- data.id?.let { id ->
- try {
- RetrofitClient.apiService.delUserCharacter(id.toInt())
- } catch (e: Exception) {
- // 处理错误
- }
- }
+ // data.id?.let { id ->
+ // try {
+ // RetrofitClient.apiService.delUserCharacter(id.toInt())
+ // } catch (e: Exception) {
+ // // 处理错误
+ // }
+ // }
}else{
val addPersonaRequest = AddPersonaClick(
characterId = data.id?.toInt() ?: 0,
@@ -86,8 +90,11 @@ class PersonaDetailDialogFragment : DialogFragment() {
)
try {
RetrofitClient.apiService.addUserCharacter(addPersonaRequest)
+ data.id?.let { personaId ->
+ AuthEventBus.emit(AuthEvent.CharacterAdded(personaId,newAdded))
+ }
} catch (e: Exception) {
- // 处理错误
+ Log.e("1314520-PersonaDetailDialogFragment", "addUserCharacter error", e)
}
}
dismissAllowingStateLoss()
diff --git a/app/src/main/java/com/example/myapplication/ui/keyboard/ConfirmDeleteDialogFragment.kt b/app/src/main/java/com/example/myapplication/ui/keyboard/ConfirmDeleteDialogFragment.kt
new file mode 100644
index 0000000..98801d9
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/ui/keyboard/ConfirmDeleteDialogFragment.kt
@@ -0,0 +1,72 @@
+package com.example.myapplication.ui.keyboard
+
+import android.app.Dialog
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.Window
+import android.widget.TextView
+import androidx.fragment.app.DialogFragment
+import com.example.myapplication.R
+
+class ConfirmDeleteDialogFragment : DialogFragment() {
+
+ companion object {
+ private const val ARG_NAME = "arg_name"
+ private const val ARG_EMOJI = "arg_emoji"
+
+ fun newInstance(
+ characterName: String?,
+ emoji: String?,
+ onConfirm: () -> Unit
+ ): ConfirmDeleteDialogFragment {
+ return ConfirmDeleteDialogFragment().apply {
+ this.onConfirm = onConfirm
+ arguments = Bundle().apply {
+ putString(ARG_NAME, characterName ?: "")
+ putString(ARG_EMOJI, emoji ?: "🙂")
+ }
+ }
+ }
+ }
+
+ private var onConfirm: (() -> Unit)? = null
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ val dialog = Dialog(requireContext())
+ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
+
+ val v = LayoutInflater.from(requireContext())
+ .inflate(R.layout.dialog_confirm_delete_character, null, false)
+ dialog.setContentView(v)
+
+ dialog.setCancelable(true)
+ dialog.setCanceledOnTouchOutside(true)
+
+ val name = arguments?.getString(ARG_NAME).orEmpty()
+ val emoji = arguments?.getString(ARG_EMOJI) ?: "🙂"
+
+ v.findViewById(R.id.tv_name).text = name
+ v.findViewById(R.id.tv_emoji).text = emoji
+
+ v.findViewById(R.id.btn_cancel).setOnClickListener {
+ dismissAllowingStateLoss()
+ }
+
+ v.findViewById(R.id.btn_confirm).setOnClickListener {
+ dismissAllowingStateLoss()
+ onConfirm?.invoke()
+ }
+
+ // 宽度更贴合弹窗
+ dialog.window?.setLayout(
+ (resources.displayMetrics.widthPixels * 0.86f).toInt(),
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+ )
+ return dialog
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ onConfirm = null
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/ui/keyboard/DragSortCallback.kt b/app/src/main/java/com/example/myapplication/ui/keyboard/DragSortCallback.kt
new file mode 100644
index 0000000..46b066f
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/ui/keyboard/DragSortCallback.kt
@@ -0,0 +1,68 @@
+package com.example.myapplication.ui.keyboard
+
+import android.view.HapticFeedbackConstants
+import androidx.recyclerview.widget.ItemTouchHelper
+import androidx.recyclerview.widget.RecyclerView
+
+class DragSortCallback(
+ private val onMove: (from: Int, to: Int) -> Unit
+) : ItemTouchHelper.Callback() {
+
+ private var didHaptic = false
+
+ override fun isLongPressDragEnabled(): Boolean = true
+ override fun isItemViewSwipeEnabled(): Boolean = false
+
+ override fun getMovementFlags(
+ recyclerView: RecyclerView,
+ viewHolder: RecyclerView.ViewHolder
+ ): Int {
+ val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN or
+ ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
+ return makeMovementFlags(dragFlags, 0)
+ }
+
+ override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
+ super.onSelectedChanged(viewHolder, actionState)
+
+ if (actionState == ItemTouchHelper.ACTION_STATE_DRAG && viewHolder != null) {
+ // ✅ 触觉反馈(只触发一次)
+ if (!didHaptic) {
+ viewHolder.itemView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
+ didHaptic = true
+ }
+ // ✅ 视觉反馈:放大 + 半透明
+ viewHolder.itemView.animate()
+ .scaleX(1.03f).scaleY(1.03f)
+ .alpha(0.85f)
+ .setDuration(120)
+ .start()
+ }
+ }
+
+ override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
+ super.clearView(recyclerView, viewHolder)
+ didHaptic = false
+
+ // ✅ 结束拖动,恢复视觉
+ viewHolder.itemView.animate()
+ .scaleX(1f).scaleY(1f)
+ .alpha(1f)
+ .setDuration(120)
+ .start()
+ }
+
+ override fun onMove(
+ recyclerView: RecyclerView,
+ viewHolder: RecyclerView.ViewHolder,
+ target: RecyclerView.ViewHolder
+ ): Boolean {
+ val from = viewHolder.adapterPosition
+ val to = target.adapterPosition
+ if (from == RecyclerView.NO_POSITION || to == RecyclerView.NO_POSITION) return false
+ onMove(from, to)
+ return true
+ }
+
+ override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}
+}
diff --git a/app/src/main/java/com/example/myapplication/ui/keyboard/KeyboardAdapter.kt b/app/src/main/java/com/example/myapplication/ui/keyboard/KeyboardAdapter.kt
new file mode 100644
index 0000000..5688e5d
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/ui/keyboard/KeyboardAdapter.kt
@@ -0,0 +1,57 @@
+package com.example.myapplication.ui.keyboard
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.example.myapplication.R
+import com.example.myapplication.network.ListByUserWithNot
+
+class KeyboardAdapter(
+ private val onItemClick: (ListByUserWithNot) -> Unit
+) : RecyclerView.Adapter() {
+
+ private val items = mutableListOf()
+
+ fun submitList(newList: List) {
+ items.clear()
+ items.addAll(newList)
+ notifyDataSetChanged()
+ }
+
+ fun getCurrentIdsInOrder(): List = items.map { it.id }
+
+ fun moveItem(from: Int, to: Int) {
+ if (from == to) return
+ val item = items.removeAt(from)
+ items.add(to, item)
+ notifyItemMoved(from, to)
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
+ val v = LayoutInflater.from(parent.context)
+ .inflate(R.layout.item_keyboard_character, parent, false)
+ return VH(v)
+ }
+
+ override fun onBindViewHolder(holder: VH, position: Int) {
+ holder.bind(items[position])
+ }
+
+ override fun getItemCount(): Int = items.size
+
+ inner class VH(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ private val root: View = itemView.findViewById(R.id.item_root)
+ private val tvEmoji: TextView = itemView.findViewById(R.id.tv_emoji)
+ private val tvName: TextView = itemView.findViewById(R.id.tv_name)
+
+ fun bind(item: ListByUserWithNot) {
+ tvEmoji.text = item.emoji ?: "🙂"
+ tvName.text = item.characterName ?: ""
+
+ // ✅ 点击整卡(不直接删,交给外层弹窗确认)
+ root.setOnClickListener { onItemClick(item) }
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/ui/keyboard/KeyboardDetailFragment.kt b/app/src/main/java/com/example/myapplication/ui/keyboard/KeyboardDetailFragment.kt
index 3cdda23..d9c53ca 100644
--- a/app/src/main/java/com/example/myapplication/ui/keyboard/KeyboardDetailFragment.kt
+++ b/app/src/main/java/com/example/myapplication/ui/keyboard/KeyboardDetailFragment.kt
@@ -42,6 +42,7 @@ import java.io.BufferedInputStream
import java.io.FileInputStream
import com.example.myapplication.ui.shop.ShopEvent
import com.example.myapplication.ui.shop.ShopEventBus
+import com.example.myapplication.network.BehaviorReporter
class KeyboardDetailFragment : Fragment() {
@@ -57,6 +58,7 @@ class KeyboardDetailFragment : Fragment() {
private lateinit var enabledButtonText: TextView
private lateinit var progressBar: android.widget.ProgressBar
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
+ private var themeDetailResp: themeDetail? = null
override fun onCreateView(
inflater: LayoutInflater,
@@ -80,7 +82,7 @@ class KeyboardDetailFragment : Fragment() {
enabledButtonText = view.findViewById(R.id.enabledButtonText)
progressBar = view.findViewById(R.id.progressBar)
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshLayout)
-
+
// 设置按钮始终防止事件穿透的触摸监听器
enabledButton.setOnTouchListener { _, event ->
// 如果按钮被禁用,消耗所有触摸事件防止穿透
@@ -120,6 +122,13 @@ class KeyboardDetailFragment : Fragment() {
viewLifecycleOwner.lifecycleScope.launch {
enableTheme()
}
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "skin_detail",
+ "element_id" to "download_btn",
+ "theme_id" to themeId,
+ "purchased" to if (themeDetailResp?.isPurchased == true) "1" else "0",
+ )
}
}
@@ -132,7 +141,7 @@ class KeyboardDetailFragment : Fragment() {
viewLifecycleOwner.lifecycleScope.launch {
try {
- val themeDetailResp = getThemeDetail(themeId)?.data
+ themeDetailResp = getThemeDetail(themeId)?.data
val recommendThemeListResp = getrecommendThemeList()?.data
Glide.with(requireView().context)
@@ -333,6 +342,13 @@ class KeyboardDetailFragment : Fragment() {
viewLifecycleOwner.lifecycleScope.launch {
setpurchaseTheme(themeId)
dialog.dismiss()
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "skin_detail",
+ "element_id" to "download_btn",
+ "theme_id" to themeId,
+ "purchased" to if (themeDetailResp?.isPurchased == true) "1" else "0",
+ )
}
}
diff --git a/app/src/main/java/com/example/myapplication/ui/keyboard/MyKeyboard.kt b/app/src/main/java/com/example/myapplication/ui/keyboard/MyKeyboard.kt
index fbbfb72..31694cb 100644
--- a/app/src/main/java/com/example/myapplication/ui/keyboard/MyKeyboard.kt
+++ b/app/src/main/java/com/example/myapplication/ui/keyboard/MyKeyboard.kt
@@ -1,28 +1,150 @@
package com.example.myapplication.ui.keyboard
import android.os.Bundle
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.fragment.app.Fragment
import android.widget.FrameLayout
+import android.widget.TextView
+import android.widget.Toast
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.ItemTouchHelper
+import androidx.recyclerview.widget.RecyclerView
import com.example.myapplication.R
+import com.example.myapplication.network.ApiResponse
+import com.example.myapplication.network.AuthEvent
+import com.example.myapplication.network.AuthEventBus
+import com.example.myapplication.ui.common.LoadingOverlay
+import com.example.myapplication.network.ListByUserWithNot
+import com.example.myapplication.network.RetrofitClient
+import com.example.myapplication.network.updateUserCharacterSortRequest
+import kotlinx.coroutines.launch
+import com.example.myapplication.network.BehaviorReporter
class MyKeyboard : Fragment() {
-
+
+ private lateinit var rv: RecyclerView
+ private lateinit var btnSave: TextView
+ private lateinit var adapter: KeyboardAdapter
+ private lateinit var loadingOverlay: LoadingOverlay
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
- ): View? {
- return inflater.inflate(R.layout.my_keyboard, container, false)
- }
+ ): View = inflater.inflate(R.layout.my_keyboard, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
-
+
view.findViewById(R.id.iv_close).setOnClickListener {
- parentFragmentManager.popBackStack()
+ requireActivity().onBackPressedDispatcher.onBackPressed()
+ }
+
+ rv = view.findViewById(R.id.rv_keyboard)
+ btnSave = view.findViewById(R.id.btn_keyboard)
+
+ adapter = KeyboardAdapter(
+ onItemClick = { item ->
+ // ✅ 点击卡片:弹自定义确认弹窗
+ ConfirmDeleteDialogFragment
+ .newInstance(item.characterName, item.emoji) {
+ // ✅ 用户确认后才删除
+ viewLifecycleOwner.lifecycleScope.launch {
+ val resp = setdelUserCharacter(item.id)
+ if (resp?.code == 0 && resp.data == true) {
+ Toast.makeText(requireContext(),"Deleted successfully", Toast.LENGTH_SHORT).show()
+ AuthEventBus.emit(AuthEvent.CharacterDeleted(item.id))
+ loadList()
+ } else {
+ Toast.makeText(requireContext(), resp?.message ?: "Delete failed", Toast.LENGTH_SHORT).show()
+ }
+ }
+ }
+ .show(parentFragmentManager, "confirm_delete")
+ }
+ )
+
+ rv.layoutManager = GridLayoutManager(requireContext(), 2)
+ rv.adapter = adapter
+
+ // ✅ 长按拖动排序 + 反馈
+ ItemTouchHelper(
+ DragSortCallback { from, to ->
+ adapter.moveItem(from, to)
+ }
+ ).attachToRecyclerView(rv)
+
+ loadingOverlay = LoadingOverlay.attach(view as ViewGroup)
+ loadList()
+
+ // ✅ Save:上传当前排序(id数组)
+ btnSave.setOnClickListener {
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "my_keyboard",
+ "element_id" to "save_btn",
+ )
+ val sortIds = adapter.getCurrentIdsInOrder()
+ Log.d("MyKeyboard-sort", sortIds.toString())
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ loadingOverlay.show()
+ try {
+ val body = updateUserCharacterSortRequest(sort = sortIds)
+ val resp = setupdateUserCharacterSort(body)
+ if (resp?.code == 0 && resp.data == true) {
+ requireActivity().onBackPressedDispatcher.onBackPressed()
+ Toast.makeText(requireContext(), "Sorting has been successfully modified.", Toast.LENGTH_SHORT).show()
+ } else {
+ Toast.makeText(requireContext(), resp?.message ?: "Save failed", Toast.LENGTH_SHORT).show()
+ }
+ } catch (e: Exception) {
+ Toast.makeText(requireContext(), "Network error: ${e.message}", Toast.LENGTH_SHORT).show()
+ } finally {
+ loadingOverlay.hide()
+ }
+ }
+ }
+ }
+
+ private fun loadList() {
+ viewLifecycleOwner.lifecycleScope.launch {
+ loadingOverlay.show()
+ try {
+ val resp = getlistByUser()
+ if (resp?.code == 0 && resp.data != null) {
+ adapter.submitList(resp.data)
+ Log.d("1314520-list", resp.data.toString())
+ } else {
+ Toast.makeText(requireContext(), resp?.message ?: "Load failed", Toast.LENGTH_SHORT).show()
+ }
+ } catch (e: Exception) {
+ Toast.makeText(requireContext(), "Load failed: ${e.message}", Toast.LENGTH_SHORT).show()
+ } finally {
+ loadingOverlay.hide()
+ }
+ }
+ }
+
+ // 获取用户人设列表
+ private suspend fun getlistByUser(): ApiResponse>? =
+ runCatching { RetrofitClient.apiService.listByUser() }.getOrNull()
+
+ // 更新用户人设排序
+ private suspend fun setupdateUserCharacterSort(body: updateUserCharacterSortRequest): ApiResponse? =
+ runCatching { RetrofitClient.apiService.updateUserCharacterSort(body) }.getOrNull()
+
+ // 删除用户人设
+ private suspend fun setdelUserCharacter(id: Int): ApiResponse? {
+ loadingOverlay.show()
+ return try {
+ runCatching { RetrofitClient.apiService.delUserCharacter(id) }.getOrNull()
+ } finally {
+ loadingOverlay.hide()
}
}
}
diff --git a/app/src/main/java/com/example/myapplication/ui/login/ForgetPasswordEmailFragment.kt b/app/src/main/java/com/example/myapplication/ui/login/ForgetPasswordEmailFragment.kt
index a4935e6..9bc479c 100644
--- a/app/src/main/java/com/example/myapplication/ui/login/ForgetPasswordEmailFragment.kt
+++ b/app/src/main/java/com/example/myapplication/ui/login/ForgetPasswordEmailFragment.kt
@@ -18,6 +18,7 @@ import com.example.myapplication.network.SendVerifyCodeRequest
import com.example.myapplication.network.RetrofitClient
import com.example.myapplication.utils.EncryptedSharedPreferencesUtil
import android.util.Log
+import com.example.myapplication.network.BehaviorReporter
class ForgetPasswordEmailFragment : Fragment() {
@@ -55,6 +56,11 @@ class ForgetPasswordEmailFragment : Fragment() {
} else if (!isValidEmail(email)) {
Toast.makeText(activity, "The email address format is incorrect", Toast.LENGTH_SHORT).show()
} else {
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "forgot_password_email",
+ "element_id" to "next_btn",
+ )
loadingOverlay?.show()
viewLifecycleOwner.lifecycleScope.launch {
try {
diff --git a/app/src/main/java/com/example/myapplication/ui/login/ForgetPasswordResetFragment.kt b/app/src/main/java/com/example/myapplication/ui/login/ForgetPasswordResetFragment.kt
index f7351e1..0589ae4 100644
--- a/app/src/main/java/com/example/myapplication/ui/login/ForgetPasswordResetFragment.kt
+++ b/app/src/main/java/com/example/myapplication/ui/login/ForgetPasswordResetFragment.kt
@@ -21,6 +21,7 @@ import com.example.myapplication.network.RetrofitClient
import com.example.myapplication.ui.common.LoadingOverlay
import com.example.myapplication.utils.EncryptedSharedPreferencesUtil
import kotlinx.coroutines.launch
+import com.example.myapplication.network.BehaviorReporter
class ForgetPasswordResetFragment : Fragment() {
@@ -110,6 +111,11 @@ class ForgetPasswordResetFragment : Fragment() {
} else if (password != confirmPassword) {
Toast.makeText(activity, "The two password entries are inconsistent", Toast.LENGTH_SHORT).show()
} else {
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "forgot_password_newpwd",
+ "element_id" to "next_btn",
+ )
loadingOverlay?.show()
viewLifecycleOwner.lifecycleScope.launch {
try {
diff --git a/app/src/main/java/com/example/myapplication/ui/login/ForgetPasswordVerifyFragment.kt b/app/src/main/java/com/example/myapplication/ui/login/ForgetPasswordVerifyFragment.kt
index b36050f..3d66bc6 100644
--- a/app/src/main/java/com/example/myapplication/ui/login/ForgetPasswordVerifyFragment.kt
+++ b/app/src/main/java/com/example/myapplication/ui/login/ForgetPasswordVerifyFragment.kt
@@ -21,6 +21,7 @@ import kotlinx.coroutines.launch
import com.example.myapplication.network.RetrofitClient
import com.example.myapplication.network.VerifyCodeRequest
import android.util.Log
+import com.example.myapplication.network.BehaviorReporter
class ForgetPasswordVerifyFragment : Fragment() {
@@ -59,6 +60,11 @@ class ForgetPasswordVerifyFragment : Fragment() {
// 显示加载遮罩层
loadingOverlay?.show()
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "forgot_password_verify",
+ "element_id" to "next_btn",
+ )
viewLifecycleOwner.lifecycleScope.launch {
try {
val body = VerifyCodeRequest(
diff --git a/app/src/main/java/com/example/myapplication/ui/login/LoginFragment.kt b/app/src/main/java/com/example/myapplication/ui/login/LoginFragment.kt
index fba0d87..7bdfbc6 100644
--- a/app/src/main/java/com/example/myapplication/ui/login/LoginFragment.kt
+++ b/app/src/main/java/com/example/myapplication/ui/login/LoginFragment.kt
@@ -26,6 +26,7 @@ import com.example.myapplication.ui.mine.MineFragment
import androidx.core.os.bundleOf
import com.example.myapplication.network.AuthEventBus
import com.example.myapplication.network.AuthEvent
+import com.example.myapplication.network.BehaviorReporter
class LoginFragment : Fragment() {
@@ -57,10 +58,20 @@ class LoginFragment : Fragment() {
// 注册
view.findViewById(R.id.tv_signup).setOnClickListener {
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "login",
+ "element_id" to "signup_btn",
+ )
findNavController().navigate(R.id.action_loginFragment_to_registerFragment)
}
// 忘记密码
view.findViewById(R.id.tv_forgot_password).setOnClickListener {
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "login",
+ "element_id" to "forgot_btn",
+ )
findNavController().navigate(R.id.action_loginFragment_to_forgetPasswordEmailFragment)
}
// 返回 - 在global_graph中,直接popBackStack回到globalEmptyFragment
@@ -110,6 +121,12 @@ class LoginFragment : Fragment() {
// 输入框不能为空
Toast.makeText(requireContext(), "The password and email address cannot be left empty!", Toast.LENGTH_SHORT).show()
} else {
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "login_email",
+ "element_id" to "submit_btn",
+ )
+
loadingOverlay?.show()
// 调用登录API
lifecycleScope.launch {
@@ -125,6 +142,7 @@ class LoginFragment : Fragment() {
EncryptedSharedPreferencesUtil.save(requireContext(), "email",email)
// 触发登录成功事件,让MainActivity关闭全局overlay
AuthEventBus.emit(AuthEvent.LoginSuccess)
+ AuthEventBus.emit(AuthEvent.CharacterDeleted(0))
// 不在这里popBackStack,让MainActivity的LoginSuccess事件处理关闭全局overlay
} else {
Toast.makeText(requireContext(), "Login failed: ${response.message}", Toast.LENGTH_SHORT).show()
diff --git a/app/src/main/java/com/example/myapplication/ui/login/RegisterFragment.kt b/app/src/main/java/com/example/myapplication/ui/login/RegisterFragment.kt
index b1eeb8c..cd00a1d 100644
--- a/app/src/main/java/com/example/myapplication/ui/login/RegisterFragment.kt
+++ b/app/src/main/java/com/example/myapplication/ui/login/RegisterFragment.kt
@@ -20,6 +20,7 @@ import com.example.myapplication.utils.EncryptedSharedPreferencesUtil
import com.example.myapplication.ui.common.LoadingOverlay
import kotlinx.coroutines.launch
import android.util.Log
+import com.example.myapplication.network.BehaviorReporter
class RegisterFragment : Fragment() {
@@ -113,6 +114,11 @@ class RegisterFragment : Fragment() {
} else if (!isValidEmail(email)) {
Toast.makeText(activity, "The email address format is incorrect", Toast.LENGTH_SHORT).show()
} else {
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "register_email",
+ "element_id" to "submit_btn",
+ )
loadingOverlay?.show()
viewLifecycleOwner.lifecycleScope.launch {
try {
diff --git a/app/src/main/java/com/example/myapplication/ui/login/RegisterVerifyFragment.kt b/app/src/main/java/com/example/myapplication/ui/login/RegisterVerifyFragment.kt
index 830e9d1..c56a400 100644
--- a/app/src/main/java/com/example/myapplication/ui/login/RegisterVerifyFragment.kt
+++ b/app/src/main/java/com/example/myapplication/ui/login/RegisterVerifyFragment.kt
@@ -20,6 +20,7 @@ import com.example.myapplication.ui.common.CodeEditText
import com.example.myapplication.utils.EncryptedSharedPreferencesUtil
import kotlinx.coroutines.launch
import android.widget.TextView
+import com.example.myapplication.network.BehaviorReporter
class RegisterVerifyFragment : Fragment() {
@@ -73,7 +74,11 @@ class RegisterVerifyFragment : Fragment() {
}
loadingOverlay?.show()
-
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "register_verify_email",
+ "element_id" to "confirm_btn",
+ )
viewLifecycleOwner.lifecycleScope.launch {
try {
val body = RegisterRequest(
diff --git a/app/src/main/java/com/example/myapplication/ui/mine/MineFragment.kt b/app/src/main/java/com/example/myapplication/ui/mine/MineFragment.kt
index fb0602f..ebb7d89 100644
--- a/app/src/main/java/com/example/myapplication/ui/mine/MineFragment.kt
+++ b/app/src/main/java/com/example/myapplication/ui/mine/MineFragment.kt
@@ -1,5 +1,8 @@
package com.example.myapplication.ui.mine
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
@@ -17,22 +20,30 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.NavController
import androidx.navigation.fragment.findNavController
+import com.bumptech.glide.Glide
import com.example.myapplication.R
import com.example.myapplication.network.AuthEvent
import com.example.myapplication.network.AuthEventBus
import com.example.myapplication.network.LoginResponse
+import com.example.myapplication.network.ApiResponse
+import com.example.myapplication.network.ShareResponse
import com.example.myapplication.network.RetrofitClient
+import com.example.myapplication.ui.common.LoadingOverlay
import com.example.myapplication.utils.EncryptedSharedPreferencesUtil
import de.hdodenhof.circleimageview.CircleImageView
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
+import com.example.myapplication.network.BehaviorReporter
class MineFragment : Fragment() {
private lateinit var nickname: TextView
private lateinit var time: TextView
private lateinit var logout: TextView
+ private lateinit var avatar: CircleImageView
+ private lateinit var share: LinearLayout
+ private lateinit var loadingOverlay: LoadingOverlay
private var loadUserJob: Job? = null
@@ -48,6 +59,7 @@ class MineFragment : Fragment() {
override fun onDestroyView() {
loadUserJob?.cancel()
+ loadingOverlay.remove()
super.onDestroyView()
}
@@ -57,24 +69,44 @@ class MineFragment : Fragment() {
nickname = view.findViewById(R.id.nickname)
time = view.findViewById(R.id.time)
logout = view.findViewById(R.id.logout)
+ avatar = view.findViewById(R.id.avatar)
+ share = view.findViewById(R.id.click_Share)
+ loadingOverlay = LoadingOverlay.attach(view.findViewById(R.id.rootCoordinator))
// 1) 先用本地缓存秒出首屏
renderFromCache()
- // 2) 首次进入不刷新,由onResume处理
-
- // // ✅ 手动刷新:不改布局也能用
- // // - 点昵称刷新
- // nickname.setOnClickListener { refreshUser(force = true, showToast = true) }
- // // - 长按 time 刷新
- // time.setOnLongClickListener {
- // refreshUser(force = true, showToast = true)
- // true
- // }
+ share.setOnClickListener {
+ viewLifecycleOwner.lifecycleScope.launch {
+ loadingOverlay.show()
+ try {
+ val response = getinviteCode()
+ response?.data?.h5Link?.let { link ->
+ val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
+ val clip = ClipData.newPlainText("h5Link", link)
+ clipboard.setPrimaryClip(clip)
+ Toast.makeText(context, "The sharing link has been copied to the clipboard.", Toast.LENGTH_LONG).show()
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "my",
+ "element_id" to "invite_copy",
+ )
+ }
+ } finally {
+ loadingOverlay.hide()
+ }
+ }
+ }
logout.setOnClickListener {
LogoutDialogFragment { doLogout() }
.show(parentFragmentManager, "logout_dialog")
+
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "person_info",
+ "element_id" to "logout_btn",
+ )
}
view.findViewById(R.id.imgLeft).setOnClickListener {
@@ -85,17 +117,43 @@ class MineFragment : Fragment() {
// 使用事件总线打开金币充值页面
AuthEventBus.emit(AuthEvent.OpenGlobalPage(R.id.goldCoinRechargeFragment))
}
- view.findViewById(R.id.avatar).setOnClickListener {
- safeNavigate(R.id.action_mineFragment_to_personalSettings)
+ avatar.setOnClickListener {
+ // 个人设置页面
+ AuthEventBus.emit(AuthEvent.OpenGlobalPage(R.id.PersonalSettings))
}
view.findViewById(R.id.keyboard_settings).setOnClickListener {
- safeNavigate(R.id.action_mineFragment_to_mykeyboard)
+ // 我的键盘页面
+ AuthEventBus.emit(AuthEvent.OpenGlobalPage(R.id.MyKeyboard))
+ }
+ view.findViewById(R.id.click_record).setOnClickListener {
+ //消费记录
+ AuthEventBus.emit(AuthEvent.OpenGlobalPage(R.id.consumptionRecordFragment))
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "my",
+ "element_id" to "menu_item",
+ "item_title" to "消费记录"
+ )
}
view.findViewById(R.id.click_Feedback).setOnClickListener {
- safeNavigate(R.id.action_mineFragment_to_feedbackFragment)
+ //反馈
+ AuthEventBus.emit(AuthEvent.OpenGlobalPage(R.id.feedbackFragment))
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "my",
+ "element_id" to "menu_item",
+ "item_title" to "反馈"
+ )
}
view.findViewById(R.id.click_Notice).setOnClickListener {
- safeNavigate(R.id.action_mineFragment_to_notificationFragment)
+ //通知
+ AuthEventBus.emit(AuthEvent.OpenGlobalPage(R.id.notificationFragment))
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "my",
+ "element_id" to "menu_item",
+ "item_title" to "通知"
+ )
}
// ✅ 监听登录成功/登出事件(跨 NavHost 可靠)
@@ -108,6 +166,10 @@ class MineFragment : Fragment() {
renderFromCache()
refreshUser(force = true, showToast = false)
}
+ AuthEvent.UserUpdated -> {
+ renderFromCache()
+ refreshUser(force = true, showToast = false)
+ }
else -> Unit
}
}
@@ -131,6 +193,11 @@ class MineFragment : Fragment() {
)
nickname.text = cached?.nickName ?: ""
time.text = cached?.vipExpiry?.let { "Due on November $it" } ?: ""
+ cached?.avatarUrl?.let { url ->
+ Glide.with(requireContext())
+ .load(url)
+ .into(avatar)
+ }
}
/**
@@ -154,15 +221,22 @@ class MineFragment : Fragment() {
Log.d(TAG, "getUser ok: nick=${u?.nickName} vip=${u?.vipExpiry}")
nickname.text = u?.nickName ?: ""
+
time.text = u?.vipExpiry?.let { "Due on November $it" } ?: ""
+ u?.avatarUrl?.let { url ->
+ Glide.with(requireContext())
+ .load(url)
+ .into(avatar)
+ }
+
EncryptedSharedPreferencesUtil.save(requireContext(), "Personal_information", u)
- if (showToast) Toast.makeText(requireContext(), "已刷新", Toast.LENGTH_SHORT).show()
+ if (showToast) Toast.makeText(requireContext(), "Refreshed", Toast.LENGTH_SHORT).show()
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) return@launch
Log.e(TAG, "getUser failed", e)
- if (showToast && isAdded) Toast.makeText(requireContext(), "刷新失败", Toast.LENGTH_SHORT).show()
+ if (showToast && isAdded) Toast.makeText(requireContext(), "Refresh failed", Toast.LENGTH_SHORT).show()
}
}
}
@@ -180,6 +254,9 @@ class MineFragment : Fragment() {
// 清空 UI
nickname.text = ""
time.text = ""
+ Glide.with(requireContext())
+ .load(R.drawable.default_avatar)
+ .into(avatar)
// 触发登出事件,让MainActivity打开登录页面
AuthEventBus.emit(AuthEvent.Logout(returnTabTag = "tab_mine"))
@@ -209,4 +286,7 @@ class MineFragment : Fragment() {
companion object {
private const val TAG = "1314520-MineFragment"
}
+
+ private suspend fun getinviteCode(): ApiResponse? =
+ runCatching> { RetrofitClient.apiService.inviteCode() }.getOrNull()
}
diff --git a/app/src/main/java/com/example/myapplication/ui/mine/consumptionRecord/TransactionAdapter.kt b/app/src/main/java/com/example/myapplication/ui/mine/consumptionRecord/TransactionAdapter.kt
new file mode 100644
index 0000000..0f6c51b
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/ui/mine/consumptionRecord/TransactionAdapter.kt
@@ -0,0 +1,174 @@
+package com.example.myapplication.ui.mine.consumptionRecord
+
+import android.graphics.Color
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ProgressBar
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.example.myapplication.R
+import com.example.myapplication.network.TransactionRecord
+
+class TransactionAdapter(
+ private val data: MutableList,
+ private val onCloseClick: () -> Unit,
+ private val onRechargeClick: () -> Unit
+) : RecyclerView.Adapter() {
+
+ companion object {
+ private const val TYPE_HEADER = 0
+ private const val TYPE_ITEM = 1
+ private const val TYPE_FOOTER = 2
+ }
+
+ // Header: balance
+ private var headerBalanceText: String = "0.00"
+
+ // Footer state
+ private var showFooter: Boolean = false
+ private var footerNoMore: Boolean = false
+
+ fun updateHeaderBalance(text: Any?) {
+ headerBalanceText = (text ?: "0.00").toString()
+ notifyItemChanged(0)
+ }
+
+ fun setFooterLoading() {
+ showFooter = true
+ footerNoMore = false
+ notifyDataSetChanged()
+ }
+
+ fun setFooterNoMore() {
+ showFooter = true
+ footerNoMore = true
+ notifyDataSetChanged()
+ }
+
+ fun hideFooter() {
+ showFooter = false
+ footerNoMore = false
+ notifyDataSetChanged()
+ }
+
+ fun replaceAll(list: List) {
+ data.clear()
+ data.addAll(list)
+ notifyDataSetChanged()
+ }
+
+ fun append(list: List) {
+ if (list.isEmpty()) return
+ val start = 1 + data.size // header占1
+ data.addAll(list)
+ notifyItemRangeInserted(start, list.size)
+ }
+
+ override fun getItemCount(): Int {
+ // header + items + optional footer
+ return 1 + data.size + if (showFooter) 1 else 0
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ return when {
+ position == 0 -> TYPE_HEADER
+ showFooter && position == itemCount - 1 -> TYPE_FOOTER
+ else -> TYPE_ITEM
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+ val inflater = LayoutInflater.from(parent.context)
+ return when (viewType) {
+ TYPE_HEADER -> {
+ val v = inflater.inflate(R.layout.layout_consumption_record_header, parent, false)
+ HeaderVH(v, onCloseClick, onRechargeClick)
+ }
+ TYPE_FOOTER -> {
+ val v = inflater.inflate(R.layout.item_loading_footer, parent, false)
+ FooterVH(v)
+ }
+ else -> {
+ val v = inflater.inflate(R.layout.item_transaction_record, parent, false)
+ ItemVH(v)
+ }
+ }
+ }
+
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+ when (holder) {
+ is HeaderVH -> holder.bind(headerBalanceText)
+ is FooterVH -> holder.bind(footerNoMore)
+ is ItemVH -> holder.bind(data[position - 1]) // position-1 because header
+ }
+ }
+
+ class HeaderVH(
+ itemView: View,
+ onCloseClick: () -> Unit,
+ onRechargeClick: () -> Unit
+ ) : RecyclerView.ViewHolder(itemView) {
+
+ private val balance: TextView = itemView.findViewById(R.id.balance)
+
+ init {
+ itemView.findViewById(R.id.iv_close).setOnClickListener { onCloseClick() }
+ itemView.findViewById(R.id.rechargeButton).setOnClickListener { onRechargeClick() }
+ }
+
+ fun bind(balanceText: String) {
+ balance.text = balanceText
+ adjustBalanceTextSize(balance, balanceText)
+ }
+
+ private fun adjustBalanceTextSize(tv: TextView, text: String) {
+ tv.textSize = when (text.length) {
+ in 0..3 -> 40f
+ 4 -> 36f
+ 5 -> 32f
+ 6 -> 28f
+ 7 -> 24f
+ 8 -> 22f
+ 9 -> 20f
+ else -> 16f
+ }
+ }
+ }
+
+ class ItemVH(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ private val tvTime: TextView = itemView.findViewById(R.id.tvTime)
+ private val tvDesc: TextView = itemView.findViewById(R.id.tvDesc)
+ private val tvAmount: TextView = itemView.findViewById(R.id.tvAmount)
+
+ fun bind(item: TransactionRecord) {
+ tvTime.text = item.createdAt
+ tvDesc.text = item.description
+ tvAmount.text = "${item.amount}"
+
+ // 根据type设置字体颜色
+ val color = when (item.type) {
+ 1 -> Color.parseColor("#CD2853") // 收入 - 红色
+ 2 -> Color.parseColor("#66CD7C") // 支出 - 绿色
+ else -> tvAmount.currentTextColor // 保持当前颜色
+ }
+ tvAmount.setTextColor(color)
+ }
+ }
+
+ class FooterVH(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ private val progress: ProgressBar = itemView.findViewById(R.id.progress)
+ private val tv: TextView = itemView.findViewById(R.id.tvLoading)
+
+ fun bind(noMore: Boolean) {
+ if (noMore) {
+ progress.visibility = View.GONE
+ tv.text = "No more"
+ } else {
+ progress.visibility = View.VISIBLE
+ tv.text = "Loading..."
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/ui/mine/consumptionRecord/consumptionRecordFragment.kt b/app/src/main/java/com/example/myapplication/ui/mine/consumptionRecord/consumptionRecordFragment.kt
new file mode 100644
index 0000000..d429110
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/ui/mine/consumptionRecord/consumptionRecordFragment.kt
@@ -0,0 +1,181 @@
+package com.example.myapplication.ui.mine.consumptionRecord
+
+import android.app.Dialog
+import android.content.DialogInterface
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.lifecycle.lifecycleScope
+import androidx.navigation.fragment.findNavController
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+import com.example.myapplication.R
+import com.example.myapplication.network.ApiResponse
+import com.example.myapplication.network.RetrofitClient
+import com.example.myapplication.network.TransactionRecord
+import com.example.myapplication.network.Wallet
+import com.example.myapplication.network.transactionsRequest
+import com.example.myapplication.network.transactionsResponse
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+import com.example.myapplication.network.AuthEvent
+import com.example.myapplication.network.AuthEventBus
+import kotlinx.coroutines.launch
+
+class ConsumptionRecordFragment : BottomSheetDialogFragment() {
+
+ private lateinit var swipeRefresh: SwipeRefreshLayout
+ private lateinit var rv: RecyclerView
+ private lateinit var adapter: TransactionAdapter
+
+ private val listData = arrayListOf()
+
+ private var pageNum = 1
+ private val pageSize = 10
+ private var totalPages = Int.MAX_VALUE
+ private var isLoading = false
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ return inflater.inflate(R.layout.fragment_consumption_record, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ swipeRefresh = view.findViewById(R.id.swipeRefresh)
+ rv = view.findViewById(R.id.rvTransactions)
+
+ setupRecycler()
+ setupRefresh()
+
+ refreshAll()
+ }
+
+ /**
+ * ✅ 重点:关闭必须走 NavController popBackStack
+ * 不要 dismiss(),否则 global_nav 栈不会变,底部导航就会一直被隐藏
+ */
+ private fun closeByNav() {
+ runCatching {
+ findNavController().popBackStack()
+ }.onFailure {
+ // 万一不是走 nav 打开的(极少情况),再兜底 dismiss
+ dismissAllowingStateLoss()
+ }
+ }
+
+ override fun onCancel(dialog: DialogInterface) {
+ super.onCancel(dialog)
+ // ✅ 用户手势下拉/点外部取消,也要 pop 返回栈
+ closeByNav()
+ }
+
+ override fun onDismiss(dialog: DialogInterface) {
+ super.onDismiss(dialog)
+ // ✅ 有些机型/场景只走 onDismiss,不走 onCancel,双保险
+ closeByNav()
+ }
+
+ private fun setupRecycler() {
+ adapter = TransactionAdapter(
+ data = listData,
+ onCloseClick = { closeByNav() }, // ✅ 改这里:不要 dismiss()
+ onRechargeClick = {
+ AuthEventBus.emit(AuthEvent.OpenGlobalPage(R.id.goldCoinRechargeFragment))
+ }
+ )
+
+ rv.layoutManager = LinearLayoutManager(requireContext())
+ rv.adapter = adapter
+
+ rv.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+ override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
+ super.onScrollStateChanged(recyclerView, newState)
+ if (newState != RecyclerView.SCROLL_STATE_IDLE) return
+
+ val reachedBottom = !recyclerView.canScrollVertically(1)
+ if (!reachedBottom) return
+
+ if (!isLoading && pageNum < totalPages) {
+ loadMore()
+ } else if (!isLoading && pageNum >= totalPages) {
+ adapter.setFooterNoMore()
+ }
+ }
+ })
+ }
+
+ private fun setupRefresh() {
+ swipeRefresh.setOnRefreshListener { refreshAll() }
+ }
+
+ private fun refreshAll() {
+ lifecycleScope.launch {
+ swipeRefresh.isRefreshing = true
+
+ pageNum = 1
+ totalPages = Int.MAX_VALUE
+ isLoading = false
+
+ adapter.hideFooter()
+ adapter.replaceAll(emptyList())
+
+ val walletResp = getwalletBalance()
+ val balanceText = walletResp?.data?.balanceDisplay ?: "0.00"
+ adapter.updateHeaderBalance(balanceText)
+
+ loadPage(targetPage = 1, isRefresh = true)
+
+ swipeRefresh.isRefreshing = false
+ }
+ }
+
+ private fun loadMore() {
+ lifecycleScope.launch { loadPage(targetPage = pageNum + 1, isRefresh = false) }
+ }
+
+ private suspend fun loadPage(targetPage: Int, isRefresh: Boolean) {
+ if (isLoading) return
+ isLoading = true
+
+ if (!isRefresh) adapter.setFooterLoading()
+
+ val body = transactionsRequest(pageNum = targetPage, pageSize = pageSize)
+ val resp = gettransactions(body)
+ val data = resp?.data
+
+ if (data != null) {
+ totalPages = data.pages
+ pageNum = data.current
+
+ val records = data.records
+
+ if (isRefresh) adapter.replaceAll(records) else adapter.append(records)
+
+ if (pageNum >= totalPages) adapter.setFooterNoMore() else adapter.hideFooter()
+
+ rv.post {
+ val notScrollableYet = !rv.canScrollVertically(1)
+ if (!isLoading && notScrollableYet && pageNum < totalPages) {
+ loadMore()
+ }
+ }
+ } else {
+ adapter.hideFooter()
+ }
+
+ isLoading = false
+ }
+
+ // ========================网络请求===========================================
+ private suspend fun getwalletBalance(): ApiResponse? =
+ runCatching { RetrofitClient.apiService.walletBalance() }.getOrNull()
+
+ private suspend fun gettransactions(body: transactionsRequest): ApiResponse? =
+ runCatching { RetrofitClient.apiService.transactions(body) }.getOrNull()
+}
diff --git a/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/FeedbackFragment.kt b/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/FeedbackFragment.kt
index 8579ba6..78d5f09 100644
--- a/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/FeedbackFragment.kt
+++ b/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/FeedbackFragment.kt
@@ -2,19 +2,24 @@ package com.example.myapplication.ui.mine.myotherpages
import android.os.Bundle
import android.view.LayoutInflater
+import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
-import androidx.core.content.ContextCompat
-import androidx.fragment.app.Fragment
-import com.example.myapplication.R
import android.widget.FrameLayout
-import com.google.android.material.bottomsheet.BottomSheetDialogFragment
-import com.google.android.material.button.MaterialButton
-import com.google.android.material.textfield.TextInputLayout
-import java.util.*
+import android.widget.Toast
+import androidx.fragment.app.Fragment
+import com.example.myapplication.R
+import com.example.myapplication.network.ApiResponse
+import com.example.myapplication.network.RetrofitClient
+import com.example.myapplication.network.feedbackRequest
+import com.google.android.material.textfield.TextInputEditText
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import com.example.myapplication.network.BehaviorReporter
class FeedbackFragment : Fragment() {
-
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -26,9 +31,56 @@ class FeedbackFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- // 设置关闭按钮点击事件
+ // 关闭按钮
view.findViewById(R.id.iv_close).setOnClickListener {
parentFragmentManager.popBackStack()
}
+
+ // 让多行输入框:不聚焦也能上下滑动内容
+ val etFeedback = view.findViewById(R.id.et_feedback)
+ etFeedback.apply {
+ // 可选:让它本身可滚动(你 XML 已经写了也没问题)
+ isVerticalScrollBarEnabled = true
+
+ setOnTouchListener { v, event ->
+ // 告诉父布局(NestedScrollView)先别抢这个触摸事件
+ v.parent?.requestDisallowInterceptTouchEvent(true)
+
+ // 手指抬起/取消时,把控制权还给父布局(页面还能继续滚)
+ if (event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL) {
+ v.parent?.requestDisallowInterceptTouchEvent(false)
+ }
+
+ // false:不吞事件,让 EditText 自己处理滚动/光标
+ false
+ }
+ }
+
+ // 提交反馈按钮点击事件
+ view.findViewById(R.id.btn_keyboard).setOnClickListener {
+ val feedbackText = etFeedback.text.toString().trim()
+ if (feedbackText.isEmpty()) {
+ Toast.makeText(context, "Please enter your feedback", Toast.LENGTH_SHORT).show()
+ return@setOnClickListener
+ }
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "feedback",
+ "element_id" to "commit_btn",
+ "content" to feedbackText
+ )
+ CoroutineScope(Dispatchers.Main).launch {
+ val response = submitFeedback(feedbackRequest(content = feedbackText))
+ if (response?.code == 0) {
+ Toast.makeText(context, "Feedback submitted successfully", Toast.LENGTH_SHORT).show()
+ parentFragmentManager.popBackStack()
+ } else {
+ Toast.makeText(context, "Failed to submit feedback", Toast.LENGTH_SHORT).show()
+ }
+ }
+ }
}
-}
\ No newline at end of file
+ //提交反馈
+ private suspend fun submitFeedback(body:feedbackRequest): ApiResponse? =
+ runCatching> { RetrofitClient.apiService.feedback(body) }.getOrNull()
+}
diff --git a/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/GenderSelectSheet.kt b/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/GenderSelectSheet.kt
new file mode 100644
index 0000000..abc3f16
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/GenderSelectSheet.kt
@@ -0,0 +1,165 @@
+package com.example.myapplication.ui.mine.myotherpages
+
+import android.graphics.Color
+import android.os.Bundle
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.LinearSnapHelper
+import androidx.recyclerview.widget.RecyclerView
+import com.example.myapplication.R
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+import kotlin.math.abs
+
+class GenderSelectSheet : BottomSheetDialogFragment() {
+
+ private val values = listOf("Male", "Female", "The third gender")
+ private var selectedIndex = 0
+
+ private val itemHeightDp = 48f // 每行高度(和 Adapter 里一致)
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ val view = inflater.inflate(R.layout.sheet_select_gender, container, false)
+
+ selectedIndex = (arguments?.getInt(ARG_INITIAL) ?: 0).coerceIn(0, values.lastIndex)
+
+ val rv = view.findViewById(R.id.gender_wheel)
+
+ val layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
+ rv.layoutManager = layoutManager
+ rv.adapter = WheelAdapter(values, dpToPx(itemHeightDp))
+ rv.overScrollMode = View.OVER_SCROLL_NEVER
+ rv.isNestedScrollingEnabled = true
+ rv.clipToPadding = false
+
+ val snapHelper = LinearSnapHelper()
+ snapHelper.attachToRecyclerView(rv)
+
+ // ✅ 关键:触摸滚轮时不让 BottomSheet 抢手势(否则会拖动弹窗)
+ rv.setOnTouchListener { v, event ->
+ when (event.actionMasked) {
+ MotionEvent.ACTION_DOWN -> {
+ setSheetDraggable(false)
+ v.parent?.requestDisallowInterceptTouchEvent(true)
+ }
+ MotionEvent.ACTION_MOVE -> v.parent?.requestDisallowInterceptTouchEvent(true)
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
+ setSheetDraggable(true)
+ v.parent?.requestDisallowInterceptTouchEvent(false)
+ }
+ }
+ false
+ }
+
+ rv.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+ updateChildColors(recyclerView)
+ }
+
+ override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
+ if (newState == RecyclerView.SCROLL_STATE_IDLE) {
+ val snapView = snapHelper.findSnapView(layoutManager) ?: return
+ val pos = layoutManager.getPosition(snapView).coerceIn(0, values.lastIndex)
+ selectedIndex = pos
+ updateChildColors(recyclerView)
+ }
+ }
+ })
+
+ // ✅ 关键:给上下 padding,让 3 条内容也能滚动/居中吸附
+ rv.post {
+ val itemPx = dpToPx(itemHeightDp)
+ val pad = (rv.height / 2 - itemPx / 2).coerceAtLeast(0)
+ rv.setPadding(rv.paddingLeft, pad, rv.paddingRight, pad)
+
+ layoutManager.scrollToPositionWithOffset(selectedIndex, pad)
+ rv.post { updateChildColors(rv) }
+ }
+
+ view.findViewById(R.id.btn_close).setOnClickListener { dismiss() }
+
+ view.findViewById(R.id.btn_save).setOnClickListener {
+ parentFragmentManager.setFragmentResult(
+ REQ_KEY,
+ Bundle().apply { putInt(BUNDLE_KEY_GENDER, selectedIndex) }
+ )
+ dismiss()
+ }
+
+ return view
+ }
+
+ private fun setSheetDraggable(draggable: Boolean) {
+ val d = dialog as? BottomSheetDialog ?: return
+ val sheet = d.findViewById(com.google.android.material.R.id.design_bottom_sheet) ?: return
+ BottomSheetBehavior.from(sheet).isDraggable = draggable
+ }
+
+ /** 根据距离中心点决定文字颜色 */
+ private fun updateChildColors(rv: RecyclerView) {
+ val centerY = rv.height / 2f
+ val selectedColor = Color.parseColor("#02BEAC")
+ val normalColor = Color.parseColor("#B5B5B5")
+
+ for (i in 0 until rv.childCount) {
+ val child = rv.getChildAt(i)
+ val tv = child as? TextView ?: continue
+
+ val childCenterY = (child.top + child.bottom) / 2f
+ val distance = abs(childCenterY - centerY)
+
+ val isSelected = distance < dpToPx(8f)
+ tv.setTextColor(if (isSelected) selectedColor else normalColor)
+ }
+ }
+
+ companion object {
+ const val REQ_KEY = "req_select_gender"
+ const val BUNDLE_KEY_GENDER = "bundle_gender"
+ private const val ARG_INITIAL = "arg_initial_gender"
+
+ fun newInstance(initialGender: Int) = GenderSelectSheet().apply {
+ arguments = Bundle().apply { putInt(ARG_INITIAL, initialGender) }
+ }
+ }
+
+ private fun dpToPx(dp: Float): Int =
+ (dp * resources.displayMetrics.density + 0.5f).toInt()
+
+ private class WheelAdapter(
+ private val items: List,
+ private val itemHeightPx: Int
+ ) : RecyclerView.Adapter() {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
+ val tv = TextView(parent.context).apply {
+ layoutParams = RecyclerView.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ itemHeightPx
+ )
+ gravity = Gravity.CENTER
+ textSize = 18f
+ setTextColor(Color.parseColor("#B5B5B5"))
+ }
+ return VH(tv)
+ }
+
+ override fun onBindViewHolder(holder: VH, position: Int) {
+ (holder.itemView as TextView).text = items[position]
+ }
+
+ override fun getItemCount(): Int = items.size
+
+ class VH(itemView: View) : RecyclerView.ViewHolder(itemView)
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/NicknameEditSheet.kt b/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/NicknameEditSheet.kt
new file mode 100644
index 0000000..dfa9147
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/NicknameEditSheet.kt
@@ -0,0 +1,64 @@
+package com.example.myapplication.ui.mine.myotherpages
+
+import android.os.Bundle
+import android.text.InputType
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import android.widget.TextView
+import android.widget.Toast
+import androidx.core.view.setPadding
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+import com.google.android.material.button.MaterialButton
+import com.google.android.material.textfield.TextInputEditText
+import com.google.android.material.textfield.TextInputLayout
+import com.example.myapplication.R
+
+class NicknameEditSheet : BottomSheetDialogFragment() {
+
+ override fun onStart() {
+ super.onStart()
+ dialog?.window?.setSoftInputMode(
+ android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE or
+ android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
+ )
+ }
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ val view = inflater.inflate(R.layout.sheet_edit_nickname, container, false)
+ val initial = arguments?.getString(ARG_INITIAL).orEmpty()
+
+ val et = view.findViewById(R.id.et_nickname)
+ et.setText(initial)
+
+ view.findViewById(R.id.btn_close).setOnClickListener { dismiss() }
+
+ view.findViewById(R.id.btn_save).setOnClickListener {
+ val nickname = et.text?.toString()?.trim().orEmpty()
+ if (nickname.isBlank()) return@setOnClickListener
+
+ parentFragmentManager.setFragmentResult(
+ REQ_KEY,
+ Bundle().apply { putString(BUNDLE_KEY_NICKNAME, nickname) }
+ )
+ dismiss()
+ }
+
+ return view
+ }
+
+ companion object {
+ const val REQ_KEY = "req_edit_nickname"
+ const val BUNDLE_KEY_NICKNAME = "bundle_nickname"
+ private const val ARG_INITIAL = "arg_initial_nickname"
+
+ fun newInstance(initial: String) = NicknameEditSheet().apply {
+ arguments = Bundle().apply { putString(ARG_INITIAL, initial) }
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/PersonalSettings.kt b/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/PersonalSettings.kt
index 2865dc2..74aea1c 100644
--- a/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/PersonalSettings.kt
+++ b/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/PersonalSettings.kt
@@ -1,34 +1,411 @@
package com.example.myapplication.ui.mine.myotherpages
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.content.Context
+import android.content.pm.PackageManager
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.net.Uri
import android.os.Bundle
+import android.os.Environment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.core.content.ContextCompat
-import androidx.fragment.app.Fragment
-import com.example.myapplication.R
import android.widget.FrameLayout
+import android.widget.TextView
+import android.widget.Toast
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.core.content.ContextCompat
+import androidx.core.content.FileProvider
+import androidx.lifecycle.lifecycleScope
+import com.bumptech.glide.Glide
+import com.example.myapplication.R
+import com.example.myapplication.network.ApiResponse
+import com.example.myapplication.network.AuthEvent
+import com.example.myapplication.network.AuthEventBus
+import com.example.myapplication.network.BehaviorReporter
+import com.example.myapplication.network.RetrofitClient
+import com.example.myapplication.network.User
+import com.example.myapplication.network.updateInfoRequest
+import com.example.myapplication.ui.common.LoadingOverlay
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
-import com.google.android.material.button.MaterialButton
-import com.google.android.material.textfield.TextInputLayout
-import java.util.*
+import de.hdodenhof.circleimageview.CircleImageView
+import kotlinx.coroutines.launch
+import okhttp3.MediaType.Companion.toMediaTypeOrNull
+import okhttp3.MultipartBody
+import okhttp3.RequestBody
+import java.io.File
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+import android.util.Log
class PersonalSettings : BottomSheetDialogFragment() {
-
+
+ private var user: User? = null
+
+ private lateinit var avatar: CircleImageView
+ private lateinit var tvNickname: TextView
+ private lateinit var tvGender: TextView
+ private lateinit var tvUserId: TextView
+ private lateinit var loadingOverlay: LoadingOverlay
+
+ /**
+ * ✅ Android Photo Picker
+ * - Android 13+:系统原生 Photo Picker
+ * - 低版本:会自动 fallback 到系统/兼容实现
+ * - 不需要 READ/WRITE_EXTERNAL_STORAGE
+ */
+ private val galleryLauncher =
+ registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri: Uri? ->
+ uri?.let { handleImageResult(it) }
+ }
+
+ private val cameraLauncher =
+ registerForActivityResult(ActivityResultContracts.TakePicture()) { success: Boolean ->
+ if (success) {
+ cameraImageUri?.let { handleImageResult(it) }
+ }
+ }
+
+ private var cameraImageUri: Uri? = null
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
- ): View? {
- return inflater.inflate(R.layout.personal_settings, container, false)
- }
+ ): View = inflater.inflate(R.layout.personal_settings, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- // 设置关闭按钮点击事件
+ // 初始化loadingOverlay
+ loadingOverlay = LoadingOverlay.attach(requireView() as ViewGroup)
+
+ // 关闭
view.findViewById(R.id.iv_close).setOnClickListener {
- parentFragmentManager.popBackStack()
+ AuthEventBus.emit(AuthEvent.UserUpdated)
+ requireActivity().onBackPressedDispatcher.onBackPressed()
+ }
+
+ // bind
+ avatar = view.findViewById(R.id.avatar)
+ tvNickname = view.findViewById(R.id.tv_nickname_value)
+ tvGender = view.findViewById(R.id.tv_gender_value)
+ tvUserId = view.findViewById(R.id.tv_userid_value)
+
+ // Avatar click listener
+ avatar.setOnClickListener {
+ showImagePickerDialog()
+ }
+
+ // ===================== FragmentResult listeners =====================
+
+ // 昵称保存回传
+ parentFragmentManager.setFragmentResultListener(
+ NicknameEditSheet.REQ_KEY,
+ viewLifecycleOwner
+ ) { _, bundle ->
+ val newName = bundle.getString(NicknameEditSheet.BUNDLE_KEY_NICKNAME).orEmpty()
+ if (newName.isBlank()) return@setFragmentResultListener
+ lifecycleScope.launch {
+ loadingOverlay.show()
+ try {
+ val returnValue = setupdateUserInfo(updateInfoRequest(nickName = newName))
+ Log.d("PersonalSettings", "setupdateUserInfo: $returnValue")
+ if (returnValue?.code == 0) {
+ tvNickname.text = newName
+ }
+ user = user?.copy(nickName = newName)
+ } catch (e: Exception) {
+ Log.e("PersonalSettings", "Failed to update nickname", e)
+ } finally {
+ loadingOverlay.hide()
+ }
+ }
+ }
+
+ // 性别保存回传
+ parentFragmentManager.setFragmentResultListener(
+ GenderSelectSheet.REQ_KEY,
+ viewLifecycleOwner
+ ) { _, bundle ->
+ val newGender = bundle.getInt(GenderSelectSheet.BUNDLE_KEY_GENDER, 0)
+
+ lifecycleScope.launch {
+ loadingOverlay.show()
+ try {
+ val returnValue = setupdateUserInfo(updateInfoRequest(gender = newGender))
+ if (returnValue?.code == 0) {
+ tvGender.text = genderText(newGender)
+ }
+ user = user?.copy(gender = newGender)
+ } catch (e: Exception) {
+ Log.e("PersonalSettings", "Failed to update gender", e)
+ } finally {
+ loadingOverlay.hide()
+ }
+ }
+ }
+
+ // ===================== row click =====================
+
+ // Nickname:打开编辑 BottomSheet(arguments 传初始值)
+ view.findViewById(R.id.row_nickname).setOnClickListener {
+ NicknameEditSheet.newInstance(user?.nickName.orEmpty())
+ .show(parentFragmentManager, "NicknameEditSheet")
+ }
+
+ // Gender:打开选择 BottomSheet
+ view.findViewById(R.id.row_gender).setOnClickListener {
+ GenderSelectSheet.newInstance(user?.gender ?: 0)
+ .show(parentFragmentManager, "GenderSelectSheet")
+ }
+
+ // UserID:点击复制
+ view.findViewById(R.id.row_userid).setOnClickListener {
+ val uid = user?.uid?.toString() ?: tvUserId.text?.toString().orEmpty()
+ if (uid.isBlank()) return@setOnClickListener
+ copyToClipboard(uid)
+ Toast.makeText(requireContext(), "Copy successfully", Toast.LENGTH_SHORT).show()
+ }
+
+ // ===================== load & render =====================
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ loadingOverlay.show()
+ try {
+ val resp = getUserdata()
+ val u = resp?.data // 如果你的 ApiResponse 字段不是 data,这里改成你的字段名
+ if (u == null) {
+ Toast.makeText(requireContext(), "Load failed", Toast.LENGTH_SHORT).show()
+ return@launch
+ }
+ user = u
+ renderUser(u)
+ } finally {
+ loadingOverlay.hide()
+ }
}
}
-}
\ No newline at end of file
+
+ private fun renderUser(u: User) {
+ tvNickname.text = u.nickName
+ tvGender.text = genderText(u.gender)
+ tvUserId.text = u.uid.toString()
+
+ Glide.with(this)
+ .load(u.avatarUrl)
+ .placeholder(R.drawable.default_avatar)
+ .error(R.drawable.default_avatar)
+ .into(avatar)
+ }
+
+ private fun genderText(gender: Int): String = when (gender) {
+ 1 -> "Female"
+ 2 -> "The third gender"
+ 0 -> "Male"
+ else -> ""
+ }
+
+ private fun copyToClipboard(text: String) {
+ val cm = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
+ cm.setPrimaryClip(ClipData.newPlainText("user_id", text))
+ }
+
+ private suspend fun getUserdata(): ApiResponse? =
+ runCatching { RetrofitClient.apiService.getUser() }.getOrNull()
+
+ private suspend fun setupdateUserInfo(body: updateInfoRequest): ApiResponse? =
+ runCatching { RetrofitClient.apiService.updateUserInfo(body) }.getOrNull()
+
+ private val cameraPermissionLauncher = registerForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) { isGranted ->
+ if (isGranted) {
+ cameraImageUri = createImageFile()
+ cameraImageUri?.let { cameraLauncher.launch(it) }
+ } else {
+ Toast.makeText(
+ requireContext(),
+ "Camera permission is required to take photos",
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ }
+
+ private fun showImagePickerDialog() {
+ val options = arrayOf(
+ getString(R.string.choose_from_gallery),
+ getString(R.string.take_photo)
+ )
+
+ androidx.appcompat.app.AlertDialog.Builder(requireContext())
+ .setTitle(R.string.change_avatar)
+ .setItems(options) { _, which ->
+ when (which) {
+ 0 -> {
+ // ✅ Photo Picker: ImageOnly
+ galleryLauncher.launch(
+ PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
+ )
+ }
+ 1 -> {
+ if (ContextCompat.checkSelfPermission(
+ requireContext(),
+ android.Manifest.permission.CAMERA
+ ) == PackageManager.PERMISSION_GRANTED
+ ) {
+ cameraImageUri = createImageFile()
+ cameraImageUri?.let { cameraLauncher.launch(it) }
+ } else {
+ cameraPermissionLauncher.launch(android.Manifest.permission.CAMERA)
+ }
+ }
+ }
+ }
+ .setNegativeButton(R.string.cancel, null)
+ .show()
+ }
+
+ private fun handleImageResult(uri: Uri) {
+ Glide.with(this)
+ .load(uri)
+ .into(avatar)
+
+ lifecycleScope.launch {
+ uploadAvatar(uri)
+ }
+ }
+
+ private suspend fun uploadAvatar(uri: Uri) {
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "person_info",
+ "element_id" to "avatar_edit",
+ )
+
+ loadingOverlay.show()
+ try {
+ val resolver = requireContext().contentResolver
+
+ // Get MIME type to determine image format
+ val mimeType = resolver.getType(uri)
+ val isPng = mimeType?.equals("image/png", ignoreCase = true) == true
+
+ // Determine file extension and media type based on MIME type
+ val fileExtension = if (isPng) ".png" else ".jpg"
+ val mediaType = if (isPng) "image/png" else "image/jpeg"
+
+ // Temp file in app-private external files dir (no storage permission needed)
+ val storageDir = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES)
+ val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
+ val tempFile = File.createTempFile("UPLOAD_${timeStamp}_", fileExtension, storageDir)
+
+ // 先读取尺寸信息(inJustDecodeBounds)
+ val boundsOptions = BitmapFactory.Options().apply { inJustDecodeBounds = true }
+ resolver.openInputStream(uri)?.use { ins ->
+ BitmapFactory.decodeStream(ins, null, boundsOptions)
+ } ?: return
+
+ // Calculate inSampleSize (粗略控制内存)
+ var inSampleSize = 1
+ val maxSizeBytes = 5 * 1024 * 1024 // 5MB 目标
+ // 简单估算:w*h*4
+ if (boundsOptions.outHeight > 0 && boundsOptions.outWidth > 0) {
+ val estimated = boundsOptions.outHeight.toLong() * boundsOptions.outWidth.toLong() * 4L
+ if (estimated > maxSizeBytes) {
+ val halfHeight = boundsOptions.outHeight / 2
+ val halfWidth = boundsOptions.outWidth / 2
+ while (halfHeight / inSampleSize >= 1024 && halfWidth / inSampleSize >= 1024) {
+ inSampleSize *= 2
+ }
+ }
+ }
+
+ // Decode bitmap with inSampleSize
+ val decodeOptions = BitmapFactory.Options().apply {
+ inJustDecodeBounds = false
+ this.inSampleSize = inSampleSize
+ }
+
+ val bitmap = resolver.openInputStream(uri)?.use { ins ->
+ BitmapFactory.decodeStream(ins, null, decodeOptions)
+ }
+
+ if (bitmap == null) {
+ Toast.makeText(requireContext(), "Failed to decode image", Toast.LENGTH_SHORT).show()
+ return
+ }
+
+ // Compress to tempFile
+ // 注意:用 outputStream 反复 compress 时不要在同一个 stream 上 truncate,
+ // 最稳是每次重新打开 stream 写入。
+ if (isPng) {
+ tempFile.outputStream().use { out ->
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
+ }
+ } else {
+ var quality = 90
+ do {
+ tempFile.outputStream().use { out ->
+ bitmap.compress(Bitmap.CompressFormat.JPEG, quality, out)
+ }
+ quality -= 10
+ } while (tempFile.length() > maxSizeBytes && quality > 10)
+ }
+
+ if (tempFile.length() > maxSizeBytes) {
+ Toast.makeText(requireContext(), "Image is too large after compression", Toast.LENGTH_SHORT).show()
+ bitmap.recycle()
+ tempFile.delete()
+ return
+ }
+
+ val requestFile: RequestBody = RequestBody.create(mediaType.toMediaTypeOrNull(), tempFile)
+ val body = MultipartBody.Part.createFormData("file", tempFile.name, requestFile)
+
+ val response = RetrofitClient.createFileUploadService()
+ .uploadFile("avatar", body)
+
+ // Clean up
+ bitmap.recycle()
+ tempFile.delete()
+
+ if (response?.code == 0) {
+ setupdateUserInfo(updateInfoRequest(avatarUrl = response.data))
+ Toast.makeText(requireContext(), R.string.avatar_updated, Toast.LENGTH_SHORT).show()
+ user = user?.copy(avatarUrl = response.data)
+ } else {
+ Toast.makeText(requireContext(), R.string.upload_failed, Toast.LENGTH_SHORT).show()
+ }
+ } catch (e: Exception) {
+ Toast.makeText(requireContext(), R.string.upload_error, Toast.LENGTH_SHORT).show()
+ Log.e("PersonalSettings", "Upload avatar error", e)
+ } finally {
+ loadingOverlay.hide()
+ }
+ }
+
+ private fun createImageFile(): Uri? {
+ val storageDir = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES)
+ val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
+ val imageFile = File.createTempFile(
+ "JPEG_${timeStamp}_",
+ ".jpg",
+ storageDir
+ )
+ return FileProvider.getUriForFile(
+ requireContext(),
+ "${requireContext().packageName}.fileprovider",
+ imageFile
+ )
+ }
+
+ override fun onDestroyView() {
+ loadingOverlay.remove()
+ super.onDestroyView()
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/ui/shop/ShopFragment.kt b/app/src/main/java/com/example/myapplication/ui/shop/ShopFragment.kt
index 0b14ad5..b768366 100644
--- a/app/src/main/java/com/example/myapplication/ui/shop/ShopFragment.kt
+++ b/app/src/main/java/com/example/myapplication/ui/shop/ShopFragment.kt
@@ -29,6 +29,7 @@ import com.google.android.material.appbar.AppBarLayout
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import java.util.concurrent.atomic.AtomicBoolean
+import com.example.myapplication.network.BehaviorReporter
class ShopFragment : Fragment(R.layout.fragment_shop) {
@@ -239,6 +240,10 @@ class ShopFragment : Fragment(R.layout.fragment_shop) {
}
}
}
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "shop_item_list",
+ )
}
}
@@ -400,10 +405,22 @@ class ShopFragment : Fragment(R.layout.fragment_shop) {
AuthEventBus.emit(AuthEvent.OpenGlobalPage(R.id.goldCoinRechargeFragment))
}
view.findViewById(R.id.skinButton).setOnClickListener {
+ // 使用事件总线打开我的皮肤页面
findNavController().navigate(R.id.action_shopfragment_to_myskin)
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "shop",
+ "element_id" to "my_skin_btn",
+ )
}
view.findViewById(R.id.searchButton).setOnClickListener {
+ // 使用事件总线打开搜索页面
findNavController().navigate(R.id.action_shopfragment_to_searchfragment)
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "shop",
+ "element_id" to "search_btn",
+ )
}
}
diff --git a/app/src/main/java/com/example/myapplication/ui/shop/ThemeCardAdapter.kt b/app/src/main/java/com/example/myapplication/ui/shop/ThemeCardAdapter.kt
index 4aafab6..4b5ba6d 100644
--- a/app/src/main/java/com/example/myapplication/ui/shop/ThemeCardAdapter.kt
+++ b/app/src/main/java/com/example/myapplication/ui/shop/ThemeCardAdapter.kt
@@ -17,6 +17,7 @@ import com.example.myapplication.network.AuthEvent
import com.example.myapplication.network.AuthEventBus
import com.example.myapplication.network.themeStyle
import com.google.android.material.card.MaterialCardView
+import com.example.myapplication.network.BehaviorReporter
class ThemeCardAdapter : ListAdapter(DiffCallback) {
@@ -56,6 +57,11 @@ class ThemeCardAdapter : ListAdapter Unit,
@@ -94,11 +96,18 @@ class MySkinAdapter(
}
holder.itemView.setOnClickListener {
+
if (editMode) {
if (selected) selectedIds.remove(item.id) else selectedIds.add(item.id)
onSelectionChanged(selectedIds.size)
notifyItemChanged(position)
} else {
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "my_skin",
+ "element_id" to "theme_card",
+ "theme_id" to item.id
+ )
// 跳转到主题详情页面
val bundle = Bundle().apply {
putInt("themeId", item.id)
diff --git a/app/src/main/java/com/example/myapplication/ui/shop/search/SearchFragment.kt b/app/src/main/java/com/example/myapplication/ui/shop/search/SearchFragment.kt
index 00f6959..f36a359 100644
--- a/app/src/main/java/com/example/myapplication/ui/shop/search/SearchFragment.kt
+++ b/app/src/main/java/com/example/myapplication/ui/shop/search/SearchFragment.kt
@@ -23,6 +23,7 @@ import com.example.myapplication.ui.shop.ThemeCardAdapter
import com.google.android.flexbox.FlexboxLayout
import com.google.android.flexbox.FlexboxLayout.LayoutParams
import kotlinx.coroutines.launch
+import com.example.myapplication.network.BehaviorReporter
@@ -86,7 +87,13 @@ class SearchFragment : Fragment() {
// 把搜索词放进 Bundle
val bundle = bundleOf("search_keyword" to keyword)
-
+
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "search",
+ "element_id" to "search_submit",
+ "keyword" to bundle,
+ )
// 跳转时带上 bundle
findNavController().navigate(
R.id.action_searchFragment_to_searchResultFragment,
@@ -102,6 +109,10 @@ class SearchFragment : Fragment() {
// 清空历史记录
view.findViewById(R.id.iv_delete_history).setOnClickListener {
clearHistory()
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "search",
+ )
}
}
@@ -155,8 +166,13 @@ class SearchFragment : Fragment() {
tv.setOnClickListener {
etInput.setText(keyword)
etInput.setSelection(keyword.length)
+ BehaviorReporter.report(
+ isNewUser = false,
+ "page_id" to "search",
+ "element_id" to "history_item",
+ "keyword" to keyword,
+ )
}
-
return tv
}
diff --git a/app/src/main/res/drawable/ai_caard_bg.xml b/app/src/main/res/drawable/ai_caard_bg.xml
new file mode 100644
index 0000000..9ee15f6
--- /dev/null
+++ b/app/src/main/res/drawable/ai_caard_bg.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/associate_close.png b/app/src/main/res/drawable/associate_close.png
new file mode 100644
index 0000000..d3c9809
Binary files /dev/null and b/app/src/main/res/drawable/associate_close.png differ
diff --git a/app/src/main/res/drawable/complete_close_bg.xml b/app/src/main/res/drawable/complete_close_bg.xml
new file mode 100644
index 0000000..c8b23d0
--- /dev/null
+++ b/app/src/main/res/drawable/complete_close_bg.xml
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/consumption_details_bg.xml b/app/src/main/res/drawable/consumption_details_bg.xml
new file mode 100644
index 0000000..445361e
--- /dev/null
+++ b/app/src/main/res/drawable/consumption_details_bg.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/first_add.png b/app/src/main/res/drawable/first_add.png
new file mode 100644
index 0000000..99249b9
Binary files /dev/null and b/app/src/main/res/drawable/first_add.png differ
diff --git a/app/src/main/res/drawable/gender_background.xml b/app/src/main/res/drawable/gender_background.xml
index 4420fb3..47a483c 100644
--- a/app/src/main/res/drawable/gender_background.xml
+++ b/app/src/main/res/drawable/gender_background.xml
@@ -2,4 +2,5 @@
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/gender_background_select.xml b/app/src/main/res/drawable/gender_background_select.xml
new file mode 100644
index 0000000..c5996f6
--- /dev/null
+++ b/app/src/main/res/drawable/gender_background_select.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/gold_coin_bg.xml b/app/src/main/res/drawable/gold_coin_bg.xml
new file mode 100644
index 0000000..b326e83
--- /dev/null
+++ b/app/src/main/res/drawable/gold_coin_bg.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/keyboard_button_bg4.xml b/app/src/main/res/drawable/keyboard_button_bg4.xml
index 235ad94..087d556 100644
--- a/app/src/main/res/drawable/keyboard_button_bg4.xml
+++ b/app/src/main/res/drawable/keyboard_button_bg4.xml
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/list_two_bg_already.xml b/app/src/main/res/drawable/list_two_bg_already.xml
new file mode 100644
index 0000000..37d8946
--- /dev/null
+++ b/app/src/main/res/drawable/list_two_bg_already.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/my_keyboard_cancel.xml b/app/src/main/res/drawable/my_keyboard_cancel.xml
new file mode 100644
index 0000000..16990a2
--- /dev/null
+++ b/app/src/main/res/drawable/my_keyboard_cancel.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/operation_add.png b/app/src/main/res/drawable/operation_add.png
new file mode 100644
index 0000000..52a253d
Binary files /dev/null and b/app/src/main/res/drawable/operation_add.png differ
diff --git a/app/src/main/res/drawable/pop_collapse.png b/app/src/main/res/drawable/pop_collapse.png
new file mode 100644
index 0000000..1a580a9
Binary files /dev/null and b/app/src/main/res/drawable/pop_collapse.png differ
diff --git a/app/src/main/res/drawable/record.png b/app/src/main/res/drawable/record.png
new file mode 100644
index 0000000..c92289f
Binary files /dev/null and b/app/src/main/res/drawable/record.png differ
diff --git a/app/src/main/res/drawable/round_bg_others_add.png b/app/src/main/res/drawable/round_bg_others_add.png
new file mode 100644
index 0000000..5bf988a
Binary files /dev/null and b/app/src/main/res/drawable/round_bg_others_add.png differ
diff --git a/app/src/main/res/drawable/round_bg_others_already.xml b/app/src/main/res/drawable/round_bg_others_already.xml
new file mode 100644
index 0000000..d440990
--- /dev/null
+++ b/app/src/main/res/drawable/round_bg_others_already.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/round_bg_three.xml b/app/src/main/res/drawable/round_bg_three.xml
index 082001b..10bd0cf 100644
--- a/app/src/main/res/drawable/round_bg_three.xml
+++ b/app/src/main/res/drawable/round_bg_three.xml
@@ -2,6 +2,6 @@
android:shape="rectangle">
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/round_bg_two.xml b/app/src/main/res/drawable/round_bg_two.xml
index 662018a..0435c16 100644
--- a/app/src/main/res/drawable/round_bg_two.xml
+++ b/app/src/main/res/drawable/round_bg_two.xml
@@ -2,6 +2,6 @@
android:shape="rectangle">
-
+
diff --git a/app/src/main/res/drawable/second_add.png b/app/src/main/res/drawable/second_add.png
new file mode 100644
index 0000000..922be08
Binary files /dev/null and b/app/src/main/res/drawable/second_add.png differ
diff --git a/app/src/main/res/drawable/third_add.png b/app/src/main/res/drawable/third_add.png
new file mode 100644
index 0000000..1e474cc
Binary files /dev/null and b/app/src/main/res/drawable/third_add.png differ
diff --git a/app/src/main/res/layout/activity_onboarding.xml b/app/src/main/res/layout/activity_onboarding.xml
index 9be21fc..280434d 100644
--- a/app/src/main/res/layout/activity_onboarding.xml
+++ b/app/src/main/res/layout/activity_onboarding.xml
@@ -80,9 +80,10 @@
android:src="@drawable/male" />
@@ -117,9 +118,10 @@
android:src="@drawable/female" />
@@ -161,6 +163,7 @@
android:src="@drawable/question_mark_one" />
+
+
+
+
+
diff --git a/app/src/main/res/layout/ai_keyboard.xml b/app/src/main/res/layout/ai_keyboard.xml
index adb2348..a84958e 100644
--- a/app/src/main/res/layout/ai_keyboard.xml
+++ b/app/src/main/res/layout/ai_keyboard.xml
@@ -13,6 +13,7 @@
android:layout_marginTop="3dp"
android:paddingStart="12dp"
android:paddingEnd="8dp">
+
+ app:layout_constraintBottom_toBottomOf="parent" />
-
+
+
-
+
+
+
+ android:src="@drawable/copy_the_message" />
+
-
+
-
-
-
-
-
-
-
-
+ app:justifyContent="flex_start"
+ app:alignContent="flex_start"/>
-
-
-
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/keyboard_button_1"
+ android:layout_marginStart="4dp"
+ android:orientation="vertical">
-
-
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+ android:orientation="vertical" />
+
-
-
diff --git a/app/src/main/res/layout/bottom_page_list1.xml b/app/src/main/res/layout/bottom_page_list1.xml
index 525dedf..afcdd61 100644
--- a/app/src/main/res/layout/bottom_page_list1.xml
+++ b/app/src/main/res/layout/bottom_page_list1.xml
@@ -62,18 +62,20 @@
android:text="Loading..."
android:textSize="10sp"
android:textColor="#1B1F1A" />
-
-
+ android:layout_width="60dp"
+ android:layout_marginTop="50dp"
+ android:layout_height="28dp"
+ android:background="@drawable/round_bg_two">
+
+
@@ -117,17 +119,19 @@
android:textSize="10sp"
android:textColor="#1B1F1A" />
-
+ android:layout_height="28dp"
+ android:background="@drawable/round_bg_one">
+
+
@@ -172,17 +176,20 @@
android:textSize="10sp"
android:textColor="#1B1F1A" />
-
+ android:layout_width="60dp"
+ android:layout_marginTop="50dp"
+ android:layout_height="28dp"
+ android:background="@drawable/round_bg_three">
+
+
diff --git a/app/src/main/res/layout/dialog_confirm_delete_character.xml b/app/src/main/res/layout/dialog_confirm_delete_character.xml
new file mode 100644
index 0000000..e226d4a
--- /dev/null
+++ b/app/src/main/res/layout/dialog_confirm_delete_character.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/feedback_fragment.xml b/app/src/main/res/layout/feedback_fragment.xml
index ece79e8..7685d00 100644
--- a/app/src/main/res/layout/feedback_fragment.xml
+++ b/app/src/main/res/layout/feedback_fragment.xml
@@ -69,14 +69,17 @@
+ android:minLines="4"
+ android:maxLines="10"
+ android:scrollbars="vertical"
+ android:isScrollContainer="true"
+ android:nestedScrollingEnabled="true" />
+
-
-
-
-
diff --git a/app/src/main/res/layout/fragment_consumption_record.xml b/app/src/main/res/layout/fragment_consumption_record.xml
new file mode 100644
index 0000000..40acb23
--- /dev/null
+++ b/app/src/main/res/layout/fragment_consumption_record.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_mine.xml b/app/src/main/res/layout/fragment_mine.xml
index 03938c7..71e6491 100644
--- a/app/src/main/res/layout/fragment_mine.xml
+++ b/app/src/main/res/layout/fragment_mine.xml
@@ -8,13 +8,13 @@
android:layout_height="match_parent"
tools:context=".ui.home.HomeFragment">
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_ai_persona_card.xml b/app/src/main/res/layout/item_ai_persona_card.xml
new file mode 100644
index 0000000..2b258e4
--- /dev/null
+++ b/app/src/main/res/layout/item_ai_persona_card.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_keyboard_character.xml b/app/src/main/res/layout/item_keyboard_character.xml
new file mode 100644
index 0000000..2d15d1d
--- /dev/null
+++ b/app/src/main/res/layout/item_keyboard_character.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_loading_footer.xml b/app/src/main/res/layout/item_loading_footer.xml
new file mode 100644
index 0000000..882af55
--- /dev/null
+++ b/app/src/main/res/layout/item_loading_footer.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_persona.xml b/app/src/main/res/layout/item_persona.xml
index 21943f5..d839a8b 100644
--- a/app/src/main/res/layout/item_persona.xml
+++ b/app/src/main/res/layout/item_persona.xml
@@ -70,20 +70,23 @@
android:textColor="#02BEAC"
android:textSize="10sp" />
-
+ android:background="@drawable/list_two_bg">
+
+
-
\ No newline at end of file
+
+
+
diff --git a/app/src/main/res/layout/item_rank_other.xml b/app/src/main/res/layout/item_rank_other.xml
index 555f9c4..0169f3e 100644
--- a/app/src/main/res/layout/item_rank_other.xml
+++ b/app/src/main/res/layout/item_rank_other.xml
@@ -57,16 +57,17 @@
android:textColor="#9A9A9A" />
-
+ android:background="@drawable/round_bg_others">
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_transaction_record.xml b/app/src/main/res/layout/item_transaction_record.xml
new file mode 100644
index 0000000..2672418
--- /dev/null
+++ b/app/src/main/res/layout/item_transaction_record.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/keyboard.xml b/app/src/main/res/layout/keyboard.xml
index 32f7158..2260d70 100644
--- a/app/src/main/res/layout/keyboard.xml
+++ b/app/src/main/res/layout/keyboard.xml
@@ -1,5 +1,6 @@
-
+
+ android:overScrollMode="never"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:id="@+id/completion_HorizontalScrollView">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/my_keyboard.xml b/app/src/main/res/layout/my_keyboard.xml
index b506bd0..0e682ef 100644
--- a/app/src/main/res/layout/my_keyboard.xml
+++ b/app/src/main/res/layout/my_keyboard.xml
@@ -56,7 +56,6 @@
android:textSize="16sp" />
-
+ android:orientation="vertical">
-
-
-
-
+
-
-
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/number_keyboard.xml b/app/src/main/res/layout/number_keyboard.xml
index 73c40fb..5f8fd9d 100644
--- a/app/src/main/res/layout/number_keyboard.xml
+++ b/app/src/main/res/layout/number_keyboard.xml
@@ -68,8 +68,8 @@
android:orientation="horizontal">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/sheet_select_gender.xml b/app/src/main/res/layout/sheet_select_gender.xml
new file mode 100644
index 0000000..536f0da
--- /dev/null
+++ b/app/src/main/res/layout/sheet_select_gender.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/symbol_keyboard.xml b/app/src/main/res/layout/symbol_keyboard.xml
index 0cf04cf..62596e7 100644
--- a/app/src/main/res/layout/symbol_keyboard.xml
+++ b/app/src/main/res/layout/symbol_keyboard.xml
@@ -62,16 +62,16 @@
android:gravity="center_horizontal"
android:orientation="horizontal">
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -82,16 +82,16 @@
android:gravity="center_horizontal"
android:orientation="horizontal">
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -101,13 +101,13 @@
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="horizontal">
-
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -117,9 +117,9 @@
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="horizontal">
-
-
-
-
+
+
+
+
diff --git a/app/src/main/res/navigation/global_graph.xml b/app/src/main/res/navigation/global_graph.xml
index 25035c3..d2ba096 100644
--- a/app/src/main/res/navigation/global_graph.xml
+++ b/app/src/main/res/navigation/global_graph.xml
@@ -11,13 +11,6 @@
android:name="com.example.myapplication.ui.EmptyFragment"
android:label="empty" />
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
9
Shift
Space
- Backspace
+ Backspace
ABC/123
,
.
Enter
+ 从相册选择
+ 拍照
+ 更换头像
+ 取消
+ 头像更新成功
+ 上传失败
+ 上传出错
diff --git a/app/src/main/res/xml/file_paths.xml b/app/src/main/res/xml/file_paths.xml
new file mode 100644
index 0000000..e1eb1c5
--- /dev/null
+++ b/app/src/main/res/xml/file_paths.xml
@@ -0,0 +1,6 @@
+
+
+
+