Compare commits
2547 Commits
v4.5.2-252
...
v5.9.2-536
| Author | SHA1 | Date | |
|---|---|---|---|
| 45b5277138 | |||
| db39ea77d5 | |||
| e5388560e0 | |||
| 9b58bf3890 | |||
| 42a7c4918f | |||
| 565c3ec8e2 | |||
| a03c673ae7 | |||
| 0e06f998f3 | |||
| 288e71d34c | |||
| e305a90748 | |||
| b752cdaeb8 | |||
| 5e5f0360dd | |||
| e0ed6da7ae | |||
| 2940d11943 | |||
| 3c18ed9a31 | |||
| f02621d564 | |||
| 8fb2b1c829 | |||
| 84b876394d | |||
| 3fb031c329 | |||
| e46afac397 | |||
| fdf2c80667 | |||
| d12a1037a2 | |||
| 056400d4d1 | |||
| 26cbdeaf2d | |||
| 6a5f7f8284 | |||
| f272e486f8 | |||
| 7b0514c055 | |||
| 909e7f33a8 | |||
| 677511b119 | |||
| 26a2605cd7 | |||
| b02f650a34 | |||
| e1d82e7fbd | |||
| 4263f53315 | |||
| f235959372 | |||
| 7bc6655b99 | |||
| 7ecc227df5 | |||
| 3046d33f91 | |||
| a32829fd0d | |||
| 4819bf3ddf | |||
| c2e02d356d | |||
| dafaeef184 | |||
| a9917c9201 | |||
| c0b61822c0 | |||
| a16a16cbdf | |||
| 0decb62237 | |||
| cdedea5002 | |||
| 88c4ebe5a0 | |||
| 10cbcdd768 | |||
| 7afbfb30a2 | |||
| 090a567887 | |||
| e55faee9fe | |||
| 362e34be8a | |||
| bf1e5586b0 | |||
| bad10158bd | |||
| 5cc58e2182 | |||
| fd836137a5 | |||
| b122e460cc | |||
| 3b29fcbef7 | |||
| 9f60a2ba86 | |||
| 8df7b3d9ee | |||
| e2040f9a2d | |||
| e39cd494b5 | |||
| f70b721441 | |||
| 877acf54f4 | |||
| a31d7a06f7 | |||
| f52937830b | |||
| b5202cc931 | |||
| dd2bfbc53f | |||
| d2f3cdadf6 | |||
| 73a88f9099 | |||
| a86c957872 | |||
| 45588a2580 | |||
| 3b55269319 | |||
| 01966a87ec | |||
| b4a8e5c68d | |||
| aeb7842299 | |||
| e0bcf947bf | |||
| 30bf685962 | |||
| 1e6fa023dc | |||
| b5920fc409 | |||
| 16ceb7115e | |||
| 279ed66270 | |||
| 9bfa8a33c8 | |||
| 99bd4da08a | |||
| 4824814981 | |||
| 10237d2fde | |||
| c1b236080e | |||
| 83d16a16b8 | |||
| 52ccffb97a | |||
| 9ed9e22df4 | |||
| 5aa29782cc | |||
| 82fcde1245 | |||
| eab945acfc | |||
| 8ed57cbfb8 | |||
| d3ed6d750b | |||
| bb4878eab4 | |||
| 53d8444c85 | |||
| 7db009a3cd | |||
| 4932fc2d7b | |||
| ca55ac02b7 | |||
| c535ffb2e0 | |||
| 9c6eb22d9d | |||
| 9ec6ec0281 | |||
| 327441e62b | |||
| ae3f07a3ee | |||
| f967e1f283 | |||
| ba1308c568 | |||
| 3144e3b007 | |||
| ec1c5713ad | |||
| 15adc93340 | |||
| c9b27f6ebe | |||
| cff7f7d2e2 | |||
| cb5d0c4840 | |||
| dea6abe399 | |||
| 20c792d4fa | |||
| b8cbd5a0fb | |||
| 0a34f23e62 | |||
| 7413b4e4ac | |||
| d2025c32c9 | |||
| 5c5dd35409 | |||
| b509e9046e | |||
| 46e99434c9 | |||
| b6ff94aed3 | |||
| 25e0bd19bc | |||
| fe5b398fad | |||
| 417ed1401e | |||
| 48698f06cd | |||
| b70a7eaf8b | |||
| ad86d7bbcc | |||
| a2eb550099 | |||
| e736855a4a | |||
| 0b49f531ac | |||
| c36f4d9abf | |||
| db72b0a5c9 | |||
| b19d21fc39 | |||
| 2d172d70ad | |||
| cc434771c0 | |||
| f5a9502261 | |||
| c48d970b0a | |||
| 9a53c7a84e | |||
| ce17ae2d2b | |||
| d97f65e9b3 | |||
| 9f1f3a2d66 | |||
| 4c9609ba52 | |||
| 270203266f | |||
| edc326ff3c | |||
| 0856b8a2ab | |||
| 8f95517dd7 | |||
| c828f3f479 | |||
| b90d026a74 | |||
| 819beb779c | |||
| de31dcaf0b | |||
| 587c217b7c | |||
| b03e86f903 | |||
| ea80942937 | |||
| 06153cbf58 | |||
| 0b94f777a2 | |||
| 3d6917cd5d | |||
| e4300ab315 | |||
| ed80533198 | |||
| 4b5c741ff9 | |||
| a93b49eb76 | |||
| 9fb25b80ea | |||
| 5c64deb874 | |||
| 33cb09e201 | |||
| 032a9750e2 | |||
| ff6fcd038e | |||
| cc3908b416 | |||
| 50a7527e7c | |||
| 75e085b80a | |||
| 39eecf0920 | |||
| 0e9f267117 | |||
| 02bb977cc0 | |||
| c4a89b9302 | |||
| cde9401b36 | |||
| edcb4a949e | |||
| 4e0516e43f | |||
| d702cafc4f | |||
| 6cd9076dff | |||
| a5c113e527 | |||
| 048bdbc367 | |||
| 1e287f56e2 | |||
| 2aacab80a6 | |||
| f9d7255556 | |||
| 2b2cf1ca8f | |||
| c651a97bbb | |||
| b54f1d125e | |||
| ead711446d | |||
| b19f0ac69c | |||
| 25a325f7f7 | |||
| 8be52f7bdd | |||
| 3cb1cd3ba6 | |||
| b68a9a43de | |||
| 0f80f5bc03 | |||
| 647ef1d8d3 | |||
| 6c6a7f0677 | |||
| 56dc30845a | |||
| da14711bbc | |||
| 33fe057a5f | |||
| a052f981c5 | |||
| 5042bddec7 | |||
| 0803e61828 | |||
| 119f304e2f | |||
| 3db6f93b73 | |||
| 3f74d8efc9 | |||
| 15aabc4db7 | |||
| 3ed3a8d47a | |||
| 3719f5e1cd | |||
| 957fca8960 | |||
| 0066d2eb7b | |||
| cbae38b41d | |||
| 021886c3cb | |||
| 6d62dd7fd1 | |||
| 20d28525cf | |||
| a406127cda | |||
| 3301b30ec2 | |||
| f761715ebd | |||
| 1f0216662a | |||
| f483f1effc | |||
| cf5b0bcd41 | |||
| 4e51830c58 | |||
| 830a45e6d2 | |||
| 24c32b7a4b | |||
| 7e30f1702d | |||
| 68ae4bf1b2 | |||
| 0b757f24d1 | |||
| 99d0525c0a | |||
| 4f3ec7aa18 | |||
| bd228459fa | |||
| c9227c20a2 | |||
| 5270bc3ed7 | |||
| e1de3dc873 | |||
| 9303f534d9 | |||
| 629a694279 | |||
| 0ef0ed7c50 | |||
| 48d335ad67 | |||
| 231434caeb | |||
| 220fd340ce | |||
| 5badb44e3e | |||
| 82d405c14c | |||
| 2157dcd51a | |||
| 872d9d89c5 | |||
| dae4543a82 | |||
| 60204d9ab2 | |||
| 0c86965260 | |||
| d0ad110d9e | |||
| 5cd88cdb7f | |||
| 60acd1bac4 | |||
| 990efacb89 | |||
| 7076a6acaa | |||
| acc4d6d8ab | |||
| 0a3ae4379d | |||
| 8197f161c0 | |||
| 0cd5f0d640 | |||
| 790093b585 | |||
| 46093c065d | |||
| 9cc1a0dd4e | |||
| 72a5d2881a | |||
| 7eabb1098c | |||
| 7a295a5d05 | |||
| da26247d30 | |||
| c797779598 | |||
| f66bf2398c | |||
| 010ce39e20 | |||
| 9320059a76 | |||
| 3003a310a0 | |||
| 91af5e6fe6 | |||
| d02dfbdaf7 | |||
| c3e06cc1ff | |||
| 3e86e368d8 | |||
| df231d1530 | |||
| 006f71def7 | |||
| 047dc9a7ae | |||
| 05abf00994 | |||
| c8278f6364 | |||
| 8cd951c669 | |||
| c43700e4d6 | |||
| fa2174319e | |||
| 4d6a71c8be | |||
| c866f16157 | |||
| b99e579ccd | |||
| 01eb074df1 | |||
| f6473b0e5e | |||
| e244797e7d | |||
| f267908122 | |||
| 6128e364e6 | |||
| 32a4fda9bc | |||
| 792ce39792 | |||
| 94b80b3f8b | |||
| 0e49985edf | |||
| 164f508cbc | |||
| cdfda4d963 | |||
| c3b850972f | |||
| 89f8aebefe | |||
| 52eca3b1ab | |||
| 0e26943d25 | |||
| 8286b0137f | |||
| 0a8bef8ac3 | |||
| a8fa4cf872 | |||
| 775f454e38 | |||
| b7de326a21 | |||
| 5b0d2e8294 | |||
| ec34de4675 | |||
| 0f6aa5ca9f | |||
| b0cdd1029e | |||
| e554e9ca5f | |||
| ff879454c1 | |||
| 7d3baaa501 | |||
| 5a57a58f0a | |||
| e01bfb4676 | |||
| 3617004de2 | |||
| de8234868a | |||
| 3d31d9a435 | |||
| 9220f13c9e | |||
| 10a3ed7f15 | |||
| 680ec6e285 | |||
| d99fbe5292 | |||
| 554eb671b2 | |||
| 9f2e46d876 | |||
| d2efb8391c | |||
| ac04cc4525 | |||
| 03855609c1 | |||
| 2408af31d9 | |||
| fc2dcbd4b0 | |||
| 3012bf6a46 | |||
| e07529846c | |||
| 8805763b1d | |||
| 5fa578c9ed | |||
| 5056dddf26 | |||
| 010c41c3c9 | |||
| bb7cea3dee | |||
| d7a81d2268 | |||
| 9b9aa8c060 | |||
| ee445c0819 | |||
| 0060312d52 | |||
| 3a4f4eaf24 | |||
| afa8c776b9 | |||
| 60cfee7ce3 | |||
| e418f5d3ea | |||
| 80f30b82b6 | |||
| 58999418ab | |||
| f4e90aa9da | |||
| 1308737382 | |||
| c1b1b2a6ce | |||
| 82ec3569dc | |||
| 3dd2b5df49 | |||
| 34ac01cde8 | |||
| c09efbfa78 | |||
| 054f6200f3 | |||
| dabe88b703 | |||
| 35dee07537 | |||
| 67e44d378c | |||
| 09d9fc446e | |||
| 1971e3bb81 | |||
| b560cdfa6e | |||
| 0bfdedfae8 | |||
| 5cd424a680 | |||
| 012103e706 | |||
| 2ad067b80d | |||
| 2f551db4c7 | |||
| 343aa77c86 | |||
| b7bbf8688f | |||
| 67a98023c6 | |||
| f0c6486855 | |||
| 201884643f | |||
| 410ad346e6 | |||
| 782bb3a825 | |||
| e77b21719c | |||
| fffdd596d2 | |||
| 6e2b6cc397 | |||
| 97ac478c8d | |||
| 1b655f27d1 | |||
| 9760aa23d7 | |||
| 6656676636 | |||
| 32549fc16f | |||
| 55d83d12bb | |||
| fd1e7f7f4c | |||
| 0fc5a16f80 | |||
| 4992845195 | |||
| e9db55bcb6 | |||
| 11056df05e | |||
| ffd04b6f60 | |||
| abdbe601b4 | |||
| bca7e2e4cf | |||
| fcdd78d668 | |||
| 09f1bdb915 | |||
| 8e5168005e | |||
| c210c3b286 | |||
| 9020022fdc | |||
| b13aede1d3 | |||
| cca1f76b85 | |||
| f667ef562e | |||
| 3565214e38 | |||
| 8f11ad7d6c | |||
| 4612f7452f | |||
| 2850789b29 | |||
| bf3dbb623d | |||
| 9cb7cc8c07 | |||
| c7cfb7d7b7 | |||
| 018959aa03 | |||
| e64f87bc0c | |||
| 891958c4d9 | |||
| f921ff6f4d | |||
| 8a0809498a | |||
| 447e3f4421 | |||
| e2172a6fe4 | |||
| ccf3e43dc1 | |||
| 218091e1be | |||
| ee1eafe5a1 | |||
| 79e95a8146 | |||
| 3d05bad997 | |||
| 82f7efdc59 | |||
| 25bc499245 | |||
| 09011ac18b | |||
| 42a958a9cc | |||
| 77e1df1bb0 | |||
| d77533a421 | |||
| e47e1d6747 | |||
| 2922878163 | |||
| 4608891a88 | |||
| 8a6e0f45c7 | |||
| e30f3269e8 | |||
| 439710d5f3 | |||
| de68d5e3f2 | |||
| 4643197fb4 | |||
| c1c63d82af | |||
| b78eb5a268 | |||
| f9361ab134 | |||
| efb6a07a13 | |||
| 6b0bacc018 | |||
| c368148bfc | |||
| d43f7240b8 | |||
| 9f2f0734ff | |||
| 5d54fbde39 | |||
| 93bfbe9c36 | |||
| 5e9e5c1892 | |||
| 8c607ae05a | |||
| ee3c431ef6 | |||
| 804f9ee111 | |||
| d343519e19 | |||
| ce43cb6226 | |||
| 08b281c428 | |||
| 6b3079343e | |||
| d4baa0b4d8 | |||
| 70b411a946 | |||
| c7d670d50c | |||
| 0e3189404e | |||
| d95b2eccaf | |||
| 1d7903ad02 | |||
| 31bae10c28 | |||
| 3081d99e12 | |||
| ac8525f4e1 | |||
| f921ee35fe | |||
| 3c9f44bda0 | |||
| 381b4f4e3b | |||
| 7defa40be5 | |||
| 02a0a9aaee | |||
| 4f4097a7a9 | |||
| 098639042b | |||
| 3a9bc99f07 | |||
| 37e1a18349 | |||
| 3aa387a72a | |||
| 52cfa3def9 | |||
| 506b3effdc | |||
| 9da9f118c9 | |||
| 86e5995c8a | |||
| cb2603d057 | |||
| e6d70f23fa | |||
| 22427d0fa5 | |||
| 9459e7ca07 | |||
| fab7dffed0 | |||
| 61f6e595f2 | |||
| 22f4c97314 | |||
| 6d7dbed929 | |||
| 22f7021ab4 | |||
| 075359ef5e | |||
| dacb7e3dd6 | |||
| 267e9f3ccc | |||
| 178865af03 | |||
| f8864b8303 | |||
| 94c31916cf | |||
| b9b82ec3d2 | |||
| ed400b7ee0 | |||
| fb653b66d1 | |||
| e92253eeb6 | |||
| 3c0f8be6ca | |||
| 46ea2f9862 | |||
| e8626221b0 | |||
| 09d7ffbf0a | |||
| 791aa46586 | |||
| e0f9416a1f | |||
| 0b4acba116 | |||
| aa9df23d18 | |||
| e3e44f0b19 | |||
| 1bbea7bd75 | |||
| 31daffa123 | |||
| e91c32c5e7 | |||
| 15bb6a722d | |||
| 16a187f7d0 | |||
| aaddcbc67a | |||
| 5476dd443c | |||
| 3ab52cadd5 | |||
| 6f7374c479 | |||
| 7e296ae7ab | |||
| f5a985b221 | |||
| 72e061b344 | |||
| a08e6d5ad8 | |||
| f719ec10a7 | |||
| b680d723b1 | |||
| 0cf410c768 | |||
| be84693610 | |||
| 047b80e0d8 | |||
| 3c255a7119 | |||
| d9c5c7ffc4 | |||
| a61b621ceb | |||
| 90ca311205 | |||
| e466d1feeb | |||
| e594703f8f | |||
| cd98550183 | |||
| 60dbb7e11a | |||
| e6cbcd385a | |||
| e1f9c4984d | |||
| 039ee400ff | |||
| d376f7291c | |||
| 76402b7bcf | |||
| c2cbfad51f | |||
| 9848c3d07e | |||
| 84a2d9cdba | |||
| f2438f80e0 | |||
| 824d8897aa | |||
| 2c432348fe | |||
| b764eb472c | |||
| 1d4333ffe4 | |||
| 337bbe7ec3 | |||
| fd482f32f0 | |||
| 755e4f24ff | |||
| 9d27eaef7d | |||
| b0d4021212 | |||
| 2f1137b97b | |||
| 78541a56f8 | |||
| 7d9a0d3308 | |||
| 0c6ace909e | |||
| 8afba620f8 | |||
| 5bef19fac2 | |||
| beb1c95a2b | |||
| ec5865fa69 | |||
| 4bd2d0aff7 | |||
| ca73e267d8 | |||
| 628b91eed8 | |||
| 878deba341 | |||
| 1376a787dd | |||
| 9c56b67dcc | |||
| f4e7c9acc5 | |||
| 0a94a27331 | |||
| 21230d17fb | |||
| b12d9528c1 | |||
| 2127356240 | |||
| ce677b13a0 | |||
| 62d6ea002d | |||
| e0aa84c877 | |||
| 25f4c027bf | |||
| 10d7bc56b1 | |||
| 59006ad921 | |||
| a821915a1a | |||
| 4a91d0db53 | |||
| 984fca183f | |||
| fc1710f3b9 | |||
| 56f817bb7a | |||
| 70a939b2b6 | |||
| c7f7804b52 | |||
| d3494774a3 | |||
| 1b976106e4 | |||
| 10a81255a6 | |||
| 4eaa55cf78 | |||
| 0e3ab3c3a7 | |||
| 948588b874 | |||
| 095139079e | |||
| 42c553b9d3 | |||
| aedd766d7d | |||
| 228ab0ca40 | |||
| fdcb7cbbf7 | |||
| 933f46e250 | |||
| 2282787bd7 | |||
| c4d3698a9b | |||
| 80cc86a791 | |||
| 3646672849 | |||
| 9a4c8f1f06 | |||
| 4eba721461 | |||
| eeafcde6e9 | |||
| 0255fdbe33 | |||
| ce303b37b7 | |||
| 84cd49478a | |||
| 0ed92483f6 | |||
| 83f22c2874 | |||
| c4f6d00540 | |||
| 6af60df87c | |||
| d4ffa6116d | |||
| dab2d40ba4 | |||
| f45f29aa43 | |||
| f793469dce | |||
| 06ced1c0d2 | |||
| 0ea396fa35 | |||
| 1714b01b34 | |||
| ced132c07f | |||
| 500e069139 | |||
| 2bf896aa3f | |||
| 9168cfc928 | |||
| 0bbffb20cd | |||
| 18a28254fb | |||
| 415626ef58 | |||
| e9ad25dc36 | |||
| 92215163bf | |||
| b3ffe61376 | |||
| 8e6048883a | |||
| 532e40efc3 | |||
| b775083437 | |||
| e4b4270731 | |||
| bf0f551768 | |||
| 213ccd86c0 | |||
| fc5de50f61 | |||
| b896af2a15 | |||
| 93ba447192 | |||
| dd8cc3c744 | |||
| 064b041d28 | |||
| a131aca047 | |||
| ad9b39a94a | |||
| c3cf93eaee | |||
| b83f98e90c | |||
| 5e4c451ccd | |||
| ab5e0136a7 | |||
| 89ca11bb81 | |||
| f220b2fd43 | |||
| 28d99cbe38 | |||
| 9c2130562b | |||
| d242aca942 | |||
| a341dcf0b7 | |||
| 8a1c7ed5a0 | |||
| 1e0a3e9881 | |||
| d76d430672 | |||
| 32034473ac | |||
| 383d349b3a | |||
| e036f2e602 | |||
| c73b70b7e7 | |||
| d105f2a175 | |||
| cf1dc4e4f1 | |||
| 1d9a877f59 | |||
| 772226fb1a | |||
| fe62dee207 | |||
| 37309918dd | |||
| 7f8e853657 | |||
| 006a8a523e | |||
| 8e497122f2 | |||
| 33d5353f75 | |||
| 6da3d46d71 | |||
| a26e61ae4b | |||
| c3d347bffe | |||
| 39cc7e98a8 | |||
| 2a4e500893 | |||
| d6e0cfd644 | |||
| dbbcd29303 | |||
| f3a5f28236 | |||
| bfae7143b1 | |||
| 3ce733ab67 | |||
| d6f75d6dd0 | |||
| 6f3ba952ab | |||
| 4203fdfb72 | |||
| fd9abc7619 | |||
| 53a9f408e7 | |||
| 4eba49b625 | |||
| e3476f9956 | |||
| aedb08ce5d | |||
| 9feb4e774a | |||
| 9515676876 | |||
| e250f69d6b | |||
| 4d4d2c91e2 | |||
| 1283ec94de | |||
| 27f14ac914 | |||
| 4565819dd8 | |||
| fa5207855e | |||
| 7f586a042b | |||
| 1adad849e0 | |||
| 56b2983252 | |||
| 8b5e0edb88 | |||
| be17d3fc97 | |||
| e898b8a7a4 | |||
| 831c698f8d | |||
| 76b8988597 | |||
| 34b46a6143 | |||
| 422cc5d396 | |||
| a048e00489 | |||
| 7dde4eca55 | |||
| e7af8840fd | |||
| 903276e5ed | |||
| 43bdfc0605 | |||
| 3c2332d6f4 | |||
| e222f504f6 | |||
| 86707ccc7d | |||
| ed8c1a6b6c | |||
| 8179715f5b | |||
| 3ea293612d | |||
| 545bb6b47d | |||
| 0b40894cda | |||
| e6246314fd | |||
| 2c23f03cc4 | |||
| 91b9502d0b | |||
| 5bbe039e9f | |||
| 8be8fbee19 | |||
| 2e5361f844 | |||
| f4ecf28808 | |||
| 5dd88e30f8 | |||
| 0095c82edb | |||
| b8260cada6 | |||
| 39ec77faae | |||
| 5d4434eebd | |||
| 553ab70d78 | |||
| 23bffe7c5e | |||
| babc41a0cb | |||
| a272033834 | |||
| 82ebdd3908 | |||
| 30cac9f86e | |||
| 6f19a2b668 | |||
| 9235c19475 | |||
| 79d9d05775 | |||
| 2b816a5914 | |||
| 7d163f454e | |||
| b44b676a59 | |||
| c71429750c | |||
| a8c273f68a | |||
| 2588999270 | |||
| 99f7bd9192 | |||
| 70419e412e | |||
| df3df01327 | |||
| 4bdc051d34 | |||
| 575dab06f6 | |||
| 78cc7b116a | |||
| 6ebc4f1f43 | |||
| 82bb0e26ec | |||
| 6c4030c965 | |||
| ba6d3746ab | |||
| 03faa42bab | |||
| 151a4fd2bc | |||
| 3d8c4038a6 | |||
| 1e0265f6ff | |||
| 19871d2bed | |||
| 3da57acf12 | |||
| f6b5e5ecc7 | |||
| 8f471b76eb | |||
| 8218aa004b | |||
| 741a4c1640 | |||
| 63e9f02cda | |||
| 8ed3f759b4 | |||
| 616abbe04c | |||
| 8ea3511451 | |||
| 52475e83e9 | |||
| c573fc3b8f | |||
| bc23240003 | |||
| f92490406e | |||
| 9ab5b67ae7 | |||
| adaffbac77 | |||
| 3c9150adbc | |||
| 884e5b3095 | |||
| 5bafb565a2 | |||
| c750e3a50c | |||
| 917ed615f9 | |||
| 19399824c2 | |||
| 998cfb9233 | |||
| ea86b09444 | |||
| b399e6284c | |||
| adcc6fa5ef | |||
| da3fe16ac0 | |||
| 5f2695f4fb | |||
| 8408091b33 | |||
| 8de2ba1c14 | |||
| 41680a3440 | |||
| 320c44815e | |||
| f4fb26ffa9 | |||
| 39ba62e5bd | |||
| a2a28d801c | |||
| 491c94e536 | |||
| 78a805a6a2 | |||
| 86f6c66f97 | |||
| 674ebbfe65 | |||
| 48d964b957 | |||
| e08ba51951 | |||
| 1c1c271195 | |||
| 9a0d49741d | |||
| 91c07dad2c | |||
| bfb2130517 | |||
| 1942cf8ce9 | |||
| 3f5b5ac2ed | |||
| 7ab424f107 | |||
| 8403db9c0e | |||
| 2139c19b40 | |||
| 34d332129b | |||
| 19617c7e26 | |||
| 1ca9472eee | |||
| 798503cf26 | |||
| fb251f95ce | |||
| 44b69e3d06 | |||
| b44caf693b | |||
| 21c456e1d0 | |||
| aeb41616d9 | |||
| 92b4658123 | |||
| 95c9afa550 | |||
| 0d28fc15e9 | |||
| f5ff23ff3a | |||
| 1b838b473c | |||
| f0c6e437a9 | |||
| 95a955c51e | |||
| f3fe3990c8 | |||
| 8690559646 | |||
| 3e71115443 | |||
| 5534e6f1a7 | |||
| ffe98bfac0 | |||
| 946503d469 | |||
| 381ce29ae9 | |||
| 67bfcc82fa | |||
| 65e07959a5 | |||
| a91195d829 | |||
| 3d75d8507c | |||
| 2ec43d4ff2 | |||
| 7c55f71bb5 | |||
| 99ac44de9e | |||
| 9df38387f2 | |||
| e870dc9fd7 | |||
| d7faf04a2e | |||
| f0e325cd2e | |||
| c64e0da8c7 | |||
| e2140f501f | |||
| 020c8d4456 | |||
| 401c0bfdfe | |||
| 33c411befa | |||
| 041619ed0d | |||
| ad6114a44b | |||
| d61008349a | |||
| 39cfa01f5a | |||
| 924f83dacf | |||
| 68db40c4c8 | |||
| b53a793e1c | |||
| 1097300211 | |||
| 1052663db0 | |||
| e3f0311cc7 | |||
| f0af5787c3 | |||
| 4ed1da1777 | |||
| 6cca81c4bb | |||
| 047c45db9a | |||
| 689fec168f | |||
| cff7f4a9f5 | |||
| fcb8a60722 | |||
| f2ad9b2235 | |||
| 49067706b7 | |||
| 098f02f0aa | |||
| 772aa7ca03 | |||
| 4e78162d7f | |||
| 017600f90e | |||
| 86d1749989 | |||
| 32bc1a4a6f | |||
| 58b37f8922 | |||
| 135b55b204 | |||
| 1f025f3556 | |||
| 54bf1af524 | |||
| 11cae2c56f | |||
| 7075f3bcc1 | |||
| 5df2a1c2f5 | |||
| 831ca71a58 | |||
| bb854161f5 | |||
| a98d6125da | |||
| b178961b9b | |||
| 9c6664aed2 | |||
| 1d254248d9 | |||
| 844c028b7b | |||
| cf320fa01b | |||
| 739b729654 | |||
| 48729b298f | |||
| df2d5a93a0 | |||
| 328d200a78 | |||
| a5f5bd8828 | |||
| 760aa6f8f6 | |||
| d6a341337a | |||
| 82d79028de | |||
| 92eece6289 | |||
| edd8b86144 | |||
| 5d5c8dfddb | |||
| 1dc2e661d1 | |||
| feb46a5887 | |||
| 4086fcdb30 | |||
| 8dd8d21e85 | |||
| 42a4210290 | |||
| 9a1266d9d9 | |||
| 2915042719 | |||
| b4730589bf | |||
| 001cbfcdb6 | |||
| 44dab8246f | |||
| be5cbf0ca0 | |||
| fa2fb66f44 | |||
| 9982ebea9e | |||
| 2b30cc1f41 | |||
| 96a19eb5ee | |||
| 85276d462f | |||
| 44aebc39ea | |||
| 52294ebb3a | |||
| 623156bdb5 | |||
| 6bac5d79a9 | |||
| 293fc635e1 | |||
| 0f0dc6dd9b | |||
| 46de8f5f9e | |||
| ed6b2960cc | |||
| f3ef7930f3 | |||
| 5ecb3d5378 | |||
| 1d4fe89900 | |||
| 3f3b19f2cf | |||
| af1ac4e888 | |||
| 1478b59479 | |||
| 335cde94fe | |||
| dd1f399425 | |||
| 3e8923f34a | |||
| 612fc8f444 | |||
| 0cbc104092 | |||
| 41f7e3a05a | |||
| 25ce3ccdc9 | |||
| 82a60413e1 | |||
| 2b4126fa71 | |||
| 52a7ab7b3c | |||
| 9b8ce918d8 | |||
| 57081f4003 | |||
| 5df0d85004 | |||
| 63b88383a4 | |||
| 7564a0a771 | |||
| 36bc35a7e3 | |||
| 1c55ab8f86 | |||
| 9a1f3b0fe3 | |||
| 438b55c08b | |||
| 12fcb98412 | |||
| 33030cf550 | |||
| 9a7acef9a3 | |||
| 61f92e544b | |||
| 57d6cc7d0e | |||
| 919ace95b9 | |||
| ec1af285af | |||
| 6b4c02e965 | |||
| 913bcdeeb2 | |||
| e40857d24b | |||
| d3fab1aa73 | |||
| d32698ca91 | |||
| 7832276560 | |||
| 2fca73dfe8 | |||
| 0f194a80bb | |||
| e0cdf42d26 | |||
| c501248408 | |||
| a559df99ec | |||
| 1cdd7ed6ef | |||
| 7e3bd14db4 | |||
| ab08a725ca | |||
| 1977c80038 | |||
| d9d8eaa611 | |||
| e0dcb70fcd | |||
| bb60f7f22a | |||
| 70821c73c1 | |||
| 1560234105 | |||
| 59f74ebb9e | |||
| d3e21251d9 | |||
| aedd996c3f | |||
| a589b77de5 | |||
| 86330c74b5 | |||
| 54a77555cc | |||
| 30ae815717 | |||
| 1eb1b6a956 | |||
| f46b303a67 | |||
| a1ecb784e2 | |||
| 142e04fa60 | |||
| 540d6cd1e4 | |||
| b078f38e62 | |||
| a2e5415b5c | |||
| 9aae1c5476 | |||
| e73b431378 | |||
| 0fe60f52e2 | |||
| 6957ca54f0 | |||
| 31de4089ad | |||
| d73a4704c8 | |||
| 2f5f52471e | |||
| 97f82d70f5 | |||
| c355bc2b79 | |||
| a011b5a947 | |||
| efdf02158c | |||
| 4ca302f5fc | |||
| e669df00b4 | |||
| 19e6f1e7b7 | |||
| 025d237be0 | |||
| 2eb5fb7526 | |||
| 3818e17976 | |||
| 1f43b8b220 | |||
| 10003fe49d | |||
| 80a8cadaae | |||
| 88098a8255 | |||
| 66a543146a | |||
| 3aafc3fcbe | |||
| 050da95a8f | |||
| 40b7b09de4 | |||
| 16091f759c | |||
| f65664b885 | |||
| 3eef9c465a | |||
| a1b4c726dd | |||
| 5e2752569b | |||
| 65a823698a | |||
| fdfad0edf1 | |||
| dcadb529c7 | |||
| 37460ceac2 | |||
| 994397ec35 | |||
| 9354ff9af8 | |||
| 2811e21911 | |||
| 746f81e45b | |||
| 69f1887926 | |||
| 6dfded50d6 | |||
| 5e01ecf0a2 | |||
| a1be42a135 | |||
| 5bdd655715 | |||
| c32e9bd0fc | |||
| 2e2985cb90 | |||
| f4a5dd47fc | |||
| 81873bd898 | |||
| d6604ff2c9 | |||
| 298b7af692 | |||
| 433df3bebd | |||
| 84dba2036c | |||
| 924ef2a727 | |||
| 5a2b997693 | |||
| ebb87962ac | |||
| 075eb1a21b | |||
| b9554d11a8 | |||
| b910075c4c | |||
| 1966e844bd | |||
| e760a63181 | |||
| bfa236f802 | |||
| 7f64b51582 | |||
| 4324126660 | |||
| a8c54de47d | |||
| 562b3e9d3b | |||
| 679c6da972 | |||
| 793adb9a03 | |||
| 23c62f1092 | |||
| 70acea8e92 | |||
| 516d8d8044 | |||
| ed2b41bc55 | |||
| 5351ffd6b6 | |||
| aaffbb376c | |||
| ac852c0484 | |||
| 4bd93ad958 | |||
| 5f131848e7 | |||
| 4915f99412 | |||
| 92841346cd | |||
| b0e8db6e0e | |||
| d533e0ceab | |||
| 672b697209 | |||
| c9bef1fb1a | |||
| 454e8bb934 | |||
| e587d0daaa | |||
| fcc23ddfc2 | |||
| c541805bed | |||
| 2c38c8fd91 | |||
| 15385cf78c | |||
| 6df3a8ec51 | |||
| bca9c6d96f | |||
| 26feccc28b | |||
| a1fc7d2e48 | |||
| 0b42b10e41 | |||
| 0a0912dbab | |||
| 5e3ff2de31 | |||
| 55db52f8f0 | |||
| 2888ab56cd | |||
| 1edc20f665 | |||
| 72c818b17b | |||
| 7393dc83f5 | |||
| 6043553a39 | |||
| 87461b52ce | |||
| df807d746f | |||
| 9e7a519806 | |||
| 6ee4af82ad | |||
| 75c8a4597c | |||
| de274f0954 | |||
| 82195950b3 | |||
| 806b3ac77e | |||
| ae95bd1db8 | |||
| 62ffefb92f | |||
| ca190c6d57 | |||
| 1c739a6269 | |||
| f5386cd3c1 | |||
| 65aab99639 | |||
| 7540659396 | |||
| bbb7904477 | |||
| 056985531b | |||
| 730739077b | |||
| 6c82184d1d | |||
| d3b6f6a1e4 | |||
| d35550900b | |||
| dfc16e38eb | |||
| 08bd5f3081 | |||
| 7d98fde53a | |||
| 486e9ae0c5 | |||
| d51587aa66 | |||
| 1fabcc756d | |||
| ee3fc058e1 | |||
| e66e9e1ba8 | |||
| 1cb4bea73c | |||
| b3a491e723 | |||
| b583aafa25 | |||
| 9709c6cbf0 | |||
| ca81eb2ecb | |||
| 910d886f65 | |||
| 376411c0cf | |||
| ad019eff69 | |||
| 3712e67594 | |||
| c98084ec39 | |||
| 5a851ea99d | |||
| 849cdb234c | |||
| 9f9313b5e7 | |||
| 1888d8cfc7 | |||
| 393b747f0e | |||
| d8f9a7ed7f | |||
| 7be4541f89 | |||
| 97e87a9f32 | |||
| 2d84309f04 | |||
| f2ed7f8b72 | |||
| c839d535d8 | |||
| 623e155c75 | |||
| b20a74de00 | |||
| 090b5fe04f | |||
| b7dca9b652 | |||
| c9402dec4e | |||
| 6dc097f60f | |||
| 636a826583 | |||
| 5c313ab7ac | |||
| 4aacc425a9 | |||
| c7db544684 | |||
| c7833bae11 | |||
| 3d777781b8 | |||
| f2501eeb3b | |||
| b72f9819d7 | |||
| 5f134fad33 | |||
| 21cec51fad | |||
| 048d3aa361 | |||
| 8871d12fa8 | |||
| 67f931f9c8 | |||
| 2fe9d6c3e1 | |||
| a662d5b90c | |||
| c30ec4bd87 | |||
| ee0a3a9d05 | |||
| 094a8abfe2 | |||
| e1c39e90a9 | |||
| aca3f2d2fc | |||
| 05d95259e2 | |||
| c278359909 | |||
| b5d8d40462 | |||
| c1b7c380da | |||
| e4f0ba0495 | |||
| ccd4a479a9 | |||
| 92941bcc38 | |||
| 6b55875547 | |||
| a0947a84d9 | |||
| fca0e03180 | |||
| 3dad7d8850 | |||
| bc18301dc1 | |||
| 1ebe8eba4e | |||
| 3ec3388b08 | |||
| 71c2e8eb6b | |||
| 009aa9f73e | |||
| 14e38bc738 | |||
| 74ffd80b10 | |||
| cb7a04ff09 | |||
| 96640ad91e | |||
| 3791dfd558 | |||
| 91017caac4 | |||
| 5d8720687a | |||
| 75b1234376 | |||
| 1e866a9eab | |||
| 80e58080c4 | |||
| 394036a0d0 | |||
| 2f2355f36e | |||
| e711e30c21 | |||
| e3d32057ee | |||
| e42c4e595e | |||
| 73d71e5dac | |||
| d6cad14e7b | |||
| 9beaf93595 | |||
| 074610a9f0 | |||
| 7881edaf21 | |||
| f2643308f9 | |||
| 1168be9a92 | |||
| c34d6141bc | |||
| e1f62e3a3a | |||
| 1d067dbaa2 | |||
| f1b6e742d9 | |||
| 149448c04c | |||
| e388a60d43 | |||
| ea49ef2691 | |||
| 741b2c23d2 | |||
| dca3542cec | |||
| 4a37af4ce4 | |||
| a2e907faeb | |||
| 7e33243fbb | |||
| 2b0abb1740 | |||
| 60e4f2a378 | |||
| a4603213b9 | |||
| da9ea61fbf | |||
| 4ceef741ea | |||
| d323fc07f9 | |||
| 2356e175f9 | |||
| 7a9c0ed8f2 | |||
| d406cf3fcb | |||
| 9741cac1cf | |||
| 456519e06c | |||
| bad6b495b1 | |||
| 638e0c988f | |||
| 40aaabad7d | |||
| 46cf8cfbdc | |||
| 2226bcf515 | |||
| 27ceb7b042 | |||
| c2226f8452 | |||
| 38dcdaf8e3 | |||
| 2a440e91fa | |||
| 2cecb9a14c | |||
| 241f976866 | |||
| 560c270723 | |||
| 5159957722 | |||
| cfb851c23e | |||
| e45218de17 | |||
| 0ff34bd3f7 | |||
| 6bc7a9f504 | |||
| 7ea53f305d | |||
| 4a04f6bb0f | |||
| 633b7df021 | |||
| 4ddaccd3f4 | |||
| 389c20b6bf | |||
| 20149b6041 | |||
| 2f6af6c18e | |||
| b764a43abe | |||
| c3fb8b9c89 | |||
| d574b09dfa | |||
| 7fd7197909 | |||
| 58178d2871 | |||
| 880838c263 | |||
| c196d5cdf5 | |||
| c370edaa72 | |||
| 0640aee554 | |||
| 8a5f11e6bd | |||
| 92b6ebcb08 | |||
| 758c331af5 | |||
| 17e5c0535d | |||
| 66fee84b49 | |||
| bbe0350f2c | |||
| b31fe4b94a | |||
| 11b414fd29 | |||
| 1a35b5ded0 | |||
| 4245c1511e | |||
| 989c3706dd | |||
| 49500768db | |||
| 65becdac85 | |||
| 125c78fccd | |||
| 58c576b950 | |||
| b08aa46992 | |||
| c77f0cf817 | |||
| bb0378c216 | |||
| f2e7a64e99 | |||
| 759cacf933 | |||
| 0b213253ce | |||
| f0ab333892 | |||
| 7516db3a60 | |||
| 29cd798ca8 | |||
| 40cb902baf | |||
| 15bbcf3eae | |||
| f508e103c1 | |||
| 94fa36125d | |||
| 1c754a6dd0 | |||
| 609e4a49b8 | |||
| 98710c9542 | |||
| 0e2f5fdf53 | |||
| e9ed5530d5 | |||
| 113f1d3ed8 | |||
| 3fc60a57b6 | |||
| d14e09187e | |||
| bf90131841 | |||
| 40b7a16b21 | |||
| 882d7273a1 | |||
| 6cfeddf3d4 | |||
| a06e6c581d | |||
| bd8bda40ec | |||
| efd81a4e0c | |||
| 493e08ce2c | |||
| 1906451060 | |||
| f865e95ae1 | |||
| 480e18b79a | |||
| 6436bc6191 | |||
| 7ec7eb29e4 | |||
| 5deb744ba3 | |||
| e908939560 | |||
| e02708bb0e | |||
| 4826c35a70 | |||
| c51f185438 | |||
| 65bd15b573 | |||
| 5283254c1a | |||
| 15458df982 | |||
| eb2228fb17 | |||
| 0e27e8ee3b | |||
| 17455efe82 | |||
| e2c80cb416 | |||
| a48b4db550 | |||
| 1d66637b11 | |||
| 3e7e98d555 | |||
| 60e542923c | |||
| e737d16f0f | |||
| a04219518b | |||
| ce2c6bed0a | |||
| 84a08493e3 | |||
| 3d411fa49c | |||
| c0a0c90a49 | |||
| a67d41296e | |||
| 25a95ddba0 | |||
| fac255a27c | |||
| 0811c8dc15 | |||
| 7537963a8e | |||
| 36f8b1de0b | |||
| 1d9e1bc9d8 | |||
| bb9fd24068 | |||
| 9c044ae98b | |||
| 9807883d65 | |||
| 04e410615d | |||
| eda1eea2de | |||
| c1f6ef35f2 | |||
| b61ce1e42a | |||
| 62e60f4309 | |||
| c6f544136f | |||
| d588315759 | |||
| b1fef73c54 | |||
| c4fc31c963 | |||
| 85b0d0eef4 | |||
| 812eb842e2 | |||
| 783cb95f24 | |||
| 63b7f294c4 | |||
| 80c95b0f11 | |||
| 5969fa2ca5 | |||
| 0187608918 | |||
| f7b155d3b4 | |||
| 82656a7187 | |||
| 9993824d93 | |||
| e2fa4990bd | |||
| 6753d04817 | |||
| 9679c75f0a | |||
| c203fa5b9d | |||
| 98531376d9 | |||
| 86edc8b919 | |||
| 7903751d85 | |||
| 2620a29a2b | |||
| 5cc40c09dc | |||
| 5c02d37852 | |||
| 2e5d445d65 | |||
| 456822f56f | |||
| c8bae7d89b | |||
| a5aceb3d1f | |||
| 578ed5d1f1 | |||
| 3ef7343309 | |||
| 2c98c38721 | |||
| 4f28c54591 | |||
| 06b5b885e9 | |||
| 612cc2ca9b | |||
| 18f7b695e5 | |||
| f29e2fe1de | |||
| 799c22093f | |||
| 2496c1d96e | |||
| 8ee76af30f | |||
| 2f35442558 | |||
| 81fa6a6233 | |||
| e5778d5b5f | |||
| f69d607ecb | |||
| 79fc2e1f93 | |||
| 6dfdbb9ee8 | |||
| 5d8f7b3f8d | |||
| cd4601a9c9 | |||
| 462a011401 | |||
| 31d55dae14 | |||
| 8dc9731299 | |||
| 66c19c644e | |||
| 2b617e2697 | |||
| 3a60a497e1 | |||
| a9337aee63 | |||
| c80cd47ccc | |||
| d9c2371488 | |||
| 096d19751a | |||
| ef70119090 | |||
| 95dba71bb9 | |||
| ec37c7a6f8 | |||
| dcc9352301 | |||
| c509c6bb38 | |||
| 4784d689f8 | |||
| 8903a075be | |||
| 48d6e91b0e | |||
| 30865239d2 | |||
| 8b7cd92ae8 | |||
| 69f336553e | |||
| ed82f96ae3 | |||
| 8f02016a76 | |||
| c06f397d12 | |||
| 912b7280cd | |||
| 50f7dd2c63 | |||
| 23469543c7 | |||
| 2d3f70dd12 | |||
| f57d19e797 | |||
| 8c529f0724 | |||
| 1583f1957a | |||
| 676ccb133f | |||
| 489b8143a1 | |||
| c4cf3efa21 | |||
| 871cfd638d | |||
| 1f3ae1c687 | |||
| 59eb101a48 | |||
| b3b8b6ba29 | |||
| 42baaa8950 | |||
| 61ad70d3b8 | |||
| 04252e3b91 | |||
| b40543d7b5 | |||
| 903fa49b71 | |||
| 03ae2699c6 | |||
| 8ecc4078bb | |||
| ebaf4f02f0 | |||
| e85437379a | |||
| 2e9642019b | |||
| 5a77522a04 | |||
| e5512604fa | |||
| 146a46f8be | |||
| 91f33f06d3 | |||
| 59da7b6ba6 | |||
| 0a1ad238f8 | |||
| 302e6c145c | |||
| a6bbf0dfc7 | |||
| 9c3d22964c | |||
| ab60ed8473 | |||
| 9a00e70cc4 | |||
| 35954f52c5 | |||
| e13d80d063 | |||
| 73ed15689d | |||
| 65d949bade | |||
| 33d0d01051 | |||
| 9998ed0a14 | |||
| 85a8a17acb | |||
| 4032cb8abe | |||
| fc2e87063e | |||
| a63b809642 | |||
| c30048c268 | |||
| 956d5a39be | |||
| 76fd1ab7e3 | |||
| 2c4c954c64 | |||
| aef4da961d | |||
| 31e00bb681 | |||
| 369fdd24ef | |||
| 2a57994c7f | |||
| 6f84b14277 | |||
| 10ed05c8ef | |||
| 799bd1042f | |||
| 6f9acab2d2 | |||
| 387d8eb5af | |||
| c813c5d5ef | |||
| 1c6df6741f | |||
| b54ab5f824 | |||
| 37fdf6c18e | |||
| 2685de9b3e | |||
| c049100f5f | |||
| 711d1f9d65 | |||
| 5d7453afec | |||
| e4acf466ba | |||
| 2d66cbd29c | |||
| 8786f7d500 | |||
| 3d40d7a819 | |||
| 6f7862a0f2 | |||
| 58c6211d54 | |||
| d10b187a1f | |||
| 1ebf34c83a | |||
| 01e2a3c708 | |||
| c9b97552d4 | |||
| 428f39aa86 | |||
| 5fecb723bc | |||
| 0168b0d93a | |||
| 895fa510fe | |||
| 0aae59a1ba | |||
| 574a2b4dc7 | |||
| acae608966 | |||
| 6ca6c34f92 | |||
| 16724a2d2e | |||
| 8b50cc561a | |||
| 1ec1d482c4 | |||
| 869b9d507b | |||
| 514fe66347 | |||
| 657c91a5ef | |||
| 82354a1156 | |||
| b7619f0c93 | |||
| 598a3ad0b6 | |||
| bf0be93dc9 | |||
| 90a676f778 | |||
| a675e4bbe3 | |||
| 5eee8c6785 | |||
| 3c54e4313c | |||
| 67c4b76de9 | |||
| 34fd08d06a | |||
| d08b95021d | |||
| 8517a2aef1 | |||
| cbe90141f1 | |||
| fd43bc9426 | |||
| 34a143b632 | |||
| d96e02eca0 | |||
| 398b1ae58b | |||
| 0749205bef | |||
| 54ec115fee | |||
| ebe7b84dc8 | |||
| 332abe66f5 | |||
| 9dccbfbd51 | |||
| 23a8c9e6aa | |||
| 1e4408ac6e | |||
| 05ece9c999 | |||
| 216905e455 | |||
| a1bd88dad8 | |||
| 16c66c707f | |||
| 50cdea2b03 | |||
| 95b0fda15a | |||
| 9c99273f81 | |||
| 8d9d36a5df | |||
| b22d168e97 | |||
| 1fee166e3b | |||
| 7c098c29b2 | |||
| ffc9dc46e2 | |||
| c491a4084c | |||
| 0f076a03c5 | |||
| 3345ab2044 | |||
| 5874bdd311 | |||
| 4669211196 | |||
| 4879e7f0aa | |||
| f5283d386b | |||
| 602333f923 | |||
| e9810c129e | |||
| c503f15df9 | |||
| cba1012bbd | |||
| 646493ae61 | |||
| b649089ab0 | |||
| e97fd9023e | |||
| 478dd658ab | |||
| b313feac01 | |||
| c04b82549e | |||
| 842e62f909 | |||
| 51c6de0506 | |||
| d350f65c1f | |||
| aaf8e28abc | |||
| 7cb921e969 | |||
| 3a4b41058d | |||
| 26308d1852 | |||
| af54932309 | |||
| 83799048ed | |||
| ce410ad2a0 | |||
| 933c40458f | |||
| 6ae7578281 | |||
| 72b804e26e | |||
| 7e58cdc59e | |||
| d782ea90b6 | |||
| efffd752f5 | |||
| 47f3f45fe6 | |||
| b4e12e2ec3 | |||
| 2d331f6294 | |||
| feb4137f84 | |||
| 6a2c4a2967 | |||
| 68fc5532ff | |||
| 43ae479271 | |||
| 7a3ea4d939 | |||
| 1bcb343355 | |||
| 1b7a601cee | |||
| 8bc9544dc2 | |||
| e586e65e44 | |||
| 8096da5f50 | |||
| 91868fda15 | |||
| a71e8824ce | |||
| 6cbc354921 | |||
| 79e2077a3d | |||
| 6ea3ed067d | |||
| 84fe33becc | |||
| 276f53fead | |||
| 0789cc6fdf | |||
| 3a563b7bd0 | |||
| 9b8ba0c3e6 | |||
| 258f054161 | |||
| 258c8db0f9 | |||
| 741d0b11af | |||
| 9267896b50 | |||
| 85c0bd69c1 | |||
| 2436507b7b | |||
| 1b68b07e53 | |||
| c4ba28de1a | |||
| 8daa55c16a | |||
| 5e6c6aca13 | |||
| e08ab790ee | |||
| a394901cb4 | |||
| 2aaa7f2795 | |||
| 7b0d2a25c8 | |||
| e3f553a96f | |||
| 809a25e3e2 | |||
| 88041d9b5f | |||
| a11d741f6f | |||
| 1283def6da | |||
| 9c621c31a2 | |||
| 9e3f40723f | |||
| 682bb9a3e7 | |||
| 9f2d164c70 | |||
| bad5130047 | |||
| e3f831e0ca | |||
| a1432452f5 | |||
| b3cc68b7c6 | |||
| cc1f5a269a | |||
| a2892a713a | |||
| c342e22ce4 | |||
| 57dcbab096 | |||
| 5aa4c5bcbc | |||
| 039e1fc957 | |||
| f7b2d10543 | |||
| 38972eca76 | |||
| c2d853d709 | |||
| 239209dbf2 | |||
| ba1f3e20d3 | |||
| 92ac62d949 | |||
| 6499e3b718 | |||
| 5f0d2604ec | |||
| 5f7bb6cb49 | |||
| 7360eef244 | |||
| 52e6feab59 | |||
| 5f44252410 | |||
| 7ae3b1cafb | |||
| 0fb77e79fd | |||
| d4ae878947 | |||
| 37a2e3cc0e | |||
| 30595741cc | |||
| 11f177d572 | |||
| 9e13675ca5 | |||
| 144a0641b6 | |||
| 95266f6f68 | |||
| 9ded647c0d | |||
| f566e4d5c3 | |||
| 5df3efd087 | |||
| 98f4361bf0 | |||
| 000d1e020a | |||
| 93431a7e37 | |||
| d54523cdb6 | |||
| ed468b7c73 | |||
| bad6211bf5 | |||
| 2743d4d0de | |||
| a0f5387917 | |||
| 1abfcfdb51 | |||
| ecc0a75f52 | |||
| c98bf26337 | |||
| 187d07d02d | |||
| c47c23ad21 | |||
| 8d011ae7ef | |||
| 36b58383f7 | |||
| e6ea1738e5 | |||
| 70b7689ac5 | |||
| 40983ecebc | |||
| 47b0a8d1ac | |||
| 1ae63d3010 | |||
| fbe73ce4da | |||
| d46617237f | |||
| 0894957d73 | |||
| 9b9777ae19 | |||
| 7f2145d0ef | |||
| acefbba6bf | |||
| 660a091b7b | |||
| 103078d63e | |||
| 178395f531 | |||
| 8db213d040 | |||
| 5904d3c5c7 | |||
| 1a511ebbad | |||
| ef97e8cc6d | |||
| 814cef6218 | |||
| fd6c4836d4 | |||
| 9df138ed7c | |||
| 7b11b0e99a | |||
| 01034f093a | |||
| 8e28766449 | |||
| d06d0bd15c | |||
| 4fe4a33b80 | |||
| 4a8e91bbff | |||
| b1f6c7d55c | |||
| 727eeac52e | |||
| fb26ec0c13 | |||
| 3e25619bf8 | |||
| e81fa4ff1a | |||
| d7d2bf2667 | |||
| c763a0a9e6 | |||
| 1f34f95e7c | |||
| 2afb99b603 | |||
| 2e09ac033b | |||
| 54747143d1 | |||
| 076c98977d | |||
| b96104fcd9 | |||
| daece9337c | |||
| 3547330e2a | |||
| 4d76fb0159 | |||
| f34a95b36f | |||
| 6e3cb7a196 | |||
| 03f87c46ba | |||
| b052ba1635 | |||
| d1183199cd | |||
| 31fed948f0 | |||
| f1c5b80dc2 | |||
| 86f2aec9ca | |||
| f826dc07c6 | |||
| 34670d08c0 | |||
| 5e5646468e | |||
| a218f13ccb | |||
| 1fc37571df | |||
| 576f06f250 | |||
| 3805cb52f6 | |||
| f8d7377236 | |||
| 354fa13a35 | |||
| 3c533b896b | |||
| 11c6618c0f | |||
| 898bf3e432 | |||
| 0faf408762 | |||
| 1dbabe3cda | |||
| a107a19d95 | |||
| 58ea0bb51d | |||
| 557f22b23b | |||
| f40a192e82 | |||
| 84f3556275 | |||
| cddc2bff2b | |||
| a0b8caa60a | |||
| 22cadc77f0 | |||
| 28fb45f0b8 | |||
| 3d7f8b3f41 | |||
| bba326cfd9 | |||
| f76bb7070a | |||
| a6a3d769bd | |||
| ea5627b3da | |||
| d9807227ec | |||
| 039203408a | |||
| 60b325812e | |||
| 06a43f617b | |||
| 5cc1cfa1a6 | |||
| 35cdd140cc | |||
| 87597dbd1f | |||
| 398f7b6642 | |||
| 85d3412fd8 | |||
| 71b8cbbef3 | |||
| dfc0183a14 | |||
| f0ca0a2ab1 | |||
| c9ffd4e5bd | |||
| d354b07fe2 | |||
| 71a61fdef0 | |||
| 156e52f619 | |||
| 252cb3825b | |||
| 2eded37321 | |||
| 075a7e4e77 | |||
| 0c95f911d1 | |||
| f35baf0e5b | |||
| 49b74c9a37 | |||
| c4240440d1 | |||
| e0205ec060 | |||
| 895024aa09 | |||
| 4d64281c78 | |||
| 95f1692434 | |||
| f1302dd1bb | |||
| fd946703a9 | |||
| 806935a81e | |||
| feb30ff3a4 | |||
| fec34f1efa | |||
| def3b55e51 | |||
| 4549f78ede | |||
| 3054b56f4a | |||
| 4cf5e0f2f4 | |||
| 1a18c82f06 | |||
| b8fbb429f1 | |||
| 005229fe8a | |||
| 3a0bbbf3d0 | |||
| 4b851732c9 | |||
| 3abf29fa75 | |||
| 4bb7d2a043 | |||
| e3d830235e | |||
| 6425f3d50c | |||
| 868e6d31b8 | |||
| a687f8d877 | |||
| e9d2c6573f | |||
| 05e36f11cf | |||
| bc76e6eddb | |||
| 6de543261b | |||
| d9a8fa0a2d | |||
| 51c88e8586 | |||
| 9e930f1b35 | |||
| eb8fb191bb | |||
| 6653467259 | |||
| 9ec29ad367 | |||
| 406ac98616 | |||
| 5832c75909 | |||
| 9d8681c50f | |||
| 9c28bf162e | |||
| 3e86a40215 | |||
| 061d5c19ed | |||
| 1241207dd2 | |||
| 6a68839d0f | |||
| 2140c9eb40 | |||
| 702da08bc4 | |||
| 942af39b2d | |||
| a0331f0437 | |||
| 8c885e38a1 | |||
| acc6935608 | |||
| 63798c94cf | |||
| 62a87d1c71 | |||
| 2ba479d3ca | |||
| 6c2ff6a94f | |||
| 3f5f3bf57c | |||
| 2211f562c7 | |||
| 4d10ca0c6c | |||
| c816fd87f4 | |||
| 547bfd98ac | |||
| 34ba85f099 | |||
| 2d251ef453 | |||
| 7a0165375c | |||
| d7c7fe8740 | |||
| 295e783d4b | |||
| 58dfad4123 | |||
| f31d8b51fa | |||
| 01ea8b9834 | |||
| 5152060515 | |||
| 0a0d805e9d | |||
| 51511c8be8 | |||
| 4bbfa3e16e | |||
| 81c0c6ee09 | |||
| 50b449ca64 | |||
| 8764412492 | |||
| c1e011cae8 | |||
| 018f814a6b | |||
| ef9cb24b61 | |||
| 245a4273de | |||
| 94c1651510 | |||
| 07f55acb3a | |||
| 43ee3b3488 | |||
| 32625f59e0 | |||
| e1e3aa5598 | |||
| c5ff2cb5bd | |||
| acf02c99e7 | |||
| 9a00999d9e | |||
| 4c5b602a3b | |||
| cbde36f3bd | |||
| f4b6193a6b | |||
| 85a596766d | |||
| 785cfecf81 | |||
| 0c8b98084a | |||
| 87a6a3a539 | |||
| 6c50082de5 | |||
| 41195ec9d4 | |||
| e280ea008a | |||
| 223646cf3d | |||
| ab4bb79a32 | |||
| 50b51e2f3c | |||
| 1fef7d51d6 | |||
| 6546961f81 | |||
| 75e4f32840 | |||
| a3f3c95e19 | |||
| 508231d547 | |||
| 8649b98032 | |||
| d35211262f | |||
| 96a5838155 | |||
| fc21799288 | |||
| 3e04d77d50 | |||
| 35f47e363a | |||
| 019724e9c6 | |||
| 70f059b4de | |||
| 34cd63d36d | |||
| 033cf6b566 | |||
| 513fc3be1c | |||
| fd1273a092 | |||
| 27a6f14d18 | |||
| 000c7978fd | |||
| 486c8f691c | |||
| 16450a7f8d | |||
| 438e660449 | |||
| b9bf4311cf | |||
| 75a324747c | |||
| 04b579cda7 | |||
| 5962a8cf08 | |||
| ea388a8e1b | |||
| 8a98d6323a | |||
| 1b67ff5d12 | |||
| 9ee0f65e02 | |||
| 557b6fee21 | |||
| c90d6ec0dc | |||
| 8109aa58c9 | |||
| 213e079e9d | |||
| 40e38e50b9 | |||
| b70e18af96 | |||
| 4c91f06d94 | |||
| 199067cf8a | |||
| fa01260f0b | |||
| f8dac32bd6 | |||
| d5641d437c | |||
| c27f62e726 | |||
| b98d4e6ec6 | |||
| a4e67cda91 | |||
| ab3616f237 | |||
| bd9032c659 | |||
| 4ded8cbfc8 | |||
| 5a14afa71e | |||
| 7736ef37e5 | |||
| 40adf1938e | |||
| 7e1f821d7a | |||
| 4f81996857 | |||
| 328a559ef0 | |||
| ee01512f84 | |||
| a366a87324 | |||
| 3c7a1e2ae3 | |||
| 87c71882f5 | |||
| 7fa912bc0c | |||
| 27dc3f73ae | |||
| be630e39cd | |||
| cded31298c | |||
| 54030fd1df | |||
| 98c1565dfb | |||
| d89c2d4a70 | |||
| 0f11f6344f | |||
| a25f265bd3 | |||
| 68adc0ff23 | |||
| ab5aa01ec5 | |||
| 71e5f798d9 | |||
| 2572c53305 | |||
| 64b5ca8efa | |||
| b5ef73517f | |||
| feb2f68778 | |||
| f0a6e79418 | |||
| 78699cc0e8 | |||
| 96805537f4 | |||
| 13c4be2ab0 | |||
| c2e5e8adbc | |||
| c5c9a3f4d2 | |||
| 8bd2fa9b3a | |||
| 4bd8fc4e51 | |||
| 5b26165aaa | |||
| 1bb4c1cc2c | |||
| d38b347eb8 | |||
| feaf2d0bfc | |||
| 210dcdc698 | |||
| fec291156a | |||
| 62bb23abc9 | |||
| 19be9e1842 | |||
| a5cfd147b4 | |||
| f15d572785 | |||
| 5e99e8b032 | |||
| af39b82da1 | |||
| ba3a4ede11 | |||
| db591d0249 | |||
| 1370261b87 | |||
| 3824de4719 | |||
| 6ee26fb401 | |||
| 865fe2729f | |||
| 85c556c593 | |||
| 86681b8ab1 | |||
| 5f2ce22506 | |||
| dafa85a791 | |||
| 9cc3a29b5a | |||
| d2e8a67971 | |||
| 1bd3168add | |||
| 81d81882e0 | |||
| 3f4e4f1a52 | |||
| 3845e1f3ee | |||
| 6d2ae4dca5 | |||
| e2efa26ac0 | |||
| 70e12ebd08 | |||
| 91fc511fe6 | |||
| c5530ba307 | |||
| 67cbd9b641 | |||
| 6735047e67 | |||
| 9993bca582 | |||
| 07258b08af | |||
| f8bd7787f6 | |||
| b6a3ef5faa | |||
| 730151f6ba | |||
| 532b98e002 | |||
| 202a26b09c | |||
| fe1821884a | |||
| eb1605cc0a | |||
| 08ad5c3455 | |||
| 0774525de6 | |||
| b69ebc8122 | |||
| 49f31da303 | |||
| 66568b09d5 | |||
| 57a944f954 | |||
| 1fb8a60b2b | |||
| 489a57a379 | |||
| 0aa02c186c | |||
| fd463ec1e3 | |||
| 0c16ae5f3f | |||
| 76cca46015 | |||
| 748cacce3f | |||
| b47985d4ff | |||
| 9e41bd2846 | |||
| 0e4b18fc5d | |||
| 5594a03cee | |||
| 59ece3be49 | |||
| a7f77076a5 | |||
| 6526d5b3ce | |||
| 3a2f877d0c | |||
| 0d344e66a1 | |||
| d3de323c06 | |||
| 721d5acc1d | |||
| 7069744191 | |||
| b1f56d5dcd | |||
| 5aaa5bd076 | |||
| 7e74dee0eb | |||
| 45b8f7f82f | |||
| 7c9b8732e2 | |||
| 86ae96253b | |||
| 9dcc0b5587 | |||
| 9ceba793e8 | |||
| 175cc55b75 | |||
| 5486d1803f | |||
| cb885f8b66 | |||
| ab9f5cf7d8 | |||
| 7f4fafc637 | |||
| acccd2330d | |||
| 2745147e73 | |||
| 8fb0e3feaf | |||
| 335fa8ba39 | |||
| c739074057 | |||
| a2569cf876 | |||
| a81b0a1603 | |||
| 76c20e4b82 | |||
| 4144cc2c4f | |||
| 5465ff7731 | |||
| fc6dedb320 | |||
| a76b98f20c | |||
| f05c0324fa | |||
| 606eb30d9f | |||
| aa41da022d | |||
| b62d2c1b9e | |||
| ba21543800 | |||
| 6f5b832a68 | |||
| eb4bbb9efa | |||
| 233f91d2b9 | |||
| 1c51b2fe41 | |||
| 5123d7ff11 | |||
| c20e06f2eb | |||
| df55caa1f8 | |||
| 61bdac3aa2 | |||
| d56dc53b06 | |||
| 18c02ad9c3 | |||
| 8a262532e1 | |||
| 876d920251 | |||
| 1bad70a83f | |||
| c04a1a5802 | |||
| ea8c47a6d6 | |||
| 53f3f48c4d | |||
| 706df0c22f | |||
| 74cee421f9 | |||
| 4b08fa56da | |||
| 522f664f30 | |||
| 87a92055a7 | |||
| f23aa496ae | |||
| 65d7322546 | |||
| d9178f8538 | |||
| 5de471d89a | |||
| 63cc104e9f | |||
| f06ba9bf45 | |||
| 682d5a2cd4 | |||
| 76ce4e2f04 | |||
| 54221cc976 | |||
| 79ae36c51f | |||
| 335d6787a7 | |||
| 2bf9c1e4d7 | |||
| 133635c495 | |||
| 2aaf6d8a9c | |||
| f119f6751a | |||
| 18f964c167 | |||
| 369275225e | |||
| d80daf104f | |||
| 71c697d596 | |||
| 169814d6a9 | |||
| 52cffde739 | |||
| 11f84b69ac | |||
| 2c3e03bd0f | |||
| 264e103395 | |||
| 7f4d3ffe3e | |||
| 79b7a15f7d | |||
| 638dd4ad1d | |||
| 04f58e036b | |||
| 6624868bfe | |||
| 2ff83b8cc5 | |||
| cab412ab70 | |||
| 1a83e64f3a | |||
| be76fb0526 | |||
| 5cc25482a9 | |||
| c683e6786c | |||
| 5f182eeddb | |||
| 3e0979fabb | |||
| 13685ec605 | |||
| 4eac5ff61c | |||
| 98f0cf5fc0 | |||
| cb345f49eb | |||
| ef95be60f1 | |||
| a85cfa47e2 | |||
| 66123b0ae8 | |||
| cdb660b884 | |||
| 026e4f71de | |||
| 01ecde0425 | |||
| f748fcbd48 | |||
| 65464eed3d | |||
| 6a5d8e915c | |||
| 5fde455bcd | |||
| e1e257b1d8 | |||
| 7a87f8dc14 | |||
| 3fe2b8ffcd | |||
| 565b7b2c38 | |||
| b3bf9d862f | |||
| c6f5ef64d5 | |||
| 95b21b5bb5 | |||
| 0da331d109 | |||
| 8c1f8833b1 | |||
| e53cd6dede | |||
| fa27e172e4 | |||
| 544f5102ea | |||
| 5d6134f0dd | |||
| e0b951e043 | |||
| f2476c84c4 | |||
| c186c32d47 | |||
| 194f30650e | |||
| a475b176b4 | |||
| 73cac4d39d | |||
| 93d0f30352 | |||
| 98d30d1183 | |||
| b1d178f8df | |||
| 0550df7039 | |||
| 13c7b8ff1d | |||
| d7b7633ffe | |||
| 5e536db94e | |||
| 55d8b6a866 | |||
| a2a8610083 | |||
| 8d9f0adebe | |||
| e386994e4d | |||
| fc5f1e9830 | |||
| fd17eaea00 | |||
| 8a65d6dd85 | |||
| 8d3c609ad0 | |||
| 2e71e11a09 | |||
| b9c17bcdb8 | |||
| 55bb1a1873 | |||
| 54601bc083 | |||
| df2bb9c020 | |||
| 0bf297d094 | |||
| db8a2a06aa | |||
| 44a9db7294 | |||
| 7db48b6923 | |||
| 0a8f2d51db | |||
| b06d2a479a | |||
| d56bd0078e | |||
| 7b87aadd86 | |||
| 8f2da187f2 | |||
| 8291caa5e3 | |||
| e30ec540bd | |||
| 3c4da21bf6 | |||
| 800260b6a4 | |||
| ddea202f85 | |||
| b3a786ee22 | |||
| 332e24b874 | |||
| 5413ebfba4 | |||
| 7ead1c89f3 | |||
| 03300a41f7 | |||
| 2d8698e070 | |||
| 078b1a18a0 | |||
| de1e3ba7a7 | |||
| a9275c83d2 | |||
| 02894879e3 | |||
| 56a202ae5c | |||
| 013969a73a | |||
| 1a5a4ba149 | |||
| 9bdae33655 | |||
| be1184583b | |||
| 68ee926897 | |||
| 4d0db8cbcc | |||
| 59e4b6e063 | |||
| e74cf92033 | |||
| 811b1bbfce | |||
| 00c25d0d47 | |||
| 66b4b4b9f0 | |||
| 4bab47d2a6 | |||
| 2c9567aa61 | |||
| 97598dc7ec | |||
| 4771c79fa9 | |||
| 3458a5d7f3 | |||
| 5bc7c333d8 | |||
| c7a24555a2 | |||
| e19a678699 | |||
| 795af0528f | |||
| d0b4ea8ec2 | |||
| 45861dff61 | |||
| 5f41463fc3 | |||
| 89cca6dbfd | |||
| e43c0efdcb | |||
| 2dbd635b8e | |||
| 728d4fd8e1 | |||
| 48b5c7efc1 | |||
| 278dcc33be | |||
| 87be3bed61 | |||
| 078b102152 | |||
| 4f6969a70a | |||
| 29140b6c0b | |||
| 11bd9bed95 | |||
| 997b5676dc | |||
| 9b8755e035 | |||
| 85e97bc64d | |||
| 0ea7c81ce9 | |||
| 951ac06948 | |||
| 92cdb6fdbb | |||
| cb0dd1d082 | |||
| 276655a8ec | |||
| df8b612188 | |||
| a550305947 | |||
| 3af4dd5494 | |||
| 4d413eb1f2 | |||
| d496ab0283 | |||
| b519ff6aac | |||
| 8c05d2be8a | |||
| 23dd89198b | |||
| 6569ab2d33 | |||
| 1bead6a756 | |||
| c0df3af298 | |||
| 267363f46d | |||
| 44cf54aee6 | |||
| 322d7117f8 | |||
| c8684f837a | |||
| 9e5ecf1520 | |||
| 2df94dddc5 | |||
| 1731980e88 | |||
| f789f0cb73 | |||
| d043a89f3e | |||
| 4ab0f066df | |||
| 096679029b | |||
| 2634228d21 | |||
| a33784221d | |||
| 2ef7216bda | |||
| 2554fab389 | |||
| a4e3121489 | |||
| 8dfde8ddd0 | |||
| 63403c7c29 | |||
| aa9269aecc | |||
| 489e7105bb | |||
| 8dbc407503 | |||
| 3e032fa540 | |||
| 3df381b255 | |||
| 7c59181bc6 | |||
| 9244a65371 | |||
| a7b8b678f7 | |||
| 9877d4d51e | |||
| 32b4570738 | |||
| af3fdb3cfb | |||
| 5ce992ee63 | |||
| ba17019428 | |||
| f77087907f | |||
| 3080f59ef6 | |||
| 74b1f388e2 | |||
| dcbc2fe352 | |||
| b892c58c52 | |||
| 0a455c7b09 | |||
| 7a254c4609 | |||
| 4f08a4b9d2 | |||
| 75670b7801 | |||
| af386f6bdc | |||
| ff813d8784 | |||
| dba87fe09c | |||
| cf13003c2a | |||
| 7fe337c557 | |||
| 1902ed29c1 | |||
| a67927e9e7 | |||
| 33b6835ae6 | |||
| 1ca6a2c795 | |||
| 372230092a | |||
| 2ee1db1074 | |||
| caabf416bb | |||
| d33d0e98c9 | |||
| a4a029f31e | |||
| 3b8d34f8c3 | |||
| 2368e268bf | |||
| 6390198899 | |||
| 4a6aef4caa | |||
| 91529f51a3 | |||
| 57096b2b08 | |||
| 40f1f57844 | |||
| b9da7fbcb8 | |||
| 7138177df2 | |||
| aa4cf291dc | |||
| 8f4c1651a3 | |||
| 30a6cfd7d9 | |||
| c1abb977bf | |||
| fef633e9d8 | |||
| 5207b5675c | |||
| b7059abcee | |||
| 45a84fc45c | |||
| cf96b00036 | |||
| 49dacc472d | |||
| 72d069ad7e | |||
| 2f49e05891 | |||
| fd2d277470 | |||
| fc52694fdc | |||
| 7123b3d59a | |||
| 7684c247bf | |||
| f2b9d26e8a | |||
| 6d5d407aa8 | |||
| 338de605ec | |||
| 53930b0257 | |||
| 34d5e2cc29 | |||
| 591a694bdf | |||
| 9fd9a4ebcc | |||
| f2712c3db7 | |||
| 61c2c7b218 | |||
| 8a54c47d9e | |||
| 7f60a575a4 | |||
| eb2dd7c6b8 | |||
| ae8433d8bd | |||
| 7ec4ba6582 | |||
| 190434a855 | |||
| 4b82e40eb4 | |||
| d8e858ddb8 | |||
| 9b8c8f328d | |||
| bebdbe64cd | |||
| a0555d6ffc | |||
| d6674efe4a | |||
| 20235c6908 | |||
| bc155d580c | |||
| 9cf852b490 | |||
| f32bc837b4 | |||
| 36efcdb75a | |||
| a232ef9d03 | |||
| 05b90bd5b4 | |||
| bc382e6f31 | |||
| 116d71bb9c | |||
| 2aadb78301 | |||
| 22801846e4 | |||
| f9f6886250 | |||
| 30c8d4db02 | |||
| 08a5ea100e | |||
| db33ed0d0d | |||
| fd424b0204 | |||
| c1bf0a628f | |||
| 086bf75314 | |||
| af31fdc562 | |||
| 99e0a2e0a2 | |||
| 069d0db00e | |||
| 68f1343c93 | |||
| fbe25c9099 | |||
| 9b893bcb42 | |||
| 5182d4cd8d | |||
| cce077de96 | |||
| 03092a0ec1 | |||
| 4906ecc06b | |||
| 7a831ce646 | |||
| 290a26c4e2 | |||
| 571080b1e5 | |||
| d9567138ee | |||
| 4a31f74dcb | |||
| 79cf96b161 | |||
| 7fdd4dbf04 | |||
| 8eeeb5de60 | |||
| 150109cef4 | |||
| 7e7cc38d6d | |||
| b3df30680e | |||
| f73048d3a5 | |||
| c540c14e1c | |||
| f216bf4097 | |||
| a42bcbd160 | |||
| bc6786b21e | |||
| 47e357143c | |||
| 74eb8ad0e8 | |||
| d2fc927ffb | |||
| db4b049591 | |||
| 86db2b5688 | |||
| baa1c9f9e5 | |||
| be3a3e82af | |||
| 120ea26455 | |||
| fc476ca889 | |||
| fc36ce6a03 | |||
| 0a1d330c2f | |||
| e5ec42576c | |||
| b57f74a411 | |||
| cb3884ea2e | |||
| 0cb3184e35 | |||
| 76c5009fbc | |||
| 312ec153f1 | |||
| 1ef79df4c5 | |||
| d7a809f8a6 | |||
| ebadeeb873 | |||
| 55155ee8ae | |||
| be98a6b6d6 | |||
| e2f396f8c7 | |||
| 2a8024368a | |||
| 0cd025b42d | |||
| d6adda78c4 | |||
| 99708d7801 | |||
| 584986a9d5 | |||
| 507da05841 | |||
| 72a8fe4764 | |||
| f9f23f6324 | |||
| e2964f93c7 | |||
| cb2a9a3ca0 | |||
| ea68941bb9 | |||
| b598d331d4 | |||
| d7b876bfed | |||
| 6aca60080a | |||
| 59d76688b9 | |||
| 8e5e677228 | |||
| dd1bfcaddd | |||
| f4c66e1ab7 | |||
| 6ebdf6e42a | |||
| 4c01dd442b | |||
| ac226e3301 | |||
| 569f1b8cf1 | |||
| 46f5da88a6 | |||
| f5b876b018 | |||
| c5df856023 | |||
| 4f775847dc | |||
| f53710fdf5 | |||
| 80c56def08 | |||
| a1d88d999e | |||
| fbc6f6adaa | |||
| e7c55b2467 | |||
| 2c45bb1da9 | |||
| 173825dc74 | |||
| a63e28809b | |||
| 0eb8cb6e66 | |||
| e1514e2b25 | |||
| bef0da821b | |||
| 54741729f3 | |||
| e2640c22f8 | |||
| 2f16d5ba99 | |||
| 1d5301f887 | |||
| cc09c702f7 | |||
| 6b2a88766e | |||
| 0eb629fe67 | |||
| 44c398c9fb | |||
| 96122c5919 | |||
| bd4c438036 | |||
| adc8648f5a | |||
| 8eb3922a33 | |||
| 30743bb015 | |||
| 853f51ad0e | |||
| 763aad99ee | |||
| 2ed2317b02 | |||
| 0663f3eb2b | |||
| 11a2eeae7a | |||
| 8c2619bb22 | |||
| 46320fe07d | |||
| 1ee933b115 | |||
| 1d9152ca1a | |||
| 81ebf21bb9 | |||
| dd4bf98f28 | |||
| ba3a06da43 | |||
| 6fe096383c | |||
| d16f99958f | |||
| 5fa3aa42dc | |||
| f4f28a2daa | |||
| 3c1cd14bdc | |||
| 6dcc251312 | |||
| f692aa4bff | |||
| 7cde55ebe0 | |||
| 94eba08af4 | |||
| 2bf207661c | |||
| 2fff7f60c2 | |||
| 1b3c93ebb5 | |||
| 5b529b044e | |||
| 91f417f479 | |||
| fc0bf595bd | |||
| c0f6499577 | |||
| b9bef384f1 | |||
| 58bfae2fdb | |||
| d2c2fff884 | |||
| ab178c47b5 | |||
| 40be004376 | |||
| 0b501c9ce1 | |||
| cff82379f5 | |||
| 825503bd58 | |||
| 77a715a4bd | |||
| 78b233c506 | |||
| a6e799bb9a | |||
| b65992099a | |||
| 3d1a5f52bf | |||
| 82d0d5745c | |||
| fe7968cb32 | |||
| 94c503af74 | |||
| bdd9815ffc | |||
| 693acceca8 | |||
| 90971bc299 | |||
| e5b3613348 | |||
| 652ca8f69c | |||
| f15e351c1e | |||
| 47c81c3dac | |||
| 7453a61e4f | |||
| 911f6397e8 | |||
| 8d21a4f774 | |||
| 991c54b680 | |||
| 48dcb5089b | |||
| 461efe7101 | |||
| 0735161a20 | |||
| 093c1e2b15 | |||
| 7d0e02c899 | |||
| 8294913f04 | |||
| e811c4f90b | |||
| 69e248b389 | |||
| 43f6bfc4c2 | |||
| c8b81ab56a | |||
| aa41fd98e8 | |||
| ff6c4c2de9 | |||
| 136761d2f7 | |||
| 9374fc5264 | |||
| 324aaa5056 | |||
| f22afbd819 | |||
| e149231cb2 | |||
| 9c8155ddf8 | |||
| dd95419a36 | |||
| 870b10dd13 | |||
| 637b426649 | |||
| ee8ce87e28 | |||
| a944a7f730 | |||
| fdbb16b45f | |||
| 29bc098dcf | |||
| b2433cf13a | |||
| e908e23bb2 | |||
| 1a4dc827b5 | |||
| 9fd5e65fa2 | |||
| 5405dcd30e | |||
| 5c35f7fe5d | |||
| fc907a398f | |||
| 1b8dc6eba0 | |||
| c17a36c866 | |||
| a29cf832f1 | |||
| c9a628a5e9 | |||
| 165ba01afd | |||
| a39a8c2cce | |||
| 4002f138bb | |||
| 48fdb38902 | |||
| 38a3120ea1 | |||
| c0e370dfd2 | |||
| e4765089fa | |||
| 721e73ca1e | |||
| e406c90027 | |||
| e7c4886219 | |||
| 80a2e4f336 | |||
| 9f5940c6f6 | |||
| d01fda44b3 | |||
| 2020033bc0 | |||
| dc2c8e590c | |||
| b115db51e9 | |||
| 7d194c7078 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,5 +7,6 @@ local.properties
|
||||
captures/
|
||||
build/
|
||||
release-app/
|
||||
test-app/
|
||||
scripts/apk-channel/
|
||||
app/src/test/java/com/gh/gamecenter
|
||||
44
.gitlab-ci.yml
Normal file
44
.gitlab-ci.yml
Normal file
@ -0,0 +1,44 @@
|
||||
stages:
|
||||
- analysis
|
||||
- sendmail
|
||||
|
||||
## 代码检查
|
||||
sonarqube_analysis:
|
||||
tags:
|
||||
- offline-test
|
||||
stage: analysis
|
||||
image: sonarsource/sonar-scanner-cli:latest
|
||||
dependencies: [] #禁止传递来的artifact
|
||||
script:
|
||||
## 获取项目的一级组和二级组和项目名作为projectKey,例如projectKey=platform-backend-eci-monitor
|
||||
- group=`echo $CI_PROJECT_PATH | sed 's#/#-#g'`
|
||||
- sonar-scanner
|
||||
-Dsonar.host.url=http://sonarqube-server.sonarqube:9000/
|
||||
-Dsonar.login=be43de7264ce4c4766eb0c020373c3e74e6df257
|
||||
-Dsonar.jacoco.reportPaths=target/jacoco.exec
|
||||
-Dsonar.projectKey=$group
|
||||
-Dsonar.projectName=$CI_PROJECT_PATH
|
||||
-Dsonar.sourceEncoding=UTF-8
|
||||
-Dsonar.exclusions=**/vendor/**,**/errcode/**
|
||||
-Dsonar.gitlab.project_id=$CI_PROJECT_ID
|
||||
-Dsonar.gitlab.commit_sha=$CI_COMMIT_SHA
|
||||
-Dsonar.gitlab.ref_name=$CI_COMMIT_REF_NAME
|
||||
-Dsonar.gitlab.ci_merge_request_iid=$CI_MERGE_REQUEST_IID
|
||||
-Dsonar.gitlab.merge_request_discussion=true
|
||||
-Dsonar.java.binaries=. # 如果不使用Maven或Gradle进行分析,则必须手动提供测试二进制文件
|
||||
only:
|
||||
- dev
|
||||
|
||||
## 发送简易检测结果报告
|
||||
send_sonar_report:
|
||||
tags:
|
||||
- offline-test
|
||||
stage: sendmail
|
||||
image: hub.shanqu.cc/library/docker:latest
|
||||
dependencies: [] #禁止传递来的artifact
|
||||
script:
|
||||
- group=`echo $CI_PROJECT_PATH | sed 's#/#-#g'`
|
||||
- docker login -u "${HARBOR_REGISTRY_USERNAME}" -p "${HARBOR_REGISTRY_PASSWORD}" "${HARBOR_REGISTRY}"
|
||||
- docker run -e PROJECTKEY=$group -e EMAIL=$GITLAB_USER_EMAIL --name send-email --rm hub.shanqu.cc/platform/send-sonar-report:latest
|
||||
only:
|
||||
- dev
|
||||
5
.gitmodules
vendored
5
.gitmodules
vendored
@ -1,4 +1,7 @@
|
||||
[submodule "libraries/LGLibrary"]
|
||||
path = libraries/LGLibrary
|
||||
url = git@gitlab.ghzs.com:android/common-library.git
|
||||
url = git@git.shanqu.cc:android/common-library.git
|
||||
branch = master
|
||||
[submodule "assistant_flutter"]
|
||||
path = assistant_flutter
|
||||
url = git@git.shanqu.cc:halo/android/flutter-module.git
|
||||
|
||||
94
README.md
94
README.md
@ -1,69 +1,65 @@
|
||||
# 光环助手Android客户端
|
||||
|
||||
### APK打包配置
|
||||
### 概述
|
||||
|
||||
* 使用[ApkChannelPackage](https://github.com/ltlovezh/ApkChannelPackage)的方案
|
||||
* 打包命令,视情况使用:
|
||||
光环助手Android客户端目前使用 Kotlin 作为主要开发语言,以 MVVM 作为参考架构模式进行开发
|
||||
|
||||
> 打包Tinker基准包:`./scripts/tinker_release_base.sh`
|
||||
### 约束
|
||||
|
||||
> 以Tinker基准包打渠道包:`./scripts/tinker_release_channel.sh`
|
||||
为编写易读易维护且较健壮的代码,可参考以下约束
|
||||
|
||||
> 以Tinker基准包打补丁包:`./scripts/tinker_release_patch.sh`
|
||||
1. 尽量将逻辑代码放置于 ViewModel 中,View 中只执行 UI 操作
|
||||
2. 尽量使 View 在被销毁之后仍能恢复状态,处理方式可参考 [保存界面状态](https://developer.android.com/topic/libraries/architecture/saving-states)
|
||||
3. 尽量参考原有文件结构及命名规范,即以 大模块 - 小模块 的形式生成包关系
|
||||
4. 遵循最小改动原则,在提交代码前务必先检查变动的代码,尽量以可控的变动规模来构成一个 commit ,以便日后追踪问题
|
||||
5. 代码规范可参考 [AOSP Java 风格](https://source.android.com/setup/contribute/code-style)
|
||||
6. 尽量使用 Kotlin 来写新文件
|
||||
7. 尽量不要使用 DataBinding
|
||||
8. Commit 前请确保不带入非项目必须文件,可手动修改 [.gitignore](https://stackoverflow.com/questions/8527597/how-do-i-ignore-files-in-a-directory-in-git) 文件忽略
|
||||
9. 优先使用 ViewBinding 获取 View 对象
|
||||
10. No AsyncTask!
|
||||
|
||||
### 混淆配置
|
||||
### 公用部分
|
||||
|
||||
* 配置文件:Android默认配置+proguard-rules.txt等
|
||||
* 参考libraries下每个项目独立的配置文件`proguard-project.txt`
|
||||
本项目使用 LiveData 实现了一个简单通用的基础列表分页功能,具体可见 `ListFragment`, `ListViewModel` 等类,理想情况下只需少量代码即可新建一个简单分页列表
|
||||
|
||||
### apk大小优化
|
||||
### 首次拉取项目代码
|
||||
|
||||
* 限制resConfig资源集
|
||||
* 开启ShrinkResources
|
||||
* 开启混淆,使用minifyEnabled(仅在release开启)
|
||||
* pngquant对png压缩、png/jpg->webp(未尝试)
|
||||
`git clone -b dev git@git.shanqu.cc:halo/android/assistant-android.git --recursive`
|
||||
|
||||
### git 版本管理
|
||||
|
||||
本项目使用简化版的 git flow 来管理分支,细节请看 [光环安卓简单 git 规范](https://git.ghzs.com/halo/android/assistant-android/-/wikis/%E5%85%89%E7%8E%AF%E5%AE%89%E5%8D%93%E7%AE%80%E5%8D%95-git-%E8%A7%84%E8%8C%83)
|
||||
|
||||
### API 环境配置
|
||||
|
||||
本项目使用 Build Variants 来切换 API 环境
|
||||
|
||||
* internal 为测试环境
|
||||
* publish 为正式环境
|
||||
|
||||
### 图片资源配置
|
||||
|
||||
* 新增图片资源时,默认只添加最高规格的 xxxhdpi 文件
|
||||
* 新增图片资源时,需要将其转换为 .webp 格式 (包括含透明图层的图片,默认质量为90%) (转换后体积变大的文件除外)
|
||||
|
||||
### 第三方appkey等配置
|
||||
|
||||
* 修改`gradle.properties`文件将各种key填入其中,实现统一管理
|
||||
* 通过gradle文件内的resValue/buildConfigField/manifestPlaceHolder方式实现编译期间修改,具体情况请参考``./build.gradle``和``./app/build.gradle``配置
|
||||
* 修改 `gradle.properties` 文件将各种key填入其中,实现统一管理
|
||||
* 通过 gradle 文件内的 resValue/buildConfigField/manifestPlaceHolder 方式实现编译期间修改,具体情况请参考 ``./build.gradle`` 和 ``./app/build.gradle`` 配置
|
||||
|
||||
### 拉取代码步骤
|
||||
### 混淆配置
|
||||
|
||||
1. 拉取主项目代码: `git clone -b dev git@gitlab.ghzhushou.com:halo/assistant-android.git`
|
||||
2. 初始化公用类库: `bash ./scripts/submodules_init.sh`
|
||||
* 本项目使用了微信的 [AndResGuard](https://github.com/shwenzhang/AndResGuard) 作为资源混淆压缩方案,新增需要使用 `getIdentifier` 获取的资源文件时需要添加至白名单
|
||||
* 本项目默认使用 R8 作为混淆工具,往 proguard-rules.txt 添加 proguard 新配置项时请检查可用性(如语法等)
|
||||
|
||||
### submodule管理方式(只拉取master)
|
||||
### APK打包配置
|
||||
|
||||
* 提交代码,需要cd到submodule文件夹去做修改
|
||||
* 更新远端代码,`bash ./scripts/submodules_update.sh`
|
||||
> 打内部测试包:`./scripts/test_build.sh`
|
||||
> 打邮件测试包:`./scripts/jenkins_build.sh`
|
||||
|
||||
### TODO
|
||||
|
||||
* GSON 序列化用统一的一个, GsonUtil fromJson
|
||||
* CleanApkAdapter 转化字符串size工具函数 比如SpeedUtils
|
||||
* getString 解决 字符串hardcode问题
|
||||
* ~~Adapter 里面clicklistener 用接口传参将点击操作委托给controller~~
|
||||
* ~~Adapter ViewHolder的功能,部分重写到ViewHolder类本身~~
|
||||
|
||||
* ~~activity 统一入口未完成(外部入口相关),去除多余activity使用,统一toolbar~~
|
||||
* ~~release / debug compile不同的类库,不需要再做什么开关~~
|
||||
|
||||
* ~~Toolbar分离,有图形按钮/没有图形按钮~~
|
||||
|
||||
### TODO Since 3.1
|
||||
|
||||
- 解决 Utils 工具类引发的内存泄漏问题
|
||||
- 把原有 EventBus 的消息 Type 统一到一个文件内
|
||||
- 将实现细节从 View(Fragment、Activity) 剥离并以 MVVM 结构改造
|
||||
- ~~将 ListViewModel 所对应的 ListRepository 合并到 ListViewModel 中~~
|
||||
- 依照光环助手界面功能以大模块 - 小模块的方式去修改包结构,包内文件建议以包名摘要作为前缀
|
||||
- ~~使用 RxJava 的 Debounce 和 Map 操作优化搜索触发机制 参考资料:[1](https://proandroiddev.com/building-an-autocompleting-edittext-using-rxjava-f69c5c3f5a40),[2](https://medium.com/@kurtisnusbaum/rxandroid-basics-part-2-6e877af352)~~
|
||||
|
||||
- ~~把 ListViewModel 的数据结构类型转换方式换为抽象方法,让继承的类实现,避免出现无响应的问题~~
|
||||
|
||||
- ~~rxjava2 如果接口返回为空 会发生异常:java.lang.NullPointerException: Null is not a valid element (答案编辑) 解决方法->com.gh.gamecenter.retrofit.Response~~
|
||||
- constraintLayout 1.1.2 导致布局出现异常(问题编辑标签选择弹窗)
|
||||
|
||||
- 搞清楚 GameManager 的用途,看能不能去掉
|
||||
- 重构一下 MainActivity
|
||||
* 把原有 EventBus 的消息 Type 统一到一个文件内
|
||||
* 将实现细节从 View(Fragment、Activity) 剥离并以 MVVM 结构改造
|
||||
* 重构 MainActivity
|
||||
|
||||
385
app/build.gradle
385
app/build.gradle
@ -1,27 +1,17 @@
|
||||
// This comment exists for a reason, do not delete
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android' // kotlin
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
// apkChannelPackage
|
||||
apply plugin: 'channel'
|
||||
apply plugin: 'AndResGuard'
|
||||
|
||||
import groovy.xml.XmlUtil
|
||||
|
||||
//apply from: 'tinker-support.gradle'
|
||||
|
||||
android {
|
||||
|
||||
androidExtensions {
|
||||
experimental = true
|
||||
}
|
||||
|
||||
dataBinding {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
viewBinding {
|
||||
enabled = true
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
dataBinding true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
@ -36,10 +26,25 @@ android {
|
||||
dexOptions {
|
||||
// jumboMode = true
|
||||
javaMaxHeapSize "4g"
|
||||
preDexLibraries true
|
||||
maxProcessCount 8
|
||||
}
|
||||
|
||||
aaptOptions {
|
||||
additionalParameters "--no-version-vectors"
|
||||
}
|
||||
|
||||
kapt {
|
||||
useBuildCache = true
|
||||
javacOptions {
|
||||
// 增加注解处理器的最大错误次数,默认为 100
|
||||
option("-Xmaxerrs", 500)
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
multiDexEnabled true
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
@ -48,7 +53,12 @@ android {
|
||||
}
|
||||
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a"
|
||||
// 如果不添加 `arm64` 调用系统的 PackageManager 的方法读取安装包信息的时候会出现 native 层闪退,草
|
||||
// 添加了 `arm64` 以后部分 5.0 的设备会报用错 so 的问题,
|
||||
// couldn't find DSO to load: libimagepipeline.so caused by: dlopen failed: "/data/data/com.gh.gamecenter/lib-main/libimagepipeline.so" is 64-bit instead of 32-bit result: 0
|
||||
// 以 OPPO R7PLUS 为例,明明设备是骁龙 615,ARMv8-64 bit 的设备却不支持 arm64 的 abi,限制了只使用 java 后还是报错,只有 5.0,5.1 设备无法复现 : (
|
||||
// 惊了
|
||||
abiFilters "armeabi-v7a", "arm64-v8a", "x86"
|
||||
}
|
||||
|
||||
renderscriptTargetApi 18
|
||||
@ -68,29 +78,21 @@ android {
|
||||
/**
|
||||
* All third-party appid/appkey
|
||||
*/
|
||||
buildConfigField "String", "API_HOST", "\"${API_HOST}\""
|
||||
buildConfigField "String", "NEW_API_HOST", "\"${NEW_API_HOST}\""
|
||||
buildConfigField "String", "WECHAT_APPID", "\"${WECHAT_APPID}\""
|
||||
buildConfigField "String", "WECHAT_SECRET", "\"${WECHAT_SECRET}\""
|
||||
buildConfigField "String", "TENCENT_APPID", "\"${TENCENT_APPID}\""
|
||||
buildConfigField "String", "WEIBO_APPKEY", "\"${WEIBO_APPKEY}\""
|
||||
buildConfigField "String", "MTA_APPKEY", "\"${MTA_APPKEY}\""
|
||||
buildConfigField "String", "TD_APPID", "\"${TD_APPID}\""
|
||||
buildConfigField "String", "LETO_APPID", "\"${LETO_APPID}\""
|
||||
buildConfigField "String", "TTAD_APPID", "\"${TTAD_APPID}\""
|
||||
buildConfigField "String", "DOUYIN_CLIENTKEY", "\"${DOUYIN_CLIENTKEY}\""
|
||||
buildConfigField "String", "DOUYIN_CLIENTSECRET", "\"${DOUYIN_CLIENTSECRET}\""
|
||||
|
||||
buildConfigField "String", "MIPUSH_APPID", "\"${MIPUSH_APPID}\""
|
||||
buildConfigField "String", "MIPUSH_APPKEY", "\"${MIPUSH_APPKEY}\""
|
||||
buildConfigField "String", "MEIZUPUSH_APPID", "\"${MEIZUPUSH_APPID}\""
|
||||
buildConfigField "String", "MEIZUPUSH_APPKEY", "\"${MEIZUPUSH_APPKEY}\""
|
||||
|
||||
resValue "string", "huawei_push_appid", "appid=${HUAWEI_PUSH_APPID}"
|
||||
buildConfigField "String", "QUICK_LOGIN_APPID", "\"${QUICK_LOGIN_APPID}\""
|
||||
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\""
|
||||
|
||||
/**
|
||||
* Build Time 供区分 jenkins 打包时间用
|
||||
* IS_NIGHT_MODE_ON 供区分夜间模式功能是否启用
|
||||
*/
|
||||
buildConfigField "long", "BUILD_TIME", "0"
|
||||
|
||||
buildConfigField "boolean", "IS_NIGHT_MODE_ON", "true"
|
||||
}
|
||||
|
||||
// gradle 2.2以上默认同时启用v1和v2(优先用于Android N)
|
||||
@ -131,6 +133,15 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore useless variant
|
||||
variantFilter { variant ->
|
||||
def names = variant.flavors*.name
|
||||
def isDebugType = variant.buildType.name == "debug"
|
||||
if ((names.contains("tea")) && isDebugType) {
|
||||
setIgnore(true)
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions("env")
|
||||
|
||||
sourceSets {
|
||||
@ -143,78 +154,40 @@ android {
|
||||
tea {
|
||||
java.srcDirs = ['src/main/java', 'src/tea/java']
|
||||
}
|
||||
gdt {
|
||||
java.srcDirs = ['src/main/java', 'src/gdt/java']
|
||||
}
|
||||
}
|
||||
|
||||
productFlavors {
|
||||
// publish release host
|
||||
publish {
|
||||
dimension "env"
|
||||
buildConfigField "String", "API_HOST", "\"${API_HOST}\""
|
||||
|
||||
buildConfigField "String", "SENSITIVE_API_HOST", "\"${SENSITIVE_API_HOST}\""
|
||||
|
||||
buildConfigField "String", "UMENG_APPKEY", "\"${UMENG_APPKEY}\""
|
||||
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${UMENG_MESSAGE_SECRET}\""
|
||||
buildConfigField "String", "BUGLY_APPID", "\"${BUGLY_APPID}\""
|
||||
}
|
||||
// internal test dev host
|
||||
internal {
|
||||
dimension "env"
|
||||
versionNameSuffix "-debug"
|
||||
|
||||
buildConfigField "String", "API_HOST", "\"${DEV_API_HOST}\""
|
||||
buildConfigField "String", "DEV_API_HOST", "\"${DEV_API_HOST}\""
|
||||
buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_DEV_API_HOST}\""
|
||||
}
|
||||
|
||||
buildConfigField "String", "SENSITIVE_API_HOST", "\"${DEV_API_HOST}\""
|
||||
// publish release host˛
|
||||
publish {
|
||||
dimension "env"
|
||||
|
||||
buildConfigField "String", "UMENG_APPKEY", "\"${DEV_UMENG_APPKEY}\""
|
||||
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${DEV_UMENG_MESSAGE_SECRET}\""
|
||||
buildConfigField "String", "BUGLY_APPID", "\"${DEV_BUGLY_APPID}\""
|
||||
buildConfigField "String", "DEV_API_HOST", "\"${API_HOST}\""
|
||||
buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_API_HOST}\""
|
||||
}
|
||||
|
||||
tea {
|
||||
dimension "env"
|
||||
|
||||
buildConfigField "String", "API_HOST", "\"${API_HOST}\""
|
||||
buildConfigField "String", "DEV_API_HOST", "\"${API_HOST}\""
|
||||
buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_API_HOST}\""
|
||||
|
||||
buildConfigField "String", "SENSITIVE_API_HOST", "\"${SENSITIVE_API_HOST}\""
|
||||
|
||||
buildConfigField "String", "UMENG_APPKEY", "\"${UMENG_APPKEY}\""
|
||||
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${UMENG_MESSAGE_SECRET}\""
|
||||
buildConfigField "String", "BUGLY_APPID", "\"${BUGLY_APPID}\""
|
||||
}
|
||||
|
||||
gdt {
|
||||
dimension "env"
|
||||
|
||||
buildConfigField "String", "API_HOST", "\"${API_HOST}\""
|
||||
|
||||
buildConfigField "String", "SENSITIVE_API_HOST", "\"${SENSITIVE_API_HOST}\""
|
||||
|
||||
buildConfigField "String", "UMENG_APPKEY", "\"${UMENG_APPKEY}\""
|
||||
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${UMENG_MESSAGE_SECRET}\""
|
||||
buildConfigField "String", "BUGLY_APPID", "\"${BUGLY_APPID}\""
|
||||
manifestPlaceholders.put("APPLOG_SCHEME", "rangersapplog.byAx6uYt".toLowerCase())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apkChannelPackage
|
||||
channel {
|
||||
//多渠道包的输出目录,默认为new File(project.buildDir,"channel")
|
||||
baseOutputDir = new File(project.buildDir, "channel")
|
||||
//多渠道包的命名规则,默认为:${appName}-${versionName}-${versionCode}-${flavorName}-${buildType}
|
||||
apkNameFormat = '${appName}-${versionName}-${versionCode}-${flavorName}-${buildType}'
|
||||
}
|
||||
|
||||
rebuildChannel {
|
||||
// baseDebugApk = 已有Debug APK
|
||||
// baseReleaseApk = 已有Release APK
|
||||
// //默认为new File(project.buildDir, "rebuildChannel/debug")
|
||||
// debugOutputDir = Debug渠道包输出目录
|
||||
// //默认为new File(project.buildDir, "rebuildChannel/release")
|
||||
// releaseOutputDir = Release渠道包输出目录
|
||||
lintOptions {
|
||||
// For flutter release build, see https://github.com/flutter/flutter/issues/58247
|
||||
checkReleaseBuilds false
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
@ -226,19 +199,17 @@ repositories {
|
||||
dependencies {
|
||||
|
||||
implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
|
||||
gdtImplementation fileTree(include: ['*.jar', '*.aar'], dir: 'src/gdt/libs')
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
|
||||
debugImplementation "com.squareup.leakcanary:leakcanary-android:${leakcanary}"
|
||||
debugImplementation "com.facebook.stetho:stetho:${stetho}"
|
||||
debugImplementation "com.facebook.stetho:stetho-okhttp3:${stetho}"
|
||||
debugImplementation "com.squareup.okhttp3:logging-interceptor:${okHttp}"
|
||||
debugImplementation "com.gu.android:toolargetool:${toolargetool}"
|
||||
debugImplementation "com.github.nichbar:WhatTheStack:$whatTheStack"
|
||||
// debugImplementation "com.gu.android:toolargetool:${toolargetool}" // 需要使用调试时才启用
|
||||
debugImplementation "com.github.nichbar:WhatTheStack:${whatTheStack}"
|
||||
debugImplementation "io.github.didi.dokit:dokitx:${dokit}"
|
||||
|
||||
implementation "androidx.core:core:${core}"
|
||||
implementation "androidx.fragment:fragment:${fragment}"
|
||||
implementation "androidx.core:core-ktx:${core}"
|
||||
implementation "androidx.fragment:fragment-ktx:${fragment}"
|
||||
implementation "androidx.multidex:multidex:${multiDex}"
|
||||
implementation "androidx.appcompat:appcompat:${appCompat}"
|
||||
implementation "androidx.cardview:cardview:${cardView}"
|
||||
@ -248,10 +219,12 @@ dependencies {
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifeCycle"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifeCycle"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifeCycle"
|
||||
implementation "androidx.lifecycle:lifecycle-extensions:$lifeCycleExtensions"
|
||||
implementation "androidx.room:room-runtime:${room}"
|
||||
implementation "androidx.room:room-rxjava2:${room}"
|
||||
implementation "androidx.core:core-ktx:${ktx}"
|
||||
implementation "androidx.viewpager2:viewpager2:${viewpager2}"
|
||||
implementation "androidx.webkit:webkit:${webkit}"
|
||||
kapt "androidx.room:room-compiler:${room}"
|
||||
|
||||
implementation "com.google.android.material:material:${material}"
|
||||
@ -259,8 +232,10 @@ dependencies {
|
||||
implementation "com.kyleduo.switchbutton:library:${switchButton}"
|
||||
|
||||
implementation "com.facebook.fresco:fresco:${fresco}"
|
||||
implementation "com.facebook.fresco:animated-gif:${fresco}"
|
||||
implementation "com.facebook.fresco:animated-gif-lite:${fresco}"
|
||||
implementation "com.facebook.fresco:animated-drawable:${fresco}"
|
||||
implementation "com.facebook.fresco:animated-webp:${fresco}"
|
||||
implementation "com.facebook.fresco:webpsupport:${fresco}"
|
||||
|
||||
implementation "com.squareup.okhttp3:okhttp:${okHttp}"
|
||||
|
||||
@ -273,8 +248,6 @@ dependencies {
|
||||
implementation "com.j256.ormlite:ormlite-android:${ormlite}"
|
||||
implementation "com.j256.ormlite:ormlite-core:${ormlite}"
|
||||
|
||||
implementation "com.jakewharton:butterknife:${butterKnife}"
|
||||
kapt "com.jakewharton:butterknife-compiler:${butterKnife}"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}"
|
||||
|
||||
implementation "org.greenrobot:eventbus:${eventbus}"
|
||||
@ -289,11 +262,6 @@ dependencies {
|
||||
|
||||
implementation "com.daimajia.swipelayout:library:${swipeLayout}"
|
||||
|
||||
implementation "com.sina.weibo.sdk:core:${weiboSDK}"
|
||||
|
||||
// bugly with tinker support
|
||||
// implementation "com.tencent.bugly:crashreport_upgrade:${buglyTinkerSupport}"
|
||||
|
||||
implementation "com.google.android:flexbox:${flexbox}"
|
||||
|
||||
implementation "pub.devrel:easypermissions:${easypermissions}"
|
||||
@ -313,51 +281,64 @@ dependencies {
|
||||
implementation "com.squareup.picasso:picasso:${picasso}"
|
||||
|
||||
// for video streaming
|
||||
implementation("com.shuyu:gsyVideoPlayer-java:$gsyVideo", {
|
||||
implementation("com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-java:$gsyVideo", {
|
||||
exclude module: "gsyvideoplayer-androidvideocache"
|
||||
exclude group: "tv.danmaku.ijk.media"
|
||||
})
|
||||
implementation "com.shuyu:GSYVideoPlayer-exo2:$gsyVideo"
|
||||
implementation "com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-exo_player2:$gsyVideo"
|
||||
|
||||
implementation "android.arch.work:work-runtime:${workManager}"
|
||||
implementation "androidx.work:work-runtime:${workManager}"
|
||||
|
||||
implementation "com.llew.huawei:verifier:${verifier}"
|
||||
|
||||
implementation "com.github.tbruyelle:rxpermissions:${rxPermissions}"
|
||||
|
||||
implementation "com.ethanhua:skeleton:${skeleton}"
|
||||
implementation "io.supercharge:shimmerlayout:${shimmerlayout}"
|
||||
implementation "com.lg:skeleton:${skeleton}"
|
||||
implementation "com.tencent.mm.opensdk:wechat-sdk-android-without-mta:${mta}"
|
||||
implementation "com.walkud.rom.checker:RomChecker:${romChecker}"
|
||||
implementation "com.github.nichbar:AndroidRomChecker:${romChecker}"
|
||||
|
||||
debugImplementation "com.github.nichbar.chucker:library:$chucker"
|
||||
releaseImplementation "com.github.nichbar.chucker:library-no-op:$chucker"
|
||||
teaImplementation "com.bytedance.applog:RangersAppLog-Lite-cn:$bytedanceApplog"
|
||||
// implementation "com.bytedance.ies.ugc.aweme:opensdk-china-external:$bytedanceAweme"
|
||||
// implementation "com.bytedance.ies.ugc.aweme:opensdk-common:$bytedanceAweme"
|
||||
debugImplementation "com.github.nichbar.chucker:library:${chucker}"
|
||||
releaseImplementation "com.github.nichbar.chucker:library-no-op:${chucker}"
|
||||
teaImplementation "com.bytedance.applog:RangersAppLog-Lite-cn:${bytedanceApplog}"
|
||||
|
||||
implementation "com.aliyun.dpa:oss-android-sdk:${oss}"
|
||||
|
||||
implementation "com.airbnb.android:lottie:$lottie"
|
||||
implementation "com.airbnb.android:lottie:${lottie}"
|
||||
|
||||
implementation "net.lingala.zip4j:zip4j:${zip4j}"
|
||||
|
||||
implementation "io.sentry:sentry-android:$sentry"
|
||||
implementation "io.sentry:sentry-android:4.3.0"
|
||||
|
||||
implementation("com.github.piasy:BigImageViewer:$bigImageViewer", {
|
||||
implementation("com.github.piasy:BigImageViewer:${bigImageViewer}", {
|
||||
exclude group: 'com.squareup.okhttp3'
|
||||
exclude group: 'androidx.swiperefreshlayout'
|
||||
exclude group: 'com.github.bumptech.glide'
|
||||
exclude group: 'com.facebook.fresco'
|
||||
})
|
||||
implementation "com.github.PhilJay:MPAndroidChart:${chart}"
|
||||
|
||||
implementation "com.lahm.library:easy-protector-release:${easyProtector}"
|
||||
|
||||
implementation "com.github.hsiafan:apk-parser:${apkParser}"
|
||||
implementation "org.nanohttpd:nanohttpd:${nanohttpd}"
|
||||
|
||||
implementation "com.aliyun.openservices:aliyun-log-android-sdk:${aliyunLog}"
|
||||
implementation "com.lg:easyfloat:${easyFloat}"
|
||||
|
||||
implementation "io.github.florent37:shapeofview:${shapeOfView}"
|
||||
|
||||
implementation "io.github.sinaweibosdk:core:${weiboSDK}"
|
||||
|
||||
implementation "com.lg:apksig:${apksig}"
|
||||
|
||||
implementation "com.lg:gid:${gid}"
|
||||
|
||||
implementation "com.louiscad.splitties:splitties-fun-pack-android-base-with-views-dsl:${splitties}"
|
||||
|
||||
compileOnly "com.github.axen1314.lancet:lancet-base:${lancet_version}"
|
||||
|
||||
implementation project(':libraries:LGLibrary')
|
||||
// implementation project(':libraries:MTA')
|
||||
implementation project(':libraries:QQShare')
|
||||
// implementation project(':libraries:TalkingData')
|
||||
// implementation project(':libraries:UmengPush')
|
||||
// implementation project(':libraries:WechatShare')
|
||||
// implementation project(':libraries:im')
|
||||
implementation project(':libraries:Matisse')
|
||||
}
|
||||
File propFile = file('sign.properties')
|
||||
@ -417,6 +398,120 @@ if (propFile.exists()) {
|
||||
// }.each { t -> t.dependsOn generateMetaJson }
|
||||
//}
|
||||
|
||||
andResGuard {
|
||||
mappingFile = null
|
||||
use7zip = true
|
||||
useSign = true
|
||||
// 打开这个开关,会keep住所有资源的原始路径,只混淆资源的名字
|
||||
keepRoot = false
|
||||
// 设置这个值,会把arsc name列混淆成相同的名字,减少string常量池的大小
|
||||
fixedResName = "arg"
|
||||
// 打开这个开关会合并所有哈希值相同的资源,但请不要过度依赖这个功能去除去冗余资源
|
||||
mergeDuplicatedRes = true
|
||||
whiteList = [
|
||||
"R.drawable.icon",
|
||||
"R.drawable.ic_bar_back",
|
||||
"R.drawable.toolbar_search_icon",
|
||||
"R.drawable.bg_notification_answer_style_1",
|
||||
"R.drawable.bg_notification_answer_style_2",
|
||||
"R.drawable.bg_notification_article_style_1",
|
||||
"R.drawable.bg_notification_article_style_2",
|
||||
"R.drawable.bg_notification_feedback_style_1",
|
||||
"R.drawable.bg_notification_feedback_style_2",
|
||||
"R.drawable.bg_notification_gift_style_1",
|
||||
"R.drawable.bg_notification_gift_style_2",
|
||||
"R.drawable.bg_notification_login_style_1",
|
||||
"R.drawable.bg_notification_login_style_2",
|
||||
"R.drawable.bg_notification_question_style_1",
|
||||
"R.drawable.bg_notification_question_style_2",
|
||||
"R.drawable.bg_notification_rating_style_1",
|
||||
"R.drawable.bg_notification_rating_style_2",
|
||||
"R.drawable.bg_notification_reserve_game_style_1",
|
||||
"R.drawable.bg_notification_reserve_game_style_2",
|
||||
"R.drawable.bg_notification_video_style_1",
|
||||
"R.drawable.bg_notification_video_style_2",
|
||||
"R.drawable.ic_search_no_1",
|
||||
"R.drawable.ic_search_no_2",
|
||||
"R.drawable.ic_search_no_3",
|
||||
"R.drawable.ic_search_no_4",
|
||||
"R.drawable.ic_search_no_5",
|
||||
"R.drawable.ic_search_no_6",
|
||||
"R.drawable.ic_search_no_7",
|
||||
"R.drawable.ic_search_no_8",
|
||||
"R.drawable.ic_search_no_9",
|
||||
"R.drawable.ic_search_no_10",
|
||||
"R.drawable.ic_search_no_11",
|
||||
"R.drawable.ic_search_no_12",
|
||||
"R.drawable.ic_search_no_13",
|
||||
"R.drawable.ic_search_no_14",
|
||||
"R.drawable.ic_search_no_15",
|
||||
"R.drawable.ic_search_no_16",
|
||||
"R.drawable.ic_search_no_17",
|
||||
"R.drawable.ic_search_no_18",
|
||||
"R.drawable.ic_search_no_19",
|
||||
"R.drawable.ic_search_no_20",
|
||||
"R.drawable.ic_recommend_activity",
|
||||
"R.drawable.ic_recommend_discount",
|
||||
"R.drawable.ic_recommend_function",
|
||||
"R.drawable.ic_recommend_gift",
|
||||
"R.drawable.ic_recommend_role",
|
||||
"R.drawable.login_btn_bg",
|
||||
"R.drawable.ic_quick_login_check",
|
||||
"R.drawable.ic_quick_login_uncheck",
|
||||
"R.anim.anim_auth_in",
|
||||
"R.anim.anim_auth_out",
|
||||
"R.id.download_speed",
|
||||
"R.id.download_percentage",
|
||||
"R.id.comment",
|
||||
"R.id.vote",
|
||||
"R.id.watermark_hint",
|
||||
"R.id.watermark_sb",
|
||||
"R.id.bottomShareIv",
|
||||
"R.id.bottomShareTv",
|
||||
"R.id.recommendStarPref",
|
||||
"R.id.recommendStar",
|
||||
"R.drawable.help_search_delete",
|
||||
"R.drawable.suggest_type_normal",
|
||||
"R.drawable.suggest_type_crash",
|
||||
"R.drawable.suggest_type_game_question",
|
||||
"R.drawable.suggest_type_game_collect",
|
||||
"R.drawable.suggest_type_function_suggest",
|
||||
"R.drawable.suggest_type_article_collect",
|
||||
"R.drawable.suggest_type_copyright",
|
||||
"R.drawable.help_result_empty",
|
||||
"R.drawable.news_comment_detail_read",
|
||||
"R.drawable.news_comment_detail_comment",
|
||||
"R.drawable.news_comment_detail_share",
|
||||
"R.drawable.ic_libao",
|
||||
"R.drawable.ic_link",
|
||||
"R.drawable.concern_message_icon",
|
||||
"R.drawable.reuse_blank_hint",
|
||||
"R.drawable.ic_concern",
|
||||
"R.drawable.concern_down",
|
||||
"R.drawable.concern_up",
|
||||
"R.drawable.ic_libao_more",
|
||||
"R.drawable.ic_libao_delete",
|
||||
"R.drawable.ic_dialog_close",
|
||||
"R.drawable.occupy2",
|
||||
"R.drawable.kc_checkbox_unselect",
|
||||
"R.drawable.kc_checkbox_select",
|
||||
"R.drawable.ic_type_unselect",
|
||||
"R.drawable.ic_type_selected",
|
||||
"R.drawable.suggest_add_pic_icon",
|
||||
"R.drawable.icon_pic_add",
|
||||
"R.drawable.ask_search_input_delete",
|
||||
"R.drawable.suggest_pic_delete"
|
||||
]
|
||||
compressFilePattern = [
|
||||
"*.png",
|
||||
"*.jpg",
|
||||
"*.jpeg",
|
||||
"*.gif",
|
||||
]
|
||||
sevenzip {
|
||||
artifact = 'com.tencent.mm:SevenZip:1.2.20'
|
||||
}
|
||||
}
|
||||
|
||||
project.afterEvaluate {
|
||||
def variants = null
|
||||
@ -448,36 +543,46 @@ project.afterEvaluate {
|
||||
if (manifestFile == null || !manifestFile.exists()) {
|
||||
return
|
||||
}
|
||||
|
||||
String[] configChanges = [
|
||||
"density",
|
||||
"fontScale",
|
||||
"keyboard",
|
||||
"keyboardHidden",
|
||||
"layoutDirection",
|
||||
"locale",
|
||||
"mcc",
|
||||
"mnc",
|
||||
"navigation",
|
||||
"orientation",
|
||||
"screenLayout",
|
||||
"screenSize",
|
||||
"smallestScreenSize",
|
||||
"touchscreen",
|
||||
"uiMode"]
|
||||
|
||||
def parser = new XmlSlurper(false, true)
|
||||
def manifest = parser.parse(manifestFile)
|
||||
def app = manifest.'application'[0]
|
||||
app.'activity'.each { act ->
|
||||
String value = act.attributes()['android:configChanges']
|
||||
if (value == null || value.isEmpty()) {
|
||||
value = "keyboardHidden|orientation|screenSize|screenLayout|density|fontScale|locale"
|
||||
if (value == null) value = ""
|
||||
configChanges.eachWithIndex { config, index ->
|
||||
if (index != configChanges.length - 1) {
|
||||
value += config + "|"
|
||||
} else {
|
||||
value += config
|
||||
}
|
||||
}
|
||||
act.attributes()['androidconfigChanges'] = value
|
||||
} else {
|
||||
String[] valueSplit = value.split("\\|")
|
||||
if (!valueSplit.contains("keyboardHidden")) {
|
||||
value += "|keyboardHidden"
|
||||
}
|
||||
if (!valueSplit.contains("orientation")) {
|
||||
value += "|orientation"
|
||||
}
|
||||
if (!valueSplit.contains("screenSize")) {
|
||||
value += "|screenSize"
|
||||
}
|
||||
if (!valueSplit.contains("screenLayout")) {
|
||||
value += "|screenLayout"
|
||||
}
|
||||
if (!valueSplit.contains("density")) {
|
||||
value += "|density"
|
||||
}
|
||||
if (!valueSplit.contains("fontScale")) {
|
||||
value += "|fontScale"
|
||||
}
|
||||
if (!valueSplit.contains("locale")) {
|
||||
value += "|locale"
|
||||
println configChanges
|
||||
configChanges.eachWithIndex { config, index ->
|
||||
if (!valueSplit.contains(config)) {
|
||||
value += ("|" + config)
|
||||
}
|
||||
}
|
||||
act.attributes()['android:configChanges'] = value
|
||||
}
|
||||
@ -490,4 +595,4 @@ project.afterEvaluate {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
BIN
app/libs/quick_login_android_5.8.1.aar
Normal file
BIN
app/libs/quick_login_android_5.8.1.aar
Normal file
Binary file not shown.
266
app/proguard-rules-legacy.txt
Normal file
266
app/proguard-rules-legacy.txt
Normal file
@ -0,0 +1,266 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in C:\Android\sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
#--------- remove logs start ----------------
|
||||
-assumenosideeffects class com.lightgame.config.CommonDebug {
|
||||
private static String getLogTag(...);
|
||||
private static String getMethodName();
|
||||
public static void logMethodName(...);
|
||||
public static void logParams(...);
|
||||
public static void logFields(...);
|
||||
public static void logMethodWithParams(...);
|
||||
}
|
||||
#-assumenosideeffects class com.lightgame.config.CommonDebug {*;}
|
||||
|
||||
#-dontoptimize
|
||||
#--------- remove logs end ----------------
|
||||
|
||||
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod
|
||||
-dontwarn InnerClasses
|
||||
|
||||
# OrmLite uses reflection
|
||||
-keep class com.j256.**
|
||||
-keepclassmembers class com.j256.** { *; }
|
||||
-keep enum com.j256.**
|
||||
-keepclassmembers enum com.j256.** { *; }
|
||||
-keep interface com.j256.**
|
||||
-keepclassmembers interface com.j256.** { *; }
|
||||
-dontwarn com.j256.**
|
||||
|
||||
#okhttp3
|
||||
-dontwarn com.squareup.okhttp3.**
|
||||
-dontwarn okio.**
|
||||
-keep class com.squareup.okhttp3.** { *;}
|
||||
|
||||
# stetho
|
||||
-keep class com.facebook.stetho.** { *; }
|
||||
-dontwarn com.facebook.stetho.**
|
||||
|
||||
# Retrofit 2.2
|
||||
# Platform calls Class.forName on types which do not exist on Android to determine platform.
|
||||
-dontnote retrofit2.Platform
|
||||
# Platform used when running on Java 8 VMs. Will not be used at runtime.
|
||||
-dontwarn retrofit2.Platform$Java8
|
||||
# Retain generic type information for use by reflection by converters and adapters.
|
||||
-keepattributes Signature
|
||||
# Retain declared checked exceptions for use by a Proxy instance.
|
||||
-keepattributes Exceptions
|
||||
|
||||
# Retrofit 2.X
|
||||
## https://square.github.io/retrofit/ ##
|
||||
|
||||
-dontwarn retrofit2.**
|
||||
-keep class retrofit2.** { *; }
|
||||
-keepattributes Signature
|
||||
-keepattributes Exceptions
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@retrofit2.http.* <methods>;
|
||||
}
|
||||
|
||||
|
||||
# rxjava
|
||||
-keep class rx.schedulers.Schedulers {
|
||||
public static <methods>;
|
||||
}
|
||||
-keep class rx.schedulers.ImmediateScheduler {
|
||||
public <methods>;
|
||||
}
|
||||
-keep class rx.schedulers.TestScheduler {
|
||||
public <methods>;
|
||||
}
|
||||
-keep class rx.schedulers.Schedulers {
|
||||
public static ** test();
|
||||
}
|
||||
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
|
||||
long producerIndex;
|
||||
long consumerIndex;
|
||||
}
|
||||
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
|
||||
long producerNode;
|
||||
long consumerNode;
|
||||
}
|
||||
-dontwarn rx.internal.util.**
|
||||
|
||||
## AutoScrollViewPager
|
||||
-keep class cn.trinea.android.** { *; }
|
||||
-keepclassmembers class cn.trinea.android.** { *; }
|
||||
-dontwarn cn.trinea.android.**
|
||||
|
||||
## butterknife
|
||||
# Retain generated class which implement Unbinder.
|
||||
#-keep public class * implements butterknife.Unbinder { public <init>(**, android.view.View); }
|
||||
#
|
||||
## Prevent obfuscation of types which use ButterKnife annotations since the simple name
|
||||
## is used to reflectively look up the generated ViewBinding.
|
||||
#-keep class butterknife.*
|
||||
#-keepclasseswithmembernames class * { @butterknife.* <methods>; }
|
||||
#-keepclasseswithmembernames class * { @butterknife.* <fields>; }
|
||||
|
||||
-dontwarn butterknife.internal.**
|
||||
-keep class **$$ViewInjector { *; }
|
||||
-keepnames class * { @butterknife.InjectView *;}
|
||||
-dontwarn butterknife.Views$InjectViewProcessor
|
||||
-dontwarn com.gc.materialdesign.views.**
|
||||
|
||||
# eventbus
|
||||
-keepattributes *Annotation*
|
||||
-keepclassmembers class ** {
|
||||
@org.greenrobot.eventbus.Subscribe <methods>;
|
||||
}
|
||||
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
|
||||
|
||||
# Only required if you use AsyncExecutor
|
||||
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
|
||||
<init>(java.lang.Throwable);
|
||||
}
|
||||
|
||||
# weiboSdk
|
||||
-keep class com.sina.weibo.sdk.** { *; }
|
||||
-dontwarn android.webkit.WebView
|
||||
-dontwarn android.webkit.WebViewClient
|
||||
|
||||
# app models
|
||||
-keep class com.gh.common.view.** {*;}
|
||||
-keep class com.gh.gamecenter.db.info.** {*;}
|
||||
-keep class com.gh.gamecenter.entity.** {*;}
|
||||
-keep class com.gh.gamecenter.qa.entity.** {*;}
|
||||
-keep class com.gh.gamecenter.retrofit.** {*;}
|
||||
-keep class com.gh.gamecenter.eventbus.** {*;}
|
||||
-keep class com.gh.gamecenter.video.detail.** {*;}
|
||||
-keep class * extends rx.Subscriber
|
||||
|
||||
#---------------------------------webview------------------------------------
|
||||
-keepclassmembers class * extends android.webkit.WebViewClient {
|
||||
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
|
||||
public boolean *(android.webkit.WebView, java.lang.String);
|
||||
}
|
||||
-keepclassmembers class * extends android.webkit.WebViewClient {
|
||||
public void *(android.webkit.WebView, java.lang.String);
|
||||
}
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
|
||||
##---------------Begin: proguard configuration for Gson ----------
|
||||
# Gson uses generic type information stored in a class file when working with fields. Proguard
|
||||
# removes such information by default, so configure it to keep all of it.
|
||||
-keepattributes Signature
|
||||
|
||||
# For using GSON @Expose annotation
|
||||
-keepattributes *Annotation*
|
||||
|
||||
# Gson specific classes
|
||||
-keep class sun.misc.Unsafe { *; }
|
||||
#-keep class com.google.gson.stream.** { *; }
|
||||
|
||||
# Prevent proguard from stripping interface information from TypeAdapterFactory,
|
||||
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
|
||||
-keep class * implements com.google.gson.TypeAdapterFactory
|
||||
-keep class * implements com.google.gson.JsonSerializer
|
||||
-keep class * implements com.google.gson.JsonDeserializer
|
||||
|
||||
-keepclassmembers enum * { *; }
|
||||
|
||||
##---------------End: proguard configuration for Gson ----------
|
||||
|
||||
# ------ bugly ---------
|
||||
-dontwarn com.tencent.bugly.**
|
||||
-keep public class com.tencent.bugly.**{*;}
|
||||
|
||||
# easypermission
|
||||
-keepclassmembers class * {
|
||||
@pub.devrel.easypermissions.AfterPermissionGranted <methods>;
|
||||
}
|
||||
|
||||
# 重命名文件为SourceFile,再配合mapping符号表,可以拿到真实的类名
|
||||
-renamesourcefileattribute SourceFile
|
||||
# 保留源文件行号
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
-ignorewarnings
|
||||
|
||||
-keep @androidx.annotation.Keep class *
|
||||
-keepclassmembers class ** {
|
||||
@androidx.annotation.Keep *;
|
||||
}
|
||||
|
||||
-keep class com.gh.loghub.** { *; }
|
||||
|
||||
### greenDAO 3
|
||||
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
|
||||
public static java.lang.String TABLENAME;
|
||||
}
|
||||
-keep class **$Properties
|
||||
-keep class org.greenrobot.greendao.** { *; }
|
||||
# If you do not use SQLCipher:
|
||||
-dontwarn org.greenrobot.greendao.database.**
|
||||
# If you do not use RxJava:
|
||||
-dontwarn rx.**
|
||||
-dontwarn org.greenrobot.greendao.rx.**
|
||||
-dontwarn org.greenrobot.greendao.**
|
||||
|
||||
### fastJson
|
||||
-dontwarn com.alibaba.fastjson.**
|
||||
-keep class com.alibaba.fastjson.** { *; }
|
||||
-keepattributes Signature
|
||||
-keepattributes Annotation
|
||||
|
||||
### AndroidX
|
||||
-keep class androidx.core.app.CoreComponentFactory { *; }
|
||||
|
||||
#阿里云上传
|
||||
-keep class com.alibaba.sdk.android.oss.** { *; }
|
||||
-dontwarn okio.**
|
||||
-dontwarn org.apache.commons.codec.binary.**
|
||||
|
||||
#视频相关
|
||||
-keep class com.shuyu.gsyvideoplayer.video.** { *; }
|
||||
-dontwarn com.shuyu.gsyvideoplayer.video.**
|
||||
-keep class com.shuyu.gsyvideoplayer.video.base.** { *; }
|
||||
-dontwarn com.shuyu.gsyvideoplayer.video.base.**
|
||||
-keep class com.shuyu.gsyvideoplayer.utils.** { *; }
|
||||
-dontwarn com.shuyu.gsyvideoplayer.utils.**
|
||||
-keep class tv.danmaku.ijk.** { *; }
|
||||
-dontwarn tv.danmaku.ijk.**
|
||||
-keep public class * extends android.view.View{
|
||||
*** get*();
|
||||
void set*(***);
|
||||
public <init>(android.content.Context);
|
||||
public <init>(android.content.Context, android.util.AttributeSet);
|
||||
public <init>(android.content.Context, android.util.AttributeSet, int);
|
||||
}
|
||||
|
||||
#穿山甲
|
||||
-keep class com.bytedance.sdk.openadsdk.** { *; }
|
||||
-keep public interface com.bytedance.sdk.openadsdk.downloadnew.** {*;}
|
||||
-keep class com.pgl.sys.ces.* {*;}
|
||||
|
||||
-keep class com.gyf.immersionbar.* {*;}
|
||||
-dontwarn com.gyf.immersionbar.**
|
||||
|
||||
-keep class com.taobao.securityjni.**{*;}
|
||||
-keep class com.taobao.wireless.security.**{*;}
|
||||
-keep class com.ut.secbody.**{*;}
|
||||
-keep class com.taobao.dp.**{*;}
|
||||
-keep class com.alibaba.wireless.security.**{*;}
|
||||
|
||||
-keep class com.alibaba.sdk.android.**{*;}
|
||||
-keep class com.ut.**{*;}
|
||||
-keep class com.ta.**{*;}
|
||||
|
||||
-keep class com.gh.gamecenter.TeaHelper { *; }
|
||||
@ -1,20 +1,3 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in C:\Android\sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
#--------- remove logs start ----------------
|
||||
-assumenosideeffects class com.lightgame.config.CommonDebug {
|
||||
@ -26,126 +9,69 @@
|
||||
public static void logMethodWithParams(...);
|
||||
}
|
||||
|
||||
#-assumenosideeffects class com.lightgame.config.CommonDebug {*;}
|
||||
|
||||
#-dontoptimize
|
||||
-assumenosideeffects class com.lightgame.utils.Utils {
|
||||
public static void log(...);
|
||||
}
|
||||
#--------- remove logs end ----------------
|
||||
|
||||
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod
|
||||
-dontwarn InnerClasses
|
||||
#--------- remove useless mtahelper class --------
|
||||
-assumenosideeffects class com.gh.common.util.MtaHelper {
|
||||
public static void onEvent(...);
|
||||
public static void onEventWithTime(...);
|
||||
public static void onEventWithBasicDeviceInfo(...);
|
||||
}
|
||||
#--------- remove useless mta class end ----
|
||||
|
||||
# OrmLite uses reflection
|
||||
-keep class com.j256.**
|
||||
-keepclassmembers class com.j256.** { *; }
|
||||
-keep enum com.j256.**
|
||||
-keepclassmembers enum com.j256.** { *; }
|
||||
-keep interface com.j256.**
|
||||
-keepclassmembers interface com.j256.** { *; }
|
||||
# TODO Dicard sourceFile in final release build but remain in internal build.
|
||||
-renamesourcefileattribute SourceFile
|
||||
# Keep Attribute
|
||||
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod,SourceFile,LineNumberTable
|
||||
|
||||
# OrmLite
|
||||
-keep class com.j256.*
|
||||
-keepclassmembers class com.j256.* { *; }
|
||||
-keep enum com.j256.*
|
||||
-keepclassmembers enum com.j256.* { *; }
|
||||
-keep interface com.j256.*
|
||||
-keepclassmembers interface com.j256.* { *; }
|
||||
-dontwarn com.j256.**
|
||||
|
||||
#okhttp3
|
||||
-dontwarn com.squareup.okhttp3.**
|
||||
-dontwarn okio.**
|
||||
-keep class com.squareup.okhttp3.** { *;}
|
||||
|
||||
# stetho
|
||||
-keep class com.facebook.stetho.** { *; }
|
||||
-dontwarn com.facebook.stetho.**
|
||||
|
||||
# Retrofit 2.2
|
||||
# Platform calls Class.forName on types which do not exist on Android to determine platform.
|
||||
-dontnote retrofit2.Platform
|
||||
# Platform used when running on Java 8 VMs. Will not be used at runtime.
|
||||
-dontwarn retrofit2.Platform$Java8
|
||||
# Retain generic type information for use by reflection by converters and adapters.
|
||||
-keepattributes Signature
|
||||
# Retain declared checked exceptions for use by a Proxy instance.
|
||||
-keepattributes Exceptions
|
||||
|
||||
# Retrofit 2.X
|
||||
## https://square.github.io/retrofit/ ##
|
||||
|
||||
-dontwarn retrofit2.**
|
||||
-keep class retrofit2.** { *; }
|
||||
-keepattributes Signature
|
||||
-keepattributes Exceptions
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@retrofit2.http.* <methods>;
|
||||
}
|
||||
|
||||
|
||||
# rxjava
|
||||
-keep class rx.schedulers.Schedulers {
|
||||
public static <methods>;
|
||||
}
|
||||
-keep class rx.schedulers.ImmediateScheduler {
|
||||
public <methods>;
|
||||
}
|
||||
-keep class rx.schedulers.TestScheduler {
|
||||
public <methods>;
|
||||
}
|
||||
-keep class rx.schedulers.Schedulers {
|
||||
public static ** test();
|
||||
}
|
||||
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
|
||||
long producerIndex;
|
||||
long consumerIndex;
|
||||
}
|
||||
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
|
||||
long producerNode;
|
||||
long consumerNode;
|
||||
}
|
||||
-dontwarn rx.internal.util.**
|
||||
|
||||
## AutoScrollViewPager
|
||||
-keep class cn.trinea.android.** { *; }
|
||||
-keepclassmembers class cn.trinea.android.** { *; }
|
||||
### AutoScrollViewPager
|
||||
-keep class cn.trinea.android.* { *; }
|
||||
-keepclassmembers class cn.trinea.android.* { *; }
|
||||
-dontwarn cn.trinea.android.**
|
||||
|
||||
## butterknife
|
||||
# Retain generated class which implement Unbinder.
|
||||
#-keep public class * implements butterknife.Unbinder { public <init>(**, android.view.View); }
|
||||
#
|
||||
## Prevent obfuscation of types which use ButterKnife annotations since the simple name
|
||||
## is used to reflectively look up the generated ViewBinding.
|
||||
#-keep class butterknife.*
|
||||
#-keepclasseswithmembernames class * { @butterknife.* <methods>; }
|
||||
#-keepclasseswithmembernames class * { @butterknife.* <fields>; }
|
||||
|
||||
-dontwarn butterknife.internal.**
|
||||
-keep class **$$ViewInjector { *; }
|
||||
-keepnames class * { @butterknife.InjectView *;}
|
||||
-dontwarn butterknife.Views$InjectViewProcessor
|
||||
-dontwarn com.gc.materialdesign.views.**
|
||||
|
||||
# eventbus
|
||||
-keepattributes *Annotation*
|
||||
-keepclassmembers class ** {
|
||||
### eventbus
|
||||
-keepclassmembers class * {
|
||||
@org.greenrobot.eventbus.Subscribe <methods>;
|
||||
}
|
||||
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
|
||||
|
||||
# Only required if you use AsyncExecutor
|
||||
### Only required if you use AsyncExecutor
|
||||
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
|
||||
<init>(java.lang.Throwable);
|
||||
}
|
||||
|
||||
# weiboSdk
|
||||
### weiboSdk
|
||||
-keep class com.sina.weibo.sdk.** { *; }
|
||||
-dontwarn android.webkit.WebView
|
||||
-dontwarn android.webkit.WebViewClient
|
||||
|
||||
# app models
|
||||
-keep class com.gh.common.view.** {*;}
|
||||
-keep class com.gh.gamecenter.db.info.** {*;}
|
||||
-keep class com.gh.gamecenter.entity.** {*;}
|
||||
-keep class com.gh.gamecenter.qa.entity.** {*;}
|
||||
-keep class com.gh.gamecenter.retrofit.** {*;}
|
||||
-keep class com.gh.gamecenter.eventbus.** {*;}
|
||||
-keep class * extends rx.Subscriber
|
||||
### wechatSdk
|
||||
### TODO 这里用 com.tencent.*{*;} 不起效?但其它地方可以?
|
||||
-keep class com.tencent.**{*;}
|
||||
|
||||
#---------------------------------webview------------------------------------
|
||||
### app models
|
||||
-keep class com.gh.common.view.* {*;}
|
||||
-keep class com.gh.gamecenter.db.info.* {*;}
|
||||
-keep class com.gh.gamecenter.entity.* {*;}
|
||||
-keep class com.gh.gamecenter.qa.entity.* {*;}
|
||||
-keep class com.gh.gamecenter.retrofit.* {*;}
|
||||
-keep class com.gh.gamecenter.eventbus.* {*;}
|
||||
-keep class com.gh.gamecenter.video.detail.* {*;}
|
||||
-keep class com.gh.gamecenter.home.gamecollection.* {*;}
|
||||
|
||||
###
|
||||
-keepclassmembers class * extends android.webkit.WebViewClient {
|
||||
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
|
||||
public boolean *(android.webkit.WebView, java.lang.String);
|
||||
@ -153,93 +79,34 @@
|
||||
-keepclassmembers class * extends android.webkit.WebViewClient {
|
||||
public void *(android.webkit.WebView, java.lang.String);
|
||||
}
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
|
||||
##---------------Begin: proguard configuration for Gson ----------
|
||||
# Gson uses generic type information stored in a class file when working with fields. Proguard
|
||||
# removes such information by default, so configure it to keep all of it.
|
||||
-keepattributes Signature
|
||||
|
||||
# For using GSON @Expose annotation
|
||||
-keepattributes *Annotation*
|
||||
|
||||
# Gson specific classes
|
||||
-keep class sun.misc.Unsafe { *; }
|
||||
#-keep class com.google.gson.stream.** { *; }
|
||||
|
||||
# Prevent proguard from stripping interface information from TypeAdapterFactory,
|
||||
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
|
||||
-keep class * implements com.google.gson.TypeAdapterFactory
|
||||
-keep class * implements com.google.gson.JsonSerializer
|
||||
-keep class * implements com.google.gson.JsonDeserializer
|
||||
|
||||
-keepclassmembers enum * { *; }
|
||||
|
||||
##---------------End: proguard configuration for Gson ----------
|
||||
|
||||
# ------ bugly ---------
|
||||
-dontwarn com.tencent.bugly.**
|
||||
-keep public class com.tencent.bugly.**{*;}
|
||||
|
||||
# easypermission
|
||||
### easypermission
|
||||
-keepclassmembers class * {
|
||||
@pub.devrel.easypermissions.AfterPermissionGranted <methods>;
|
||||
}
|
||||
|
||||
# 重命名文件为SourceFile,再配合mapping符号表,可以拿到真实的类名
|
||||
-renamesourcefileattribute SourceFile
|
||||
# 保留源文件行号
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# TODO What's this ?
|
||||
-ignorewarnings
|
||||
|
||||
### Keep Annotation
|
||||
-keep @androidx.annotation.Keep class *
|
||||
-keepclassmembers class ** {
|
||||
-keepclassmembers class * {
|
||||
@androidx.annotation.Keep *;
|
||||
}
|
||||
|
||||
-keep class com.gh.loghub.** { *; }
|
||||
|
||||
### greenDAO 3
|
||||
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
|
||||
public static java.lang.String TABLENAME;
|
||||
}
|
||||
-keep class **$Properties
|
||||
-keep class org.greenrobot.greendao.** { *; }
|
||||
# If you do not use SQLCipher:
|
||||
-dontwarn org.greenrobot.greendao.database.**
|
||||
# If you do not use RxJava:
|
||||
-dontwarn rx.**
|
||||
-dontwarn org.greenrobot.greendao.rx.**
|
||||
-dontwarn org.greenrobot.greendao.**
|
||||
|
||||
### fastJson
|
||||
-dontwarn com.alibaba.fastjson.**
|
||||
-keep class com.alibaba.fastjson.** { *; }
|
||||
-keepattributes Signature
|
||||
-keepattributes Annotation
|
||||
|
||||
### 广点通
|
||||
-dontwarn com.qq.gdt.action.**
|
||||
-keep class com.qq.gdt.action.** {*;}
|
||||
|
||||
### AndroidX
|
||||
-keep class androidx.core.app.CoreComponentFactory { *; }
|
||||
|
||||
#阿里云上传
|
||||
-keep class com.alibaba.sdk.android.oss.** { *; }
|
||||
### 阿里云上传
|
||||
-keep class com.alibaba.sdk.android.oss.* { *; }
|
||||
-dontwarn okio.**
|
||||
-dontwarn org.apache.commons.codec.binary.**
|
||||
|
||||
#视频相关
|
||||
-keep class com.shuyu.gsyvideoplayer.video.** { *; }
|
||||
### 视频相关
|
||||
-keep class com.shuyu.gsyvideoplayer.video.* { *; }
|
||||
-dontwarn com.shuyu.gsyvideoplayer.video.**
|
||||
-keep class com.shuyu.gsyvideoplayer.video.base.** { *; }
|
||||
-keep class com.shuyu.gsyvideoplayer.video.base.* { *; }
|
||||
-dontwarn com.shuyu.gsyvideoplayer.video.base.**
|
||||
-keep class com.shuyu.gsyvideoplayer.utils.** { *; }
|
||||
-keep class com.shuyu.gsyvideoplayer.utils.* { *; }
|
||||
-dontwarn com.shuyu.gsyvideoplayer.utils.**
|
||||
-keep class tv.danmaku.ijk.** { *; }
|
||||
-keep class tv.danmaku.ijk.* { *; }
|
||||
-dontwarn tv.danmaku.ijk.**
|
||||
-keep public class * extends android.view.View{
|
||||
*** get*();
|
||||
@ -249,23 +116,42 @@
|
||||
public <init>(android.content.Context, android.util.AttributeSet, int);
|
||||
}
|
||||
|
||||
#穿山甲
|
||||
-keep class com.bytedance.sdk.openadsdk.** { *; }
|
||||
-keep public interface com.bytedance.sdk.openadsdk.downloadnew.** {*;}
|
||||
-keep class com.pgl.sys.ces.* {*;}
|
||||
-keep class com.alibaba.sdk.android.*{*;}
|
||||
-keep class com.ut.*{*;}
|
||||
-keep class com.ta.*{*;}
|
||||
|
||||
-keep class com.gyf.immersionbar.* {*;}
|
||||
-dontwarn com.gyf.immersionbar.**
|
||||
### TEA
|
||||
-keep class com.gh.gamecenter.TeaHelper { *; }
|
||||
|
||||
-keep class com.taobao.securityjni.**{*;}
|
||||
-keep class com.taobao.wireless.security.**{*;}
|
||||
-keep class com.ut.secbody.**{*;}
|
||||
-keep class com.taobao.dp.**{*;}
|
||||
-keep class com.alibaba.wireless.security.**{*;}
|
||||
### 阿里云日志
|
||||
-keep class com.aliyun.sls.android.producer.* { *; }
|
||||
-keep interface com.aliyun.sls.android.producer.* { *; }
|
||||
|
||||
-keep class com.alibaba.sdk.android.**{*;}
|
||||
-keep class com.ut.**{*;}
|
||||
-keep class com.ta.**{*;}
|
||||
### 中国移动一键登录
|
||||
-dontwarn com.cmic.sso.sdk.**
|
||||
-keep class com.cmic.sso.sdk.* { *; }
|
||||
|
||||
### EasyFloat
|
||||
-keep class com.lzf.easyfloat.* {*;}
|
||||
|
||||
### 避免 WebChromeClient 被混淆
|
||||
-keepclassmembers class * extends android.webkit.WebChromeClient{
|
||||
public void openFileChooser(...);
|
||||
}
|
||||
|
||||
### emoji4j
|
||||
-keep class emoji4j.* {*;}
|
||||
|
||||
### dokit
|
||||
-keep class com.didichuxing.** {*;}
|
||||
|
||||
# Flutter模块
|
||||
-keep class com.gh.common.util.DirectUtils {
|
||||
public static void directToQa(...);
|
||||
public static void directToQaCollection(...);
|
||||
public static void directToGift(...);
|
||||
public static void directToConcernInfo(...);
|
||||
public static void directToFeedback(...);
|
||||
public static void directToSuggestion(...);
|
||||
}
|
||||
|
||||
-keep class com.gh.gamecenter.GdtHelper { *; }
|
||||
-keep class com.gh.gamecenter.TeaHelper { *; }
|
||||
@ -2,8 +2,6 @@ package com.gh.gamecenter;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import com.facebook.stetho.Stetho;
|
||||
import com.facebook.stetho.okhttp3.StethoInterceptor;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
@ -17,9 +15,6 @@ import okhttp3.logging.HttpLoggingInterceptor;
|
||||
public class Injection {
|
||||
|
||||
public static boolean appInit(Application application) {
|
||||
// init stetho
|
||||
Stetho.initializeWithDefaults(application);
|
||||
|
||||
// 监控Bundle大小,预防溢出(需要调试的时候再开启吧!)
|
||||
// TooLargeTool.startLogging(application);
|
||||
return true;
|
||||
@ -30,7 +25,6 @@ public class Injection {
|
||||
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
|
||||
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
|
||||
builder.addNetworkInterceptor(interceptor);
|
||||
builder.addNetworkInterceptor(new StethoInterceptor());
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
@ -1,75 +0,0 @@
|
||||
package com.gh.gamecenter
|
||||
|
||||
import android.app.Application
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import com.gh.common.util.ToastUtils
|
||||
import com.lightgame.utils.Utils
|
||||
import com.qq.gdt.action.GDTAction
|
||||
import org.json.JSONObject
|
||||
|
||||
/**
|
||||
* 广点通辅助类 [https://gitlab.ghzhushou.com/pm/halo-app-issues/issues/403]
|
||||
*
|
||||
* 更换帐号 [https://gitlab.ghzs.com/pm/yunying/issues/893]
|
||||
*/
|
||||
object GdtHelper {
|
||||
|
||||
const val NETWORK_TYPE = "NETWORK_TYPE"
|
||||
const val PAGE_TYPE = "PAGE_TYPE"
|
||||
const val CONTENT_TYPE = "CONTENT_TYPE"
|
||||
const val CONTENT_ID = "CONTENT_ID"
|
||||
const val KEYWORD = "KEYWORD"
|
||||
const val GAME_ID = "GAME_ID"
|
||||
const val SCORE = "SCORE"
|
||||
const val PLATFORM = "PLATFORM"
|
||||
|
||||
@JvmStatic
|
||||
fun init(application: Application, channel: String) {
|
||||
if (shouldUseGdtHelper()) {
|
||||
if (channel == "GH_728") {
|
||||
GDTAction.init(application, "1111012969", "9d3d9da5b0948a317c03d08f14d445dc")
|
||||
} else if (channel == "GH_729") {
|
||||
GDTAction.init(application, "1111013063", "f53dabf458a356b101d99fc4069eb7f1")
|
||||
} else {
|
||||
GDTAction.init(application, "1110680399", "f5ddaafbf520d7d7385499232a408d0a")
|
||||
}
|
||||
}
|
||||
Utils.log("init GdtHelper")
|
||||
}
|
||||
|
||||
// fun logAction(type: String) {
|
||||
// if (shouldUseGdtHelper()) {
|
||||
// GDTAction.logAction(type)
|
||||
// Utils.log("GDT", type)
|
||||
// }
|
||||
// }
|
||||
@JvmStatic
|
||||
fun logAction(type: String, vararg kv: String?) {
|
||||
try {
|
||||
val actionParam = JSONObject()
|
||||
for (i in kv.indices) {
|
||||
if (i % 2 != 0) {
|
||||
val key = kv[i - 1]
|
||||
val value = kv[i]
|
||||
if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) {
|
||||
actionParam.put(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
Utils.log("GDT", "$type + [${kv.joinToString(" , ")}]")
|
||||
GDTAction.logAction(type, actionParam)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO 确认开启的渠道条件
|
||||
private fun shouldUseGdtHelper(): Boolean {
|
||||
return true
|
||||
//
|
||||
// val channel = HaloApp.getInstance().channel
|
||||
// return !(TextUtils.isEmpty(channel) || channel.contains("GDT".toLowerCase(Locale.CHINA)))
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
@ -21,22 +21,20 @@
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<!-- 允许应用程序改变Wi-Fi连接状态 -->
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<!-- 允许应用程序打开系统窗口,显示其他应用程序 -->
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<!-- 创建快捷方式的权限 -->
|
||||
<!-- 允许应用程序改变网络连接状态 -->
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
|
||||
<!-- 允许应用程序快捷方式 -->
|
||||
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
||||
<!-- 前台服务权限-->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.PACKAGE_USAGE_STATS"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
|
||||
<!-- bugly with tinker -->
|
||||
<!-- <uses-permission android:name="android.permission.READ_LOGS" />-->
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
|
||||
<!--可选,穿山甲提供“获取地理位置权限”和“不给予地理位置权限,开发者传入地理位置参数”两种方式上报用户位置,两种方式均可不选,添加位置权限或参数将帮助投放定位广告-->
|
||||
<!--请注意:无论通过何种方式提供给穿山甲用户地理位置,均需向用户声明地理位置权限将应用于穿山甲广告投放,穿山甲不强制获取地理位置信息-->
|
||||
<!--<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />-->
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||
|
||||
<!-- 如果有视频相关的广告且使用textureView播放,请务必添加,否则黑屏 -->
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
@ -47,9 +45,11 @@
|
||||
com.shuyu.gsyvideoplayer.armv7a,
|
||||
com.shuyu.gsyvideoplayer.x86,
|
||||
com.shuyu.gsy.base,
|
||||
shuyu.com.androidvideocache,
|
||||
com.google.android.exoplayer2,
|
||||
tv.danmaku.ijk.media.exo2,
|
||||
pl.droidsonroids.gif" />
|
||||
pl.droidsonroids.gif,
|
||||
com.lzf.easyfloat" />
|
||||
|
||||
<!-- 去掉 SDK 一些流氓权限 -->
|
||||
<uses-permission
|
||||
@ -70,16 +70,21 @@
|
||||
android:icon="@mipmap/logo"
|
||||
android:label="@string/app_name"
|
||||
android:largeHeap="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:resizeableActivity="true"
|
||||
android:theme="@style/AppCompatTheme.APP"
|
||||
tools:replace="android:allowBackup"
|
||||
tools:replace="android:name,android:allowBackup"
|
||||
tools:targetApi="n">
|
||||
|
||||
<meta-data
|
||||
android:name="io.sentry.auto-init"
|
||||
android:value="false" />
|
||||
|
||||
<!--android:launchMode = "singleTask"-->
|
||||
<!-- 不让 sentry 读取系统事件 -->
|
||||
<meta-data
|
||||
android:name="io.sentry.breadcrumbs.system-events"
|
||||
android:value="false" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.SplashScreenActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
@ -106,7 +111,6 @@
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<!--android:theme = "@android:style/Theme.Black.NoTitleBar.Fullscreen" 退出时屏幕抖动 -->
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.ImageViewerActivity"
|
||||
android:theme="@style/Theme.Transparent" />
|
||||
@ -162,6 +166,10 @@
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.WebActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.SingletonWebActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
@ -236,11 +244,6 @@
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="stateAlwaysHidden|adjustResize" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.ToolBoxActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.WeiBoShareActivity"
|
||||
android:screenOrientation="portrait"
|
||||
@ -280,18 +283,10 @@
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.qa.search.AskSearchActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.qa.answer.detail.AnswerDetailActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".qa.answer.fold.AnswerFoldActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
@ -312,14 +307,6 @@
|
||||
android:name="com.gh.gamecenter.MessageKeFuActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.qa.select.CommunitiesSelectActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.qa.subject.CommunitySubjectActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.MessageInviteActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
@ -336,10 +323,6 @@
|
||||
android:name="com.gh.gamecenter.qa.myqa.MyAskActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.qa.column.order.AskTabOrderActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.qa.questions.edit.QuestionEditActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
@ -361,10 +344,6 @@
|
||||
android:name="com.gh.gamecenter.amway.AmwayActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.qa.column.detail.AskColumnDetailActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.NetworkDiagnosisActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
@ -410,14 +389,6 @@
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="stateVisible" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.qa.questions.edit.manager.HistoryDetailActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.qa.questions.edit.manager.HistoryActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.qa.editor.InsertAnswerWrapperActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
@ -451,10 +422,6 @@
|
||||
android:name="com.gh.gamecenter.tag.TagsActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.qa.article.SimpleArticleListActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.video.videomanager.VideoManagerActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
@ -469,7 +436,7 @@
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.qa.editor.VideoActivity"
|
||||
android:name="com.gh.gamecenter.qa.editor.LocalMediaActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
@ -503,7 +470,11 @@
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.qa.comment.CommentActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.Transparent"
|
||||
android:windowSoftInputMode="adjustNothing" />
|
||||
|
||||
<activity
|
||||
android:name=".qa.dialog.ChooseForumActivity"
|
||||
android:theme="@style/Theme.Transparent"
|
||||
android:windowSoftInputMode="adjustNothing" />
|
||||
|
||||
@ -546,10 +517,6 @@
|
||||
android:name=".forum.select.ForumSelectActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".forum.follow.ForumMyFollowActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".forum.detail.ForumDetailActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
@ -558,6 +525,10 @@
|
||||
android:name=".forum.moderator.ModeratorListActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".forum.moderator.ApplyModeratorActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".video.label.VideoLabelActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
@ -579,10 +550,6 @@
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/TransparentStatusBarAndNavigationBar" />
|
||||
|
||||
<activity
|
||||
android:name=".personalhome.excellentcomments.ExcellentCommentsActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".simulatorgame.SimulatorGameActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
@ -603,6 +570,99 @@
|
||||
android:name=".forum.search.ForumOrUserSearchActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".energy.EnergyCenterActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".energy.EnergyHouseActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".personal.NewPersonalActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".qa.questions.draft.QuestionDraftActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".servers.GameServerTestActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".category2.CategoryV2Activity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".personal.DeliveryInfoActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".qa.editor.PreviewVideoActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".qa.video.publish.VideoPublishActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".setting.GameDownloadSettingActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".setting.VideoSettingActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".qa.video.detail.ForumVideoDetailActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".video.videomanager.VideoDraftActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".qa.questions.newdetail.NewQuestionDetailActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".qa.editor.FullScreenVideoActivity"
|
||||
android:theme="@style/AppFullScreenTheme" />
|
||||
|
||||
<activity
|
||||
android:name=".forum.list.ForumListActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".qa.answer.detail.SimpleAnswerDetailActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".game.commoncollection.detail.CommonCollectionDetailActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".gamecollection.detail.GameCollectionDetailActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".gamecollection.detail.GameCollectionPosterActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.cmic.sso.sdk.activity.LoginAuthActivity"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@android:style/Theme.Dialog" />
|
||||
|
||||
|
||||
<activity
|
||||
android:name=".home.skip.PackageSkipActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<!-- <!– 使用小米/华为推送弹窗功能提高推送成功率–>-->
|
||||
<!-- <activity-->
|
||||
<!-- android:name="com.gh.gamecenter.PushProxyActivity"-->
|
||||
@ -611,6 +671,7 @@
|
||||
<!-- android:theme="@android:style/Theme.Translucent" />-->
|
||||
|
||||
<activity
|
||||
android:exported="true"
|
||||
android:name="com.gh.gamecenter.SkipActivity"
|
||||
android:theme="@style/Theme.AppCompat.Light.Fullscreen.Transparent">
|
||||
<intent-filter>
|
||||
@ -630,12 +691,56 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.teenagermode.TeenagerModeActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".gamecollection.publish.GameCollectionEditActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".gamecollection.choose.ChooseGamesActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".gamecollection.choose.AddGamesActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".gamecollection.mine.MyGameCollectionActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.gamecollection.square.GameCollectionSquareActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.gamecollection.tag.GameCollectionTagSelectActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".qa.editor.InsertGameCollectionWrapperActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".qa.editor.InsertVideoWrapperActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.toolbox.ToolBoxBlockActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.qa.subject.CommunitySubjectActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="${applicationId}.wxapi.WXEntryActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"></activity>
|
||||
|
||||
<!-- <activity-->
|
||||
@ -654,6 +759,24 @@
|
||||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
|
||||
<provider
|
||||
android:name="com.gh.gamecenter.provider.GhContentProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:enabled="true"
|
||||
android:exported="true"/>
|
||||
|
||||
<provider
|
||||
android:name="androidx.startup.InitializationProvider"
|
||||
android:authorities="${applicationId}.androidx-startup"
|
||||
android:exported="false"
|
||||
tools:node="merge">
|
||||
<!-- If you are using androidx.startup to initialize other components -->
|
||||
<meta-data
|
||||
android:name="androidx.work.WorkManagerInitializer"
|
||||
android:value="androidx.startup"
|
||||
tools:node="remove" />
|
||||
</provider>
|
||||
|
||||
<receiver
|
||||
android:name="com.gh.gamecenter.receiver.DownloadReceiver"
|
||||
android:exported="false">
|
||||
@ -677,48 +800,6 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- <receiver android:name="com.gh.gamecenter.receiver.UmengMessageReceiver">-->
|
||||
<!-- <intent-filter>-->
|
||||
<!-- <action android:name="com.gh.gamecenter.UMENG" />-->
|
||||
<!-- </intent-filter>-->
|
||||
<!-- </receiver>-->
|
||||
|
||||
<!-- <!–魅族push应用定义消息receiver声明 –>-->
|
||||
<!-- <receiver android:name="com.gh.gamecenter.receiver.UmengMeizuPushReceiver">-->
|
||||
<!-- <intent-filter>-->
|
||||
<!-- <!– 接收push消息 –>-->
|
||||
<!-- <action android:name="com.meizu.flyme.push.intent.MESSAGE" />-->
|
||||
<!-- <!– 接收register消息 –>-->
|
||||
<!-- <action android:name="com.meizu.flyme.push.intent.REGISTER.FEEDBACK" />-->
|
||||
<!-- <!– 接收unregister消息–>-->
|
||||
<!-- <action android:name="com.meizu.flyme.push.intent.UNREGISTER.FEEDBACK" />-->
|
||||
<!-- <!– 兼容低版本Flyme3推送服务配置 –>-->
|
||||
<!-- <action android:name="com.meizu.c2dm.intent.REGISTRATION" />-->
|
||||
<!-- <action android:name="com.meizu.c2dm.intent.RECEIVE" />-->
|
||||
|
||||
<!-- <category android:name="${applicationId}" />-->
|
||||
<!-- </intent-filter>-->
|
||||
<!-- </receiver>-->
|
||||
|
||||
<!-- <receiver-->
|
||||
<!-- android:name="com.gh.common.im.ImReceiver"-->
|
||||
<!-- android:enabled="true">-->
|
||||
<!-- <intent-filter android:priority="2147483647">-->
|
||||
<!-- <action android:name="com.gh.im" />-->
|
||||
<!-- <action android:name="action_finish" />-->
|
||||
<!-- </intent-filter>-->
|
||||
<!-- </receiver>-->
|
||||
|
||||
<!-- <meta-data-->
|
||||
<!-- android:name="com.huawei.hms.client.appid"-->
|
||||
<!-- android:value="@string/huawei_push_appid" />-->
|
||||
|
||||
<!-- <service-->
|
||||
<!-- android:name="com.gh.base.GHUmengNotificationService"-->
|
||||
<!-- android:permission="android.permission.BIND_JOB_SERVICE" />-->
|
||||
|
||||
<!--<service android:name = "com.gh.gamecenter.statistics.AppStaticService" />-->
|
||||
|
||||
<!-- 梦工厂配置 开始 -->
|
||||
<!--<meta-data
|
||||
android:name="MGC_APPID"
|
||||
|
||||
File diff suppressed because one or more lines are too long
1
app/src/main/assets/lottie/loading.json
Normal file
1
app/src/main/assets/lottie/loading.json
Normal file
@ -0,0 +1 @@
|
||||
{"v":"5.6.9","fr":60,"ip":0,"op":76,"w":90,"h":90,"nm":"loading","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"圆环1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[45,45,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[63,63],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.75],"y":[0.833]},"o":{"x":[0.25],"y":[0.167]},"t":28,"s":[0]},{"t":76,"s":[96]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.75],"y":[0.833]},"o":{"x":[0.25],"y":[0.167]},"t":0,"s":[4]},{"t":48,"s":[100]}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.75],"y":[0.712]},"o":{"x":[0.25],"y":[0.288]},"t":0,"s":[-7.2]},{"t":76,"s":[367.2]}],"ix":3},"m":1,"ix":2,"nm":"修剪路径 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.141176477075,0.588235318661,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":76,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"圆环2","sr":1,"ks":{"o":{"a":0,"k":40,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[45,45,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[63,63],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.75],"y":[0.861]},"o":{"x":[0.25],"y":[0.139]},"t":36,"s":[0]},{"t":76,"s":[96]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.75],"y":[0.806]},"o":{"x":[0.25],"y":[0.194]},"t":0,"s":[4]},{"t":56,"s":[100]}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.75],"y":[0.712]},"o":{"x":[0.25],"y":[0.288]},"t":0,"s":[-7.2]},{"t":76,"s":[367.2]}],"ix":3},"m":1,"ix":2,"nm":"修剪路径 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.141176477075,0.588235318661,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":76,"st":0,"bm":0}],"markers":[{"tm":-5940,"cm":"S:[false]","dr":0},{"tm":0.740234375,"cm":"FOCUSED -- TO SELECTION","dr":0}]}
|
||||
1
app/src/main/assets/lottie/switch_turnoff.json
Normal file
1
app/src/main/assets/lottie/switch_turnoff.json
Normal file
@ -0,0 +1 @@
|
||||
{"v":"5.6.9","fr":60,"ip":0,"op":36,"w":120,"h":66,"nm":"开关动画-关闭","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"按钮手柄","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.667,"y":0},"t":0,"s":[87,33,0],"to":[7.682,0,0],"ti":[-13.443,0,0]},{"i":{"x":0.333,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[31,33,0],"to":[2.306,0,0],"ti":[-1.318,0,0]},{"t":24,"s":[33,33,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"指示器-on","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[100]},{"t":18,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[6.5,6.5]},{"t":18,"s":[4.5,4.5]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.966666666667,0.966666666667,0.966666666667,0.420000005762],"ix":3},"o":{"a":0,"k":40,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"指示器-off","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[87,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[1.5,4]},{"t":18,"s":[1.5,6]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0.75,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":5,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":90,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0]},{"t":18,"s":[100]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"按钮背景","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[60,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sy":[{"c":{"a":0,"k":[0,0,0,1],"ix":2},"o":{"a":0,"k":5,"ix":3},"a":{"a":0,"k":120,"ix":5},"s":{"a":0,"k":1,"ix":8},"d":{"a":0,"k":0,"ix":6},"ch":{"a":0,"k":100,"ix":7},"bm":{"a":0,"k":5,"ix":1},"no":{"a":0,"k":0,"ix":9},"ty":2,"nm":"内阴影"}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0.141176477075,0.588235318661,1,1]},{"t":18,"s":[0.933333337307,0.933333337307,0.933333337307,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"filling","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0.118,0.006],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0}],"markers":[]}
|
||||
1
app/src/main/assets/lottie/switch_turnon.json
Normal file
1
app/src/main/assets/lottie/switch_turnon.json
Normal file
@ -0,0 +1 @@
|
||||
{"v":"5.6.9","fr":60,"ip":0,"op":36,"w":120,"h":66,"nm":"开关动画-打开","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"按钮手柄","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.667,"y":0},"t":0,"s":[33,33,0],"to":[7.682,0,0],"ti":[-13.443,0,0]},{"i":{"x":0.333,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[89,33,0],"to":[2.306,0,0],"ti":[-1.318,0,0]},{"t":24,"s":[87,33,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"指示器-on","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0]},{"t":18,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[4.5,4.5]},{"t":18,"s":[6.5,6.5]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.966666666667,0.966666666667,0.966666666667,0.420000005762],"ix":3},"o":{"a":0,"k":40,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"指示器-off","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[87,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[1.5,6]},{"t":18,"s":[1.5,4]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0.75,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":5,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":90,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[100]},{"t":18,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"按钮背景","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[60,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sy":[{"c":{"a":0,"k":[0,0,0,1],"ix":2},"o":{"a":0,"k":5,"ix":3},"a":{"a":0,"k":120,"ix":5},"s":{"a":0,"k":1,"ix":8},"d":{"a":0,"k":0,"ix":6},"ch":{"a":0,"k":100,"ix":7},"bm":{"a":0,"k":5,"ix":1},"no":{"a":0,"k":0,"ix":9},"ty":2,"nm":"内阴影"}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0.933332979679,0.933332979679,0.933332979679,1]},{"t":18,"s":[0.141176477075,0.588235318661,1,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"filling","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0.118,0.006],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0}],"markers":[]}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,758 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
<title>隐私政策</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.page {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.date p {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: "SourceHanSansSC-regular" !important;
|
||||
color: #101010;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
margin-bottom: 6px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
b {
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.points {
|
||||
margin: 14px 0;
|
||||
}
|
||||
|
||||
.points p {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.introduce p {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.content p b {
|
||||
margin: 6px 0;
|
||||
display: block;
|
||||
}
|
||||
.link-text {
|
||||
color: rgb(19, 131, 235);
|
||||
cursor: pointer;
|
||||
}
|
||||
.link-text a {
|
||||
color: rgb(19, 131, 235);
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
.left-indent {
|
||||
margin-left: 20px;
|
||||
}
|
||||
.page-title {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
margin: 20px 0 10px 0;
|
||||
}
|
||||
.red-style {
|
||||
color: red;
|
||||
}
|
||||
.bold-font {
|
||||
font-weight: bold;
|
||||
}
|
||||
span.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.link-text {
|
||||
color: #005ad0;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="page-title">欢迎您使用光环助手!</div>
|
||||
|
||||
<div class="introduce">
|
||||
<p>
|
||||
为了向您提供游戏预约、论坛互动交流等相关服务,受制于手机系统限制,我们会申请您的设备信息权限;
|
||||
</p>
|
||||
<p>为了让您正常使用游戏下载和论坛功能,我们会申请您的储存权限;</p>
|
||||
<p>以下为完整《隐私权限政策》</p>
|
||||
<p>
|
||||
光环助手(简称“我们”)深知个人信息对您的重要性,我们将依据《中华人民共和国网络安全法》、《信息安全技术
|
||||
个人信息安全规范》(GB/T
|
||||
35273-2017)以及其他相关法律法规和技术规范收集和使用您的个人信息,以帮助我们向您提供更优质的产品和/或服务,
|
||||
保护您的个人信息及隐私安全。我们制定本“隐私指引”并特别提示:希望您在使用光环助手及相关服务前仔细阅读并理解本隐私政策,以便做出适当的选择。
|
||||
</p>
|
||||
<p>
|
||||
下文将帮您详细了解我们如何收集、使用、存储、传输、共享、转让(如适用)与保护个人信息;帮您了解查询、访问、删除、更正、撤回授权个人信息的方式。其中,
|
||||
<b>
|
||||
有关您个人信息权益的条款重要内容我们已用加粗形式提示,请特别关注。
|
||||
</b>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="points">
|
||||
<p><b>1.我们处理个人信息的法律依据</b></p>
|
||||
<p><b>2.我们如何共享、转让、公开披露个人信息</b></p>
|
||||
<p><b>3.我们如何收集和使用个人信息</b></p>
|
||||
<p><b>4.我们如何存储个人信息</b></p>
|
||||
<p><b>5.我们如何保护个人信息的安全</b></p>
|
||||
<p><b>6.管理您的个人信息</b></p>
|
||||
<p><b>7.未成年人使用条款</b></p>
|
||||
<p><b>8.隐私政策的修订和通知</b></p>
|
||||
<p><b>9.联系我们</b></p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<p><b>1.我们处理个人信息的法律依据</b></p>
|
||||
<p>
|
||||
如果您是中华人民共和国大陆地区的用户,我们将依据《中华人民共和国网络安全法》、《信息安全技术
|
||||
个人信息安全规范》(GB/T
|
||||
35273-2017)以及其他相关法律法规收集和使用您的个人信息,为您提供产品或服务。
|
||||
</p>
|
||||
<p>
|
||||
我们通常只会在征得您同意的情况下收集您的个人信息。
|
||||
在某些情况下,我们可能还会基于法律义务或者履行合同之必需向您收集个人信息,或者可能需要个人信息来保护您的重要利益或其他人的利益。
|
||||
</p>
|
||||
|
||||
<p><b>2.我们如何共享、转让、公开披露个人信息</b></p>
|
||||
|
||||
<p class="title margintop"><b>2.1第三方SDK接入说明</b></p>
|
||||
<p>
|
||||
为保障光环助手App相关功能的实现与应用安全稳定的运行,我们会接入由第三方提供的软件开发包(SDK)实现相关功能。
|
||||
<br />
|
||||
我们会对合作方获取有关信息的软件工具开发包(SDK)进行严格的安全检测,并与授权合作伙伴约定严格的数据保护措施,令其按照我们的委托目的、服务说明、本隐私权政策以及其他任何相关的保密和安全措施来处理个人信息。
|
||||
<br />
|
||||
<span class="red-style">
|
||||
下方为整个光环助手
|
||||
<span class="bold">所有版本</span>
|
||||
内接入的所有信息收集类第三方SDK的权限说明,因隐私政策会因光环助手版本迭代而新接入SDK或停止合作部分SDK,方便照顾
|
||||
<span class="bold">所有版本</span>
|
||||
的用户查看自己SDK第三方权限说明。
|
||||
<br />
|
||||
我们对涉及用户信息使用的SDK相关情况进行了逐项列举,具体如下:
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p class="margintop red-style bold-font"><b>(1)数据统计类</b></p>
|
||||
<p>1.头条推广</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">
|
||||
https://ad.oceanengine.com/openapi/index.html
|
||||
</span>
|
||||
</p>
|
||||
<p>SDK包名:com.bytedance</p>
|
||||
<p>企业主体:北京有竹居网络技术有限公司</p>
|
||||
<p>使用目的:用于广告流量统计相关服务</p>
|
||||
<p>
|
||||
收集信息类型:设备品牌、型号、软件系统相关信息、安卓(oaid、无线网SSID名称、WiFi路由器MAC地址、设备MAC地址、IMEI、地理位置)
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
https://ad.oceanengine.com/openapi/register/protocol.html?rid=vo25p8sfqde
|
||||
</span>
|
||||
</p>
|
||||
<p>2.talkingdata统计</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">http://www.talkingdata.com/</span>
|
||||
</p>
|
||||
<p>SDK包名:com.tendcloud</p>
|
||||
<p>企业主体:北京腾云天下科技有限公司</p>
|
||||
<p>使用目的:用于统计数据和效果分析,以便为用户提供更好的服务</p>
|
||||
<p>收集信息类型:设备信息、网络信息、位置信息、应用信息</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
http://www.talkingdata.com/privacy.jsp?languagetype=zh_cn
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p>3.腾讯MTA</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://mta.qq.com/mta/</span>
|
||||
</p>
|
||||
<p>SDK包名:com.tencent</p>
|
||||
<p>企业主体:深圳市腾讯计算机系统有限公司</p>
|
||||
<p>使用目的:用于统计数据和效果分析</p>
|
||||
<p>
|
||||
收集信息类型:Mac地址、唯一设备识别码(IMEI、android
|
||||
ID、IDFA、OPENUDID、GUID/SIM卡IMSI信息)、地理位置信息
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
https://mta.qq.com/mta/ctr_index/protocol_v2/
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p>4.腾讯广点通</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://developers.e.qq.com/</span>
|
||||
</p>
|
||||
<p>SDK包名:com.tencent</p>
|
||||
<p>企业主体:深圳市腾讯计算机系统有限公司</p>
|
||||
<p>使用目的:用于广告流量统计相关服务</p>
|
||||
<p>
|
||||
收集信息类型:
|
||||
个人常用设备信息(IMEI、AndroidID)、位置信息,IP地址、软件版本号
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">https://e.qq.com/optout.html</span>
|
||||
</p>
|
||||
|
||||
<p class="margintop red-style bold-font"><b>(2)社交登录类</b></p>
|
||||
<p>5.微信登录分享</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://open.weixin.qq.com/</span>
|
||||
</p>
|
||||
<p>SDK包名:com.tencent.mm.opensdk</p>
|
||||
<p>企业主体:深圳市腾讯计算机系统有限公司</p>
|
||||
<p>使用目的:用于支持微信登录、分享</p>
|
||||
<p>
|
||||
收集信息类型:个人常用设备信息(MAC地址、IMEI、AndroidID)、硬件型号、操作系统类型、软件信息(软件版本号、浏览器类型)、IP地址、服务日志信息、通讯日志信息
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">https://privacy.tencent.com/</span>
|
||||
</p>
|
||||
|
||||
<p>6.QQ登录分享</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://connect.qq.com/</span>
|
||||
</p>
|
||||
<p>SDK包名:com.tentcent</p>
|
||||
<p>企业主体:深圳市腾讯计算机系统有限公司</p>
|
||||
<p>使用目的:用于支持QQ登录、分享</p>
|
||||
<p>
|
||||
收集信息类型:个人常用设备信息(MAC地址、IMEI、AndroidID、IMSI、ICCID、序列号)、设备型号、操作系统版本、软件信息(软件版本号、浏览器类型)、网络信息、IP地址、服务日志信息、通讯日志信息
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
https://wiki.connect.qq.com/qq互联sdk隐私保护声明
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p>7.微博登录分享</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">http://open.weibo.com/authentication</span>
|
||||
</p>
|
||||
<p>SDK包名:com.sina.weibo.sdk</p>
|
||||
<p>企业主体:北京微梦创科网络技术有限公司</p>
|
||||
<p>使用目的:用于支持微博登录、分享</p>
|
||||
<p>
|
||||
收集信息类型:个人常用设备信息(MAC地址、IMEI、AndroidID、IMSI、ICCID、序列号)、网络信息、应用列表,硬件型号、操作系统类型、软件信息(软件版本号、浏览器类型)、IP地址、服务日志信息、通讯日志信息
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">https://open.weibo.com/wiki/开发者协议</span>
|
||||
</p>
|
||||
|
||||
<p>8.头条抖音登录</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://open.douyin.com/platform</span>
|
||||
</p>
|
||||
<p>SDK包名:com.bytedance.sdk</p>
|
||||
<p>企业主体:北京字节跳动科技有限公司</p>
|
||||
<p>使用目的:用于支持抖音登录</p>
|
||||
<p>
|
||||
收集信息类型:个人常用设备信息(MAC地址、IMEI、AndroidID)、硬件型号、操作系统类型、软件信息(软件版本号、浏览器类型)、IP地址、服务日志信息、通讯日志信息
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
https://www.douyin.com/agreements/?id=6773901168964798477
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p class="margintop red-style bold-font"><b>(3)推送通知类</b></p>
|
||||
<p>9.友盟推送</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://www.umeng.com/push</span>
|
||||
</p>
|
||||
<p>SDK包名:com.umeng</p>
|
||||
<p>企业主体:北京友盟网络科技有限公司</p>
|
||||
<p>使用目的:用于游戏相关信息的提醒通知</p>
|
||||
<p>
|
||||
收集信息类型:Mac地址、唯一设备识别码(IMEI、android
|
||||
ID、IDFA、OPENUDID、GUID/SIM卡IMSI信息)、地理位置信息
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
https://www.umeng.com/page/policy?spm=a213m0.14063960.0.0.7f626e72hx3nnv
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p class="margintop red-style bold-font"><b>(4)其他功能类</b></p>
|
||||
<p>10.阿里云反爬虫</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://www.aliyun.com/product/antibot</span>
|
||||
</p>
|
||||
<p>SDK包名:com.alibaba.wireless</p>
|
||||
<p>企业主体:阿里巴巴网络技术有限公司</p>
|
||||
<p>使用目的:为APP提供网络应用安全防护</p>
|
||||
<p>
|
||||
收集信息类型:设备相关信息(例如设备型号、操作系统版本、设备设置、唯一设备标识符等软硬件特征信息)、设备所在位置相关信息(例如IP地址、GPS位置以及能够提供相关信息的Wi-Fi接入点、蓝牙和基站等传感器信息)。
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
http://terms.aliyun.com/legal-agreement/terms/suit_bu1_ali_cloud/suit_bu1_ali_cloud201902141711_54837.html?spm=a2c4g.11186623.J_9220772140.81.b7574832gmk0vr
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p>11.腾讯bugly</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://bugly.qq.com/v2/</span>
|
||||
</p>
|
||||
<p>SDK包名:com.tencent.bugly</p>
|
||||
<p>企业主体:深圳市腾讯计算机系统有限公司</p>
|
||||
<p>使用目的:APP异常上报</p>
|
||||
<p>
|
||||
收集信息类型:设备及应用信息。如:设备名称、设备识别符、硬件型号、操作系统版本、应用程序版本
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">https://bugly.qq.com/v2/contract</span>
|
||||
</p>
|
||||
|
||||
<p>12.阿里云文件上传</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://www.alibabacloud.com/zh</span>
|
||||
</p>
|
||||
<p>SDK包名:com.alibaba.sdk.android</p>
|
||||
<p>SDK包名:com.alibaba.sdk.android</p>
|
||||
<p>企业主体:阿里巴巴网络技术有限公司</p>
|
||||
<p>使用目的:用于支持用户上传视频等相关内容</p>
|
||||
<p>
|
||||
收集信息类型:设备相关信息(例如设备型号、操作系统版本、设备设置、唯一设备标识符等软硬件特征信息)、设备所在位置相关信息(例如IP地址、GPS位置以及能够提供相关信息的Wi-Fi接入点、蓝牙和基站等传感器信息)。
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
http://terms.aliyun.com/legal-agreement/terms/suit_bu1_ali_cloud/suit_bu1_ali_cloud201902141711_54837.html?spm=a2c4g.11186623.J_9220772140.81.b7574832gmk0vr
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p>13.阿里云日志上传</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://www.alibabacloud.com/zh</span>
|
||||
</p>
|
||||
<p>SDK包名:com.aliyun.sls.android.sdk</p>
|
||||
<p>企业主体:阿里巴巴网络技术有限公司</p>
|
||||
<p>
|
||||
使用目的:通过网络日志分析这些信息以便更及时响应您的帮助请求,以及用于改进服务
|
||||
</p>
|
||||
<p>
|
||||
收集信息类型:设备相关信息(例如设备型号、操作系统版本、设备设置、唯一设备标识符等软硬件特征信息)、设备所在位置相关信息(例如IP地址、GPS位置以及能够提供相关信息的Wi-Fi接入点、蓝牙和基站等传感器信息)。
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
http://terms.aliyun.com/legal-agreement/terms/suit_bu1_ali_cloud/suit_bu1_ali_cloud201902141711_54837.html?spm=a2c4g.11186623.J_9220772140.81.b7574832gmk0vr
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p>14.容联七陌</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://www.7moor.com/developer</span>
|
||||
</p>
|
||||
<p>SDK包名:com.m7.imkfsdk</p>
|
||||
<p>企业主体:北京七陌科技有限公司</p>
|
||||
<p>使用目的:用于提供对应在线客服功能</p>
|
||||
<p>
|
||||
收集信息类型:设备相关信息(设备名称、设备型号、硬件序列号、操作系统和应用程序版本及类型、语言设置、分辨率、移动终端随机存储内存、摄像头/相册、通讯录权限等)
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
http://m.7moor.com/72/57/p5077783560e807/
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p><b>2.2 共享您的个人信息</b></p>
|
||||
<p>
|
||||
(1)我们不会与任何公司、组织和个人共享您的个人信息,但以下情况除外:
|
||||
</p>
|
||||
<p>
|
||||
(2)事先获得您的明确授权或同意:
|
||||
获得您的明确同意后,我们会与其他方共享您的个人信息;
|
||||
</p>
|
||||
<p>
|
||||
(3)在法定情形下的共享:
|
||||
根据适用的法律法规、法律程序、政府的强制命令或司法裁定而需共享您的个人信息;
|
||||
</p>
|
||||
<p>
|
||||
(4)在法律要求或允许的范围内,为了保护光环助手及其用户或社会公众的利益、财产或安全免遭损害而有必要提供您的个人信息给第三方;
|
||||
</p>
|
||||
<p>
|
||||
(5)与我们的关联公司共享:
|
||||
您的个人信息可能会在我们的关联公司之间共享。我们会对共享的个人信息进行匿名化处理,且这种共享受本指引所声明目的的约束。关联公司如要改变个人信息的处理目的,将再次征求您的授权同意。
|
||||
</p>
|
||||
<p><b>2.3转让</b></p>
|
||||
<p>
|
||||
(1)我们不会转让您的个人信息给任何其他第三方,除非征得您的明确同意。
|
||||
</p>
|
||||
<p>
|
||||
(2)随着我们业务的持续发展,我们将有可能进行合并、收购、资产转让,您的个人信息有可能因此而被转移。在发生前述变更时,我们将按照法律法规及不低于本隐私政策所载明的安全标准要求继受方保护您的个人信息,否则我们将要求继受方重新征得您的授权同意。
|
||||
</p>
|
||||
<p><b>2.4披露</b></p>
|
||||
<p>
|
||||
(1)我们不会公开披露您的信息,除非遵循国家法律法规规定或者获得您的同意。我们公开披露您的个人信息会采用符合行业内标准的安全保护措施。
|
||||
</p>
|
||||
<p>
|
||||
(2)基于法律、法律程序、诉讼或政府主管部门强制性要求的情况下,我们可能会向有权机关披露您的个人信息。但我们保证,在上述情况发生时,我们会要求披露请求方必须出具与之相应的有效法律文件,并对被披露的信息采取符合法律和业界标准的安全防护措施。
|
||||
</p>
|
||||
<p>
|
||||
(3)对违规账号、欺诈行为进行处罚公告时,我们会披露相关账号的信息。
|
||||
</p>
|
||||
|
||||
<p><b>2.5依法豁免征得同意共享、转让、公开披露的个人信息</b></p>
|
||||
<p>
|
||||
请您理解,在下列情形中,根据法律法规及国家标准,我们共享、转让、公开披露您的个人信息无需征得您的授权同意:
|
||||
</p>
|
||||
<p>(1)与国家安全、国防安全直接相关的;</p>
|
||||
<p>(2)与公共安全、公共卫生、重大公共利益直接相关的;</p>
|
||||
<p>(3)与犯罪侦查、起诉、审判和判决执行等直接相关的;</p>
|
||||
<p>
|
||||
(4)出于维护您或其他个人的生命、财产等重大合法权益但又很难得到本人同意的;
|
||||
</p>
|
||||
<p>(5)您自行向社会公众公开的个人信息;</p>
|
||||
<p>
|
||||
(6)从合法公开披露的信息中收集个人信息的,如合法的新闻报道、政府信息公开等渠道。
|
||||
</p>
|
||||
|
||||
<p><b>3.我们如何收集和使用个人信息</b></p>
|
||||
<p>
|
||||
我们会遵循正当、合法、必要的原则,出于本指引所述的以下目的,收集和使用您在使用服务过程中主动提供或因使用产品或服务而产生的个人信息。
|
||||
</p>
|
||||
<p>
|
||||
我们收集和使用的您的个人信息类型包括两种:第一种:我们产品或服务的核心业务功能所必需的信息:此类信息为产品或服务正常运行的必备信息,您须授权我们收集。如您拒绝提供,您将无法正常使用我们的功能,以"仅浏览(游客身份)"
|
||||
的状态体验;第二种:我们产品或服务的附加业务功能可能需要收集的信息:此信息为非核心业务功能所需的信息,您可以选择是否授权我们收集。如您拒绝提供,将导致附加业务功能无法实现或无法达到我们拟达到的效果,但不影响您对核心业务功能的正常使用。
|
||||
</p>
|
||||
<p>
|
||||
如果我们要将您的个人信息用于本指引未载明的其它用途,或基于特定目的将收集而来的信息用于其他目的,我们将以合理的方式向您告知,并在使用前再次征得您的同意。
|
||||
</p>
|
||||
|
||||
<p><b>3.1实现产品或服务的基本功能</b></p>
|
||||
<p>
|
||||
(1)手机管理和内容资源下载功能。为实现手机管理及手机内容资源下载的基本功能,我们会通过手机系统的公用接口收集经过MD5算法加密的国际移动设备身份码(IMEI)和网络设备地址(MAC),以及手机型号、手机系统版本号、系统编号、系统ID号、屏幕分辨率、上网类型、手机中软件的名称、版本号、版本名、包名、软件使用时间和频率、软件崩溃信息、设备和软件相关的信息。这些信息是提供服务所必须收集的基础信息,如您拒绝提供上述权限将可能导致您无法使用我们的服务。
|
||||
</p>
|
||||
<p>
|
||||
(2)软件升级管理功能。为实现手机软件下载、安装、升级、卸载软件管理功能,在您使用产品时,我们会采集您手机中已安装软件的软件名称、版本号、版本名、软件包名信息并上传到我们的服务器进行软件版本比对。发现有更新的版本,我们会提示您升级相应的软件。上述软件信息为实现此功能所必需,不涉及您个人身份敏感信息。
|
||||
</p>
|
||||
<p>
|
||||
(3)过滤无法使用的软件功能。为了过滤您手机无法使用的软件,我们会收集您手机的手机型号、手机系统版本号、系统版本号、屏幕分辨率信息,并依据这些信息排除您手机无法使用的软件,以保证您在光环助手下载的软件都可安装使用。
|
||||
</p>
|
||||
|
||||
<p><b>3.2关于获取手机设备信息的说明</b></p>
|
||||
<p>
|
||||
(1)为方便区分每个用户的个人信息等,本软件需获取用户的手机设备信息,用于游戏主动预约、论坛互动交流后进行推送等用户相关的行为
|
||||
</p>
|
||||
<p>
|
||||
(2)为了保障软件与服务的安全运行,我们会收集您的硬件型号、操作系统版本号、国际移动设备识别码、唯一设备标识符、网络设备硬件地址、IP
|
||||
地址、WLAN接入点、蓝牙、基站、软件版本号、网络接入方式、类型、状态、网络质量数据、操作、使用、服务日志。
|
||||
</p>
|
||||
<p>
|
||||
(3)为了预防恶意程序及安全运营所必需,我们会收集安装的应用信息或正在运行的进程信息、应用程序的总体运行、使用情况与频率、应用崩溃情况、总体安装使用情况、性能数据、应用来源。
|
||||
</p>
|
||||
<p>
|
||||
(4)我们可能使用您的账户信息、设备信息、服务日志信息以及我们关联公司、合作方在获得您授权或依法可以共享的信息,用于判断账户安全、进行身份验证、检测及防范安全事件。
|
||||
</p>
|
||||
<p>(5)具体会发生获取手机设备信息场景如下说明:</p>
|
||||
|
||||
<p class="left-indent">
|
||||
1) 首次启动光环助手
|
||||
<b></b>
|
||||
2) 游戏列表/游戏详情/资讯文章详情/搜索结果页-预约功能
|
||||
<b></b>
|
||||
3) 礼包中心/礼包详情-领取功能
|
||||
<b></b>
|
||||
4) 评论详情-发送评论功能
|
||||
<b></b>
|
||||
5) 回答/问题详情-我来回答功能
|
||||
<b></b>
|
||||
6) 问答首页-提问功能
|
||||
<b></b>
|
||||
7) 个人主页-发文章功能
|
||||
<b></b>
|
||||
8) 帖子草稿/我的草稿-编辑功能
|
||||
<b></b>
|
||||
9) 游戏投稿功能
|
||||
<b></b>
|
||||
10)视频投稿-上传视频功能
|
||||
<b></b>
|
||||
11)游戏详情-关注游戏功能
|
||||
</p>
|
||||
|
||||
<p><b>3.3帮助您成为我们的在线用户</b></p>
|
||||
<p>(1)注册账号/登录账号</p>
|
||||
<p>
|
||||
a.当您注册、登录我们相关服务时,您可以通过手机号创建账号,并且您可以完善相关的网络身份识别信息(头像、昵称、密码),收集这些信息是为了帮助您完成注册。您还可以根据自身需求选择填写性别、生日、地区及个人介绍来完善您的信息。
|
||||
</p>
|
||||
<p>
|
||||
b.您也可以使用第三方账号登录并使用,您将授权我们获取您在第三方平台注册的公开信息(头像、昵称以及您授权的其他信息),用于与光环助手账号绑定,使您可以直接登录并使用本产品和相关服务。
|
||||
</p>
|
||||
<p>(2)认证用户</p>
|
||||
<p>
|
||||
a.在您使用身份认证的功能或服务时,根据相关法律法规,您可能需要提供您的真实身份信息(真实姓名、身份证号码、电话号码)以完成实名验证。
|
||||
</p>
|
||||
<p>
|
||||
b.这些信息属于个人敏感信息,您可以拒绝提供,但您将可能无法获得相关服务,但不影响其他功能与服务的正常使用。
|
||||
</p>
|
||||
|
||||
<p><b>3.4搜索</b></p>
|
||||
<p>
|
||||
(1)您使用“光环助手”的搜索服务时,我们会收集您的搜索关键字信息、日志记录。
|
||||
</p>
|
||||
<p>
|
||||
(2)为了提供高效的搜索服务,部分前述信息会暂时存储在您的本地存储设备之中,并可向您展示搜索结果内容、搜索历史记录。
|
||||
</p>
|
||||
|
||||
<p><b>3.5预约游戏</b></p>
|
||||
<p>
|
||||
当您使用游戏预约、游戏开测提醒功能时,您可以根据需要是否填写手机号。如您拒绝提供,仅会使您无法接收该预约游戏的短信快速提醒功能,但并不影响您正常使用产品与服务的其他。
|
||||
</p>
|
||||
|
||||
<p><b>3.6游戏时长统计</b></p>
|
||||
<p>
|
||||
您可以授权我们使用应用使用记录访问权限,我们会获取您使用某款游戏应用的使用时长,以便于提供游戏时长展示服务以及对应的大数据统计分析。
|
||||
</p>
|
||||
|
||||
<p><b>3.7信息发布功能</b></p>
|
||||
<p>
|
||||
(1)注册成为光环用户后,可在光环平台上发布提问、帖子、视频,并对别人的提问作出回答或邀请其他用户回答,您还可以对别人的回答、帖子和视频的评论作出回复、赞同、感谢。
|
||||
</p>
|
||||
<p>
|
||||
(2)上述功能基于相册(图片库/视频库)的图片/视频访问及上传的附加服务,我们会请求您授权相机、照片、麦克风权限,您可以使用该功能上传您的照片/图片/视频,以实现发布照片/图片/视频的功能、与其他用户进行照片/图片分享等功能。如您拒绝提供该权限和内容的,仅会使您无法使用该功能,但并不影响您正常使用产品与/或服务的其他功能。
|
||||
</p>
|
||||
<p>
|
||||
(3)您发布内容、评论、提问或回答时,我们将收集您发布的信息,并展示您的昵称、头像、发布内容。
|
||||
</p>
|
||||
<p>
|
||||
(4)用户因使用我们的产品或者服务而被我们收集的信息,例如其他用户发布的信息中可能含有您的部分信息(如:在评论、留言、发布图文、音视频中涉及到与您相关的信息)。
|
||||
</p>
|
||||
|
||||
<p><b>3.8浏览、关注与收藏功能</b></p>
|
||||
<p>(1)您可浏览的内容包括问答、评论、专栏、文章。</p>
|
||||
<p>
|
||||
(2)在浏览的过程中,您还可以关注您感兴趣的用户、专栏、问题、收藏,并收藏上述内容。
|
||||
</p>
|
||||
<p>
|
||||
(3)为此,
|
||||
我们可能会收集您使用时的设备信息,如设备型号、唯一设备标识符、操作系统、分辨率、电信运营商等软硬件信息。
|
||||
我们还可能收集您的浏览器类型,以此来为您提供信息展示的最优方案。
|
||||
</p>
|
||||
<p>
|
||||
(4)此外,在您使用浏览和收藏功能的过程中,我们会自动收集您使用的详细情况,并作为有关的
|
||||
网络日志保存,包括但不限于您输入的搜索关键词信息和点击的链接。
|
||||
</p>
|
||||
<p>
|
||||
(5)您浏览和发布的内容及评论信息,您上传的图片信息、您的交易信息、您使用的语言、访问的日期和时间、及您请求的网页记录、操作系统、软件版本号、登录
|
||||
IP 信息。
|
||||
</p>
|
||||
<p>
|
||||
(6)在此过程中,
|
||||
我们会收集您的浏览记录,浏览记录包括您浏览的问答、主页、文章、专栏,
|
||||
您可以自主删除浏览记录。
|
||||
</p>
|
||||
<p><b>3.9互动交流</b></p>
|
||||
<p>
|
||||
(1)您主动关注您感兴趣的账号、内容、视频并与之进行互动,进行浏览、评论、收藏、点赞或分享内容时,我们会收集您关注的账号,并向您展示您关注账号发布内容。
|
||||
</p>
|
||||
<p>
|
||||
(2)您使用推荐通讯录好友功能时,我们会请求通讯录权限,并将通讯录中的信息进行高强度加密算法处理后,用于向您推荐通信录中的好友。通讯录信息属于个人敏感信息,拒绝提供该信息仅会使您无法使用上述功能,但不影响您正常使用“光环助手”及相关服务的其他功能。
|
||||
</p>
|
||||
|
||||
<p><b>3.10收集、使用个人信息目的变更</b></p>
|
||||
<p>
|
||||
(1)请您了解,随着我们业务的发展,可能会对“光环助手”的功能和提供的服务有所调整变化。
|
||||
</p>
|
||||
<p>
|
||||
(2)原则上,当新功能或服务与我们当前提供的功能或服务相关时,收集与使用的个人信息将与原处理目的具有直接或合理关联。
|
||||
</p>
|
||||
<p>
|
||||
(3)在与原处理目的无直接或合理关联的场景下,我们收集、使用您的个人信息,会再次进行告知,并征得您的同意。
|
||||
</p>
|
||||
|
||||
<p><b>3.11依法豁免征得同意收集和使用的个人信息</b></p>
|
||||
<p>
|
||||
请您理解,在下列情形中,根据法律法规及相关国家标准,我们收集和使用您的个人信息无需征得您的授权同意:
|
||||
</p>
|
||||
<p>(1)与国家安全、国防安全直接相关的;</p>
|
||||
<p>(2)与公共安全、公共卫生、重大公共利益直接相关的;</p>
|
||||
<p>(3)与犯罪侦查、起诉、审判和判决执行等直接相关的;</p>
|
||||
<p>
|
||||
(4)出于维护个人信息主体或其他个人的生命、财产等重大合法权益但又很难得到本人同意的;
|
||||
</p>
|
||||
<p>(5)所收集的您的个人信息是您自行向社会公众公开的;</p>
|
||||
<p>
|
||||
(6)从合法公开披露的信息中收集的您的个人信息的,如合法的新闻报道、政府信息公开等渠道;
|
||||
</p>
|
||||
<p>(7)根据您的要求签订或履行合同所必需的;</p>
|
||||
<p>
|
||||
(8)用于维护软件及相关服务的安全稳定运行所必需的,例如发现、处置软件及相关服务的故障;
|
||||
</p>
|
||||
<p>(9)为合法的新闻报道所必需的;</p>
|
||||
<p>
|
||||
(10)学术研究机构基于公共利益开展统计或学术研究所必要,且对外提供学术研究或描述的结果时,对结果中所包含的个人信息进行去标识化处理的。
|
||||
</p>
|
||||
<p>(11)法律法规规定的其他情形。</p>
|
||||
<p>
|
||||
特别提示您注意,如信息无法单独或结合其他信息识别到您的个人身份,其不属于法律意义上您的个人信息;当您的信息可以单独或结合其他信息识别到您的个人身份时或我们将无法与任何特定个人信息建立联系的数据与其他您的个人信息结合使用时,这些信息在结合使用期间,将作为您的个人信息按照本隐私政策处理与保护。
|
||||
</p>
|
||||
|
||||
<p><b>4.我们如何存储个人信息</b></p>
|
||||
<p><b>4.1 存储地点</b></p>
|
||||
<p>
|
||||
(1)我们依照法律法规的规定,将在境内运营过程中收集和产生的您的个人信息存储于中华人民共和国境内。
|
||||
</p>
|
||||
<p>
|
||||
(2)目前,我们不会将上述信息传输至境外,如果我们向境外传输,我们将会遵循相关国家规定或者征求您的同意。
|
||||
</p>
|
||||
<p><b>4.2存储期限</b></p>
|
||||
<p>
|
||||
(1)我们仅在为提供“光环助手”及服务之目的所必需的期间内保留您的个人信息:您发布的信息、评论、点赞及相关信息,在您未撤回、删除或未注销账号期间,我们会保留相关信息。
|
||||
</p>
|
||||
<p>
|
||||
(2)超出必要期限后,我们将对您的个人信息进行删除或匿名化处理,但法律法规另有规定的除外。
|
||||
</p>
|
||||
|
||||
<p><b>5.我们如何保护个人信息的安全</b></p>
|
||||
<p>
|
||||
(1)我们非常重视您个人信息的安全,将努力采取合理的安全措施(包括技术方面和管理方面)来保护您的个人信息,防止您提供的个人信息被不当使用或未经授权的情况下被访问、公开披露、使用、修改、损坏、丢失或泄漏。
|
||||
</p>
|
||||
<p>
|
||||
(2)我们会使用不低于行业同行的加密技术、匿名化处理及相关合理可行的手段保护您的个人信息,并使用安全保护机制防止您的个人信息遭到恶意攻击。
|
||||
</p>
|
||||
<p>
|
||||
(3)我们会建立专门的安全部门、安全管理制度、数据安全流程保障您的个人信息安全。我们采取严格的数据使用和访问制度,确保只有授权人员才可访问您的个人信息,并适时对数据和技术进行安全审计。
|
||||
</p>
|
||||
<p>
|
||||
(4)尽管已经采取了上述合理有效措施,并已经遵守了相关法律规定要求的标准,但请您理解,由于技术的限制以及可能存在的各种恶意手段,在互联网行业,即便竭尽所能加强安全措施,也不可能始终保证信息百分之百的安全,我们将尽力确保您提供给我们的个人信息的安全性。
|
||||
</p>
|
||||
<p>
|
||||
(5)您知悉并理解,您接入我们的服务所用的系统和通讯网络,有可能因我们可控范围外的因素而出现问题。因此,我们强烈建议您采取积极措施保护个人信息的安全,包括但不限于使用复杂密码、定期修改密码、不将自己的账号密码及相关个人信息透露给他人。
|
||||
</p>
|
||||
<p>
|
||||
(6)我们会制定应急处理预案,并在发生用户信息安全事件时立即启动应急预案,努力阻止这些安全事件的影响和后果扩大。一旦发生用户信息安全事件(泄露、丢失)后,我们将按照法律法规的要求,及时向您告知:安全事件的基本情况和可能的影响、我们已经采取或将要采取的处置措施、您可自主防范和降低风险的建议、对您的补救措施。我们将及时将事件相关情况以推送通知、邮件、信函、短信及相关形式告知您,难以逐一告知时,我们会采取合理、有效的方式发布公告。同时,我们还将按照相关监管部门要求,上报用户信息安全事件的处置情况。
|
||||
</p>
|
||||
<p>
|
||||
(7)您一旦离开“光环助手”及相关服务,浏览或使用其他网站、服务及内容资源,我们将没有能力和直接义务保护您在光环助手及相关服务之外的软件、网站提交的任何个人信息,无论您登录、浏览或使用上述软件、网站是否基于“光环助手”的链接或引导。
|
||||
</p>
|
||||
|
||||
<p><b>6.管理您的个人信息</b></p>
|
||||
<p>
|
||||
我们非常重视您对个人信息的管理,并尽全力保护您的隐私,对于您个人信息的查询、访问、修改、删除、撤回同意授权、注销账号、投诉举报以及设置隐私功能的相关权利,以使您有能力保障您的隐私和信息安全。
|
||||
</p>
|
||||
|
||||
<p><b>6.1 访问、删除、更正您的个人信息</b></p>
|
||||
<p>(1)访问个人账号信息</p>
|
||||
<p>a. 您可以查询、访问您的头像、用户名、简介、性别、生日、地区</p>
|
||||
<p>b.您可以在光环助手的“个人中心”中进行查询、访问。</p>
|
||||
<p>(2)查询访问、更正、取消您关注账号、查询访问粉丝、访客信息</p>
|
||||
<p>a.进入“关注”在关注列表中查询、访问、取消关注您关注的账号。</p>
|
||||
<p>
|
||||
(3)查询访问、更改、删除您的收藏、点赞、浏览记录、阅读历史记录、搜索历史历史记录
|
||||
</p>
|
||||
<p>
|
||||
a.点击“我的”—点击“我的收藏”、
|
||||
“我的点赞”、或“浏览历史”进入查询访问、删除;
|
||||
</p>
|
||||
<p>b.点击搜索栏—删除搜索“历史记录”</p>
|
||||
<p>c.您可以通过点击“系统设置”—点击“清理缓存”。</p>
|
||||
<p>(4)投诉举报</p>
|
||||
<p>a.您可按照我们公示的制度进行投诉或举报。</p>
|
||||
<p>
|
||||
b.如果您认为您的个人信息权利可能受到侵害,或者发现侵害个人信息权利的线索(例如:认为我们收集您的个人信息违反法律规定或者双方约定),“我的”—“基础功能”—“用户反馈”,进入用户反馈界面与我们联系。
|
||||
</p>
|
||||
<p>c.我们核查后会及时反馈您的投诉与举报。</p>
|
||||
<p>(5)访问隐私政策</p>
|
||||
<p>
|
||||
a.您可以在注册页面,或者在登录个人账号“设置”—“关于”查看本隐私政策的全部内容
|
||||
</p>
|
||||
<p>
|
||||
b.请您了解,本隐私政策中所述的“光环助手”及相关服务可能会根据您所使用的手机型号、系统版本、软件应用程序版本、移动客户端等因素而有所不同。最终的产品和服务以您所使用的“光环助手”软件及相关服务为准。
|
||||
</p>
|
||||
<p>(6)停止运营向您告知</p>
|
||||
<p>
|
||||
a.如我们停止运营,我们将及时停止收集您个人信息的活动,将停止运营的通知以逐一送达或公告的形式通知您,并对所持有的您的个人信息进行删除或匿名化处理。
|
||||
</p>
|
||||
|
||||
<p><b>6.2 注销您的个人账号</b></p>
|
||||
<p>
|
||||
如需要注销个人账户,可前往光环助手,我的光环> 设置 > 账号与安全 >
|
||||
账号安全中心 >
|
||||
注销账号,进行注销操作。请您注意,如果您选择注销光环助手账户,那么您的光环助手账号将不可被使用且相关账号信息将被删除,包括所发布的所有内容,包括:提问、回答、社区文章、评论、关注的人等均会被清空;您将无法再通过光环助手账号登录光环助手的服务(但不会影响您使用无需账号登录即可使用的服务和功能)
|
||||
</p>
|
||||
<p>
|
||||
当您注销账户后,除法律法规要求我们保存相关信息的情况外,我们将停止为您提供相应的产品(或服务),并在60个工作日内删除或匿名化您的个人信息。
|
||||
</p>
|
||||
|
||||
<p><b>7.未成年人条款</b></p>
|
||||
<p>
|
||||
a.若您是未满18周岁的未成年人,在使用“光环助手”及相关服务前,应在您的父母或其他监护人监护、指导下共同阅读并同意本隐私政策。
|
||||
</p>
|
||||
<p>
|
||||
b.我们根据国家相关法律法规的规定保护未成年人的个人信息,只会在法律允许、父母或其他监护人明确同意或保护未成年人所必要的情况下收集、使用、储存、共享、转让或披露未成年人的个人信息;如果我们发现在未事先获得可证实的父母同意的情况下收集了未成年人的个人信息,则会设法尽快删除相关信息。
|
||||
</p>
|
||||
<p>
|
||||
c.若您是未成年人的监护人,当您对您所监护的未成年人的个人信息有相关疑问时,请通过公司本隐私政策公示的联系方式与我们联系。
|
||||
</p>
|
||||
<p><b>8.隐私政策的修订和通知</b></p>
|
||||
<p>
|
||||
(1)为了给您提供更好的服务,光环助手及相关服务将不时更新与变化,我们会适时对本隐私政策进行修订,这些修订构成本隐私政策的一部分并具有等同于本隐私政策的效力,未经您明确同意,我们不会削减您依据当前生效的本隐私政策所应享受的权利。
|
||||
</p>
|
||||
<p>
|
||||
(2)本隐私政策更新后,我们会在光环助手发出更新版本,并在更新后的条款生效前通过公告或其他适当的方式提醒您更新的内容,以便您及时了解本隐私政策的最新版本。
|
||||
</p>
|
||||
<p><b>9.联系我们</b></p>
|
||||
<p>
|
||||
如果您对我们的隐私政策及对您个人信息的处理有任何疑问、意见、建议、或投诉,请通过以下方式与我们联系
|
||||
</p>
|
||||
<p>广州加兔网络科技有限公司</p>
|
||||
<p>注册地址:广州市番禺区市桥街丹山村青云一街2号229房</p>
|
||||
<p>在线客服QQ:350473523</p>
|
||||
<p>信息保护事务联系电话:020-85526920</p>
|
||||
<p>在一般情况下,我们会在15个工作日内对您的请求予以答复</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -280,12 +280,8 @@ RE.replaceAllDfImage = function(imgRuleFlag, gifRuleFlag) {
|
||||
i--;
|
||||
} else {
|
||||
if(img.src.indexOf(".gif") > 0) {
|
||||
if(gifRuleFlag.indexOf(",default") > 0) {
|
||||
img.style.cssText = "max-width: 100%; display:block; margin:8px auto; height: auto;"
|
||||
img.src = img.src.split("?")[0] + gifRuleFlag
|
||||
}
|
||||
img.src = img.src.split("?")[0] + gifRuleFlag
|
||||
} else {
|
||||
img.style.cssText = "max-width: 100%; display:block; margin:8px auto; height: auto;"
|
||||
img.src = img.src.split("?")[0] + imgRuleFlag
|
||||
}
|
||||
}
|
||||
@ -301,7 +297,7 @@ RE.hideShowBigPic = function() {
|
||||
var img = imgs[i];
|
||||
var imageClassName = img.className;
|
||||
if (imageClassName == "image-link" || img.className == "poster") continue;
|
||||
if(img.src.indexOf(",thumbnail") > 0 && img.src.indexOf(".gif") == -1) {
|
||||
if (img.src.indexOf(".gif") == -1) {
|
||||
j++;
|
||||
}
|
||||
}
|
||||
@ -327,7 +323,6 @@ RE.replaceDfImageByUrl = function(imgUrl, imgRuleFlag, gifRuleFlag) {
|
||||
var imageClassName = img.className;
|
||||
if (imageClassName == "image-link" || img.className == "poster") continue;
|
||||
if (img.src.indexOf(imgUrl) != -1) {
|
||||
img.style.cssText = "max-width: 100%; display:block; margin:8px auto; height: auto;"
|
||||
if(img.src.indexOf(".gif") > 0) {
|
||||
img.src = img.src.split("?")[0] + gifRuleFlag
|
||||
} else {
|
||||
|
||||
@ -1,493 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head lang="en">
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
<title>光环助手软件许可及服务协议</title>
|
||||
</head>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 14px;
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.top {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
padding: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.margintop {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.left-indent {
|
||||
margin-left: 20px;
|
||||
}
|
||||
.red-style {
|
||||
color: red;
|
||||
}
|
||||
.bold-font {
|
||||
font-weight: bold;
|
||||
}
|
||||
span.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.link-text {
|
||||
color: #005ad0;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<h3 class="top">光环助手软件许可及服务协议</h3>
|
||||
<h5 class="title">首部及导言</h5>
|
||||
<p>欢迎使用光环助手软件许可及服务</p>
|
||||
<p>
|
||||
各位用户在使用光环助手前,请您务必审慎阅读、并充分理解本协议中的各项条款,
|
||||
<span class="bold">
|
||||
特别是免除或者限制责任的条款,以及开通或使用某项服务的单独协议,并选择接受或不接受。
|
||||
</span>
|
||||
除非您已阅读并接受本协议所有条款,否则您无权下载、安装或使用本软件及相关服务。您的下载、安装、使用、登录等行为即视为您已阅读并同意上述协议的约束。
|
||||
</p>
|
||||
<p>如果您未满18周岁,请在法定监护人的陪同下阅读本协议及其他上述协议。</p>
|
||||
<h5 class="title margintop">一、权利声明</h5>
|
||||
<p>
|
||||
“光环助手”的一切知识产权,以及与“光环助手”相关的所有信息内容,包括但不限于:文字表述及其组合、图标、图饰、图像、图表、色彩、界面设计、版面框架、有关数据、附加程序、印刷材料或电子文档等均为光环助手所有,受著作权法和国际著作权条约以及其他知识产权法律法规的保护。
|
||||
</p>
|
||||
<h5 class="title margintop">二、软件使用规范</h5>
|
||||
<p>
|
||||
2.1
|
||||
本软件是基于Android(安卓)系统手机、平板电脑(PAD)等设备开发的一款软件,提供注册登录、手机游戏管理、游戏推荐、文章阅读等功能
|
||||
</p>
|
||||
<p>2.2 软件的下载、安装和使用</p>
|
||||
<p>
|
||||
本软件为免费软件,用户可以非商业性、无限制数量地从光环授权的渠道下载、安装及使用本软件。
|
||||
</p>
|
||||
<p>
|
||||
<span class="bold">
|
||||
如果您从未经光环授权的第三方获取本软件或与本软件名称相同的安装程序,光环无法保证该软件能够正常使用,并对因此给您造成的损失不予负责。
|
||||
</span>
|
||||
</p>
|
||||
<p>2.3 软件的复制、分发和传播</p>
|
||||
<p>
|
||||
本产品以学习、研究交流为目的。用户可以非商业性、无限制数量地复制、分发和传播本软件产品。但必须保证每一份复制、分发和传播都是完整和真实的,
|
||||
包括所有有关本软件产品的软件、电子文档, 版权和商标,亦包括本协议。
|
||||
</p>
|
||||
<p>2.4 软件的更新</p>
|
||||
<p>
|
||||
为了改善用户体验、完善服务内容,光环将不断努力开发新的服务,并为您不时提供软件更新(这些更新可能会采取软件替换、修改、功能强化、版本升级等形式)。为了保证本软件及服务的安全性和功能的一致性,光环有权不经向您特别通知而对软件进行更新,或者对软件的部分功能效果进行改变或限制。本软件新版本发布后,旧版本的软件可能无法使用。光环不保证旧版本软件继续可用及相应的客户服务,请您随时核对并下载最新版本。
|
||||
</p>
|
||||
<h5 class="title margintop">三、用户使用须知</h5>
|
||||
<p>3.1 您理解并同意:</p>
|
||||
<p>
|
||||
为了向您提供有效的服务,本软件会利用您移动通讯终端的处理器和带宽等资源。本软件使用过程中可能产生数据流量的费用,用户需自行向运营商了解相关资费信息,并自行承担相关费用.
|
||||
</p>
|
||||
<p>3.2 您理解并同意:</p>
|
||||
<p>
|
||||
由本软件进行收录、推荐并提供下载、升级服务的第三方软件,由第三方享有一切合法权利,光环并不能识别用户利用本软件下载、安装的第三方软件是否有合法来源。
|
||||
<span class="bold">
|
||||
因第三方软件引发的任何纠纷,由该第三方负责解决,光环不承担任何责任。
|
||||
</span>
|
||||
同时光环不对第三方软件或技术提供客服支持,若用户需要获取支持,请与该软件或技术提供商联系,若您为有关软件的权利人,不愿本软件为您的软件提供用户下载、安装、使用的服务,也可按本协议约定的联系方式联系我们,我们将会积极配合进行处理。
|
||||
</p>
|
||||
<p>3.3 您理解并同意:</p>
|
||||
<p>
|
||||
<span class="bold">
|
||||
如果因您不正当使用本软件造成了不良影响,或因使用本软件造成的包括但不限于数据异常等问题,均由使用者自行承担,光环团队不对任意类型的使用结果承担责任;
|
||||
</span>
|
||||
</p>
|
||||
<p>3.4 您理解并同意:</p>
|
||||
<p>
|
||||
本软件不含任何破坏用户移动通讯设备数据和获取用户隐私信息的恶意代码,不会泄露用户的个人信息和隐私;
|
||||
</p>
|
||||
<p>3.5 您理解并同意:</p>
|
||||
<p>
|
||||
<span class="bold">
|
||||
对于包括但不限于互联网网络故障、计算机故障、手机故障或病毒、信息损坏或丢失、计算机系统问题,或其它任何基于不可抗力原因而产生的损失,光环团队不承担任何责任。
|
||||
</span>
|
||||
</p>
|
||||
<p>3.6 您理解并同意:</p>
|
||||
<p>光环发布、收录的文章均不代表光环立场。</p>
|
||||
<p>3.7 您理解并同意:</p>
|
||||
<p>
|
||||
为实现软件包括但不限于集中展示、下载、安装、卸载等游戏管理功能以及文章优先推荐功能,本软件会检测用户手机中已安装游戏的包名、版本号、版本名、游戏名称信息。除征得用户明确同意和法律明确规定外,光环不会向第三方泄露任何的用户信息
|
||||
</p>
|
||||
<p>3.8 您理解并同意:</p>
|
||||
<p>
|
||||
用户应在遵守法律及本协议的前提下使用本软件。用户无权实施包括但不限于下列行为:
|
||||
</p>
|
||||
3.8.1 不得删除或者改变本软件上的所有权利管理电子信息
|
||||
<br />
|
||||
3.8.2 不得故意避开或者破坏著作权人为保护本软件著作权而采取的技术措施;
|
||||
<br />
|
||||
3.8.3 用户不得利用本软件误导、欺骗他人;
|
||||
<br />
|
||||
3.8.4
|
||||
违反国家规定,对计算机信息系统功能进行删除、修改、增加、干扰,造成计算机信息系统不能正常运行;
|
||||
<br />
|
||||
3.8.5 未经允许,进入计算机信息网络或者使用计算机信息网络资源;
|
||||
<br />
|
||||
3.8.6 未经允许,对计算机信息网络功能进行删除、修改或者增加;
|
||||
<br />
|
||||
3.8.7
|
||||
未经允许,对计算机信息网络中存储、处理或者传输的数据和应用程序进行删除、修改或者增加;
|
||||
<br />
|
||||
3.8.8 破坏本软件系统或网站的正常运行,故意传播计算机病毒等破坏性程序;
|
||||
<br />
|
||||
3.8.9 其他任何危害计算机信息网络安全的行为。
|
||||
<br />
|
||||
<p>3.9 您理解并同意:</p>
|
||||
<p>
|
||||
本软件经过详细的测试,但不能保证与所有的软硬件系统完全兼容,不能保证本软件完全没有错误。如果出现不兼容及软件错误的情况,用户可通过各反馈途径将情况告知光环团队,获得技术支持。如果无法解决兼容性问题,用户可以删除本软件。
|
||||
</p>
|
||||
<h5 class="title margintop">四、争议解决处理</h5>
|
||||
<p>
|
||||
本《协议》的解释、效力及纠纷的解决,适用于中华人民共和国法律。若用户和光环助手之间发生任何纠纷或争议,首先应友好协商解决,协商不成的,用户在此完全同意将纠纷或争议提交光环助手所在地法院管辖
|
||||
</p>
|
||||
|
||||
<p class="title margintop"><b>五、第三方SDK接入说明</b></p>
|
||||
<p>
|
||||
为保障光环助手App相关功能的实现与应用安全稳定的运行,我们会接入由第三方提供的软件开发包(SDK)实现相关功能。
|
||||
<br />
|
||||
我们会对合作方获取有关信息的软件工具开发包(SDK)进行严格的安全检测,并与授权合作伙伴约定严格的数据保护措施,令其按照我们的委托目的、服务说明、本隐私权政策以及其他任何相关的保密和安全措施来处理个人信息。
|
||||
<br />
|
||||
<span class="red-style">
|
||||
下方为整个光环助手
|
||||
<span class="bold">所有版本</span>
|
||||
内接入的所有信息收集类第三方SDK的权限说明,因隐私政策会因光环助手版本迭代而新接入SDK或停止合作部分SDK,方便照顾
|
||||
<span class="bold">所有版本</span>
|
||||
的用户查看自己SDK第三方权限说明。
|
||||
<br />
|
||||
我们对涉及用户信息使用的SDK相关情况进行了逐项列举,具体如下:
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p class="margintop red-style bold-font"><b>(1)数据统计类</b></p>
|
||||
<p>1.头条推广</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">
|
||||
https://ad.oceanengine.com/openapi/index.html
|
||||
</span>
|
||||
</p>
|
||||
<p>SDK包名:com.bytedance</p>
|
||||
<p>企业主体:北京有竹居网络技术有限公司</p>
|
||||
<p>使用目的:用于广告流量统计相关服务</p>
|
||||
<p>
|
||||
收集信息类型:设备品牌、型号、软件系统相关信息、安卓(oaid、无线网SSID名称、WiFi路由器MAC地址、设备MAC地址、IMEI、地理位置)
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
https://ad.oceanengine.com/openapi/register/protocol.html?rid=vo25p8sfqde
|
||||
</span>
|
||||
</p>
|
||||
<p>2.talkingdata统计</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">http://www.talkingdata.com/</span>
|
||||
</p>
|
||||
<p>SDK包名:com.tendcloud</p>
|
||||
<p>企业主体:北京腾云天下科技有限公司</p>
|
||||
<p>使用目的:用于统计数据和效果分析,以便为用户提供更好的服务</p>
|
||||
<p>收集信息类型:设备信息、网络信息、位置信息、应用信息</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
http://www.talkingdata.com/privacy.jsp?languagetype=zh_cn
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p>3.腾讯MTA</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://mta.qq.com/mta/</span>
|
||||
</p>
|
||||
<p>SDK包名:com.tencent</p>
|
||||
<p>企业主体:深圳市腾讯计算机系统有限公司</p>
|
||||
<p>使用目的:用于统计数据和效果分析</p>
|
||||
<p>
|
||||
收集信息类型:Mac地址、唯一设备识别码(IMEI、android
|
||||
ID、IDFA、OPENUDID、GUID/SIM卡IMSI信息)、地理位置信息
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
https://mta.qq.com/mta/ctr_index/protocol_v2/
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p>4.腾讯广点通</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://developers.e.qq.com/</span>
|
||||
</p>
|
||||
<p>SDK包名:com.tencent</p>
|
||||
<p>企业主体:深圳市腾讯计算机系统有限公司</p>
|
||||
<p>使用目的:用于广告流量统计相关服务</p>
|
||||
<p>
|
||||
收集信息类型:
|
||||
个人常用设备信息(IMEI、AndroidID)、位置信息,IP地址、软件版本号
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">https://e.qq.com/optout.html</span>
|
||||
</p>
|
||||
|
||||
<p class="margintop red-style bold-font"><b>(2)社交登录类</b></p>
|
||||
<p>5.微信登录分享</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://open.weixin.qq.com/</span>
|
||||
</p>
|
||||
<p>SDK包名:com.tencent.mm.opensdk</p>
|
||||
<p>企业主体:深圳市腾讯计算机系统有限公司</p>
|
||||
<p>使用目的:用于支持微信登录、分享</p>
|
||||
<p>
|
||||
收集信息类型:个人常用设备信息(MAC地址、IMEI、AndroidID)、硬件型号、操作系统类型、软件信息(软件版本号、浏览器类型)、IP地址、服务日志信息、通讯日志信息
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">https://privacy.tencent.com/</span>
|
||||
</p>
|
||||
|
||||
<p>6.QQ登录分享</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://connect.qq.com/</span>
|
||||
</p>
|
||||
<p>SDK包名:com.tentcent</p>
|
||||
<p>企业主体:深圳市腾讯计算机系统有限公司</p>
|
||||
<p>使用目的:用于支持QQ登录、分享</p>
|
||||
<p>
|
||||
收集信息类型:个人常用设备信息(MAC地址、IMEI、AndroidID、IMSI、ICCID、序列号)、设备型号、操作系统版本、软件信息(软件版本号、浏览器类型)、网络信息、IP地址、服务日志信息、通讯日志信息
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
https://wiki.connect.qq.com/qq互联sdk隐私保护声明
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p>7.微博登录分享</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">http://open.weibo.com/authentication</span>
|
||||
</p>
|
||||
<p>SDK包名:com.sina.weibo.sdk</p>
|
||||
<p>企业主体:北京微梦创科网络技术有限公司</p>
|
||||
<p>使用目的:用于支持微博登录、分享</p>
|
||||
<p>
|
||||
收集信息类型:个人常用设备信息(MAC地址、IMEI、AndroidID、IMSI、ICCID、序列号)、网络信息、应用列表,硬件型号、操作系统类型、软件信息(软件版本号、浏览器类型)、IP地址、服务日志信息、通讯日志信息
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">https://open.weibo.com/wiki/开发者协议</span>
|
||||
</p>
|
||||
|
||||
<p>8.头条抖音登录</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://open.douyin.com/platform</span>
|
||||
</p>
|
||||
<p>SDK包名:com.bytedance.sdk</p>
|
||||
<p>企业主体:北京字节跳动科技有限公司</p>
|
||||
<p>使用目的:用于支持抖音登录</p>
|
||||
<p>
|
||||
收集信息类型:个人常用设备信息(MAC地址、IMEI、AndroidID)、硬件型号、操作系统类型、软件信息(软件版本号、浏览器类型)、IP地址、服务日志信息、通讯日志信息
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
https://www.douyin.com/agreements/?id=6773901168964798477
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p class="margintop red-style bold-font"><b>(3)推送通知类</b></p>
|
||||
<p>9.友盟推送</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://www.umeng.com/push</span>
|
||||
</p>
|
||||
<p>SDK包名:com.umeng</p>
|
||||
<p>企业主体:北京友盟网络科技有限公司</p>
|
||||
<p>使用目的:用于游戏相关信息的提醒通知</p>
|
||||
<p>
|
||||
收集信息类型:Mac地址、唯一设备识别码(IMEI、android
|
||||
ID、IDFA、OPENUDID、GUID/SIM卡IMSI信息)、地理位置信息
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
https://www.umeng.com/page/policy?spm=a213m0.14063960.0.0.7f626e72hx3nnv
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p class="margintop red-style bold-font"><b>(4)其他功能类</b></p>
|
||||
<p>10.阿里云反爬虫</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://www.aliyun.com/product/antibot</span>
|
||||
</p>
|
||||
<p>SDK包名:com.alibaba.wireless</p>
|
||||
<p>企业主体:阿里巴巴网络技术有限公司</p>
|
||||
<p>使用目的:为APP提供网络应用安全防护</p>
|
||||
<p>
|
||||
收集信息类型:设备相关信息(例如设备型号、操作系统版本、设备设置、唯一设备标识符等软硬件特征信息)、设备所在位置相关信息(例如IP地址、GPS位置以及能够提供相关信息的Wi-Fi接入点、蓝牙和基站等传感器信息)。
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
http://terms.aliyun.com/legal-agreement/terms/suit_bu1_ali_cloud/suit_bu1_ali_cloud201902141711_54837.html?spm=a2c4g.11186623.J_9220772140.81.b7574832gmk0vr
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p>11.腾讯bugly</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://bugly.qq.com/v2/</span>
|
||||
</p>
|
||||
<p>SDK包名:com.tencent.bugly</p>
|
||||
<p>企业主体:深圳市腾讯计算机系统有限公司</p>
|
||||
<p>使用目的:APP异常上报</p>
|
||||
<p>
|
||||
收集信息类型:设备及应用信息。如:设备名称、设备识别符、硬件型号、操作系统版本、应用程序版本
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">https://bugly.qq.com/v2/contract</span>
|
||||
</p>
|
||||
|
||||
<p>12.阿里云文件上传</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://www.alibabacloud.com/zh</span>
|
||||
</p>
|
||||
<p>SDK包名:com.alibaba.sdk.android</p>
|
||||
<p>SDK包名:com.alibaba.sdk.android</p>
|
||||
<p>企业主体:阿里巴巴网络技术有限公司</p>
|
||||
<p>使用目的:用于支持用户上传视频等相关内容</p>
|
||||
<p>
|
||||
收集信息类型:设备相关信息(例如设备型号、操作系统版本、设备设置、唯一设备标识符等软硬件特征信息)、设备所在位置相关信息(例如IP地址、GPS位置以及能够提供相关信息的Wi-Fi接入点、蓝牙和基站等传感器信息)。
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
http://terms.aliyun.com/legal-agreement/terms/suit_bu1_ali_cloud/suit_bu1_ali_cloud201902141711_54837.html?spm=a2c4g.11186623.J_9220772140.81.b7574832gmk0vr
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p>13.阿里云日志上传</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://www.alibabacloud.com/zh</span>
|
||||
</p>
|
||||
<p>SDK包名:com.aliyun.sls.android.sdk</p>
|
||||
<p>企业主体:阿里巴巴网络技术有限公司</p>
|
||||
<p>
|
||||
使用目的:通过网络日志分析这些信息以便更及时响应您的帮助请求,以及用于改进服务
|
||||
</p>
|
||||
<p>
|
||||
收集信息类型:设备相关信息(例如设备型号、操作系统版本、设备设置、唯一设备标识符等软硬件特征信息)、设备所在位置相关信息(例如IP地址、GPS位置以及能够提供相关信息的Wi-Fi接入点、蓝牙和基站等传感器信息)。
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">
|
||||
http://terms.aliyun.com/legal-agreement/terms/suit_bu1_ali_cloud/suit_bu1_ali_cloud201902141711_54837.html?spm=a2c4g.11186623.J_9220772140.81.b7574832gmk0vr
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p>14.容联七陌</p>
|
||||
<p>
|
||||
SDK官网:
|
||||
<span class="link-text">https://www.7moor.com/developer</span>
|
||||
</p>
|
||||
<p>SDK包名:com.m7.imkfsdk</p>
|
||||
<p>企业主体:北京七陌科技有限公司</p>
|
||||
<p>使用目的:用于提供对应在线客服功能</p>
|
||||
<p>
|
||||
收集信息类型:设备相关信息(设备名称、设备型号、硬件序列号、操作系统和应用程序版本及类型、语言设置、分辨率、移动终端随机存储内存、摄像头/相册、通讯录权限等)
|
||||
</p>
|
||||
<p>
|
||||
隐私政策链接:
|
||||
<span class="link-text">http://m.7moor.com/72/57/p5077783560e807/</span>
|
||||
</p>
|
||||
|
||||
<h5 class="title margintop">六、关于获取手机设备信息的说明</h5>
|
||||
<div>
|
||||
(1)为方便区分每个用户的个人信息等,本软件需获取用户的手机设备信息,用于游戏主动预约、论坛互动交流后进行推送等用户相关的行为
|
||||
<br />
|
||||
(2)为了保障软件与服务的安全运行,我们会收集您的硬件型号、操作系统版本号、国际移动设备识别码、唯一设备标识符、网络设备硬件地址、IP
|
||||
地址、WLAN接入点、蓝牙、基站、软件版本号、网络接入方式、类型、状态、网络质量数据、操作、使用、服务日志。
|
||||
<br />
|
||||
(3)为了预防恶意程序及安全运营所必需,我们会收集安装的应用信息或正在运行的进程信息、应用程序的总体运行、使用情况与频率、应用崩溃情况、总体安装使用情况、性能数据、应用来源。
|
||||
<br />
|
||||
(4)我们可能使用您的账户信息、设备信息、服务日志信息以及我们关联公司、合作方在获得您授权或依法可以共享的信息,用于判断账户安全、进行身份验证、检测及防范安全事件。
|
||||
<br />
|
||||
(5)具体会发生获取手机设备信息场景如下说明:
|
||||
<br />
|
||||
<p class="left-indent">
|
||||
1) 首次启动光环助手
|
||||
<br />
|
||||
2) 游戏列表/游戏详情/资讯文章详情/搜索结果页-预约功能
|
||||
<br />
|
||||
3) 礼包中心/礼包详情-领取功能
|
||||
<br />
|
||||
4) 评论详情-发送评论功能
|
||||
<br />
|
||||
5) 回答/问题详情-我来回答功能
|
||||
<br />
|
||||
6) 问答首页-提问功能
|
||||
<br />
|
||||
7) 个人主页-发文章功能
|
||||
<br />
|
||||
8) 帖子草稿/我的草稿-编辑功能
|
||||
<br />
|
||||
9) 游戏投稿功能
|
||||
<br />
|
||||
10) 视频投稿-上传视频功能
|
||||
<br />
|
||||
11) 游戏详情-关注游戏功能
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h5 class="title margintop">七、其他</h5>
|
||||
<p>
|
||||
7.1
|
||||
本协议所有条款的标题仅为阅读方便,本身并无实际涵义,不能作为本协议涵义解释的依据。
|
||||
<br />
|
||||
7.2
|
||||
如果本协议中的任何条款无论因何种原因完全或部分无效或不具有执行力,或违反任何适用的法律,则该条款被视为删除,但本协议的其余条款仍应有效并且有约束力。
|
||||
<br />
|
||||
7.3
|
||||
光环有权随时根据有关法律、法规的变化以及公司经营状况和经营策略的调整等修改本协议。修改后的协议会在软件设置内发布。
|
||||
当发生有关争议时,以最新的协议文本为准。如果不同意改动的内容,用户可以自行删除本软件。如果用户继续使用本软件,则视为您接受本协议的变动。
|
||||
<br />
|
||||
<span class="bold">
|
||||
7.4 光环在法律允许的最大范围内对本协议拥有解释权与修改权。
|
||||
</span>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,58 @@
|
||||
package androidx.swiperefreshlayout.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
public class ViewPagerSwipeRefreshLayout extends SwipeRefreshLayout {
|
||||
|
||||
private float startY;
|
||||
private float startX;
|
||||
// 记录viewPager是否拖拽的标记
|
||||
private boolean mIsVpDragger;
|
||||
private final int mTouchSlop;
|
||||
|
||||
public ViewPagerSwipeRefreshLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
int action = ev.getAction();
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// 记录手指按下的位置
|
||||
startY = ev.getY();
|
||||
startX = ev.getX();
|
||||
// 初始化标记
|
||||
mIsVpDragger = false;
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
// 如果viewpager正在拖拽中,那么不拦截它的事件,直接return false;
|
||||
if(mIsVpDragger) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取当前手指位置
|
||||
float endY = ev.getY();
|
||||
float endX = ev.getX();
|
||||
float distanceX = Math.abs(endX - startX);
|
||||
float distanceY = Math.abs(endY - startY);
|
||||
// 如果X轴位移大于Y轴位移,那么将事件交给viewPager处理。
|
||||
if(distanceX > mTouchSlop && distanceX > distanceY) {
|
||||
mIsVpDragger = true;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
// 初始化标记
|
||||
mIsVpDragger = false;
|
||||
break;
|
||||
}
|
||||
// 如果是Y轴位移大于X轴,事件交给swipeRefreshLayout处理。
|
||||
return super.onInterceptTouchEvent(ev);
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Looper;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
@ -25,8 +26,9 @@ import java.lang.Thread.UncaughtExceptionHandler;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import io.sentry.core.Sentry;
|
||||
import io.sentry.Sentry;
|
||||
|
||||
public class AppUncaughtHandler implements UncaughtExceptionHandler {
|
||||
|
||||
@ -38,22 +40,26 @@ public class AppUncaughtHandler implements UncaughtExceptionHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable ex) {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
if (("FinalizerWatchdogDaemon").equals(t.getName())
|
||||
&& e instanceof TimeoutException) {
|
||||
// ignore timeoutException
|
||||
// detail can be found in this didi tech blog post https://mp.weixin.qq.com/s?__biz=MzU1ODEzNjI2NA==&mid=2247487185&idx=2&sn=cf2d9e10053f625bde0f61d246f14870&source=41#wechat_redirect
|
||||
} else {
|
||||
new Thread(() -> {
|
||||
Looper.prepare();
|
||||
Utils.toast(mContext.getApplicationContext(), "\"光环助手\"发生错误");
|
||||
Looper.loop();
|
||||
}
|
||||
});
|
||||
|
||||
saveLocalLog(mContext, ex);
|
||||
Sentry.captureException(ex);
|
||||
restart(mContext);
|
||||
});
|
||||
saveLocalLog(mContext, e);
|
||||
restart(mContext);
|
||||
Sentry.captureException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void restart(final Context context) {
|
||||
// S450 这机器会无限重启循环 : (
|
||||
if ("S450".equals(Build.MODEL)) return;
|
||||
|
||||
// 防止重复奔溃,导致助手一直重启,20秒内不做处理
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
package com.gh.base;
|
||||
|
||||
import static com.gh.common.util.EntranceUtils.KEY_ENTRANCE;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
@ -8,10 +11,13 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Parcel;
|
||||
import android.os.TransactionTooLargeException;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.LinearLayout;
|
||||
@ -19,18 +25,27 @@ import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.view.LayoutInflaterCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
|
||||
import com.gh.base.fragment.BaseFragment;
|
||||
import com.gh.common.constant.Constants;
|
||||
import com.gh.common.tracker.IBusiness;
|
||||
import com.gh.common.util.DialogHelper;
|
||||
import com.gh.common.util.DialogUtils;
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.common.util.EntranceUtils;
|
||||
import com.gh.common.util.EnvHelper;
|
||||
import com.gh.common.util.ExtensionsKt;
|
||||
import com.gh.common.util.MtaHelper;
|
||||
import com.gh.common.util.NetworkUtils;
|
||||
import com.gh.common.util.NightModeUtils;
|
||||
import com.gh.common.util.PackageFlavorHelper;
|
||||
import com.gh.common.util.PackageInstaller;
|
||||
import com.gh.common.util.QuickLoginHelper;
|
||||
import com.gh.common.util.RunningUtils;
|
||||
import com.gh.common.util.SPUtils;
|
||||
import com.gh.common.util.ShareUtils;
|
||||
@ -55,28 +70,32 @@ import org.json.JSONObject;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import kotlin.Pair;
|
||||
import pub.devrel.easypermissions.EasyPermissions;
|
||||
|
||||
import static com.gh.common.util.EntranceUtils.KEY_ENTRANCE;
|
||||
|
||||
/**
|
||||
* 只提供基础的服务(EventBus/ButterKnife/Share/GlobalDialog/Permissions)
|
||||
* 只提供基础的服务(EventBus/Share/GlobalDialog/Permissions)
|
||||
* <p>
|
||||
* 需要工具栏的页面请继承{@link ToolBarActivity}
|
||||
*/
|
||||
|
||||
public abstract class BaseActivity extends BaseAppCompatActivity implements EasyPermissions.PermissionCallbacks {
|
||||
public abstract class BaseActivity extends BaseAppCompatActivity implements EasyPermissions.PermissionCallbacks, IBusiness {
|
||||
|
||||
// global dialog key
|
||||
public final static String DOWNLOAD_HIJACK = "hijack";
|
||||
public final static String LOGIN_EXCEPTION = "loginException";
|
||||
public final static String PLUGGABLE = "plugin";
|
||||
public final static String SIGNATURE_CONFLICT = "signature_conflict";
|
||||
public final static int ID_ROOT_INDICATOR = 999;
|
||||
public final static int ID_NIGHT_INDICATOR = 998;
|
||||
public final int MAX_BUNDLE_SIZE = 300;
|
||||
|
||||
@NonNull
|
||||
protected String mEntrance;
|
||||
|
||||
private boolean mIsExistLogoutDialog;
|
||||
protected boolean mNightMode;
|
||||
public long startPageTime = 0;
|
||||
|
||||
protected final Handler mBaseHandler = new BaseHandler(this);
|
||||
|
||||
@ -115,9 +134,13 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
if (PackageFlavorHelper.IS_TEST_FLAVOR && isAutoResetViewBackgroundEnabled()) {
|
||||
LayoutInflaterCompat.setFactory2(getLayoutInflater(), new CustomLayoutInflaterFactory(this));
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (useEventBus()) EventBus.getDefault().register(this);
|
||||
if (useButterKnife()) ButterKnife.bind(this);
|
||||
mEntrance = getIntent().getStringExtra(KEY_ENTRANCE);
|
||||
if (TextUtils.isEmpty(mEntrance)) {
|
||||
mEntrance = Constants.ENTRANCE_UNKNOWN;
|
||||
@ -127,28 +150,46 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
Utils.log("ACTIVITY_ENTRANCE -> " + mEntrance);
|
||||
}
|
||||
|
||||
disableAutofill();
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
String xapkUnzipActivity = SPUtils.getString(Constants.SP_XAPK_UNZIP_ACTIVITY);
|
||||
String xapkUrl = SPUtils.getString(Constants.SP_XAPK_URL);
|
||||
Utils.log("页面重建了--" + xapkUnzipActivity + "--" + xapkUrl);
|
||||
if (this.getClass().getName().equals(SplashScreenActivity.class.getName())) {
|
||||
if (this.getClass().isAssignableFrom(SplashScreenActivity.class)) {
|
||||
SPUtils.setString(Constants.SP_XAPK_UNZIP_ACTIVITY, "");
|
||||
SPUtils.setString(Constants.SP_XAPK_URL, "");
|
||||
return;
|
||||
}
|
||||
if (this.getClass().getName().equals(xapkUnzipActivity) && !TextUtils.isEmpty(xapkUrl)) {
|
||||
DownloadEntity downloadEntity = DownloadManager.getInstance(this).getDownloadEntityByUrl(xapkUrl);
|
||||
PackageInstaller.install(this, downloadEntity, false);
|
||||
SPUtils.setString(Constants.SP_XAPK_UNZIP_ACTIVITY, "");
|
||||
SPUtils.setString(Constants.SP_XAPK_URL, "");
|
||||
DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntityByUrl(xapkUrl);
|
||||
if (downloadEntity != null) {
|
||||
PackageInstaller.install(this, downloadEntity, false);
|
||||
SPUtils.setString(Constants.SP_XAPK_UNZIP_ACTIVITY, "");
|
||||
SPUtils.setString(Constants.SP_XAPK_URL, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mNightMode = NightModeUtils.INSTANCE.isNightMode(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
startPageTime = System.currentTimeMillis();
|
||||
|
||||
if (BuildConfig.IS_NIGHT_MODE_ON
|
||||
&& !NightModeUtils.INSTANCE.getSystemMode()
|
||||
&& mNightMode != NightModeUtils.INSTANCE.isNightMode(this)) {
|
||||
onNightModeChange();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public void setContentView(View view) {
|
||||
if (BuildConfig.DEBUG || BuildConfig.BUILD_TIME != 0) {
|
||||
if (!(this instanceof SplashScreenActivity) && PackageFlavorHelper.IS_TEST_FLAVOR) {
|
||||
view = getRootViewWithEnvIndicator(view);
|
||||
}
|
||||
super.setContentView(view);
|
||||
@ -188,6 +229,15 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭 editText 自动填充帐号 (我们也用不上),开启的时候有小概率出发 TimeoutException
|
||||
*/
|
||||
private void disableAutofill() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
getWindow().getDecorView().setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
|
||||
}
|
||||
}
|
||||
|
||||
private View getRootViewWithEnvIndicator(View view) {
|
||||
RelativeLayout screenRootView = new RelativeLayout(this);
|
||||
screenRootView.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));
|
||||
@ -196,9 +246,9 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
TextView tv = new TextView(this);
|
||||
String envText = "正式环境";
|
||||
tv.setBackground(ContextCompat.getDrawable(this, R.color.theme));
|
||||
if (BuildConfig.FLAVOR.equals("internal")) {
|
||||
if (EnvHelper.isDevEnv()) {
|
||||
envText = "测试环境";
|
||||
tv.setBackground(ContextCompat.getDrawable(this, R.color.red));
|
||||
tv.setBackground(ContextCompat.getDrawable(this, R.color.theme_red));
|
||||
}
|
||||
tv.setText(envText);
|
||||
tv.setGravity(Gravity.CENTER);
|
||||
@ -206,6 +256,7 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
|
||||
tv.measure(0, 0);
|
||||
tv.setAlpha(0.15F);
|
||||
tv.setId(ID_ROOT_INDICATOR);
|
||||
int height = tv.getMeasuredHeight();
|
||||
int width = tv.getMeasuredWidth();
|
||||
tv.setPadding(DisplayUtils.dip2px(20), 0, DisplayUtils.dip2px(20), 0);
|
||||
@ -223,6 +274,9 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
|
||||
screenRootView.addView(view);
|
||||
screenRootView.addView(ll);
|
||||
if (BuildConfig.IS_NIGHT_MODE_ON) {
|
||||
screenRootView.addView(getNightModeIndicatorView());
|
||||
}
|
||||
|
||||
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) ll.getLayoutParams();
|
||||
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
|
||||
@ -230,6 +284,80 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
return screenRootView;
|
||||
}
|
||||
|
||||
private View getNightModeIndicatorView() {
|
||||
LinearLayout ll = new LinearLayout(this);
|
||||
TextView tv = new TextView(this);
|
||||
String envText = NightModeUtils.INSTANCE.isNightMode(this) ? "夜间模式" : "日间模式";
|
||||
tv.setBackground(ContextCompat.getDrawable(this, R.color.theme));
|
||||
tv.setText(envText);
|
||||
tv.setGravity(Gravity.CENTER);
|
||||
tv.setTextColor(Color.WHITE);
|
||||
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
|
||||
tv.measure(0, 0);
|
||||
tv.setAlpha(NightModeUtils.INSTANCE.isNightMode(this) ? 0.8F : 0.15F);
|
||||
tv.setId(ID_NIGHT_INDICATOR);
|
||||
int height = tv.getMeasuredHeight();
|
||||
int width = tv.getMeasuredWidth();
|
||||
tv.setPadding(DisplayUtils.dip2px(20), 0, DisplayUtils.dip2px(20), 0);
|
||||
ll.setTranslationX(DisplayUtils.dip2px(-20));
|
||||
ll.setRotation(-45);
|
||||
ll.addView(tv);
|
||||
ll.setPadding(0, (width - height) / 2, 0, (width - height) / 2);
|
||||
|
||||
if (PackageFlavorHelper.IS_TEST_FLAVOR) {
|
||||
tv.setOnClickListener(v -> {
|
||||
//切换深色模式
|
||||
String mode;
|
||||
String positive;
|
||||
String negative;
|
||||
if (NightModeUtils.INSTANCE.getSystemMode()) {
|
||||
mode = "跟随系统模式";
|
||||
positive = "普通模式";
|
||||
negative = "深色模式";
|
||||
} else if (NightModeUtils.INSTANCE.getNightMode()) {
|
||||
mode = "深色模式";
|
||||
positive = "跟随系统模式";
|
||||
negative = "普通模式";
|
||||
} else {
|
||||
mode = "普通模式";
|
||||
positive = "跟随系统模式";
|
||||
negative = "深色模式";
|
||||
}
|
||||
DialogHelper.showCenterDialog(this, "选择模式", "当前为 " + mode, positive, negative, () -> {
|
||||
if (NightModeUtils.INSTANCE.getSystemMode()) {
|
||||
NightModeUtils.INSTANCE.setNightMode(false);
|
||||
NightModeUtils.INSTANCE.setSystemMode(false);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||
}
|
||||
} else {
|
||||
NightModeUtils.INSTANCE.setSystemMode(true);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
|
||||
}
|
||||
}
|
||||
NightModeUtils.INSTANCE.initNightMode();
|
||||
}, () -> {
|
||||
if (NightModeUtils.INSTANCE.getSystemMode()) {
|
||||
NightModeUtils.INSTANCE.setNightMode(true);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||
}
|
||||
} else {
|
||||
boolean nightMode = NightModeUtils.INSTANCE.getNightMode();
|
||||
NightModeUtils.INSTANCE.setNightMode(!NightModeUtils.INSTANCE.getNightMode());
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
getDelegate().setLocalNightMode(nightMode ? AppCompatDelegate.MODE_NIGHT_NO : AppCompatDelegate.MODE_NIGHT_YES);
|
||||
}
|
||||
}
|
||||
NightModeUtils.INSTANCE.setSystemMode(false);
|
||||
NightModeUtils.INSTANCE.initNightMode();
|
||||
});
|
||||
});
|
||||
}
|
||||
return ll;
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(final EBShowDialog showDialog) {
|
||||
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)
|
||||
@ -237,12 +365,18 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
if (DOWNLOAD_HIJACK.equals(showDialog.getType())) {
|
||||
DialogUtils.showQqSessionDialog(this);// 建议用户联系客服
|
||||
} else if (PLUGGABLE.equals(showDialog.getType())) {
|
||||
DialogUtils.showPluginDialog(this, () -> {
|
||||
DialogHelper.showPluginDialog(this, () -> {
|
||||
if (FileUtils.isEmptyFile(showDialog.getPath())) {
|
||||
toast(R.string.install_failure_hint);
|
||||
} else {
|
||||
PackageInstaller.uninstall(BaseActivity.this, showDialog.getPath());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
} else if (SIGNATURE_CONFLICT.equals(showDialog.getType())) {
|
||||
DialogHelper.showSignatureConflictDialog(this, () -> {
|
||||
PackageInstaller.uninstall(BaseActivity.this, showDialog.getPath());
|
||||
return null;
|
||||
});
|
||||
} else if (LOGIN_EXCEPTION.equals(showDialog.getType())) {
|
||||
if (mIsExistLogoutDialog) return;
|
||||
@ -251,12 +385,19 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
JSONObject object = new JSONObject(showDialog.getPath());
|
||||
JSONObject device = object.getJSONObject("device");
|
||||
String model = device.getString("model");
|
||||
DialogUtils.showAlertDialog(this, "你的账号已在另外一台设备登录"
|
||||
DialogHelper.showCenterDialog(this, "你的账号已在另外一台设备登录"
|
||||
, StringUtils.buildString("(", model, ")")
|
||||
, "知道了", "重新登录"
|
||||
, null
|
||||
, () -> startActivity(LoginActivity.getIntent(BaseActivity.this,
|
||||
"你的账号已在另外一台设备登录多设备-重新登录"))
|
||||
, () -> {
|
||||
}
|
||||
, () -> {
|
||||
if (SPUtils.getBoolean(Constants.SP_HAS_GET_PHONE_INFO) || NetworkUtils.isOpenMobileData(BaseActivity.this)) {
|
||||
QuickLoginHelper.startLogin(BaseActivity.this, "你的账号已在另外一台设备登录多设备-重新登录");
|
||||
} else {
|
||||
startActivity(LoginActivity.getIntent(BaseActivity.this,
|
||||
"你的账号已在另外一台设备登录多设备-重新登录"));
|
||||
}
|
||||
}
|
||||
);
|
||||
mBaseHandler.postDelayed(() -> mIsExistLogoutDialog = false, 5000);
|
||||
} catch (Exception e) {
|
||||
@ -269,6 +410,7 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
if (isFinishing()) {
|
||||
onFinish();
|
||||
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
|
||||
@ -279,11 +421,6 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
/**
|
||||
* 此回调可用于确认当前 activity 已经执行了 finish() 方法并处于 isFinishing 状态
|
||||
*/
|
||||
@ -345,10 +482,6 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean useButterKnife() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
Resources resources = super.getResources();
|
||||
@ -359,4 +492,138 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ActivityThread每次调用onSaveInstanceState时outState大小都会累加,最终会导致{@link TransactionTooLargeException}异常
|
||||
* 解决方案:判断每次获取到的outState大小,当达到300k时手动clear掉
|
||||
*/
|
||||
@Override
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
if (preventRecreateFragmentByFragmentManager()) {
|
||||
outState = discardFragmentFromSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
long bundleSize = getBundleSize(outState);
|
||||
if (bundleSize > MAX_BUNDLE_SIZE * 1024) {
|
||||
outState.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否停用 Activity 重建时 FragmentManager 根据 saveState 自动重建保存的 Fragment 的功能
|
||||
*/
|
||||
protected boolean preventRecreateFragmentByFragmentManager() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private Bundle discardFragmentFromSaveInstanceState(Bundle outState) {
|
||||
if (outState != null) {
|
||||
outState.remove("android:support:fragments");
|
||||
}
|
||||
return outState;
|
||||
}
|
||||
|
||||
private long getBundleSize(Bundle bundle) {
|
||||
long dataSize;
|
||||
Parcel obtain = Parcel.obtain();
|
||||
try {
|
||||
obtain.writeBundle(bundle);
|
||||
dataSize = obtain.dataSize();
|
||||
} finally {
|
||||
obtain.recycle();
|
||||
}
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<String, String> getBusinessId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
onNightModeChange();
|
||||
}
|
||||
|
||||
protected void onNightModeChange() {
|
||||
if (BuildConfig.IS_NIGHT_MODE_ON) {
|
||||
mNightMode = NightModeUtils.INSTANCE.isNightMode(this);
|
||||
TextView tv = findViewById(ID_NIGHT_INDICATOR);
|
||||
if (tv != null) {
|
||||
tv.setText(NightModeUtils.INSTANCE.isNightMode(this) ? "夜间模式" : "日间模式");
|
||||
tv.setAlpha(NightModeUtils.INSTANCE.isNightMode(this) ? 0.8F : 0.15F);
|
||||
}
|
||||
if (isAutoResetViewBackgroundEnabled()) {
|
||||
updateStaticViewBackground(getWindow().getDecorView());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isAutoResetViewBackgroundEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动重置部分 view 的背景颜色/资源
|
||||
*
|
||||
* @param view 父 view
|
||||
*/
|
||||
private void updateStaticViewBackground(View view) {
|
||||
if (view instanceof ViewGroup) {
|
||||
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
|
||||
View child = ((ViewGroup) view).getChildAt(i);
|
||||
updateStaticViewBackground(child);
|
||||
}
|
||||
}
|
||||
|
||||
String backgroundString = (String) view.getTag(CustomLayoutInflaterFactory.TAG_BACKGROUND_ID);
|
||||
String textColorString = (String) view.getTag(CustomLayoutInflaterFactory.TAG_TEXT_COLOR_ID);
|
||||
if (backgroundString != null) {
|
||||
if (backgroundString.startsWith("#")) return;
|
||||
int backgroundId = Integer.parseInt(
|
||||
backgroundString
|
||||
.replace("@", "")
|
||||
.replace("?", "")
|
||||
);
|
||||
|
||||
if (backgroundId != 0) {
|
||||
try {
|
||||
TypedValue value = new TypedValue();
|
||||
getResources().getValue(backgroundId, value, true);
|
||||
|
||||
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
||||
view.setBackgroundColor(ExtensionsKt.toColor(backgroundId, this));
|
||||
} else {
|
||||
view.setBackground(ExtensionsKt.toDrawable(backgroundId, this));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (textColorString != null && view instanceof TextView) {
|
||||
if (textColorString.startsWith("#")) return;
|
||||
int textColorId = Integer.parseInt(
|
||||
textColorString
|
||||
.replace("@", "")
|
||||
.replace("?", "")
|
||||
);
|
||||
|
||||
if (textColorId != 0) {
|
||||
try {
|
||||
final ColorStateList colorStateList = ContextCompat.getColorStateList(this, textColorId);
|
||||
((TextView) view).setTextColor(colorStateList);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -15,9 +15,9 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import butterknife.BindView;
|
||||
|
||||
/**
|
||||
* Created by khy on 15/03/18.
|
||||
@ -27,12 +27,10 @@ public abstract class BaseActivity_TabLayout extends ToolBarActivity implements
|
||||
|
||||
public static final String PAGE_INDEX = "PAGE_INDEX";
|
||||
|
||||
@BindView(R.id.activity_tab_layout)
|
||||
protected TabLayout mTabLayout;
|
||||
@BindView(R.id.activity_view_pager)
|
||||
protected NoScrollableViewPager mViewPager;
|
||||
@BindView(R.id.activity_tab_indicator)
|
||||
protected TabIndicatorView mTabIndicatorView;
|
||||
protected View mDividerLineView;
|
||||
|
||||
protected List<Fragment> mFragmentsList;
|
||||
|
||||
@ -71,6 +69,12 @@ public abstract class BaseActivity_TabLayout extends ToolBarActivity implements
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mTabLayout = findViewById(R.id.activity_tab_layout);
|
||||
mViewPager = findViewById(R.id.activity_view_pager);
|
||||
mTabIndicatorView = findViewById(R.id.activity_tab_indicator);
|
||||
mDividerLineView = findViewById(R.id.dividerLine);
|
||||
|
||||
if (getIntent() != null) mCheckedIndex = getIntent().getIntExtra(PAGE_INDEX, 0);
|
||||
mTabTitleList = new ArrayList<>();
|
||||
initTabTitleList(mTabTitleList);
|
||||
@ -95,7 +99,7 @@ public abstract class BaseActivity_TabLayout extends ToolBarActivity implements
|
||||
String tabTitle = tab.getText() != null ? tab.getText().toString() : "";
|
||||
View tabView = provideTabView(i, tabTitle);
|
||||
if (tabView == null)
|
||||
tabView = BaseFragment_TabLayout.createDefaultTabCustomView(tabTitle);
|
||||
tabView = BaseFragment_TabLayout.createDefaultTabCustomView(this, tabTitle);
|
||||
tab.setCustomView(tabView);
|
||||
}
|
||||
|
||||
@ -130,4 +134,22 @@ public abstract class BaseActivity_TabLayout extends ToolBarActivity implements
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNightModeChange() {
|
||||
super.onNightModeChange();
|
||||
View container = findViewById(R.id.activity_tab_container);
|
||||
if (container != null) {
|
||||
container.setBackgroundColor(ContextCompat.getColor(this, R.color.background_white));
|
||||
for (int i = 0; i < mTabLayout.getTabCount(); i++) {
|
||||
TabLayout.Tab tab = mTabLayout.getTabAt(i);
|
||||
if (tab != null) {
|
||||
BaseFragment_TabLayout.updateTabStyle(tab, tab.isSelected());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mDividerLineView != null) {
|
||||
mDividerLineView.setBackgroundColor(ContextCompat.getColor(this, R.color.divider));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,8 +3,6 @@ package com.gh.base;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
/**
|
||||
* 目前仅提供butterknife bind方法
|
||||
*
|
||||
@ -20,7 +18,6 @@ public abstract class BaseRecyclerViewHolder<T> extends RecyclerView.ViewHolder
|
||||
|
||||
public BaseRecyclerViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
ButterKnife.bind(this, itemView);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.gh.base
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.ClipboardManager
|
||||
@ -9,213 +10,580 @@ import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.widget.CheckBox
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import butterknife.OnClick
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.util.MtaHelper
|
||||
import androidx.lifecycle.Observer
|
||||
import com.gh.common.AppExecutor
|
||||
import com.gh.common.runOnIoThread
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.view.RichEditor
|
||||
import com.gh.gamecenter.CropImageActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.MyVideoEntity
|
||||
import com.gh.gamecenter.qa.answer.edit.AnswerEditActivity
|
||||
import com.gh.gamecenter.qa.editor.GameActivity
|
||||
import com.gh.gamecenter.qa.editor.InsertAnswerWrapperActivity
|
||||
import com.gh.gamecenter.qa.editor.InsertArticleWrapperActivity
|
||||
import com.gh.gamecenter.qa.editor.VideoActivity
|
||||
import com.gh.gamecenter.entity.*
|
||||
import com.gh.gamecenter.qa.editor.*
|
||||
import com.gh.gamecenter.qa.entity.AnswerEntity
|
||||
import com.gh.gamecenter.qa.entity.ArticleEntity
|
||||
import com.gh.gamecenter.qa.entity.EditorInsertEntity
|
||||
import com.gh.gamecenter.video.poster.PosterEditActivity
|
||||
import com.gh.gamecenter.video.upload.UploadManager
|
||||
import com.google.gson.JsonObject
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Util_System_Keyboard
|
||||
import com.lightgame.utils.Utils
|
||||
import com.lightgame.view.CheckableImageView
|
||||
import kotterknife.bindView
|
||||
import io.reactivex.disposables.Disposable
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
|
||||
abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> : ToolBarActivity(),
|
||||
KeyboardHeightObserver, UploadVideoListener {
|
||||
|
||||
val mRichEditor by bindView<RichEditor>(R.id.rich_editor)
|
||||
val mDraftBtn by bindView<TextView>(R.id.draft_btn)
|
||||
lateinit var mRichEditor: RichEditor
|
||||
|
||||
private val mEditorTextNumTv by bindView<TextView>(R.id.editorTextNumTv)
|
||||
private val mEditorFont by bindView<CheckableImageView>(R.id.editor_font)
|
||||
private val mEditorLink by bindView<CheckableImageView>(R.id.editor_link)
|
||||
private val mEditorParagraph by bindView<CheckableImageView>(R.id.editor_paragraph)
|
||||
private val mEditorFontBold by bindView<CheckableImageView>(R.id.editor_font_bold)
|
||||
private val mEditorFontItalic by bindView<CheckableImageView>(R.id.editor_font_italic)
|
||||
private val mEditorFontStrikeThrough by bindView<CheckableImageView>(R.id.editor_font_strikethrough)
|
||||
private val mEditorParagraphH1 by bindView<CheckableImageView>(R.id.editor_paragraph_h1)
|
||||
private val mEditorParagraphH2 by bindView<CheckableImageView>(R.id.editor_paragraph_h2)
|
||||
private val mEditorParagraphH3 by bindView<CheckableImageView>(R.id.editor_paragraph_h3)
|
||||
private val mEditorParagraphH4 by bindView<CheckableImageView>(R.id.editor_paragraph_h4)
|
||||
private val mEditorParagraphQuote by bindView<CheckableImageView>(R.id.editor_paragraph_quote)
|
||||
private val mEditorFontContainer by bindView<View>(R.id.editor_font_container)
|
||||
private val mEditorParagraphContainer by bindView<View>(R.id.editor_paragraph_container)
|
||||
private val mEditorLinkContainer by bindView<View>(R.id.editor_link_container)
|
||||
private val mEditorInsertDetail by bindView<View>(R.id.editor_insert_detail)
|
||||
private lateinit var mEditorTextNumTv: TextView
|
||||
private lateinit var mEditorFont: CheckableImageView
|
||||
private lateinit var mEditorLink: CheckableImageView
|
||||
|
||||
private lateinit var mEditorFontBold: CheckableImageView
|
||||
private lateinit var mEditorFontItalic: CheckableImageView
|
||||
private lateinit var mEditorFontStrikeThrough: CheckableImageView
|
||||
private lateinit var mEditorFontUnderline: CheckableImageView
|
||||
private lateinit var mEditorParagraphH1: CheckableImageView
|
||||
private lateinit var mEditorParagraphH2: CheckableImageView
|
||||
private lateinit var mEditorParagraphH3: CheckableImageView
|
||||
private lateinit var mEditorParagraphH4: CheckableImageView
|
||||
private lateinit var mEditorParagraphQuote: CheckableImageView
|
||||
private lateinit var mEditorFontContainer: View
|
||||
private lateinit var mEditorParagraphContainer: View
|
||||
private lateinit var mEditorLinkContainer: View
|
||||
private lateinit var mEditorInsertDetailContainer: View
|
||||
private lateinit var mTagsContainer: FrameLayout
|
||||
private lateinit var mUploadVideoGuideContainer: View
|
||||
protected lateinit var mOriginalCb: CheckBox
|
||||
private lateinit var mOriginalTipsContainer: View
|
||||
private lateinit var mOriginalTipsClose: TextView
|
||||
|
||||
private var mCurrentParagraphStyle = ""
|
||||
|
||||
private var mIsExtendedKeyboardShow = false
|
||||
private var mAgreePostPic: Boolean = false
|
||||
private var mGuideDisposable: Disposable? = null
|
||||
protected lateinit var mViewModel: VM
|
||||
protected var mIsKeyBoardShow = false
|
||||
private var mKeyboardHeightProvider: KeyboardHeightProvider? = null
|
||||
private var mMaxUploadVideoGuideCount = 2
|
||||
val FILE_HOST = "file:///"
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
DialogUtils.fixWebViewKeyboardNotWorking(this)
|
||||
if (resultCode != Activity.RESULT_OK) return
|
||||
var insertData: EditorInsertEntity? = null
|
||||
val insertData: EditorInsertEntity?
|
||||
when (requestCode) {
|
||||
INSERT_ANSWER_CODE -> {
|
||||
val answer = data?.getParcelableExtra<AnswerEntity>(AnswerEntity::class.java.simpleName)
|
||||
if (answer != null) insertData = EditorInsertEntity.transform(answer)
|
||||
val answer =
|
||||
data?.getParcelableExtra<AnswerEntity>(AnswerEntity::class.java.simpleName)
|
||||
if (answer != null) {
|
||||
mRichEditor.focusEditor()
|
||||
insertData = EditorInsertEntity.transform(answer)
|
||||
mRichEditor.insertCustomStyleLink(insertData)
|
||||
}
|
||||
}
|
||||
INSERT_ARTICLE_CODE -> {
|
||||
val article = data?.getParcelableExtra<ArticleEntity>(ArticleEntity::class.java.simpleName)
|
||||
if (article != null) insertData = EditorInsertEntity.transform(article)
|
||||
val article =
|
||||
data?.getParcelableExtra<ArticleEntity>(ArticleEntity::class.java.simpleName)
|
||||
if (article != null) {
|
||||
mRichEditor.focusEditor()
|
||||
insertData = EditorInsertEntity.transform(article)
|
||||
mRichEditor.insertCustomStyleLink(insertData)
|
||||
}
|
||||
}
|
||||
INSERT_GAME_CODE -> {
|
||||
val game = data?.getParcelableExtra<GameEntity>(GameEntity::class.java.simpleName)
|
||||
if (game != null) insertData = EditorInsertEntity.transform(game)
|
||||
if (game != null) {
|
||||
mRichEditor.focusEditor()
|
||||
insertData = EditorInsertEntity.transform(game)
|
||||
mRichEditor.insertCustomStyleLink(insertData)
|
||||
}
|
||||
}
|
||||
VideoActivity.INSERT_VIDEO_CODE -> {
|
||||
val video = data?.getParcelableExtra<MyVideoEntity>(MyVideoEntity::class.java.simpleName)
|
||||
if (video != null) mRichEditor.insertCustomVideo(video)
|
||||
return
|
||||
INSERT_GAME_COLLECTION_CODE -> {
|
||||
val gameCollectionEntity = data?.getParcelableExtra<GamesCollectionEntity>(GamesCollectionEntity::class.java.simpleName)
|
||||
if (gameCollectionEntity != null) {
|
||||
mRichEditor.focusEditor()
|
||||
insertData = EditorInsertEntity.transform(gameCollectionEntity)
|
||||
mRichEditor.insertCustomStyleLink(insertData)
|
||||
}
|
||||
}
|
||||
REQUEST_CODE_IMAGE -> {
|
||||
if (data != null) mViewModel.uploadPic(data)
|
||||
}
|
||||
INSERT_MEDIA_VIDEO_CODE -> {
|
||||
val localVideoList = data?.getParcelableArrayListExtra<LocalVideoEntity>(LocalVideoEntity::class.java.name) ?: arrayListOf()
|
||||
if (localVideoList.isNotEmpty()) {
|
||||
mRichEditor.focusEditor()
|
||||
uploadVideo(localVideoList)
|
||||
}
|
||||
}
|
||||
REQUEST_CODE_IMAGE_CROP -> {
|
||||
val imagePath = data?.getStringExtra(CropImageActivity.RESULT_CLIP_PATH)
|
||||
if (!imagePath.isNullOrEmpty()) {
|
||||
mViewModel.uploadPoster(imagePath)
|
||||
}
|
||||
}
|
||||
INSERT_VIDEO_CODE -> {
|
||||
val videoEntity = data?.getParcelableExtra<MyVideoEntity>(MyVideoEntity::class.java.simpleName)
|
||||
if (videoEntity != null) {
|
||||
mRichEditor.focusEditor()
|
||||
insertData = EditorInsertEntity.transform(videoEntity)
|
||||
mRichEditor.insertCustomStyleLink(insertData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mRichEditor.insertCustomStyleLink(insertData)
|
||||
closeExtendedKeyboard()
|
||||
AppExecutor.uiExecutor.executeWithDelay(Runnable {
|
||||
Util_System_Keyboard.showSoftKeyboard(this)
|
||||
}, 100)
|
||||
}
|
||||
|
||||
@SuppressLint("AddJavascriptInterface")
|
||||
private fun uploadVideo(localVideoList: ArrayList<LocalVideoEntity>) {
|
||||
mViewModel.localVideoList.addAll(localVideoList)
|
||||
runOnIoThread {
|
||||
localVideoList.forEach {
|
||||
if (it.poster.startsWith("http")) {
|
||||
runOnUiThread {
|
||||
mRichEditor.focusEditor()
|
||||
mRichEditor.insertPlaceholderVideo(it.id, it.poster)
|
||||
}
|
||||
} else {
|
||||
val videoThumbnail = BitmapUtils.getVideoThumbnail(it.filePath)
|
||||
val filePath = "${cacheDir.absolutePath}${File.separator}${it.id}.webp"
|
||||
BitmapUtils.saveBitmap(videoThumbnail, filePath)
|
||||
it.poster = filePath
|
||||
runOnUiThread {
|
||||
mRichEditor.focusEditor()
|
||||
mRichEditor.insertPlaceholderVideo(it.id, "$FILE_HOST${it.poster}")
|
||||
}
|
||||
}
|
||||
}
|
||||
mViewModel.uploadVideo()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun findView() {
|
||||
mRichEditor = findViewById(R.id.rich_editor)
|
||||
mEditorTextNumTv = findViewById(R.id.editorTextNumTv)
|
||||
mEditorFont = findViewById(R.id.editor_font)
|
||||
mEditorLink = findViewById(R.id.editor_link)
|
||||
mEditorFontBold = findViewById(R.id.editor_font_bold)
|
||||
mEditorFontItalic = findViewById(R.id.editor_font_italic)
|
||||
mEditorFontStrikeThrough = findViewById(R.id.editor_font_strikethrough)
|
||||
mEditorFontUnderline = findViewById(R.id.editor_font_underline)
|
||||
mEditorParagraphH1 = findViewById(R.id.editor_paragraph_h1)
|
||||
mEditorParagraphH2 = findViewById(R.id.editor_paragraph_h2)
|
||||
mEditorParagraphH3 = findViewById(R.id.editor_paragraph_h3)
|
||||
mEditorParagraphH4 = findViewById(R.id.editor_paragraph_h4)
|
||||
mEditorParagraphQuote = findViewById(R.id.editor_paragraph_quote)
|
||||
mEditorFontContainer = findViewById(R.id.editor_font_container)
|
||||
mEditorParagraphContainer = findViewById(R.id.editor_paragraph_container)
|
||||
mEditorLinkContainer = findViewById(R.id.editor_link_container)
|
||||
mEditorInsertDetailContainer = findViewById(R.id.editor_insert_detail_container)
|
||||
mTagsContainer = findViewById(R.id.tagsContainer)
|
||||
mUploadVideoGuideContainer = findViewById(R.id.uploadVideoGuideContainer)
|
||||
mOriginalCb = findViewById(R.id.originalCb)
|
||||
mOriginalTipsContainer = findViewById(R.id.originalTipsContainer)
|
||||
mOriginalTipsClose = findViewById(R.id.originalTipsClose)
|
||||
}
|
||||
|
||||
@SuppressLint("AddJavascriptInterface", "ClickableViewAccessibility")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mRichEditor.setPadding(20, 15, 20, 15)
|
||||
findView()
|
||||
onRichClick()
|
||||
mViewModel = provideViewModel()
|
||||
mViewModel.setUploadVideoListener(this)
|
||||
mKeyboardHeightProvider = KeyboardHeightProvider(this)
|
||||
mRichEditor.post { mKeyboardHeightProvider?.start() }
|
||||
// 防止个别手机在Js里无法获取粘贴内容
|
||||
mRichEditor.addJavascriptInterface(OnPasteListener(), "onPasteListener")
|
||||
mRichEditor.addJavascriptInterface(OnCursorChangeListener(), "OnCursorChangeListener")
|
||||
mRichEditor.addJavascriptInterface(OnEditorTextChangeListener(), "OnEditorTextChangeListener")
|
||||
mRichEditor.addJavascriptInterface(
|
||||
OnEditorTextChangeListener(),
|
||||
"OnEditorTextChangeListener"
|
||||
)
|
||||
mRichEditor.addJavascriptInterface(OnVideoListener(), "onVideoListener")
|
||||
mRichEditor.addJavascriptInterface(
|
||||
OnQuoteCountChangeListener(),
|
||||
"OnQuoteCountChangeListener"
|
||||
)
|
||||
mRichEditor.setInputEnabled(true)
|
||||
mRichEditor.setPadding(16, 12, 16, 12)
|
||||
|
||||
mDraftBtn.text = if (this is AnswerEditActivity) {
|
||||
"回答草稿"
|
||||
} else "帖子草稿"
|
||||
mRichEditor.setOnTouchListener { _, _ ->
|
||||
if (mIsExtendedKeyboardShow) {
|
||||
closeExtendedKeyboard()
|
||||
Util_System_Keyboard.showSoftKeyboard(this)
|
||||
//是否消费事件根据mRichEditor是否含有焦点决定,mRichEditor没有焦点则不消费事件
|
||||
mRichEditor.hasFocus()
|
||||
} else false
|
||||
}
|
||||
mOriginalCb.setOnCheckedChangeListener { _, isChecked ->
|
||||
if (isChecked) {
|
||||
mOriginalTipsContainer.alpha = 0f
|
||||
mOriginalTipsContainer.visibility = View.VISIBLE
|
||||
ObjectAnimator.ofFloat(mOriginalTipsContainer, "alpha", 0f, 1f).setDuration(200).start()
|
||||
}
|
||||
}
|
||||
observeData()
|
||||
}
|
||||
|
||||
@OnClick(R.id.editor_image, R.id.editor_font, R.id.editor_link, R.id.editor_paragraph,
|
||||
R.id.editor_font_bold, R.id.editor_font_italic, R.id.editor_font_strikethrough,
|
||||
R.id.editor_paragraph_h1, R.id.editor_paragraph_h2, R.id.editor_paragraph_h3,
|
||||
R.id.editor_paragraph_h4, R.id.editor_font_container, R.id.editor_paragraph_container,
|
||||
R.id.editor_paragraph_quote, R.id.editor_link_answer, R.id.editor_link_article,
|
||||
R.id.editor_link_game, R.id.editor_link_video)
|
||||
fun onRichClick(view: View) {
|
||||
when (view.id) {
|
||||
R.id.editor_font -> {
|
||||
mEditorFont.isChecked = !mEditorFont.isChecked
|
||||
mEditorParagraph.isChecked = false
|
||||
mEditorLink.isChecked = false
|
||||
mEditorFontContainer.visibility = if (mEditorFont.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorParagraphContainer.visibility = if (!mEditorFont.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorLinkContainer.visibility = if (!mEditorFont.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorInsertDetail.visibility = mEditorFontContainer.visibility
|
||||
private fun observeData() {
|
||||
mViewModel.chooseImagesUpload.observe(this, Observer {
|
||||
mRichEditor.focusEditor()
|
||||
for (key in it.keys) {
|
||||
mRichEditor.insertPlaceholderImage(key)
|
||||
}
|
||||
R.id.editor_paragraph -> {
|
||||
mEditorParagraph.isChecked = !mEditorParagraph.isChecked
|
||||
mEditorFont.isChecked = false
|
||||
mEditorLink.isChecked = false
|
||||
mEditorParagraphContainer.visibility = if (mEditorParagraph.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorFontContainer.visibility = if (!mEditorParagraph.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorLinkContainer.visibility = if (!mEditorParagraph.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorInsertDetail.visibility = mEditorParagraphContainer.visibility
|
||||
})
|
||||
mViewModel.chooseImagesUploadSuccess.observe(this, Observer {
|
||||
val jsonArray = JSONArray()
|
||||
for (key in it.keys) {
|
||||
val jsonObject = JSONObject()
|
||||
jsonObject.put("id", key)
|
||||
jsonObject.put("url", it[key])
|
||||
jsonArray.put(jsonObject)
|
||||
}
|
||||
R.id.editor_link -> {
|
||||
mEditorLink.isChecked = !mEditorLink.isChecked
|
||||
mEditorFont.isChecked = false
|
||||
mEditorParagraph.isChecked = false
|
||||
mEditorLinkContainer.visibility = if (mEditorLink.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorParagraphContainer.visibility = if (!mEditorLink.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorFontContainer.visibility = if (!mEditorLink.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorInsertDetail.visibility = mEditorLinkContainer.visibility
|
||||
}
|
||||
R.id.editor_font_bold -> {
|
||||
mEditorFontBold.isChecked = !mEditorFontBold.isChecked
|
||||
mRichEditor.setBold()
|
||||
if (mEditorFontBold.isChecked) {
|
||||
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-加粗")
|
||||
}
|
||||
}
|
||||
R.id.editor_font_italic -> {
|
||||
mEditorFontItalic.isChecked = !mEditorFontItalic.isChecked
|
||||
mRichEditor.setItalic()
|
||||
if (mEditorFontItalic.isChecked) {
|
||||
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-斜体")
|
||||
}
|
||||
}
|
||||
R.id.editor_font_strikethrough -> {
|
||||
mEditorFontStrikeThrough.isChecked = !mEditorFontStrikeThrough.isChecked
|
||||
mRichEditor.setStrikeThrough()
|
||||
mRichEditor.replacePlaceholderImage(jsonArray.toString())
|
||||
})
|
||||
}
|
||||
|
||||
if (mEditorFontStrikeThrough.isChecked) {
|
||||
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-删除线")
|
||||
override fun onKeyboardHeightChanged(height: Int, orientation: Int) {
|
||||
mIsKeyBoardShow = height > 0
|
||||
if (height > 0) {
|
||||
closeExtendedKeyboard()
|
||||
}
|
||||
}
|
||||
|
||||
fun closeExtendedKeyboard() {
|
||||
mEditorInsertDetailContainer.visibility = View.GONE
|
||||
mEditorFont.isChecked = false
|
||||
mEditorLink.isChecked = false
|
||||
mIsExtendedKeyboardShow = false
|
||||
}
|
||||
|
||||
protected fun controlEditorInsertContainerEnabled(isEnabled: Boolean) {
|
||||
mEditorFont.isEnabled = isEnabled
|
||||
}
|
||||
|
||||
private fun onRichClick() {
|
||||
mEditorFont.setOnClickListener {
|
||||
controlEditorFontContainer()
|
||||
}
|
||||
mEditorLink.setOnClickListener {
|
||||
controlEditorLinkContainer()
|
||||
}
|
||||
mEditorFontBold.setOnClickListener {
|
||||
mEditorFontBold.isChecked = !mEditorFontBold.isChecked
|
||||
mRichEditor.setBold()
|
||||
if (mEditorFontBold.isChecked) {
|
||||
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-加粗")
|
||||
}
|
||||
}
|
||||
mEditorFontItalic.setOnClickListener {
|
||||
mEditorFontItalic.isChecked = !mEditorFontItalic.isChecked
|
||||
mRichEditor.setItalic()
|
||||
if (mEditorFontItalic.isChecked) {
|
||||
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-斜体")
|
||||
}
|
||||
}
|
||||
mEditorFontStrikeThrough.setOnClickListener {
|
||||
mEditorFontStrikeThrough.isChecked = !mEditorFontStrikeThrough.isChecked
|
||||
mRichEditor.setStrikeThrough()
|
||||
|
||||
if (mEditorFontStrikeThrough.isChecked) {
|
||||
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-删除线")
|
||||
}
|
||||
}
|
||||
mEditorFontUnderline.setOnClickListener {
|
||||
mEditorFontUnderline.isChecked = !mEditorFontUnderline.isChecked
|
||||
mRichEditor.setUnderline()
|
||||
|
||||
if (mEditorFontUnderline.isChecked) {
|
||||
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-下滑线")
|
||||
}
|
||||
}
|
||||
mEditorParagraphH1.setOnClickListener {
|
||||
if (mEditorParagraphH1.isChecked) {
|
||||
mRichEditor.formatBlock()
|
||||
} else {
|
||||
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-1级标题")
|
||||
mRichEditor.setHeading(1)
|
||||
}
|
||||
mEditorParagraphH1.isChecked = !mEditorParagraphH1.isChecked
|
||||
}
|
||||
mEditorParagraphH2.setOnClickListener {
|
||||
if (mEditorParagraphH2.isChecked) {
|
||||
mRichEditor.formatBlock()
|
||||
} else {
|
||||
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-2级标题")
|
||||
mRichEditor.setHeading(2)
|
||||
}
|
||||
mEditorParagraphH2.isChecked = !mEditorParagraphH2.isChecked
|
||||
}
|
||||
mEditorParagraphH3.setOnClickListener {
|
||||
if (mEditorParagraphH3.isChecked) {
|
||||
mRichEditor.formatBlock()
|
||||
} else {
|
||||
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-3级标题")
|
||||
mRichEditor.setHeading(3)
|
||||
}
|
||||
mEditorParagraphH3.isChecked = !mEditorParagraphH3.isChecked
|
||||
}
|
||||
mEditorParagraphH4.setOnClickListener {
|
||||
if (mEditorParagraphH4.isChecked) {
|
||||
mRichEditor.formatBlock()
|
||||
} else {
|
||||
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-4级标题")
|
||||
mRichEditor.setHeading(4)
|
||||
}
|
||||
mEditorParagraphH4.isChecked = !mEditorParagraphH4.isChecked
|
||||
}
|
||||
mEditorParagraphQuote.setOnClickListener {
|
||||
if (mEditorParagraphQuote.isChecked) {
|
||||
mRichEditor.formatBlock()
|
||||
} else {
|
||||
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-引用")
|
||||
mRichEditor.setBlockquote()
|
||||
}
|
||||
mEditorParagraphQuote.isChecked = !mEditorParagraphQuote.isChecked
|
||||
}
|
||||
findViewById<View>(R.id.editor_link_answer).setOnClickListener {
|
||||
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-回答")
|
||||
startActivityForResult(
|
||||
InsertAnswerWrapperActivity.getIntent(this),
|
||||
INSERT_ANSWER_CODE
|
||||
)
|
||||
}
|
||||
findViewById<View>(R.id.editor_link_article).setOnClickListener {
|
||||
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-文章")
|
||||
startActivityForResult(
|
||||
InsertArticleWrapperActivity.getIntent(this),
|
||||
INSERT_ARTICLE_CODE
|
||||
)
|
||||
}
|
||||
findViewById<View>(R.id.editor_link_game).setOnClickListener {
|
||||
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-游戏")
|
||||
startActivityForResult(
|
||||
GameActivity.getIntent(this, GameActivity.INSERT_GAME_TITLE),
|
||||
INSERT_GAME_CODE
|
||||
)
|
||||
}
|
||||
findViewById<View>(R.id.editor_link_video).setOnClickListener {
|
||||
startActivityForResult(
|
||||
InsertVideoWrapperActivity.getIntent(this),
|
||||
INSERT_VIDEO_CODE
|
||||
)
|
||||
}
|
||||
findViewById<View>(R.id.editor_link_game_collection).setOnClickListener {
|
||||
startActivityForResult(
|
||||
InsertGameCollectionWrapperActivity.getIntent(this),
|
||||
INSERT_GAME_COLLECTION_CODE
|
||||
)
|
||||
}
|
||||
findViewById<View>(R.id.editor_video).setOnClickListener {
|
||||
chooseVideo()
|
||||
}
|
||||
findViewById<View>(R.id.editor_image).setOnClickListener {
|
||||
if (!mAgreePostPic && !NetworkUtils.isWifiOr4GOr3GConnected(this)) {
|
||||
mAgreePostPic = true
|
||||
DialogHelper.showDialog(
|
||||
this,
|
||||
"警告",
|
||||
"当前使用移动网络,上传图片会消耗手机流量",
|
||||
"我知道了", "", { chooseImage() },
|
||||
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
|
||||
)
|
||||
return@setOnClickListener
|
||||
}
|
||||
chooseImage()
|
||||
NewLogUtils.logChooseMedia(
|
||||
"view_media",
|
||||
if (mtaEventName() == "提问帖") "提问帖" else "帖子",
|
||||
"图片"
|
||||
)
|
||||
}
|
||||
findViewById<View>(R.id.uploadVideoGuideClose).setOnClickListener {
|
||||
hideUploadVideoGuide()
|
||||
if (mGuideDisposable != null && !mGuideDisposable!!.isDisposed) {
|
||||
mGuideDisposable!!.dispose()
|
||||
mGuideDisposable = null
|
||||
}
|
||||
}
|
||||
findViewById<View>(R.id.originalTipsClose).setOnClickListener {
|
||||
val animator = ObjectAnimator.ofFloat(mOriginalTipsContainer, "alpha", 1f, 0f).setDuration(200)
|
||||
animator.doOnEnd {
|
||||
mOriginalTipsContainer.visibility = View.GONE
|
||||
}
|
||||
animator.start()
|
||||
}
|
||||
}
|
||||
|
||||
private fun chooseVideo() {
|
||||
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-视频")
|
||||
val videoCount = mViewModel.quoteCountEntity.videoCount
|
||||
if (videoCount >= MAX_MEDIA_COUNT) {
|
||||
toast(R.string.answer_edit_max_video_hint)
|
||||
return
|
||||
}
|
||||
try {
|
||||
PermissionHelper.checkStoragePermissionBeforeAction(this,
|
||||
object : EmptyCallback {
|
||||
override fun onCallback() {
|
||||
val maxChooseCount = if (videoCount + 3 <= MAX_MEDIA_COUNT) 3 else MAX_MEDIA_COUNT - videoCount
|
||||
startActivityForResult(
|
||||
LocalMediaActivity.getIntent(
|
||||
this@BaseRichEditorActivity,
|
||||
LocalMediaActivity.ChooseType.VIDEO,
|
||||
maxChooseCount,
|
||||
if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
|
||||
), INSERT_MEDIA_VIDEO_CODE
|
||||
)
|
||||
NewLogUtils.logChooseMedia(
|
||||
"view_media",
|
||||
if (mtaEventName() == "提问帖") "提问帖" else "帖子",
|
||||
"视频"
|
||||
)
|
||||
}
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
toast(R.string.media_image_hint)
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun chooseImage() {
|
||||
MtaHelper.onEvent(mtaEventName(), "插入图片", "插入图片")
|
||||
val imageCount = mViewModel.quoteCountEntity.imageCount
|
||||
if (imageCount >= MAX_MEDIA_COUNT) {
|
||||
toast(R.string.answer_edit_max_img_hint)
|
||||
return
|
||||
}
|
||||
try {
|
||||
PermissionHelper.checkStoragePermissionBeforeAction(this, object : EmptyCallback {
|
||||
override fun onCallback() {
|
||||
val maxChooseCount = if (imageCount + 10 <= MAX_MEDIA_COUNT) 10 else MAX_MEDIA_COUNT - imageCount
|
||||
val intent = LocalMediaActivity.getIntent(
|
||||
this@BaseRichEditorActivity,
|
||||
LocalMediaActivity.ChooseType.IMAGE,
|
||||
maxChooseCount,
|
||||
if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
|
||||
)
|
||||
startActivityForResult(intent, REQUEST_CODE_IMAGE)
|
||||
}
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
toast(R.string.media_image_hint)
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun controlEditorFontContainer() {
|
||||
mEditorFont.isChecked = !mEditorFont.isChecked
|
||||
mEditorLink.isChecked = false
|
||||
val isShouldDelay = if (mEditorFont.isChecked) {
|
||||
Util_System_Keyboard.hideSoftKeyboard(this)
|
||||
true
|
||||
} else {
|
||||
Util_System_Keyboard.showSoftKeyboard(this)
|
||||
false
|
||||
}
|
||||
mEditorInsertDetailContainer.postDelayed({
|
||||
mEditorInsertDetailContainer.visibility =
|
||||
if (mEditorFont.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorFontContainer.visibility = if (mEditorFont.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorParagraphContainer.visibility =
|
||||
if (mEditorFont.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorLinkContainer.visibility = View.GONE
|
||||
mTagsContainer.visibility = View.GONE
|
||||
mIsExtendedKeyboardShow = mEditorFont.isChecked
|
||||
}, if (isShouldDelay) 200 else 0L)
|
||||
}
|
||||
|
||||
private fun controlEditorLinkContainer() {
|
||||
mEditorLink.isChecked = !mEditorLink.isChecked
|
||||
mEditorFont.isChecked = false
|
||||
val isShouldDelay = if (mEditorLink.isChecked) {
|
||||
Util_System_Keyboard.hideSoftKeyboard(this)
|
||||
true
|
||||
} else {
|
||||
Util_System_Keyboard.showSoftKeyboard(this)
|
||||
false
|
||||
}
|
||||
mEditorInsertDetailContainer.postDelayed({
|
||||
mEditorInsertDetailContainer.visibility =
|
||||
if (mEditorLink.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorLinkContainer.visibility = if (mEditorLink.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorFontContainer.visibility = View.GONE
|
||||
mEditorParagraphContainer.visibility = View.GONE
|
||||
mTagsContainer.visibility = View.GONE
|
||||
mIsExtendedKeyboardShow = mEditorLink.isChecked
|
||||
}, if (isShouldDelay) 200 else 0L)
|
||||
}
|
||||
|
||||
|
||||
override fun handleBackPressed(): Boolean {
|
||||
if (mIsExtendedKeyboardShow) {
|
||||
closeExtendedKeyboard()
|
||||
return true
|
||||
}
|
||||
return super.handleBackPressed()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
mKeyboardHeightProvider?.setKeyboardHeightObserver(this)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
mKeyboardHeightProvider?.setKeyboardHeightObserver(null)
|
||||
}
|
||||
|
||||
//视频上传功能引导
|
||||
fun showUploadVideoGuide() {
|
||||
mUploadVideoGuideContainer.postDelayed({
|
||||
val count = SPUtils.getInt(getVideoGuideKey(), 0)
|
||||
if (count >= mMaxUploadVideoGuideCount) return@postDelayed
|
||||
mUploadVideoGuideContainer.alpha = 0f
|
||||
mUploadVideoGuideContainer.visibility = View.VISIBLE
|
||||
mUploadVideoGuideContainer.animate().alpha(1f).setDuration(200).start()
|
||||
mGuideDisposable = countDownTimer(3) { finish, _ ->
|
||||
if (finish) {
|
||||
hideUploadVideoGuide()
|
||||
}
|
||||
}
|
||||
R.id.editor_paragraph_h1 -> {
|
||||
if (mEditorParagraphH1.isChecked) {
|
||||
mRichEditor.formatBlock()
|
||||
} else {
|
||||
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-1级标题")
|
||||
mRichEditor.setHeading(1)
|
||||
}
|
||||
mEditorParagraphH1.isChecked = !mEditorParagraphH1.isChecked
|
||||
}
|
||||
R.id.editor_paragraph_h2 -> {
|
||||
if (mEditorParagraphH2.isChecked) {
|
||||
mRichEditor.formatBlock()
|
||||
} else {
|
||||
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-2级标题")
|
||||
mRichEditor.setHeading(2)
|
||||
}
|
||||
mEditorParagraphH2.isChecked = !mEditorParagraphH2.isChecked
|
||||
}
|
||||
R.id.editor_paragraph_h3 -> {
|
||||
if (mEditorParagraphH3.isChecked) {
|
||||
mRichEditor.formatBlock()
|
||||
} else {
|
||||
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-3级标题")
|
||||
mRichEditor.setHeading(3)
|
||||
}
|
||||
mEditorParagraphH3.isChecked = !mEditorParagraphH3.isChecked
|
||||
}
|
||||
R.id.editor_paragraph_h4 -> {
|
||||
if (mEditorParagraphH4.isChecked) {
|
||||
mRichEditor.formatBlock()
|
||||
} else {
|
||||
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-4级标题")
|
||||
mRichEditor.setHeading(4)
|
||||
}
|
||||
mEditorParagraphH4.isChecked = !mEditorParagraphH4.isChecked
|
||||
}
|
||||
R.id.editor_paragraph_quote -> {
|
||||
if (mEditorParagraphQuote.isChecked) {
|
||||
mRichEditor.formatBlock()
|
||||
} else {
|
||||
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-引用")
|
||||
mRichEditor.setBlockquote()
|
||||
}
|
||||
mEditorParagraphQuote.isChecked = !mEditorParagraphQuote.isChecked
|
||||
}
|
||||
R.id.editor_link_answer -> {
|
||||
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-回答")
|
||||
startActivityForResult(InsertAnswerWrapperActivity.getIntent(this), INSERT_ANSWER_CODE)
|
||||
}
|
||||
R.id.editor_link_article -> {
|
||||
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-文章")
|
||||
startActivityForResult(InsertArticleWrapperActivity.getIntent(this), INSERT_ARTICLE_CODE)
|
||||
}
|
||||
R.id.editor_link_game -> {
|
||||
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-游戏")
|
||||
startActivityForResult(GameActivity.getIntent(this, GameActivity.INSERT_GAME_TITLE), INSERT_GAME_CODE)
|
||||
}
|
||||
R.id.editor_link_video -> {
|
||||
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-视频")
|
||||
startActivityForResult(VideoActivity.getIntent(this), VideoActivity.INSERT_VIDEO_CODE)
|
||||
}
|
||||
SPUtils.setInt(getVideoGuideKey(), count + 1)
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
fun hideUploadVideoGuide() {
|
||||
val animate = mUploadVideoGuideContainer.animate().alpha(0f).setDuration(200)
|
||||
animate.doOnEnd {
|
||||
mUploadVideoGuideContainer.visibility = View.GONE
|
||||
}
|
||||
animate.start()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
mKeyboardHeightProvider?.close()
|
||||
val path = mViewModel.currentUploadingVideo?.filePath
|
||||
if (path != null && UploadManager.isUploading(path)) {
|
||||
UploadManager.cancelTask(path)
|
||||
}
|
||||
if (mGuideDisposable != null && !mGuideDisposable!!.isDisposed) {
|
||||
mGuideDisposable!!.dispose()
|
||||
mGuideDisposable = null
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,6 +605,7 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
mEditorFontBold.isChecked = elements.contains(ELEMENT_NAME_BOLD)
|
||||
mEditorFontItalic.isChecked = elements.contains(ELEMENT_NAME_ITALIC)
|
||||
mEditorFontStrikeThrough.isChecked = elements.contains(ELEMENT_NAME_STRIKE)
|
||||
mEditorFontUnderline.isChecked = elements.contains(ELEMENT_NAME_UNDERLINE)
|
||||
mEditorParagraphH1.isChecked = elements.contains(ELEMENT_PARAGRAPH_H1)
|
||||
mEditorParagraphH2.isChecked = elements.contains(ELEMENT_PARAGRAPH_H2)
|
||||
mEditorParagraphH3.isChecked = elements.contains(ELEMENT_PARAGRAPH_H3)
|
||||
@ -250,11 +619,12 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
@JavascriptInterface
|
||||
fun onPaste() {
|
||||
val clipboard =
|
||||
HaloApp.getInstance().application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
HaloApp.getInstance().application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clipText = clipboard.text.toString()
|
||||
if (!TextUtils.isEmpty(clipText)) {
|
||||
// 替换换行符号否则 插入失败
|
||||
val text = clipText.replace("[ ]".toRegex(), " ").replace("[\r\n]".toRegex(), "<br/>")
|
||||
val text = clipText.replace("[ ]".toRegex(), " ")
|
||||
.replace("[\r\n]".toRegex(), "<br/>")
|
||||
mBaseHandler.post { mRichEditor.insertHtml(text) }
|
||||
}
|
||||
}
|
||||
@ -264,16 +634,112 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
@JavascriptInterface
|
||||
fun onTextChange(count: Int) {
|
||||
val num = if (count > MAX_INPUT_TEXT_NUM) MAX_INPUT_TEXT_NUM - count else count
|
||||
mEditorTextNumTv.text = num.toString()
|
||||
mEditorTextNumTv.post {
|
||||
mEditorTextNumTv.text = num.toString()
|
||||
mViewModel.quoteCountEntity.textCount = num
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class OnQuoteCountChangeListener {
|
||||
@JavascriptInterface
|
||||
fun onQuoteCountChange(
|
||||
imageCount: Int,
|
||||
articleCount: Int,
|
||||
answerCount: Int,
|
||||
videoCount: Int,
|
||||
gameCount: Int
|
||||
) {
|
||||
mEditorTextNumTv.post {
|
||||
mViewModel.quoteCountEntity.apply {
|
||||
this.imageCount = imageCount
|
||||
this.articleCount = articleCount
|
||||
this.answerCount = answerCount
|
||||
this.videoCount = videoCount
|
||||
this.gameCount = gameCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class OnVideoListener {
|
||||
@JavascriptInterface
|
||||
fun showDeleteDialog(id: String) {
|
||||
DialogHelper.showDialog(this@BaseRichEditorActivity, "提示", "确定删除吗?", "确定", "取消", {
|
||||
runOnUiThread {
|
||||
mRichEditor.delPlaceholderVideo(id)
|
||||
mViewModel.deleteVideo(id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun updatePoster(id: String, videoId: String, url: String) {
|
||||
mViewModel.id = id
|
||||
mViewModel.videoId = videoId
|
||||
val videoEntity = VideoEntity(url = url)
|
||||
val intent =
|
||||
PosterEditActivity.getIntentByVideo(this@BaseRichEditorActivity, videoEntity)
|
||||
startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun deleteUploadingVideo(id: String) {
|
||||
mViewModel.deleteVideo(id)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun reUploadVideo(id: String) {
|
||||
val video = mViewModel.uploadVideoErrorList.find { it.id == id }
|
||||
if (video != null) {
|
||||
mViewModel.localVideoList.add(video)
|
||||
mViewModel.uploadVideoErrorList.remove(video)
|
||||
mViewModel.uploadVideo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun insertPlaceholderVideo(id: String, poster: String) {
|
||||
mRichEditor.insertPlaceholderVideo(id, poster)
|
||||
}
|
||||
|
||||
override fun updateVideoProgress(id: String, progress: String) {
|
||||
mRichEditor.updateVideoProgress(id, progress)
|
||||
}
|
||||
|
||||
override fun videoUploadFinished(id: String, url: String, msg: JsonObject) {
|
||||
try {
|
||||
val obj = JSONObject()
|
||||
obj.put("poster", msg.get("poster").asString)
|
||||
obj.put("url", msg.get("url").asString)
|
||||
obj.put("duration", RichEditor.formatVideoDuration(msg.get("length").asLong))
|
||||
obj.put("id", msg.get("_id").asString)
|
||||
obj.put("status", "pending")
|
||||
mRichEditor.videoUploadFinished(id, url, obj.toString())
|
||||
} catch (e: java.lang.Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
override fun changePoster(id: String, poster: String) {
|
||||
mRichEditor.changePoster(id, poster)
|
||||
}
|
||||
|
||||
override fun videoUploadFailed(id: String) {
|
||||
mRichEditor.videoUploadFailed(id)
|
||||
}
|
||||
|
||||
open fun getSelectedLabel(): Int = 0
|
||||
open fun onActivityDialogResult(requestCode: Int, resultCode: Int, data: Intent?) {}
|
||||
abstract fun mtaEventName(): String
|
||||
abstract fun provideViewModel(): VM
|
||||
abstract fun getVideoGuideKey(): String
|
||||
|
||||
companion object {
|
||||
const val ELEMENT_NAME_BOLD = " b "
|
||||
const val ELEMENT_NAME_ITALIC = " i "
|
||||
const val ELEMENT_NAME_STRIKE = " strike "
|
||||
const val ELEMENT_NAME_UNDERLINE = " u "
|
||||
const val ELEMENT_PARAGRAPH_H1 = " h1 "
|
||||
const val ELEMENT_PARAGRAPH_H2 = " h2 "
|
||||
const val ELEMENT_PARAGRAPH_H3 = " h3 "
|
||||
@ -283,6 +749,13 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
const val INSERT_ANSWER_CODE = 411
|
||||
const val INSERT_ARTICLE_CODE = 412
|
||||
const val INSERT_GAME_CODE = 413
|
||||
const val INSERT_GAME_COLLECTION_CODE = 414
|
||||
const val INSERT_VIDEO_CODE = 415
|
||||
const val MAX_INPUT_TEXT_NUM = 10000
|
||||
const val MAX_MEDIA_COUNT = 20
|
||||
|
||||
const val REQUEST_CODE_IMAGE = 120
|
||||
const val INSERT_MEDIA_VIDEO_CODE = 121
|
||||
const val REQUEST_CODE_IMAGE_CROP = 122
|
||||
}
|
||||
}
|
||||
444
app/src/main/java/com/gh/base/BaseRichEditorViewModel.kt
Normal file
444
app/src/main/java/com/gh/base/BaseRichEditorViewModel.kt
Normal file
@ -0,0 +1,444 @@
|
||||
package com.gh.base
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.media.ThumbnailUtils
|
||||
import android.provider.MediaStore
|
||||
import android.text.TextUtils
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.base.fragment.WaitingDialogFragment
|
||||
import com.gh.common.runOnUiThread
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.ErrorEntity
|
||||
import com.gh.gamecenter.entity.LocalVideoEntity
|
||||
import com.gh.gamecenter.entity.QuoteCountEntity
|
||||
import com.gh.gamecenter.qa.BbsType
|
||||
import com.gh.gamecenter.retrofit.Response
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.gamecenter.retrofit.service.ApiService
|
||||
import com.gh.gamecenter.video.upload.OnUploadListener
|
||||
import com.gh.gamecenter.video.upload.UploadManager
|
||||
import com.google.gson.JsonObject
|
||||
import com.lightgame.utils.Utils
|
||||
import com.zhihu.matisse.Matisse
|
||||
import com.zhihu.matisse.internal.utils.PathUtils
|
||||
import io.reactivex.disposables.Disposable
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.HttpException
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.LinkedHashMap
|
||||
import kotlin.collections.List
|
||||
import kotlin.collections.Map
|
||||
import kotlin.collections.find
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.set
|
||||
|
||||
abstract class BaseRichEditorViewModel(application: Application) : AndroidViewModel(application) {
|
||||
val mApi: ApiService = RetrofitManager.getInstance().api
|
||||
val processDialog = MediatorLiveData<WaitingDialogFragment.WaitingDialogData>()
|
||||
val uploadingImage = ArrayList<LinkedHashMap<String, String>>()
|
||||
val chooseImagesUpload = MutableLiveData<LinkedHashMap<String, String>>()
|
||||
val chooseImagesUploadSuccess = MutableLiveData<LinkedHashMap<String, String>>()
|
||||
var uploadImageSubscription: Disposable? = null
|
||||
val mapImages = HashMap<String, String>()
|
||||
val localVideoList = ArrayList<LocalVideoEntity>()
|
||||
val uploadVideoErrorList = ArrayList<LocalVideoEntity>()
|
||||
var currentUploadingVideo: LocalVideoEntity? = null
|
||||
var type: String = "" //游戏论坛:game_bbs 官方论坛:official_bbs
|
||||
private var mUploadVideoListener: UploadVideoListener? = null
|
||||
val TITLE_MIN_LENGTH = 6
|
||||
val MIN_TEXT_LENGTH = 6
|
||||
val MAX_TEXT_LENGTH = 10000
|
||||
val FILE_HOST = "file:///"
|
||||
var id = ""//视频标记
|
||||
var videoId = ""//更改封面视频id
|
||||
val quoteCountEntity = QuoteCountEntity()//数据上报用
|
||||
|
||||
fun setUploadVideoListener(uploadVideoListener: UploadVideoListener) {
|
||||
this.mUploadVideoListener = uploadVideoListener
|
||||
}
|
||||
|
||||
//检查图片是否符合规则并上传图片
|
||||
fun uploadPic(data: Intent) {
|
||||
val uris = Matisse.obtainResult(data)
|
||||
val pictureList = ArrayList<String>()
|
||||
for (uri in uris) {
|
||||
val picturePath = PathUtils.getPath(getApplication(), uri)
|
||||
if (picturePath != null) {
|
||||
if (File(picturePath).length() > ImageUtils.getUploadFileMaxSize()) {
|
||||
val count = ImageUtils.getUploadFileMaxSize() / 1024 / 1024
|
||||
val application: Application = getApplication()
|
||||
Utils.toast(
|
||||
getApplication(),
|
||||
application.getString(R.string.pic_max_hint, count)
|
||||
)
|
||||
continue
|
||||
}
|
||||
Utils.log("picturePath = $picturePath")
|
||||
pictureList.add(picturePath)
|
||||
} else {
|
||||
Utils.log("picturePath is null")
|
||||
}
|
||||
}
|
||||
if (pictureList.size == 0) return
|
||||
val imageType = when (getRichType()) {
|
||||
RichType.ARTICLE -> UploadImageUtils.UploadType.community_article
|
||||
RichType.QUESTION -> UploadImageUtils.UploadType.question
|
||||
else -> UploadImageUtils.UploadType.poster
|
||||
}
|
||||
uploadImageSubscription = UploadImageUtils.compressAndUploadImageList(
|
||||
imageType,
|
||||
pictureList,
|
||||
false,
|
||||
object : UploadImageUtils.OnUploadImageListListener {
|
||||
override fun onProgress(total: Long, progress: Long) {}
|
||||
|
||||
override fun onCompressSuccess(imageUrls: List<String>) {
|
||||
val chooseImageMd5Map = LinkedHashMap<String, String>()
|
||||
imageUrls.forEach {
|
||||
chooseImageMd5Map[MD5Utils.getUrlMD5(it)] = ""
|
||||
}
|
||||
uploadingImage.add(chooseImageMd5Map)
|
||||
chooseImagesUpload.postValue(chooseImageMd5Map)
|
||||
}
|
||||
|
||||
override fun onSingleSuccess(imageUrl: Map<String, String>) {
|
||||
val map = LinkedHashMap<String, String>()
|
||||
for (key in imageUrl.keys) {
|
||||
map[MD5Utils.getUrlMD5(key)] = FILE_HOST + key.decodeURI()
|
||||
mapImages[TextUtils.htmlEncode(key).decodeURI()] = imageUrl[key] ?: ""
|
||||
}
|
||||
chooseImagesUploadSuccess.postValue(map)
|
||||
}
|
||||
|
||||
override fun onSuccess(
|
||||
imageUrl: LinkedHashMap<String, String>,
|
||||
errorMap: Map<String, Exception>
|
||||
) {
|
||||
val uploadMap = uploadingImage.find {
|
||||
it.containsKey(
|
||||
MD5Utils.getUrlMD5(
|
||||
imageUrl.entries.iterator().next().key
|
||||
)
|
||||
)
|
||||
}
|
||||
uploadMap?.let {
|
||||
uploadingImage.remove(uploadMap)
|
||||
}
|
||||
|
||||
val errorSize = pictureList.size - imageUrl.size
|
||||
if (errorSize > 0) {
|
||||
val map = LinkedHashMap<String, String>()
|
||||
for (key in errorMap.keys) {
|
||||
map[MD5Utils.getUrlMD5(key)] = ""
|
||||
}
|
||||
//value为空会删除PlaceholderImage
|
||||
chooseImagesUploadSuccess.postValue(map)
|
||||
|
||||
for (error in errorMap.values) {
|
||||
if (error is HttpException && error.code() == 403) {
|
||||
Utils.toast(getApplication(), errorSize.toString() + "张违规图片上传失败")
|
||||
return
|
||||
}
|
||||
}
|
||||
Utils.toast(getApplication(), errorSize.toString() + "张图片上传失败")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(errorMap: Map<String, Exception>) {
|
||||
val errorSize = errorMap.size
|
||||
if (errorSize > 0) {
|
||||
val map = LinkedHashMap<String, String>()
|
||||
for (key in errorMap.keys) {
|
||||
map[MD5Utils.getUrlMD5(key)] = ""
|
||||
}
|
||||
//value为空会删除PlaceholderImage
|
||||
chooseImagesUploadSuccess.postValue(map)
|
||||
}
|
||||
|
||||
for (error in errorMap.values) {
|
||||
if (error is HttpException && error.code() == 403) {
|
||||
val e = error.response()?.errorBody()?.string()?.toObject<ErrorEntity>()
|
||||
if (e != null && e.code == 403017) {
|
||||
Utils.toast(
|
||||
getApplication(),
|
||||
errorSize.toString() + "张图片的宽或高超过限制,请裁剪后上传"
|
||||
)
|
||||
} else {
|
||||
Utils.toast(getApplication(), errorSize.toString() + "张违规图片上传失败")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
if (errorSize == 1) {
|
||||
Utils.toast(getApplication(), "图片上传失败")
|
||||
} else {
|
||||
Utils.toast(getApplication(), errorSize.toString() + "张图片上传失败")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun uploadPoster(picturePath: String) {
|
||||
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", true))
|
||||
uploadImageSubscription =
|
||||
UploadImageUtils.compressAndUploadImage(UploadImageUtils.UploadType.poster,
|
||||
picturePath,
|
||||
false,
|
||||
object : UploadImageUtils.OnUploadImageListener {
|
||||
override fun onSuccess(imageUrl: String) {
|
||||
patchVideoPoster(imageUrl)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable?) {
|
||||
handleUploadPosterResult(true)
|
||||
}
|
||||
|
||||
override fun onProgress(total: Long, progress: Long) {
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun patchVideoPoster(poster: String) {
|
||||
if (id.isEmpty() || videoId.isEmpty()) return
|
||||
val map = hashMapOf("poster" to poster, "type" to getVideoType())
|
||||
mApi.patchInsertVideo(videoId, map.toRequestBody())
|
||||
.compose(observableToMain())
|
||||
.subscribe(object : Response<ResponseBody>() {
|
||||
override fun onResponse(response: ResponseBody?) {
|
||||
super.onResponse(response)
|
||||
mUploadVideoListener?.changePoster(id, poster)
|
||||
handleUploadPosterResult(false)
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
super.onFailure(e)
|
||||
handleUploadPosterResult(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun handleUploadPosterResult(isFailure: Boolean = false) {
|
||||
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", false))
|
||||
if (isFailure) {
|
||||
ToastUtils.showToast("封面更改失败")
|
||||
}
|
||||
id = ""
|
||||
videoId = ""
|
||||
}
|
||||
|
||||
fun deleteVideo(id: String) {
|
||||
if (localVideoList.isNotEmpty()) {
|
||||
val video = localVideoList.find { it.id == id }
|
||||
if (video != null) {
|
||||
if (UploadManager.isUploading(video.filePath)) {
|
||||
UploadManager.cancelTask(video.filePath)
|
||||
}
|
||||
localVideoList.remove(video)
|
||||
}
|
||||
}
|
||||
if (uploadVideoErrorList.isNotEmpty()) {
|
||||
val video = uploadVideoErrorList.find { it.id == id }
|
||||
if (video != null) {
|
||||
uploadVideoErrorList.remove(video)
|
||||
}
|
||||
}
|
||||
if (currentUploadingVideo?.id == id) {
|
||||
currentUploadingVideo = null
|
||||
uploadVideo()
|
||||
}
|
||||
}
|
||||
|
||||
fun uploadVideo() {
|
||||
if (currentUploadingVideo != null) return
|
||||
if (localVideoList.isEmpty()) return
|
||||
currentUploadingVideo = localVideoList[0]
|
||||
UploadManager.createUploadTask(currentUploadingVideo?.filePath
|
||||
?: "", object : OnUploadListener {
|
||||
override fun onProgressChanged(
|
||||
uploadFilePath: String,
|
||||
currentSize: Long,
|
||||
totalSize: Long,
|
||||
speed: Long
|
||||
) {
|
||||
runOnUiThread {
|
||||
val percent = (currentSize * 100 / totalSize.toFloat()).roundTo(1)
|
||||
currentUploadingVideo?.id?.let {
|
||||
mUploadVideoListener?.updateVideoProgress(it, percent.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUploadSuccess(uploadFilePath: String, url: String) {
|
||||
if (currentUploadingVideo != null) {
|
||||
postVideoPosterAndInfo(uploadFilePath, url)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUploadFailure(uploadFilePath: String, errorMsg: String) {
|
||||
uploadVideoFailure()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun postVideoPosterAndInfo(uploadFilePath: String, url: String) {
|
||||
val localVideoPoster =
|
||||
getApplication<Application>().cacheDir.absolutePath + File.separator + System.currentTimeMillis() + ".jpg"
|
||||
try {
|
||||
val bmp = ThumbnailUtils.createVideoThumbnail(
|
||||
uploadFilePath,
|
||||
MediaStore.Images.Thumbnails.MINI_KIND
|
||||
)
|
||||
// bmp 可能为空
|
||||
FileOutputStream(localVideoPoster).use { out ->
|
||||
bmp?.compress(Bitmap.CompressFormat.PNG, 100, out)
|
||||
}
|
||||
} catch (e: java.lang.Exception) {
|
||||
e.printStackTrace()
|
||||
ToastUtils.showToast("视频封面操作失败")
|
||||
uploadVideoFailure()
|
||||
return
|
||||
}
|
||||
uploadImageSubscription =
|
||||
UploadImageUtils.compressAndUploadImage(UploadImageUtils.UploadType.poster,
|
||||
localVideoPoster,
|
||||
false,
|
||||
object : UploadImageUtils.OnUploadImageListener {
|
||||
override fun onSuccess(imageUrl: String) {
|
||||
postVideoInfo(url, imageUrl)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable?) {
|
||||
uploadVideoFailure()
|
||||
}
|
||||
|
||||
override fun onProgress(total: Long, progress: Long) {
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun postVideoInfo(url: String, poster: String) {
|
||||
val map = HashMap<String, Any>().apply {
|
||||
put("poster", poster)
|
||||
put("url", url)
|
||||
put("format", currentUploadingVideo?.format ?: "")
|
||||
put("size", currentUploadingVideo?.size ?: 0)
|
||||
put("length", (currentUploadingVideo?.duration ?: 0) / 1000)
|
||||
put("type", getVideoType())
|
||||
}
|
||||
val requestBody = map.toRequestBody()
|
||||
mApi.insertVideo(requestBody)
|
||||
.compose(observableToMain())
|
||||
.subscribe(object : Response<JsonObject>() {
|
||||
override fun onResponse(response: JsonObject?) {
|
||||
super.onResponse(response)
|
||||
if (response != null) {
|
||||
uploadVideoSuccess(poster, url, response)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
super.onFailure(e)
|
||||
uploadVideoFailure()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun uploadVideoSuccess(poster: String, url: String, data: JsonObject) {
|
||||
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", false))
|
||||
currentUploadingVideo?.let {
|
||||
mUploadVideoListener?.changePoster(it.id, poster)
|
||||
mUploadVideoListener?.videoUploadFinished(it.id, url, data)
|
||||
UploadManager.cancelTask(it.filePath)
|
||||
localVideoList.remove(it)
|
||||
}
|
||||
currentUploadingVideo = null
|
||||
uploadVideo()
|
||||
}
|
||||
|
||||
private fun uploadVideoFailure() {
|
||||
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", false))
|
||||
currentUploadingVideo?.let {
|
||||
runOnUiThread {
|
||||
mUploadVideoListener?.videoUploadFailed(it.id)
|
||||
}
|
||||
uploadVideoErrorList.add(it)
|
||||
localVideoList.remove(it)
|
||||
UploadManager.cancelTask(it.filePath)
|
||||
}
|
||||
currentUploadingVideo = null
|
||||
uploadVideo()
|
||||
}
|
||||
|
||||
fun checkIsAllUploadedAndToast(): Boolean {
|
||||
if (localVideoList.isNotEmpty() || uploadVideoErrorList.isNotEmpty()) {
|
||||
ToastUtils.showToast("视频未上传完成,视频内容保存失败")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun getVideoType(): String {
|
||||
return when (type) {
|
||||
BbsType.GAME_BBS.value -> {
|
||||
when (getRichType()) {
|
||||
RichType.ARTICLE -> BbsType.GAME_BBS_ARTICLE_INSERT.value
|
||||
RichType.QUESTION -> BbsType.GAME_BBS_QUESTION_INSERT.value
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
BbsType.OFFICIAL_BBS.value -> {
|
||||
when (getRichType()) {
|
||||
RichType.ARTICLE -> BbsType.OFFICIAL_BBS_ARTICLE_INSERT.value
|
||||
RichType.QUESTION -> BbsType.OFFICIAL_BBS_QUESTION_INSERT.value
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun getRichType(): RichType
|
||||
}
|
||||
|
||||
interface UploadVideoListener {
|
||||
/**
|
||||
* 插入视频占位图
|
||||
*/
|
||||
fun insertPlaceholderVideo(id: String, poster: String)
|
||||
|
||||
/**
|
||||
* 更新视频进度条
|
||||
*/
|
||||
fun updateVideoProgress(id: String, progress: String)
|
||||
|
||||
/**
|
||||
* 上传视频完成
|
||||
*/
|
||||
fun videoUploadFinished(id: String, url: String, msg: JsonObject)
|
||||
|
||||
/**
|
||||
* 更换封面图
|
||||
*/
|
||||
fun changePoster(id: String, poster: String)
|
||||
|
||||
/**
|
||||
* 上传失败
|
||||
*/
|
||||
fun videoUploadFailed(id: String)
|
||||
}
|
||||
|
||||
enum class RichType {
|
||||
ARTICLE,
|
||||
QUESTION,
|
||||
ANSWER
|
||||
}
|
||||
93
app/src/main/java/com/gh/base/BaseSimpleDao.kt
Normal file
93
app/src/main/java/com/gh/base/BaseSimpleDao.kt
Normal file
@ -0,0 +1,93 @@
|
||||
package com.gh.base
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import com.gh.common.util.SPUtils
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
/**
|
||||
* 用 SP 实现的简单列表持久化结构
|
||||
*/
|
||||
abstract class BaseSimpleDao {
|
||||
|
||||
// 使用独有的 SP 文件
|
||||
private val mSp: SharedPreferences by lazy {
|
||||
HaloApp.getInstance().application.getSharedPreferences("SimpleDao", Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
fun add(key: String) {
|
||||
val originString = SPUtils.getString(mSp, getSPKey())
|
||||
|
||||
if (originString.isEmpty()) {
|
||||
// Insert keyword only for the very first time.
|
||||
SPUtils.setString(mSp, getSPKey(), key)
|
||||
} else {
|
||||
getAll()?.let {
|
||||
if (getMaxSize() != -1 && it.size > getMaxSize()) {
|
||||
it.removeAt(it.size - 1)
|
||||
}
|
||||
|
||||
// Move keyword to the very front if it exists.
|
||||
if (it.contains(key)) {
|
||||
it.remove(key)
|
||||
}
|
||||
it.add(0, key)
|
||||
val builder = StringBuilder()
|
||||
for ((index, k) in it.withIndex()) {
|
||||
builder.append(k)
|
||||
if (index != it.size - 1) {
|
||||
builder.append(DIVIDER_KEY)
|
||||
}
|
||||
}
|
||||
SPUtils.setString(mSp, getSPKey(), builder.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun delete(key: String) {
|
||||
val originString = SPUtils.getString(mSp, getSPKey())
|
||||
|
||||
if (originString.isEmpty()) {
|
||||
// do nothing
|
||||
} else {
|
||||
getAll()?.let {
|
||||
if (it.contains(key)) {
|
||||
it.remove(key)
|
||||
}
|
||||
val builder = StringBuilder()
|
||||
for ((index, k) in it.withIndex()) {
|
||||
builder.append(k)
|
||||
if (index != it.size - 1) {
|
||||
builder.append(DIVIDER_KEY)
|
||||
}
|
||||
}
|
||||
SPUtils.setString(mSp, getSPKey(), builder.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getAll(): ArrayList<String>? {
|
||||
val list = SPUtils.getString(mSp, getSPKey()).split(DIVIDER_KEY)
|
||||
|
||||
return if (list.size == 1 && list[0].isEmpty()) null else ArrayList(list)
|
||||
}
|
||||
|
||||
fun getRawString(): String = SPUtils.getString(mSp, getSPKey())
|
||||
|
||||
fun contains(key: String): Boolean {
|
||||
return getAll()?.contains(key) == true
|
||||
}
|
||||
|
||||
fun deleteAll() {
|
||||
SPUtils.setString(mSp, getSPKey(), "")
|
||||
}
|
||||
|
||||
open fun getMaxSize(): Int = -1
|
||||
|
||||
abstract fun getSPKey(): String
|
||||
|
||||
companion object {
|
||||
private const val DIVIDER_KEY = "<-||->"
|
||||
}
|
||||
|
||||
}
|
||||
53
app/src/main/java/com/gh/base/CustomLayoutInflaterFactory.kt
Normal file
53
app/src/main/java/com/gh/base/CustomLayoutInflaterFactory.kt
Normal file
@ -0,0 +1,53 @@
|
||||
package com.gh.base
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.gh.gamecenter.R
|
||||
|
||||
class CustomLayoutInflaterFactory(
|
||||
private val mAppCompatActivity: AppCompatActivity
|
||||
) : LayoutInflater.Factory2 {
|
||||
|
||||
override fun onCreateView(
|
||||
parent: View?,
|
||||
name: String,
|
||||
context: Context,
|
||||
attrs: AttributeSet
|
||||
): View? {
|
||||
|
||||
val view: View?
|
||||
|
||||
try {
|
||||
view = mAppCompatActivity.delegate.createView(parent, name, context, attrs)
|
||||
?: mAppCompatActivity.onCreateView(parent, name, context, attrs)
|
||||
?: mAppCompatActivity.layoutInflater.createView(name, null, attrs)
|
||||
} catch (e: Exception) {
|
||||
return null
|
||||
}
|
||||
|
||||
val n = attrs.attributeCount
|
||||
for (i in 0 until n) {
|
||||
val attributeName = attrs.getAttributeName(i).toString()
|
||||
if (attributeName.contains("background")) {
|
||||
view?.setTag(TAG_BACKGROUND_ID, attrs.getAttributeValue(i))
|
||||
} else if (attributeName.contains("textColor")) {
|
||||
view?.setTag(TAG_TEXT_COLOR_ID, attrs.getAttributeValue(i))
|
||||
}
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
|
||||
return mAppCompatActivity.onCreateView(name, context, attrs)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG_BACKGROUND_ID = R.string.background_id
|
||||
const val TAG_TEXT_COLOR_ID = R.string.text_color_id
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application.ActivityLifecycleCallbacks;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.gh.common.notifier.Notifier;
|
||||
import com.gh.common.util.DataUtils;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.halo.assistant.HaloApp;
|
||||
import com.lightgame.utils.AppManager;
|
||||
|
||||
/**
|
||||
* 1、写点针对生命周期的统计代码
|
||||
* 2、写点通用的逻辑
|
||||
* 3、接口解耦
|
||||
*
|
||||
* @author CsHeng
|
||||
* @Date 09/05/2017
|
||||
* @Time 6:22 PM
|
||||
*/
|
||||
public class GHActivityLifecycleCallbacksImpl implements ActivityLifecycleCallbacks {
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
|
||||
AppManager.getInstance().addActivity(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
CurrentActivityHolder.getActivitySet().add(activity);
|
||||
|
||||
if (HaloApp.isUserAcceptPrivacyPolicy(activity)) {
|
||||
DataUtils.onResume(activity);
|
||||
//FIXME 这里应该只是部分Activity需要
|
||||
try {
|
||||
// 初始化gameMap
|
||||
DownloadManager.getInstance(activity).initGameMap();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
CurrentActivityHolder.getActivitySet().remove(activity);
|
||||
|
||||
if (HaloApp.isUserAcceptPrivacyPolicy(activity)) {
|
||||
DataUtils.onPause(activity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
Notifier.hide();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
if (activity.isFinishing()) {
|
||||
AppManager.getInstance().finishActivity(activity);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
17
app/src/main/java/com/gh/base/GHThreadFactory.kt
Normal file
17
app/src/main/java/com/gh/base/GHThreadFactory.kt
Normal file
@ -0,0 +1,17 @@
|
||||
package com.gh.base
|
||||
|
||||
import java.util.concurrent.ThreadFactory
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class GHThreadFactory(threadNamePrefix: String) : ThreadFactory {
|
||||
|
||||
private val THREAD_NAME_STEM = "${threadNamePrefix}_%d"
|
||||
private val mThreadId = AtomicInteger(0)
|
||||
|
||||
override fun newThread(r: Runnable?): Thread {
|
||||
val t = Thread(r)
|
||||
t.name = String.format(THREAD_NAME_STEM, mThreadId.getAndIncrement())
|
||||
return t
|
||||
}
|
||||
|
||||
}
|
||||
@ -174,7 +174,7 @@
|
||||
// jsonObject.put("type", type)
|
||||
// val body = RequestBody.create(MediaType.parse("application/json"), jsonObject.toString())
|
||||
//
|
||||
// RetrofitManager.getInstance(application).api.postMessageRead(UserManager.getInstance().userId, data?.id, body)
|
||||
// RetrofitManager.getInstance().api.postMessageRead(UserManager.getInstance().userId, data?.id, body)
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(object : Response<ResponseBody>() {
|
||||
|
||||
@ -0,0 +1,97 @@
|
||||
package com.gh.base
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.os.Bundle
|
||||
import com.gh.common.notifier.Notifier
|
||||
import com.gh.common.util.DataUtils
|
||||
import com.gh.common.util.FloatingBackViewManager
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.MainActivity
|
||||
import com.gh.gamecenter.SplashScreenActivity
|
||||
import com.gh.gamecenter.energy.EnergyCenterActivity
|
||||
import com.gh.gamecenter.forum.detail.ForumDetailActivity
|
||||
import com.gh.gamecenter.forum.list.ForumListActivity
|
||||
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
|
||||
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity
|
||||
import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.AppManager
|
||||
|
||||
class GlobalActivityLifecycleObserver : Application.ActivityLifecycleCallbacks {
|
||||
|
||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||
AppManager.getInstance().addActivity(activity)
|
||||
}
|
||||
|
||||
override fun onActivityStarted(activity: Activity) {
|
||||
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
CurrentActivityHolder.activitySet.add(activity)
|
||||
|
||||
// 判断是否需要显示或隐藏返回小浮窗
|
||||
if (FloatingBackViewManager.getType().isNotEmpty()) {
|
||||
if (activity is EnergyCenterActivity
|
||||
&& FloatingBackViewManager.getType() == FloatingBackViewManager.TYPE_TASK
|
||||
) {
|
||||
FloatingBackViewManager.disableBackView()
|
||||
} else if (!shouldShowActivityBackView(activity)
|
||||
&& FloatingBackViewManager.getType() == FloatingBackViewManager.TYPE_ACTIVITY
|
||||
) {
|
||||
FloatingBackViewManager.disableBackView()
|
||||
} else {
|
||||
FloatingBackViewManager.showBackView(activity)
|
||||
}
|
||||
}
|
||||
|
||||
if (HaloApp.isUserAcceptPrivacyPolicy(activity)) {
|
||||
DataUtils.onResume(activity)
|
||||
// FIXME 这里应该只是部分Activity需要
|
||||
try {
|
||||
// 初始化gameMap
|
||||
if (activity !is SplashScreenActivity) {
|
||||
DownloadManager.getInstance().initGameMap()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldShowActivityBackView(activity: Activity): Boolean {
|
||||
return (activity is MainActivity
|
||||
|| activity is ArticleDetailActivity
|
||||
|| activity is ForumVideoDetailActivity
|
||||
|| activity is ForumDetailActivity
|
||||
|| activity is ForumListActivity
|
||||
|| activity is NewQuestionDetailActivity)
|
||||
}
|
||||
|
||||
override fun onActivityPaused(activity: Activity) {
|
||||
CurrentActivityHolder.activitySet.remove(activity)
|
||||
FloatingBackViewManager.dismissBackView()
|
||||
|
||||
if (HaloApp.isUserAcceptPrivacyPolicy(activity)) {
|
||||
DataUtils.onPause(activity)
|
||||
}
|
||||
|
||||
if (activity.isFinishing) {
|
||||
AppManager.getInstance().finishActivity(activity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityStopped(activity: Activity) {
|
||||
Notifier.hide()
|
||||
}
|
||||
|
||||
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
|
||||
|
||||
}
|
||||
|
||||
override fun onActivityDestroyed(activity: Activity) {
|
||||
AppManager.getInstance().finishActivity(activity)
|
||||
}
|
||||
|
||||
}
|
||||
@ -3,6 +3,7 @@ package com.gh.base;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Menu;
|
||||
@ -11,17 +12,26 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.widget.ActionMenuView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.gh.common.constant.Constants;
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.common.util.ImageUtils;
|
||||
import com.gh.common.util.SPUtils;
|
||||
import com.gh.common.view.GameIconView;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.gh.gamecenter.DownloadManagerActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
@ -44,7 +54,7 @@ import java.util.List;
|
||||
* 特殊页面请参考{@link BaseActivity}
|
||||
*/
|
||||
|
||||
public abstract class ToolBarActivity extends BaseActivity implements ToolbarController, Toolbar.OnMenuItemClickListener {
|
||||
public abstract class ToolBarActivity extends BaseActivity implements ToolbarController, ActionMenuView.OnMenuItemClickListener {
|
||||
|
||||
@Nullable
|
||||
private PackageViewModel mPackageViewModel;
|
||||
@ -55,6 +65,22 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
|
||||
|
||||
protected TextView mTitleTv;
|
||||
|
||||
protected LinearLayout mTitleContainer;
|
||||
|
||||
protected LinearLayout mIconTitleContainer;
|
||||
|
||||
protected FrameLayout mBackContainer;
|
||||
|
||||
protected ActionMenuView mActionMenuView;
|
||||
|
||||
protected View mBackBtn;
|
||||
|
||||
protected GameIconView mGameIconView;
|
||||
|
||||
protected SimpleDraweeView mUserAvatarIv;
|
||||
|
||||
protected TextView mIconTitle;
|
||||
|
||||
@Nullable
|
||||
private TextView mDownloadCountHint;
|
||||
|
||||
@ -64,7 +90,7 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
|
||||
setStatusBarDarkMode(true, this);
|
||||
initToolbar();
|
||||
|
||||
if (showDownloadMenu()) {
|
||||
if (!SPUtils.getBoolean(Constants.SP_TEENAGER_MODE) && showDownloadMenu()) {
|
||||
mPackageViewModel = ViewModelProviders.of(this, new PackageViewModel.Factory()).get(PackageViewModel.class);
|
||||
mPackageViewModel.getFilterSameUpdateLiveData().observe(this, this::updateDownloadCountHint);
|
||||
}
|
||||
@ -89,10 +115,20 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
|
||||
mToolbarContainer = findViewById(R.id.normal_toolbar_container);
|
||||
mToolbar = findViewById(R.id.normal_toolbar);
|
||||
mTitleTv = findViewById(R.id.normal_title);
|
||||
mActionMenuView = findViewById(R.id.actionMenuView);
|
||||
mTitleContainer = findViewById(R.id.titleContainer);
|
||||
mIconTitleContainer = findViewById(R.id.iconTitleContainer);
|
||||
mBackContainer = findViewById(R.id.backContainer);
|
||||
mBackBtn = findViewById(R.id.backBtn);
|
||||
mGameIconView = findViewById(R.id.gameIv);
|
||||
mUserAvatarIv = findViewById(R.id.userAvatar);
|
||||
mIconTitle = findViewById(R.id.iconTitle);
|
||||
if (mToolbar != null) {
|
||||
// setSupportActionBar(mToolbar); // 替换actionBar后 toolBar无法控制
|
||||
mToolbar.setNavigationIcon(provideNavigationIcon());
|
||||
mToolbar.setNavigationOnClickListener(provideNavigationItemClickListener());
|
||||
// mToolbar.setNavigationIcon(provideNavigationIcon());
|
||||
// mToolbar.setNavigationOnClickListener(provideNavigationItemClickListener());
|
||||
if (mBackBtn != null) mBackBtn.setOnClickListener(provideNavigationItemClickListener());
|
||||
if (mBackContainer != null) mBackContainer.setOnClickListener(provideNavigationItemClickListener());
|
||||
if (mTitleTv != null) {
|
||||
mTitleTv.setOnClickListener(view -> {
|
||||
final List<Fragment> fragmentList = getSupportFragmentManager().getFragments();
|
||||
@ -114,6 +150,7 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
|
||||
@Override
|
||||
public void setNavigationTitle(String title) {
|
||||
if (mTitleTv != null) mTitleTv.setText(title);
|
||||
if (mIconTitle != null) mIconTitle.setText(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -130,15 +167,20 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
|
||||
|
||||
@Override
|
||||
public void setToolbarMenu(int res) {
|
||||
if (mToolbar == null) return;
|
||||
mToolbar.inflateMenu(res);
|
||||
mToolbar.setOnMenuItemClickListener(this);
|
||||
if (mActionMenuView == null) return;
|
||||
// 青少年模式下要隐藏下载按钮
|
||||
if (SPUtils.getBoolean(Constants.SP_TEENAGER_MODE) && res == R.menu.menu_download) return;
|
||||
// mToolbar.inflateMenu(res);
|
||||
// mToolbar.setOnMenuItemClickListener(this);
|
||||
|
||||
getMenuInflater().inflate(res, mActionMenuView.getMenu());
|
||||
mActionMenuView.setOnMenuItemClickListener(this);
|
||||
|
||||
if (showDownloadMenu()) {
|
||||
createDownloadMenu(res);
|
||||
}
|
||||
|
||||
Menu menu = mToolbar.getMenu();
|
||||
Menu menu = mActionMenuView.getMenu();
|
||||
for (int i = 0; i < menu.size(); i++) {
|
||||
MenuItem menuItem = menu.getItem(i);
|
||||
// menu设置actionLayout后,无法捕捉点击事件,以icon为tag,如果icon is null 手动设置menuItem点击事件
|
||||
@ -149,51 +191,61 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
|
||||
}
|
||||
}
|
||||
|
||||
// 限制标题实际宽度 防止标题挡住toolbar menu按钮
|
||||
if (menu.size() > 2 && mTitleTv != null) {
|
||||
ViewGroup.LayoutParams layoutParams = mTitleTv.getLayoutParams();
|
||||
if (layoutParams instanceof RelativeLayout.LayoutParams) {
|
||||
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) layoutParams;
|
||||
if (showToolbarAtLeft()) {
|
||||
params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
|
||||
params.addRule(RelativeLayout.CENTER_VERTICAL);
|
||||
params.setMargins(DisplayUtils.dip2px(55), 0, DisplayUtils.dip2px(48 * menu.size()), 0);
|
||||
} else {
|
||||
params.setMargins(DisplayUtils.dip2px(90), 0, DisplayUtils.dip2px(90), 0);
|
||||
}
|
||||
mTitleTv.setLayoutParams(params);
|
||||
}
|
||||
} else {
|
||||
if (showToolbarAtLeft()) {
|
||||
ViewGroup.LayoutParams layoutParams = mTitleTv.getLayoutParams();
|
||||
if (layoutParams instanceof RelativeLayout.LayoutParams) {
|
||||
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) layoutParams;
|
||||
params.addRule(RelativeLayout.CENTER_VERTICAL);
|
||||
params.setMargins(DisplayUtils.dip2px(55), 0, DisplayUtils.dip2px(48 * menu.size()), 0);
|
||||
mTitleTv.setLayoutParams(params);
|
||||
}
|
||||
}
|
||||
if (showToolbarAtLeft() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && mTitleTv != null) {
|
||||
mTitleTv.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START);
|
||||
}
|
||||
setTitleCenter();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
setTitleCenter();
|
||||
}
|
||||
|
||||
// 设置标题居中
|
||||
public void setTitleCenter() {
|
||||
if (mActionMenuView != null && mTitleContainer != null && mBackContainer != null && !showToolbarAtLeft()) {
|
||||
mActionMenuView.post(() -> {
|
||||
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mTitleContainer.getLayoutParams();
|
||||
params.setMargins(mActionMenuView.getWidth() - mBackContainer.getWidth(), 0, 0, 0);
|
||||
mTitleContainer.setLayoutParams(params);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void setGameIconToolbar(String icon, String iconSubscript) {
|
||||
mTitleContainer.setVisibility(View.GONE);
|
||||
mGameIconView.displayGameIcon(icon, iconSubscript);
|
||||
mGameIconView.setVisibility(View.VISIBLE);
|
||||
mIconTitleContainer.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void setUserAvatarIconToolbar(String icon) {
|
||||
mTitleContainer.setVisibility(View.GONE);
|
||||
ImageUtils.display(mUserAvatarIv, icon);
|
||||
mUserAvatarIv.setVisibility(View.VISIBLE);
|
||||
mIconTitleContainer.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void createDownloadMenu(int res) {
|
||||
if (res != R.menu.menu_download) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.menu_download, mToolbar.getMenu());
|
||||
inflater.inflate(R.menu.menu_download, mActionMenuView.getMenu());
|
||||
}
|
||||
|
||||
if (mPackageViewModel != null) {
|
||||
updateDownloadCountHint(mPackageViewModel.getFilterSameUpdateLiveData().getValue());
|
||||
}
|
||||
|
||||
View downloadMenuView = mToolbar.getMenu().findItem(R.id.menu_download).getActionView();
|
||||
View downloadMenuView = mActionMenuView.getMenu().findItem(R.id.menu_download).getActionView();
|
||||
mDownloadCountHint = downloadMenuView.findViewById(R.id.menu_download_count_hint);
|
||||
}
|
||||
|
||||
private void updateDownloadCountHint(List<GameUpdateEntity> updateList) {
|
||||
if (mDownloadCountHint == null) return;
|
||||
|
||||
String count = DownloadManager.getInstance(getApplicationContext()).getDownloadOrUpdateCount(updateList);
|
||||
String count = DownloadManager.getInstance().getDownloadOrUpdateCount(updateList);
|
||||
if (count != null) {
|
||||
mDownloadCountHint.setVisibility(View.VISIBLE);
|
||||
mDownloadCountHint.setText(count);
|
||||
@ -214,7 +266,7 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(EBDownloadStatus status) {
|
||||
if (showDownloadMenu() && mPackageViewModel != null) {
|
||||
if (!SPUtils.getBoolean(Constants.SP_TEENAGER_MODE) && showDownloadMenu() && mPackageViewModel != null) {
|
||||
updateDownloadCountHint(mPackageViewModel.getFilterSameUpdateLiveData().getValue());
|
||||
}
|
||||
}
|
||||
@ -222,17 +274,18 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
|
||||
@Override
|
||||
public MenuItem getMenuItem(int res) {
|
||||
if (mToolbar == null) return null; //后续页面做好判断
|
||||
return mToolbar.getMenu().findItem(res);
|
||||
return mActionMenuView.getMenu().findItem(res);
|
||||
}
|
||||
|
||||
public void clearMenu() {
|
||||
if (mToolbar != null) {
|
||||
mToolbar.getMenu().clear();
|
||||
mActionMenuView.getMenu().clear();
|
||||
setTitleCenter();
|
||||
}
|
||||
}
|
||||
|
||||
public Menu getMenu() {
|
||||
return mToolbar.getMenu();
|
||||
return mActionMenuView.getMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -259,4 +312,25 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
|
||||
mToolbarContainer.setVisibility(isHide ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNightModeChange() {
|
||||
super.onNightModeChange();
|
||||
if (mToolbar != null) {
|
||||
mToolbar.setBackgroundColor(ContextCompat.getColor(this, R.color.background_white));
|
||||
}
|
||||
if (mBackBtn != null) {
|
||||
if (mBackBtn instanceof ImageView) {
|
||||
((ImageView) mBackBtn).setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_bar_back));
|
||||
} else if (mBackBtn instanceof TextView) {
|
||||
((TextView) mBackBtn).setTextColor(ContextCompat.getColor(this, R.color.text_subtitle));
|
||||
}
|
||||
}
|
||||
if (mTitleTv != null) {
|
||||
mTitleTv.setTextColor(ContextCompat.getColor(this, R.color.text_black));
|
||||
}
|
||||
if (showDownloadMenu() && getMenuItem(R.id.menu_download) != null) {
|
||||
((ImageView) getMenuItem(R.id.menu_download).getActionView().findViewById(R.id.menu_download_iv)).setImageDrawable(ContextCompat.getDrawable(this, R.drawable.toolbar_download));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
package com.gh.base.fragment;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import com.gh.common.util.ClickUtils;
|
||||
import com.gh.common.util.NightModeUtils;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.lightgame.utils.RuntimeUtils;
|
||||
import com.lightgame.utils.Utils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
@ -26,10 +30,18 @@ import androidx.lifecycle.Lifecycle;
|
||||
|
||||
public class BaseDialogFragment extends DialogFragment {
|
||||
|
||||
protected boolean mNightMode;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mNightMode = NightModeUtils.INSTANCE.isNightMode(requireContext());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final Dialog dialog = new Dialog(getActivity(), R.style.DialogWindowTransparent);
|
||||
final Dialog dialog = new Dialog(getActivity(), getThemeRes());
|
||||
dialog.setCanceledOnTouchOutside(false);
|
||||
dialog.setOnKeyListener((dialog1, keyCode, event) -> {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && !ClickUtils.isFastDoubleClick()) {
|
||||
@ -41,6 +53,10 @@ public class BaseDialogFragment extends DialogFragment {
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public int getThemeRes() {
|
||||
return R.style.DialogWindowTransparent;
|
||||
}
|
||||
|
||||
public void toast(@StringRes int res) {
|
||||
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED))
|
||||
toast(getString(res));
|
||||
@ -71,9 +87,34 @@ public class BaseDialogFragment extends DialogFragment {
|
||||
transaction.show(fragment);
|
||||
transaction.commit();
|
||||
} else {
|
||||
super.show(manager, tag);
|
||||
try {
|
||||
Class<?> clazz = DialogFragment.class;
|
||||
Field dismissed = clazz.getDeclaredField("mDismissed");
|
||||
dismissed.setAccessible(true);
|
||||
dismissed.set(this, false);
|
||||
|
||||
Field shownByMe = clazz.getDeclaredField("mShownByMe");
|
||||
shownByMe.setAccessible(true);
|
||||
shownByMe.set(this, true);
|
||||
FragmentTransaction transaction = manager.beginTransaction();
|
||||
transaction.add(this, tag);
|
||||
transaction.commitAllowingStateLoss();
|
||||
} catch (Exception e) {
|
||||
super.show(manager, tag);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
onNightModeChange();
|
||||
}
|
||||
|
||||
protected void onNightModeChange() {
|
||||
mNightMode = NightModeUtils.INSTANCE.isNightMode(requireContext());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package com.gh.base.fragment;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
@ -8,6 +9,7 @@ import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
@ -23,7 +25,9 @@ import com.gh.common.constant.Constants;
|
||||
import com.gh.common.syncpage.ISyncAdapterHandler;
|
||||
import com.gh.common.syncpage.SyncDataEntity;
|
||||
import com.gh.common.syncpage.SyncPageRepository;
|
||||
import com.gh.common.util.NightModeUtils;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.eventbus.EBMiPush;
|
||||
import com.lightgame.OnTitleClickListener;
|
||||
import com.lightgame.utils.RuntimeUtils;
|
||||
@ -37,7 +41,6 @@ import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
@ -58,9 +61,13 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
|
||||
|
||||
protected boolean isEverPause;
|
||||
|
||||
protected boolean mNightMode;
|
||||
|
||||
@NonNull
|
||||
protected String mEntrance = "";
|
||||
|
||||
public long startPageTime = 0;
|
||||
|
||||
protected final Handler mBaseHandler = new BaseFragment.BaseHandler(this);
|
||||
|
||||
protected static class BaseHandler extends Handler {
|
||||
@ -108,6 +115,12 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
|
||||
|
||||
|
||||
protected void initView(View view) {
|
||||
View mBackBtn = view.findViewById(R.id.backBtn);
|
||||
View mBackContainer = view.findViewById(R.id.backContainer);
|
||||
if (mBackBtn != null && mBackContainer != null) {
|
||||
mBackBtn.setOnClickListener(v -> requireActivity().onBackPressed());
|
||||
mBackContainer.setOnClickListener(v -> requireActivity().onBackPressed());
|
||||
}
|
||||
}
|
||||
|
||||
protected void postRunnable(Runnable runnable) {
|
||||
@ -140,21 +153,22 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
|
||||
EventBus.getDefault().register(this);
|
||||
|
||||
// For data binding.
|
||||
if (getInflatedLayout() != null) {
|
||||
mCachedView = getInflatedLayout();
|
||||
} else {
|
||||
mCachedView = getInflatedLayout();
|
||||
if (mCachedView == null) {
|
||||
mCachedView = View.inflate(getContext(), getLayoutId(), null);
|
||||
}
|
||||
|
||||
if (useButterKnife()) {
|
||||
ButterKnife.bind(this, mCachedView);
|
||||
}
|
||||
|
||||
initView(mCachedView);
|
||||
|
||||
if (addSyncPageObserver()) {
|
||||
initSyncPageObserver();
|
||||
}
|
||||
|
||||
if (BuildConfig.IS_NIGHT_MODE_ON) {
|
||||
mNightMode = NightModeUtils.INSTANCE.isNightMode(requireContext());
|
||||
} else {
|
||||
mNightMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void initSyncPageObserver() {
|
||||
@ -207,6 +221,10 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
if (container != null) {
|
||||
container.removeView(mCachedView);
|
||||
// TODO 页面重建 (framgent 的重新获取) 有大问题,这里只是修修补补
|
||||
if (mCachedView != null && mCachedView.getParent() instanceof ViewGroup) {
|
||||
((ViewGroup) mCachedView.getParent()).removeView(mCachedView);
|
||||
}
|
||||
}
|
||||
return mCachedView;
|
||||
}
|
||||
@ -222,6 +240,13 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
isEverPause = false;
|
||||
startPageTime = System.currentTimeMillis();
|
||||
|
||||
if (BuildConfig.IS_NIGHT_MODE_ON
|
||||
&& !NightModeUtils.INSTANCE.getSystemMode()
|
||||
&& mNightMode != NightModeUtils.INSTANCE.isNightMode(requireContext())) {
|
||||
onNightModeChange();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -307,7 +332,9 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
|
||||
|
||||
// 为 fragment 附加 bundle (setArgument())
|
||||
public BaseFragment with(Bundle bundle) {
|
||||
this.setArguments(bundle);
|
||||
if (!isStateSaved()) {
|
||||
this.setArguments(bundle);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -324,7 +351,15 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean useButterKnife() {
|
||||
return true;
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
if (BuildConfig.IS_NIGHT_MODE_ON) {
|
||||
onNightModeChange();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onNightModeChange() {
|
||||
mNightMode = NightModeUtils.INSTANCE.isNightMode(requireContext());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.gh.base.fragment;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Bundle;
|
||||
@ -8,6 +9,12 @@ import android.view.View;
|
||||
import android.widget.CheckedTextView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.gh.base.adapter.FragmentAdapter;
|
||||
import com.gh.common.view.TabIndicatorView;
|
||||
import com.gh.gamecenter.R;
|
||||
@ -20,12 +27,6 @@ import com.lightgame.view.NoScrollableViewPager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import butterknife.BindView;
|
||||
|
||||
/**
|
||||
* Created by khy on 15/03/18.
|
||||
*/
|
||||
@ -34,12 +35,11 @@ public abstract class BaseFragment_TabLayout extends NormalFragment implements V
|
||||
|
||||
public static final String PAGE_INDEX = "PAGE_INDEX";
|
||||
|
||||
@BindView(R.id.fragment_tab_layout)
|
||||
protected TabLayout mTabLayout;
|
||||
@BindView(R.id.fragment_view_pager)
|
||||
protected NoScrollableViewPager mViewPager;
|
||||
@BindView(R.id.fragment_tab_indicator)
|
||||
protected TabIndicatorView mTabIndicatorView;
|
||||
@Nullable
|
||||
protected View mDividerLineView;
|
||||
|
||||
protected List<Fragment> mFragmentsList;
|
||||
|
||||
@ -75,19 +75,6 @@ public abstract class BaseFragment_TabLayout extends NormalFragment implements V
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (getArguments() != null) mCheckedIndex = getArguments().getInt(PAGE_INDEX, 0);
|
||||
mTabTitleList = new ArrayList<>();
|
||||
initTabTitleList(mTabTitleList);
|
||||
mFragmentsList = new ArrayList<>(restoreFragments());
|
||||
if (mFragmentsList.isEmpty() || mFragmentsList.size() != mTabTitleList.size()) {
|
||||
mFragmentsList.clear();
|
||||
initFragmentList(mFragmentsList);
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<Fragment> restoreFragments() {
|
||||
String tag = "android:switcher:" + mViewPager.getId() + ":";
|
||||
ArrayList<Fragment> fragments = new ArrayList<>();
|
||||
@ -105,6 +92,21 @@ public abstract class BaseFragment_TabLayout extends NormalFragment implements V
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
mTabLayout = view.findViewById(R.id.fragment_tab_layout);
|
||||
mViewPager = view.findViewById(R.id.fragment_view_pager);
|
||||
mTabIndicatorView = view.findViewById(R.id.fragment_tab_indicator);
|
||||
mDividerLineView = view.findViewById(R.id.dividerLine);
|
||||
|
||||
if (getArguments() != null) mCheckedIndex = getArguments().getInt(PAGE_INDEX, 0);
|
||||
mTabTitleList = new ArrayList<>();
|
||||
initTabTitleList(mTabTitleList);
|
||||
mFragmentsList = new ArrayList<>(restoreFragments());
|
||||
if (mFragmentsList.isEmpty() || mFragmentsList.size() != mTabTitleList.size()) {
|
||||
mFragmentsList.clear();
|
||||
initFragmentList(mFragmentsList);
|
||||
}
|
||||
|
||||
mViewPager.setOffscreenPageLimit(mFragmentsList.size());
|
||||
mViewPager.addOnPageChangeListener(this);
|
||||
mViewPager.setAdapter(new FragmentAdapter(getChildFragmentManager(), mFragmentsList, mTabTitleList));
|
||||
@ -119,7 +121,7 @@ public abstract class BaseFragment_TabLayout extends NormalFragment implements V
|
||||
if (tab == null) continue;
|
||||
String tabTitle = tab.getText() != null ? tab.getText().toString() : "";
|
||||
View tabView = provideTabView(i, tabTitle);
|
||||
if (tabView == null) tabView = createDefaultTabCustomView(tabTitle);
|
||||
if (tabView == null) tabView = createDefaultTabCustomView(requireContext(), tabTitle);
|
||||
tab.setCustomView(tabView);
|
||||
}
|
||||
|
||||
@ -152,7 +154,7 @@ public abstract class BaseFragment_TabLayout extends NormalFragment implements V
|
||||
});
|
||||
}
|
||||
|
||||
private static void updateTabStyle(TabLayout.Tab tab, boolean isChecked) {
|
||||
public static void updateTabStyle(TabLayout.Tab tab, boolean isChecked) {
|
||||
View tabView = tab.getCustomView();
|
||||
if (tabView == null) {
|
||||
Utils.log("TabLayout->Tab样式不是通用样式,请检查");
|
||||
@ -171,12 +173,13 @@ public abstract class BaseFragment_TabLayout extends NormalFragment implements V
|
||||
return;
|
||||
}
|
||||
tabTitle.setTypeface(isChecked ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
|
||||
tabTitle.setTextColor(ContextCompat.getColorStateList(tabTitle.getContext(), R.color.text_tabbar_style));
|
||||
}
|
||||
|
||||
// 如果不设置View的话,无法动态设置字体样式
|
||||
@NonNull
|
||||
public static View createDefaultTabCustomView(String title) {
|
||||
View view = LayoutInflater.from(HaloApp.getInstance().getApplication().getBaseContext()).inflate(R.layout.tab_item, null);
|
||||
public static View createDefaultTabCustomView(Context context, String title) {
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.tab_item, null);
|
||||
View tabTitle = view.findViewById(R.id.tab_title);
|
||||
if (tabTitle instanceof CheckedTextView) {
|
||||
((CheckedTextView) tabTitle).setText(title);
|
||||
@ -198,4 +201,22 @@ public abstract class BaseFragment_TabLayout extends NormalFragment implements V
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNightModeChange() {
|
||||
super.onNightModeChange();
|
||||
View container = requireView().findViewById(R.id.fragment_tab_container);
|
||||
if (container != null) {
|
||||
container.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.background_white));
|
||||
for (int i = 0; i < mTabLayout.getTabCount(); i++) {
|
||||
TabLayout.Tab tab = mTabLayout.getTabAt(i);
|
||||
if (tab != null) {
|
||||
updateTabStyle(tab, tab.isSelected());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mDividerLineView != null) {
|
||||
mDividerLineView.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.divider));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,12 +11,14 @@ package com.gh.base.fragment;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.gh.gamecenter.normal.NormalFragment;
|
||||
@ -53,19 +55,21 @@ public abstract class BaseFragment_ViewPager extends NormalFragment implements D
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mFragmentsList = new ArrayList<>();
|
||||
initFragmentList(mFragmentsList);
|
||||
mAdapter = BaseFragmentPagerAdapter.newInstance(getChildFragmentManager(), mFragmentsList);
|
||||
final Bundle args = getArguments();
|
||||
if (args != null) {
|
||||
mCheckedIndex = args.getInt(ARGS_INDEX);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mViewPager = (ViewPager) view.findViewById(getViewPagerId());
|
||||
mViewPager = view.findViewById(getViewPagerId());
|
||||
mFragmentsList = restoreFragments();
|
||||
if (mFragmentsList.size() == 0) {
|
||||
initFragmentList(mFragmentsList);
|
||||
}
|
||||
mAdapter = BaseFragmentPagerAdapter.newInstance(getChildFragmentManager(), mFragmentsList);
|
||||
final Bundle args = getArguments();
|
||||
if (args != null) {
|
||||
mCheckedIndex = args.getInt(ARGS_INDEX);
|
||||
}
|
||||
mViewPager.setOffscreenPageLimit(mFragmentsList.size());
|
||||
mViewPager.setAdapter(mAdapter);
|
||||
if (mCheckedIndex < mFragmentsList.size()) {
|
||||
@ -116,9 +120,24 @@ public abstract class BaseFragment_ViewPager extends NormalFragment implements D
|
||||
fragment.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ArrayList<Fragment> restoreFragments() {
|
||||
String tag = "android:switcher:" + mViewPager.getId() + ":";
|
||||
ArrayList<Fragment> fragments = new ArrayList<>();
|
||||
int childCount = getChildCount();
|
||||
for (int index = 0; index < childCount; index++) {
|
||||
Fragment fragment = getChildFragmentManager().findFragmentByTag(tag + index);
|
||||
if (fragment != null) {
|
||||
fragments.add(fragment);
|
||||
}
|
||||
}
|
||||
|
||||
return fragments;
|
||||
}
|
||||
|
||||
public abstract int getChildCount();
|
||||
|
||||
public int getCurrentItem() {
|
||||
return mViewPager != null ? mViewPager.getCurrentItem() : 0;
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ package com.gh.base.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import com.gh.gamecenter.normal.NormalFragment
|
||||
import com.lightgame.utils.Utils
|
||||
|
||||
/**
|
||||
* 懒加载(支持多层嵌套)
|
||||
@ -105,15 +104,25 @@ abstract class BaseLazyFragment : NormalFragment() {
|
||||
isSupportVisible = visible
|
||||
|
||||
if (visible) {
|
||||
if (mIsFirstVisible && view != null) {
|
||||
|
||||
// TODO 当 fragment 重建时这里的被调用很奇怪,onActivityCreated 回调触发,但此时的 view 是空的,原因是 createView 还没被调用
|
||||
// TODO 这样就造成了 onFragmentResume 里可能用到 view 的地方出现空指针异常,所以这里遇到 view 为空的时候 return 等下一次被调用才进去
|
||||
if (view == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (mIsFirstVisible) {
|
||||
mIsFirstVisible = false
|
||||
onFragmentFirstVisible()
|
||||
}
|
||||
onFragmentResume()
|
||||
dispatchChildVisibleState(true)
|
||||
} else {
|
||||
dispatchChildVisibleState(false)
|
||||
onFragmentPause()
|
||||
// 当 fragment 重建时,这个代码块可能在第一次 view 为空的 visible 后调用导致在 onFragmentPause 里可能用到 view 的地方出现空指针异常
|
||||
if (!mIsFirstVisible) {
|
||||
dispatchChildVisibleState(false)
|
||||
onFragmentPause()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,26 +3,20 @@ package com.gh.base.fragment
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager.widget.PagerAdapter
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import butterknife.BindView
|
||||
import com.gh.base.adapter.FragmentAdapter
|
||||
import com.gh.common.view.TabIndicatorView
|
||||
import com.gh.gamecenter.R
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.lightgame.view.NoScrollableViewPager
|
||||
import java.util.*
|
||||
|
||||
abstract class BaseLazyTabFragment : BaseLazyFragment(), ViewPager.OnPageChangeListener {
|
||||
|
||||
@BindView(R.id.fragment_tab_layout)
|
||||
lateinit var mTabLayout: TabLayout
|
||||
|
||||
@BindView(R.id.fragment_view_pager)
|
||||
lateinit var mViewPager: NoScrollableViewPager
|
||||
|
||||
@BindView(R.id.fragment_tab_indicator)
|
||||
lateinit var mTabIndicatorView: TabIndicatorView
|
||||
|
||||
var mFragmentsList: MutableList<Fragment> = arrayListOf()
|
||||
@ -67,6 +61,14 @@ abstract class BaseLazyTabFragment : BaseLazyFragment(), ViewPager.OnPageChangeL
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mTabLayout = view.findViewById(R.id.fragment_tab_layout)
|
||||
mViewPager = view.findViewById(R.id.fragment_view_pager)
|
||||
mTabIndicatorView = view.findViewById(R.id.fragment_tab_indicator)
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
super.onFragmentFirstVisible()
|
||||
mTabTitleList.clear()
|
||||
@ -82,7 +84,7 @@ abstract class BaseLazyTabFragment : BaseLazyFragment(), ViewPager.OnPageChangeL
|
||||
mViewPager.offscreenPageLimit = mFragmentsList.size
|
||||
mViewPager.addOnPageChangeListener(this)
|
||||
mViewPager.adapter = providePagerAdapter()
|
||||
?: FragmentAdapter(childFragmentManager, mFragmentsList, mTabTitleList)
|
||||
?: FragmentAdapter(childFragmentManager, mFragmentsList, mTabTitleList)
|
||||
mViewPager.currentItem = mCheckedIndex
|
||||
mTabLayout.setupWithViewPager(mViewPager)
|
||||
mTabIndicatorView.setupWithTabLayout(mTabLayout)
|
||||
@ -92,7 +94,7 @@ abstract class BaseLazyTabFragment : BaseLazyFragment(), ViewPager.OnPageChangeL
|
||||
val tab = mTabLayout.getTabAt(i) ?: continue
|
||||
val tabTitle = if (tab.text != null) tab.text.toString() else ""
|
||||
var tabView = provideTabView(i, tabTitle)
|
||||
if (tabView == null) tabView = BaseFragment_TabLayout.createDefaultTabCustomView(tabTitle)
|
||||
if (tabView == null) tabView = BaseFragment_TabLayout.createDefaultTabCustomView(requireContext(), tabTitle)
|
||||
tab.customView = tabView
|
||||
}
|
||||
BaseFragment_TabLayout.initTabStyle(mTabLayout, mCheckedIndex)
|
||||
@ -105,18 +107,35 @@ abstract class BaseLazyTabFragment : BaseLazyFragment(), ViewPager.OnPageChangeL
|
||||
for (index in 0 until childCount) {
|
||||
val fragment = childFragmentManager.findFragmentByTag("$tag$index")
|
||||
if (fragment != null) {
|
||||
restoreFragment(fragment)
|
||||
fragments.add(fragment)
|
||||
}
|
||||
}
|
||||
return fragments
|
||||
}
|
||||
|
||||
open fun restoreFragment(fragment: Fragment) {}
|
||||
|
||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
|
||||
|
||||
override fun onPageSelected(position: Int) {}
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) {}
|
||||
|
||||
override fun onNightModeChange() {
|
||||
super.onNightModeChange()
|
||||
val container = requireView().findViewById<View>(R.id.fragment_tab_container)
|
||||
if (container != null) {
|
||||
container.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.background_white))
|
||||
for (i in 0 until mTabLayout.tabCount) {
|
||||
val tab = mTabLayout.getTabAt(i)
|
||||
if (tab != null) {
|
||||
BaseFragment_TabLayout.updateTabStyle(tab, tab.isSelected)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PAGE_INDEX = "PAGE_INDEX"
|
||||
}
|
||||
|
||||
99
app/src/main/java/com/gh/base/fragment/LazyFragment.kt
Normal file
99
app/src/main/java/com/gh/base/fragment/LazyFragment.kt
Normal file
@ -0,0 +1,99 @@
|
||||
package com.gh.base.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewStub
|
||||
import com.gh.gamecenter.R
|
||||
|
||||
/**
|
||||
* 这是在 BaseLazyFragment 之上添加了一些通用功能的抽象类
|
||||
*
|
||||
* 怎么将一个已有的 fragment 转化为懒加载 (延迟渲染) 的 fragment 呢?
|
||||
* (继承 ListFragment 的类请改为继承 LazyListFragment)
|
||||
*
|
||||
* 0. 删掉旧的 getInflatedLayout() 的代码,现在由 getStubLayoutId() 提供 Stub 布局 (默认为 R.layout.fragment_stub,若重写请注意提供 id 为 stub 的 ViewStub)
|
||||
* 1. 重写 getRealLayoutId(),提供实际要延迟渲染的 layout Id
|
||||
* 1. 将原有在 onCreate() 的代码移动到 onFragmentFirstVisible()
|
||||
* 2. 将原有在 onViewCreated() 的代码移动到 initRealView()
|
||||
* (注意,initRealView() 在 onFragmentFirstVisible() 中被调用,如果要初始化 viewModel 等非 UI 对象请在 super.onFragmentVisible() 调用)
|
||||
* 3. 如需使用 ViewBinding ,在 onRealLayoutInflated() 的回调中初始化 ViewBinding 即可
|
||||
* 4. onResume() 的代码移动到 onFragmentResume(),onPause() 的代码移动到 onFragmentPause()
|
||||
* 5. Done!
|
||||
*/
|
||||
abstract class LazyFragment : BaseLazyFragment() {
|
||||
|
||||
// ViewStub + ViewBinding 有莫名的 bug,语法上没问题,但编译时通不过。
|
||||
private var mViewStub: ViewStub? = null
|
||||
|
||||
private var mIsRecreatedByFragmentManager = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
if (savedInstanceState != null) {
|
||||
mIsRecreatedByFragmentManager = true
|
||||
}
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
"使用了 LazyFragment 以后不要 override getLayoutId(),建议 override getRealLayoutId() 或者 getStubLayoutId()",
|
||||
ReplaceWith("LazyFragment.getRealLayoutId()")
|
||||
)
|
||||
override fun getLayoutId() = if (isRecreatedByFragmentManager()) {
|
||||
getRealLayoutId()
|
||||
} else {
|
||||
getStubLayoutId()
|
||||
}
|
||||
|
||||
override fun initView(view: View?) {
|
||||
super.initView(view)
|
||||
if (!isRecreatedByFragmentManager()) {
|
||||
mViewStub = mCachedView.findViewById(R.id.stub)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
super.onFragmentFirstVisible()
|
||||
inflateRealView()
|
||||
initRealView()
|
||||
}
|
||||
|
||||
/**
|
||||
* 真正 inflate View 的地方
|
||||
*/
|
||||
protected open fun inflateRealView() {
|
||||
if (isRecreatedByFragmentManager()) {
|
||||
onRealLayoutInflated(mCachedView)
|
||||
} else {
|
||||
mViewStub?.layoutResource = getRealLayoutId()
|
||||
mViewStub?.setOnInflateListener { _, inflatedView -> onRealLayoutInflated(inflatedView) }
|
||||
mViewStub?.inflate()?.let { mCachedView = it }
|
||||
}
|
||||
}
|
||||
|
||||
protected fun isRecreatedByFragmentManager() = mIsRecreatedByFragmentManager
|
||||
|
||||
/**
|
||||
* 请在这个方法之后获取初始化后的各种 view
|
||||
*
|
||||
* 替换旧 fragment 实现时,等同于 onViewCreated
|
||||
*/
|
||||
protected open fun initRealView() {}
|
||||
|
||||
/**
|
||||
* 提供要 stub inflate 的 layout
|
||||
*/
|
||||
protected abstract fun getRealLayoutId(): Int
|
||||
|
||||
/**
|
||||
* 提供含有 stub 的 layout
|
||||
*/
|
||||
protected open fun getStubLayoutId(): Int {
|
||||
return R.layout.fragment_stub
|
||||
}
|
||||
|
||||
/**
|
||||
* 真实 layout inflate 完成的回调,可用于 viewBinding
|
||||
*/
|
||||
protected open fun onRealLayoutInflated(inflatedView: View) {}
|
||||
|
||||
}
|
||||
@ -4,12 +4,15 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.common.util.ExtensionsKt;
|
||||
import com.gh.gamecenter.R;
|
||||
|
||||
/**
|
||||
@ -52,6 +55,14 @@ public class WaitingDialogFragment extends BaseDialogFragment {
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
WindowManager.LayoutParams layoutParams = getDialog().getWindow().getAttributes();
|
||||
layoutParams.width = DisplayUtils.dip2px(160);
|
||||
getDialog().getWindow().setAttributes(layoutParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(FragmentManager manager, String tag) {
|
||||
try {
|
||||
@ -87,7 +98,11 @@ public class WaitingDialogFragment extends BaseDialogFragment {
|
||||
@Override
|
||||
public void dismissAllowingStateLoss() {
|
||||
mBackListener = null;
|
||||
super.dismissAllowingStateLoss();
|
||||
try {
|
||||
super.dismissAllowingStateLoss();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static class WaitingDialogData {
|
||||
|
||||
@ -2,12 +2,14 @@ package com.gh.common
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import com.gh.base.GHThreadFactory
|
||||
import com.gh.common.AppExecutor.heavyWeightIoExecutor
|
||||
import com.gh.common.AppExecutor.ioExecutor
|
||||
import com.gh.common.AppExecutor.lightWeightIoExecutor
|
||||
import com.gh.common.AppExecutor.logExecutor
|
||||
import com.gh.common.AppExecutor.uiExecutor
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.util.concurrent.Executor
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.*
|
||||
|
||||
/**
|
||||
* APP 线程池管理类
|
||||
@ -15,16 +17,34 @@ import java.util.concurrent.Executors
|
||||
* [ioExecutor] 是一个最大线程数固定的线程池,较为繁重的 IO 任务可以交给它
|
||||
* [uiExecutor] 是主线程的包裹,需要切换至主线程执行可以用它
|
||||
* [lightWeightIoExecutor] 是一个单线程的线程池,轻量级且需要保证同一线程的 IO 任务可以交给它
|
||||
*
|
||||
* [heavyWeightIoExecutor] 重量级的线程池,一些高频调用但不用保证结果的任务可以交给它
|
||||
* [logExecutor] 只为上传 log 而使用的线程池
|
||||
*/
|
||||
object AppExecutor {
|
||||
|
||||
private val mCoreSize = Runtime.getRuntime().availableProcessors()
|
||||
private val mMinimumPoolSize = 6.coerceAtLeast(mCoreSize)
|
||||
private val mMaximumPoolSize = 24.coerceAtLeast(mCoreSize * 3)
|
||||
|
||||
@JvmStatic
|
||||
val uiExecutor by lazy { MainThreadExecutor() }
|
||||
|
||||
@JvmStatic
|
||||
val lightWeightIoExecutor by lazy { Executors.newSingleThreadExecutor() }
|
||||
val lightWeightIoExecutor: ExecutorService by lazy { Executors.newSingleThreadExecutor(GHThreadFactory("GH_LIGHT_WEIGHT_IO_THREAD")) }
|
||||
|
||||
@JvmStatic
|
||||
val ioExecutor = Executors.newCachedThreadPool() // 用 by lazy 可能影响初始化速度
|
||||
val heavyWeightIoExecutor: ExecutorService by lazy { Executors.newFixedThreadPool(2, GHThreadFactory("GH_HEAVY_WEIGHT_IO_THREAD")) }
|
||||
|
||||
@JvmStatic
|
||||
val logExecutor: ExecutorService by lazy { Executors.newSingleThreadExecutor(GHThreadFactory("GH_LOG_THREAD")) }
|
||||
|
||||
@JvmStatic
|
||||
val ioExecutor = ThreadPoolExecutor(
|
||||
mMinimumPoolSize,
|
||||
mMaximumPoolSize,
|
||||
20L, TimeUnit.SECONDS,
|
||||
LinkedBlockingQueue(256),
|
||||
GHThreadFactory("GH_IO_THREAD"))
|
||||
|
||||
val cachedScheduler by lazy { Schedulers.from(ioExecutor) }
|
||||
|
||||
@ -41,14 +61,14 @@ object AppExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
fun runOnIoThread(isLightWeightTask: Boolean = false, f: () -> Unit) {
|
||||
if (isLightWeightTask) {
|
||||
AppExecutor.lightWeightIoExecutor.execute(f)
|
||||
} else {
|
||||
AppExecutor.ioExecutor.execute(f)
|
||||
fun runOnIoThread(isLightWeightTask: Boolean = false, isHeavyWightTask: Boolean = false, f: () -> Unit) {
|
||||
when {
|
||||
isLightWeightTask -> lightWeightIoExecutor.execute(f)
|
||||
isHeavyWightTask -> heavyWeightIoExecutor.execute(f)
|
||||
else -> ioExecutor.execute(f)
|
||||
}
|
||||
}
|
||||
|
||||
fun runOnUiThread(f: () -> Unit) {
|
||||
AppExecutor.uiExecutor.execute(f)
|
||||
uiExecutor.execute(f)
|
||||
}
|
||||
@ -3,38 +3,39 @@ package com.gh.common
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.util.Base64
|
||||
import android.webkit.JavascriptInterface
|
||||
import androidx.annotation.Keep
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.gh.base.CurrentActivityHolder
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.loghub.LoghubUtils
|
||||
import com.gh.common.tracker.Tracker
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.view.dsbridge.CompletionHandler
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.ImageViewerActivity
|
||||
import com.gh.gamecenter.LoginActivity
|
||||
import com.gh.gamecenter.WebActivity
|
||||
import com.gh.gamecenter.entity.Badge
|
||||
import com.gh.gamecenter.entity.MtaEvent
|
||||
import com.gh.gamecenter.entity.NotificationUgc
|
||||
import com.gh.gamecenter.*
|
||||
import com.gh.gamecenter.energy.EnergyCenterActivity
|
||||
import com.gh.gamecenter.energy.EnergyHouseActivity
|
||||
import com.gh.gamecenter.entity.*
|
||||
import com.gh.gamecenter.help.QaFeedbackDialogFragment
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.gamecenter.personalhome.border.AvatarBorderActivity
|
||||
import com.gh.gamecenter.security.BindPhoneActivity
|
||||
import com.gh.gamecenter.user.LoginTag
|
||||
import com.gh.gamecenter.user.UserRepository
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
import org.json.JSONObject
|
||||
import retrofit2.HttpException
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class DefaultJsApi(var context: Context) {
|
||||
|
||||
private var mLoginHandler: CompletionHandler<Any>? = null
|
||||
|
||||
@JavascriptInterface
|
||||
fun isGhzs(msg: Any): String {
|
||||
return "true"
|
||||
@ -64,8 +65,12 @@ class DefaultJsApi(var context: Context) {
|
||||
|
||||
@JavascriptInterface
|
||||
fun login(msg: Any) {
|
||||
val intent = LoginActivity.getIntent(context, "浏览器")
|
||||
context.startActivity(intent)
|
||||
if (SPUtils.getBoolean(Constants.SP_HAS_GET_PHONE_INFO) || NetworkUtils.isOpenMobileData(context)) {
|
||||
QuickLoginHelper.startLogin(context, "浏览器")
|
||||
} else {
|
||||
val intent = LoginActivity.getIntent(context, "浏览器")
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@ -92,7 +97,7 @@ class DefaultJsApi(var context: Context) {
|
||||
|
||||
@JavascriptInterface
|
||||
fun getAppVersionCode(msg: Any): Int {
|
||||
return PackageUtils.getVersionCode()
|
||||
return PackageUtils.getGhVersionCode()
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@ -108,35 +113,39 @@ class DefaultJsApi(var context: Context) {
|
||||
wechatLoginInfoMap["access_token"] = jsonContent.getString("access_token")
|
||||
wechatLoginInfoMap["refresh_token"] = jsonContent.getString("refresh_token")
|
||||
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application)
|
||||
.api
|
||||
.postBindWechat(wechatLoginInfoMap.createRequestBody())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
handler.complete(true)
|
||||
}
|
||||
WechatBindHelper.bindWechat(wechatLoginInfoMap, object : BiCallback<Boolean, Boolean> {
|
||||
override fun onFirst(first: Boolean) {
|
||||
EnergyTaskHelper.postEnergyTask("bind_wechat")
|
||||
handler.complete(true)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
handler.complete(false)
|
||||
if (exception is HttpException) {
|
||||
ErrorHelper.handleError(HaloApp.getInstance().application, exception.response().errorBody()?.string())
|
||||
}
|
||||
}
|
||||
})
|
||||
override fun onSecond(second: Boolean) {
|
||||
handler.complete(false)
|
||||
}
|
||||
})
|
||||
|
||||
LoginHelper.unregisterCallback()
|
||||
}
|
||||
|
||||
override fun onLoginFailure(loginType: LoginTag, error: String) {
|
||||
handler.complete(false)
|
||||
|
||||
LoginHelper.unregisterCallback()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun refreshWechatBindData(msg: Any) {
|
||||
WechatBindHelper.getWechatConfig(null)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun copyText(msg: Any) {
|
||||
msg.toString().copyTextAndToast()
|
||||
runOnUiThread {
|
||||
msg.toString().copyTextAndToast()
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@ -198,6 +207,7 @@ class DefaultJsApi(var context: Context) {
|
||||
@JavascriptInterface
|
||||
fun updateRegulationTestStatus(msg: Any) {
|
||||
if (msg.toString().toLowerCase(Locale.getDefault()) == "pass") {
|
||||
EnergyTaskHelper.postEnergyTask("finish_etiquette_exam")
|
||||
SPUtils.setString(Constants.SP_REGULATION_TEST_PASS_STATUS, "pass")
|
||||
}
|
||||
}
|
||||
@ -215,7 +225,7 @@ class DefaultJsApi(var context: Context) {
|
||||
|
||||
@JavascriptInterface
|
||||
fun showIncompatibleVersionDialog(msg: Any) {
|
||||
DialogUtils.showLowVersionDialog(context)
|
||||
DialogHelper.showUpgradeDialog(context)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@ -228,6 +238,20 @@ class DefaultJsApi(var context: Context) {
|
||||
MessageShareUtils.getInstance(context).shareFromWeb(context, imageShareEvent.type)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun inviteFriends(event: Any) {
|
||||
val inviteEvent = event.toString().toObject() ?: InviteFriendsEvent()
|
||||
val context = CurrentActivityHolder.getCurrentActivity()
|
||||
if ("poster" == inviteEvent.type) {
|
||||
Base64ImageHolder.image = inviteEvent.poster.run {
|
||||
if (this.startsWith("data:image/png;base64")) this.split(",")[1] else this
|
||||
}
|
||||
MessageShareUtils.getInstance(context).shareInviteFriends(context, inviteEvent.way)
|
||||
} else {
|
||||
ShareUtils.getInstance(context).shareInviteFriends(context, inviteEvent.url, inviteEvent.way)
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun bindPhone(msg: Any) {
|
||||
val intent = BindPhoneActivity.getNormalIntent(context, false)
|
||||
@ -243,7 +267,7 @@ class DefaultJsApi(var context: Context) {
|
||||
|
||||
@JavascriptInterface
|
||||
fun logoutExitWebViewAndRedirectToLogin() {
|
||||
UserRepository.getInstance(context).logout()
|
||||
UserRepository.getInstance().logout()
|
||||
if (context is Activity) {
|
||||
AppExecutor.uiExecutor.executeWithDelay(Runnable {
|
||||
context.ifLogin("内部网页")
|
||||
@ -257,10 +281,170 @@ class DefaultJsApi(var context: Context) {
|
||||
runOnUiThread { DirectUtils.directToWebView(context, url.toString(), "内部网页") }
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun postWearBadgeTask(msg: Any) {
|
||||
EnergyTaskHelper.postEnergyTask("wear_badge")
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun startEnergyCenter(msg: Any) {
|
||||
context.startActivity(EnergyCenterActivity.getIntent(context))
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun startEnergyHouse(msg: Any) {
|
||||
context.startActivity(EnergyHouseActivity.getIntent(context))
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun showQaFeedbackDialog(msg: Any) {
|
||||
QaFeedbackDialogFragment.show(context as AppCompatActivity, msg.toString())
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun getMetaObject(msg: Any): String {
|
||||
return LogUtils.getMetaObject().toString()
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun getLaunchId(msg: Any): String {
|
||||
return Tracker.launchId
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun getSessionId(msg: Any): String {
|
||||
return Tracker.sessionId
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun postLogEvent(event: Any) {
|
||||
val logEvent = event.toString().toObject() ?: LogEvent()
|
||||
debugOnly {
|
||||
Utils.log("LogUtils->${logEvent.jsonString}")
|
||||
}
|
||||
LoghubUtils.log(logEvent.jsonString, logEvent.logStore, false)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun startAvatarBorderPage(msg: Any) {
|
||||
context.startActivity(AvatarBorderActivity.getIntent(context, msg.toString()))
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun isNetworkConnected(): Boolean {
|
||||
return NetworkUtils.isNetworkConnected(context)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun isWifiConnected(): Boolean {
|
||||
return NetworkUtils.isWifiConnected(context)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun enableBackToActivity(msg: Any) {
|
||||
FloatingBackViewManager.enableBackView(FloatingBackViewManager.TYPE_ACTIVITY, msg.toString())
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun startBBSStayTimeCount(msg: Any) {
|
||||
BbsStayTimeHelper.enableStayTimeCount(msg.toString().toInt())
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun saveBase64ImageToGallery(msg: Any) {
|
||||
val base64StringData = msg.toString()
|
||||
runOnUiThread {
|
||||
(context as? FragmentActivity)?.checkStoragePermissionBeforeAction {
|
||||
runOnIoThread {
|
||||
val base64String = base64StringData.replace("data:image/png;base64", "")
|
||||
tryWithDefaultCatch {
|
||||
val imageFile = File(HaloApp.getInstance().cacheDir.absolutePath + File.separator + System.currentTimeMillis() + ".png")
|
||||
val decodedString = Base64.decode(base64String, Base64.DEFAULT)
|
||||
val bos = BufferedOutputStream(FileOutputStream(imageFile))
|
||||
bos.write(decodedString)
|
||||
bos.flush()
|
||||
bos.close()
|
||||
|
||||
ImageUtils.saveImageToFile(imageFile, "", true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun loginWithCallback(msg: Any, handler: CompletionHandler<Any>) {
|
||||
mLoginHandler = handler
|
||||
login(msg)
|
||||
}
|
||||
|
||||
fun onLogin() {
|
||||
mLoginHandler?.complete(true)
|
||||
mLoginHandler = null
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun openInNewFullWebview(url: Any) {
|
||||
runOnUiThread { DirectUtils.directToFullScreenWebPage(context, url.toString(), true) }
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun startGameCollectionSquareBrowseTask(event: Any) {
|
||||
val browseTimeEvent = event.toString().toObject() ?: BrowseTaskEvent()
|
||||
GameCollectionSquareBrowseTaskHelper.enableBrowseTimeCount(
|
||||
browseTimeEvent.timeout.toInt(),
|
||||
browseTimeEvent.isFinished == "true"
|
||||
)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun checkUpdateGhzs(msg: Any) {
|
||||
context.startActivity(AboutActivity.getIntent(context, true))
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public fun clickGameActivityDownloadBtn(event: Any) {
|
||||
val gameActivityEvent = event.toString().toObject() ?: GameActivityEvent()
|
||||
GameActivityDownloadHelper.start(context, gameActivityEvent)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun isGameActivityTaskCompleted(event: Any, handler: CompletionHandler<Any>) {
|
||||
val gameActivityEvent = event.toString().toObject() ?: GameActivityEvent()
|
||||
GameActivityDownloadHelper.checkTaskComplete(context, gameActivityEvent, handler)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun postGameActivityExposureEvent(event: Any) {
|
||||
val gameActivityEvent = event.toString().toObject() ?: GameActivityEvent()
|
||||
GameActivityDownloadHelper.postExposureEvent(gameActivityEvent)
|
||||
}
|
||||
|
||||
@Keep
|
||||
internal data class ImageEvent(var imageList: ArrayList<String> = arrayListOf(), var position: Int = 0)
|
||||
|
||||
@Keep
|
||||
internal data class ImageShareEvent(var image: String = "", var type: String = "")
|
||||
|
||||
@Keep
|
||||
internal data class InviteFriendsEvent(
|
||||
var type: String = "",
|
||||
var way: String = "",
|
||||
var url: String = "",
|
||||
var poster: String = ""
|
||||
)
|
||||
|
||||
@Keep
|
||||
internal data class LogEvent(var jsonString: String = "", var logStore: String = "")
|
||||
|
||||
@Keep
|
||||
internal data class BrowseTaskEvent(var timeout: String = "", var isFinished: String = "")
|
||||
|
||||
@Keep
|
||||
data class GameActivityEvent(
|
||||
var gameId: String = "",
|
||||
var activityTitle: String = "",
|
||||
var activityId: String = "",
|
||||
var platform: String = ""
|
||||
)
|
||||
}
|
||||
|
||||
@ -5,20 +5,25 @@ import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.text.TextUtils
|
||||
import android.util.Base64
|
||||
import com.gh.base.CurrentActivityHolder
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.util.DirectUtils.directToFeedback
|
||||
import com.gh.common.util.DirectUtils.directToGameDetailVideoStreaming
|
||||
import com.gh.common.util.DirectUtils.directToGameServerCalendar
|
||||
import com.gh.common.util.DirectUtils.directToGameVideo
|
||||
import com.gh.common.util.DirectUtils.directToLegacyVideoDetail
|
||||
import com.gh.common.util.DirectUtils.directToLinkPage
|
||||
import com.gh.common.util.DirectUtils.directToQa
|
||||
import com.gh.common.util.DirectUtils.directToVideoDetail
|
||||
import com.gh.common.util.GsonUtils.gson
|
||||
import com.gh.gamecenter.LibaoDetailActivity
|
||||
import com.gh.gamecenter.MainActivity
|
||||
import com.gh.gamecenter.NewsDetailActivity
|
||||
import com.gh.gamecenter.WebActivity
|
||||
import com.gh.gamecenter.entity.*
|
||||
import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity
|
||||
import com.gh.gamecenter.qa.BbsType
|
||||
import com.gh.gamecenter.qa.video.publish.VideoPublishActivity
|
||||
import com.gh.gamecenter.subject.SubjectActivity
|
||||
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
|
||||
import com.lightgame.utils.Utils
|
||||
@ -28,6 +33,14 @@ object DefaultUrlHandler {
|
||||
|
||||
@JvmStatic
|
||||
fun interceptUrl(context: Context, url: String, entrance: String): Boolean {
|
||||
return interceptUrl(context, url, entrance, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bringAppToFront 是否需要在不匹配 host 的时候把 APP 调回到前台 (如微信调起)
|
||||
*/
|
||||
@JvmStatic
|
||||
fun interceptUrl(context: Context, url: String, entrance: String, bringAppToFront: Boolean = false): Boolean {
|
||||
val uri = Uri.parse(url)
|
||||
if ("ghzhushou" == uri.scheme) {
|
||||
Utils.log("url = $url")
|
||||
@ -38,11 +51,21 @@ object DefaultUrlHandler {
|
||||
if (!TextUtils.isEmpty(path)) {
|
||||
id = path!!.substring(1)
|
||||
}
|
||||
if (TextUtils.isEmpty(id)) {
|
||||
id = uri.getQueryParameter("id") ?: ""
|
||||
}
|
||||
|
||||
val intent: Intent
|
||||
when (host) {
|
||||
"article" -> context.startActivity(NewsDetailActivity.getIntentById(context, id, entrance))
|
||||
|
||||
"game" -> DirectUtils.directToGameDetail(context, id = id, tab = uri.getQueryParameter("to"), autoDownload = uri.getQueryParameter("auto_download") == "true", entrance = entrance)
|
||||
"game" -> DirectUtils.directToGameDetail(
|
||||
context,
|
||||
id = id,
|
||||
tab = uri.getQueryParameter("to"),
|
||||
autoDownload = uri.getQueryParameter("auto_download") == "true",
|
||||
entrance = entrance
|
||||
)
|
||||
|
||||
"column" -> SubjectActivity.startSubjectActivity(context, id, uri.getQueryParameter("name"), false, entrance)
|
||||
|
||||
@ -63,9 +86,7 @@ object DefaultUrlHandler {
|
||||
}
|
||||
|
||||
"inurl" -> {
|
||||
intent = Intent(context, WebActivity::class.java)
|
||||
intent.putExtra(EntranceUtils.KEY_URL, uri.getQueryParameter("url"))
|
||||
context.startActivity(intent)
|
||||
DirectUtils.directToWebView(context, uri.getQueryParameter("url") ?: "")
|
||||
}
|
||||
|
||||
"outurl" -> {
|
||||
@ -82,6 +103,8 @@ object DefaultUrlHandler {
|
||||
}
|
||||
"question" -> DirectUtils.directToQuestionDetail(context, id, entrance, "文章链接")
|
||||
|
||||
"real_name" -> DirectUtils.directToRealName(context)
|
||||
|
||||
"community" -> {
|
||||
val community = CommunityEntity()
|
||||
community.id = id
|
||||
@ -120,8 +143,9 @@ object DefaultUrlHandler {
|
||||
}
|
||||
if ("articles" == type) {
|
||||
DirectUtils.directToCommunityArticle(
|
||||
context, typeId, communityId,
|
||||
entrance, "文章链接")
|
||||
context, typeId, communityId,
|
||||
entrance, "文章链接"
|
||||
)
|
||||
}
|
||||
}
|
||||
EntranceUtils.HOST_UPLOAD_VIDEO -> {
|
||||
@ -141,7 +165,8 @@ object DefaultUrlHandler {
|
||||
}
|
||||
EntranceUtils.HOST_USERHOME -> {
|
||||
val position = uri.getQueryParameter("position")
|
||||
DirectUtils.directToHomeActivity(context, id, if (position.isNullOrEmpty()) -1 else position.toInt(), entrance, "")
|
||||
val subtype = uri.getQueryParameter("sub_type") ?: ""
|
||||
DirectUtils.directToHomeActivity(context, id, subtype, if (position.isNullOrEmpty()) -1 else position.toInt(), entrance, "")
|
||||
}
|
||||
EntranceUtils.HOST_VIDEO_MORE -> {
|
||||
val referer = uri.getQueryParameter("referer") ?: ""
|
||||
@ -151,7 +176,7 @@ object DefaultUrlHandler {
|
||||
val fieldId = uri.getQueryParameter("fieldId") ?: ""
|
||||
val sectionName = uri.getQueryParameter("sectionName") ?: ""
|
||||
val paginationType = uri.getQueryParameter("paginationType")
|
||||
?: "page"//活动分页方式 page filter
|
||||
?: "page"//活动分页方式 page filter
|
||||
val location = if (!TextUtils.isEmpty(act)) {
|
||||
VideoDetailContainerViewModel.Location.VIDEO_ACTIVITY.value
|
||||
} else if (!TextUtils.isEmpty(fieldId)) {
|
||||
@ -159,12 +184,31 @@ object DefaultUrlHandler {
|
||||
} else {
|
||||
id
|
||||
}
|
||||
directToVideoDetail(context, id, location, false, gameId, entrance, "", referer, type, act, paginationType, fieldId, sectionName)
|
||||
directToLegacyVideoDetail(
|
||||
context,
|
||||
id,
|
||||
location,
|
||||
false,
|
||||
gameId,
|
||||
entrance,
|
||||
"",
|
||||
referer,
|
||||
type,
|
||||
act,
|
||||
paginationType,
|
||||
fieldId,
|
||||
sectionName
|
||||
)
|
||||
}
|
||||
EntranceUtils.HOST_VIDEO_DETAIL -> {
|
||||
DirectUtils.directToVideoDetail(context, id, entrance, path)
|
||||
}
|
||||
EntranceUtils.HOST_VIDEO_SINGLE -> {
|
||||
val referer = uri.getQueryParameter("referer") ?: ""
|
||||
directToVideoDetail(context, id, VideoDetailContainerViewModel.Location.SINGLE_VIDEO.value,
|
||||
false, "", entrance, "", if (TextUtils.isEmpty(referer)) "" else referer)
|
||||
DirectUtils.directToVideoDetail(
|
||||
context, id, VideoDetailContainerViewModel.Location.SINGLE_VIDEO.value,
|
||||
false, "", entrance, "", if (TextUtils.isEmpty(referer)) "" else referer
|
||||
)
|
||||
}
|
||||
EntranceUtils.HOST_VIDEO_STREAMING_HOME -> {
|
||||
intent = Intent(context, MainActivity::class.java)
|
||||
@ -207,7 +251,7 @@ object DefaultUrlHandler {
|
||||
|
||||
EntranceUtils.HOST_BLOCK -> {
|
||||
val name = uri.getQueryParameter("name")
|
||||
?: ""
|
||||
?: ""
|
||||
val entity = SubjectRecommendEntity(link = id, name = name, text = name)
|
||||
DirectUtils.directToBlock(context, entity, entrance)
|
||||
}
|
||||
@ -222,13 +266,13 @@ object DefaultUrlHandler {
|
||||
|
||||
EntranceUtils.HOST_HELP -> {
|
||||
val name = uri.getQueryParameter("name")
|
||||
?: ""
|
||||
?: ""
|
||||
DirectUtils.directToQa(context, name, id)
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_HELP_COLLECTION -> {
|
||||
val name = uri.getQueryParameter("name")
|
||||
?: ""
|
||||
?: ""
|
||||
DirectUtils.directToQaCollection(context, name, id)
|
||||
}
|
||||
|
||||
@ -256,10 +300,11 @@ object DefaultUrlHandler {
|
||||
|
||||
EntranceUtils.HOST_GAME_NEWS -> {
|
||||
DirectUtils.directToGameNews(
|
||||
context,
|
||||
uri.getQueryParameter(EntranceUtils.KEY_GAME_ID),
|
||||
uri.getQueryParameter(EntranceUtils.KEY_GAME_NAME),
|
||||
entrance);
|
||||
context,
|
||||
uri.getQueryParameter(EntranceUtils.KEY_GAME_ID),
|
||||
uri.getQueryParameter(EntranceUtils.KEY_GAME_NAME),
|
||||
entrance
|
||||
);
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_GAME_CALENDAR -> {
|
||||
@ -275,11 +320,56 @@ object DefaultUrlHandler {
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_GAME_RATING_DETAIL -> {
|
||||
DirectUtils.directToGameRatingDetail(context, uri.getQueryParameter(EntranceUtils.KEY_GAME_ID), uri.getQueryParameter(EntranceUtils.KEY_COMMENT_ID), EntranceUtils.ENTRANCE_BROWSER)
|
||||
DirectUtils.directToGameRatingDetail(
|
||||
context,
|
||||
uri.getQueryParameter(EntranceUtils.KEY_GAME_ID),
|
||||
uri.getQueryParameter(EntranceUtils.KEY_COMMENT_ID),
|
||||
EntranceUtils.ENTRANCE_BROWSER
|
||||
)
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_FORUM -> {
|
||||
DirectUtils.directToForum(context)
|
||||
val position = uri.getQueryParameter(EntranceUtils.KEY_POSITION)?.toInt()
|
||||
|
||||
DirectUtils.directToForum(context, position ?: 0)
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_UPLOAD_VIDEO_NEW -> {
|
||||
val activityName = uri.getQueryParameter("activity_name") ?: ""
|
||||
val activityId = uri.getQueryParameter("activity_id") ?: ""
|
||||
val original = uri.getQueryParameter("original") ?: ""
|
||||
val forumName = uri.getQueryParameter("forum_name") ?: ""
|
||||
val forumId = uri.getQueryParameter("forum_id") ?: ""
|
||||
val forumIcon = uri.getQueryParameter("forum_icon") ?: ""
|
||||
val forumType = uri.getQueryParameter("forum_type") ?: BbsType.OFFICIAL_BBS.value
|
||||
val gameId = uri.getQueryParameter("game_id") ?: ""
|
||||
val gameName = uri.getQueryParameter("game_name") ?: ""
|
||||
val icon = uri.getQueryParameter("game_icon") ?: ""
|
||||
val iconSubscript = uri.getQueryParameter("game_icon_subscript") ?: ""
|
||||
val gameEntity =
|
||||
if (forumType == BbsType.OFFICIAL_BBS.value && gameId.isNotEmpty() && gameName.isNotEmpty() && icon.isNotEmpty()) {
|
||||
GameEntity(id = gameId, mName = gameName, mIcon = icon, mIconSubscript = iconSubscript)
|
||||
} else null
|
||||
val activityLabelEntity = if (activityId.isNotEmpty() && activityName.isNotEmpty()) {
|
||||
ActivityLabelEntity(id = activityId, name = activityName, original = original.ifEmpty { "false" }.toBoolean())
|
||||
} else null
|
||||
val communityEntity = if (forumId.isNotEmpty() && forumName.isNotEmpty() && forumIcon.isNotEmpty()) {
|
||||
CommunityEntity(id = forumId, name = forumName, icon = forumIcon)
|
||||
} else null
|
||||
|
||||
context.startActivity(
|
||||
VideoPublishActivity.getIntent(
|
||||
context,
|
||||
communityEntity,
|
||||
gameEntity,
|
||||
activityLabelEntity,
|
||||
forumType,
|
||||
disableForumSelection = false,
|
||||
isFromCommunityActivity = true,
|
||||
entrance,
|
||||
""
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_SUGGESTION -> {
|
||||
@ -287,19 +377,25 @@ object DefaultUrlHandler {
|
||||
val platformName = PlatformUtils.getInstance(context).getPlatformName(platform)
|
||||
val gameId = uri.getQueryParameter(EntranceUtils.KEY_GAMEID)
|
||||
val packageMd5 = uri.getQueryParameter(EntranceUtils.KEY_PACKAGE_MD5)
|
||||
val content = if (TextUtils.isEmpty(gameId) || TextUtils.isEmpty(packageMd5)) String.format("%s-%s-V%s,",
|
||||
uri.getQueryParameter(EntranceUtils.KEY_GAME_NAME),
|
||||
if (TextUtils.isEmpty(platformName)) platform else platformName,
|
||||
uri.getQueryParameter(EntranceUtils.KEY_VERSION)) else String.format("%s-%s-V%s\n游戏ID:%s\n游戏包MD5:%s\n",
|
||||
uri.getQueryParameter(EntranceUtils.KEY_GAME_NAME),
|
||||
if (TextUtils.isEmpty(platformName)) platform else platformName,
|
||||
uri.getQueryParameter(EntranceUtils.KEY_VERSION), gameId, packageMd5)
|
||||
val isQaFeedback = uri.getQueryParameter(EntranceUtils.KEY_IS_QA_FEEDBACK) == "true"
|
||||
val content = if (TextUtils.isEmpty(gameId) || TextUtils.isEmpty(packageMd5)) String.format(
|
||||
"%s-%s-V%s,",
|
||||
uri.getQueryParameter(EntranceUtils.KEY_GAME_NAME),
|
||||
if (TextUtils.isEmpty(platformName)) platform else platformName,
|
||||
uri.getQueryParameter(EntranceUtils.KEY_VERSION)
|
||||
) else String.format(
|
||||
"%s-%s-V%s\n游戏ID:%s\n游戏包MD5:%s\n",
|
||||
uri.getQueryParameter(EntranceUtils.KEY_GAME_NAME),
|
||||
if (TextUtils.isEmpty(platformName)) platform else platformName,
|
||||
uri.getQueryParameter(EntranceUtils.KEY_VERSION), gameId, packageMd5
|
||||
)
|
||||
val qaId = uri.getQueryParameter("qa_id") ?: ""
|
||||
val qaContentId = uri.getQueryParameter(EntranceUtils.KEY_QA_CONTENT_ID) ?: ""
|
||||
val qaTitle = uri.getQueryParameter(EntranceUtils.KEY_QA_TITLE)
|
||||
if (!TextUtils.isEmpty(qaId)) {
|
||||
directToQa(context, qaTitle, qaId)
|
||||
} else {
|
||||
directToFeedback(context, content, EntranceUtils.ENTRANCE_BROWSER)
|
||||
directToFeedback(context, content, null, isQaFeedback, qaContentId, EntranceUtils.ENTRANCE_BROWSER)
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,7 +403,50 @@ object DefaultUrlHandler {
|
||||
val position = uri.getQueryParameter("position") ?: ""
|
||||
DirectUtils.directToHelpAndFeedback(context, position.toInt())
|
||||
}
|
||||
else -> DialogUtils.showLowVersionDialog(context)
|
||||
|
||||
EntranceUtils.HOST_HELP_DETAIL -> {
|
||||
var url = uri.getQueryParameter("url")
|
||||
if (!url.isNullOrEmpty()) {
|
||||
context.startActivity(WebActivity.getIntent(context, url, false))
|
||||
} else {
|
||||
url = if (EnvHelper.isDevEnv) {
|
||||
Constants.HELP_ADDRESS_DEV
|
||||
} else {
|
||||
Constants.HELP_ADDRESS
|
||||
}
|
||||
val id = uri.getQueryParameter("id")
|
||||
val name = uri.getQueryParameter("name")
|
||||
val qaCollectionId = uri.getQueryParameter("collection_id")
|
||||
context.startActivity(WebActivity.getIntent(context, "$url$id", name, true, !qaCollectionId.isNullOrEmpty()))
|
||||
}
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_GAME_COLLECTION_DETAIL -> {
|
||||
DirectUtils.directToGameCollectionDetail(context, id, entrance)
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_GAME_COLLECTION_SQUARE -> {
|
||||
DirectUtils.directToGameCollectionSquare(context, entrance)
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_GAME_COLLECTION_EDIT -> {
|
||||
context.startActivity(GameCollectionEditActivity.getIntent(context, entrance))
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (bringAppToFront) {
|
||||
DirectUtils.directToMainActivity(context)
|
||||
if (!TextUtils.isEmpty(host)) {
|
||||
AppExecutor.uiExecutor.executeWithDelay({
|
||||
CurrentActivityHolder.getCurrentActivity()?.let {
|
||||
DialogHelper.showUpgradeDialog(it)
|
||||
}
|
||||
}, 200)
|
||||
}
|
||||
} else {
|
||||
DialogHelper.showUpgradeDialog(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
} else if ("zhiqu" == uri.scheme) {
|
||||
@ -344,6 +483,11 @@ object DefaultUrlHandler {
|
||||
return true
|
||||
}
|
||||
|
||||
// 处理内部页面逻辑
|
||||
if (transformNormalScheme(context, url, entrance)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if ("http" != uri.scheme && "https" != uri.scheme) return true
|
||||
return false
|
||||
}
|
||||
@ -352,15 +496,17 @@ object DefaultUrlHandler {
|
||||
fun transformNormalScheme(context: Context, url: String, entrance: String): Boolean {
|
||||
val uri = Uri.parse(url)
|
||||
if (uri.host == "www.ghzs666.com"
|
||||
|| uri.host == "www.ghzs.com"
|
||||
|| uri.host == "ask.ghzs.com"
|
||||
|| uri.host == "m.ghzs.com"
|
||||
|| uri.host == "m.ghzs666.com") {
|
||||
|| uri.host == "www.ghzs.com"
|
||||
|| uri.host == "ask.ghzs.com"
|
||||
|| uri.host == "m.ghzs.com"
|
||||
|| uri.host == "m.ghzs666.com"
|
||||
) {
|
||||
Utils.log(uri.path)
|
||||
uri.path?.apply {
|
||||
when {
|
||||
contains("game") -> {
|
||||
val gameId = uri.getQueryParameter("gameId") ?: ""
|
||||
val gameId = uri.getQueryParameter("gameId") ?: uri.pathSegments.last()
|
||||
?: ""
|
||||
DirectUtils.directToGameDetail(context, gameId, entrance, autoDownload = false, traceEvent = null)
|
||||
}
|
||||
contains("question") -> {
|
||||
@ -372,11 +518,14 @@ object DefaultUrlHandler {
|
||||
DirectUtils.directToAnswerDetail(context, answerId, entrance, "")
|
||||
}
|
||||
}
|
||||
contains("communities") && contains("article") -> {
|
||||
((contains("bbs")) && contains("article") ||
|
||||
(contains("communities")) && contains("article")) -> {
|
||||
var communityId = ""
|
||||
var type = ""
|
||||
var typeId = ""
|
||||
val split = replace("/communities", "").replace(".html", "").split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
val split =
|
||||
replace("/communities", "").replace("/bbs", "").replace(".html", "").split("/".toRegex()).dropLastWhile { it.isEmpty() }
|
||||
.toTypedArray()
|
||||
for (text in split) {
|
||||
if (TextUtils.isEmpty(communityId)) {
|
||||
communityId = text
|
||||
@ -390,15 +539,20 @@ object DefaultUrlHandler {
|
||||
typeId = text
|
||||
}
|
||||
}
|
||||
if ("articles" == type) {
|
||||
if ("articles" == type || "article" == type) {
|
||||
DirectUtils.directToCommunityArticle(
|
||||
context, typeId, communityId,
|
||||
entrance, "文章链接")
|
||||
context, typeId, communityId,
|
||||
entrance, "文章链接"
|
||||
)
|
||||
}
|
||||
}
|
||||
contains("article") -> {
|
||||
val articleId = split("/")[2].replace(".html", "")
|
||||
DirectUtils.directToArticle(context, articleId, entrance)
|
||||
if (entrance == "隐私政策") {
|
||||
DirectUtils.directToArticle(context, articleId, true, entrance)
|
||||
} else {
|
||||
DirectUtils.directToArticle(context, articleId, entrance)
|
||||
}
|
||||
}
|
||||
contains("columns") -> {
|
||||
val columnsId = split("/")[3]
|
||||
@ -406,14 +560,35 @@ object DefaultUrlHandler {
|
||||
val name = uri.getQueryParameter("communityName") ?: ""
|
||||
DirectUtils.directToCommunityColumn(context, CommunityEntity(id, name), columnsId, entrance, "")
|
||||
}
|
||||
contains("zone") -> {
|
||||
contains("zone") && split("/").size > 2 -> {
|
||||
val gameId = split("/")[2]
|
||||
DirectUtils.directGameZone(context, gameId, url, entrance)
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 url 转换为 LinkEntity (实际只有 type 和 link 两个字段,仅供日志,不保证能用)
|
||||
*/
|
||||
fun urlToLinkEntity(url: String): LinkEntity? {
|
||||
val uri = Uri.parse(url)
|
||||
if ("ghzhushou" == uri.scheme) {
|
||||
Utils.log("url = $url")
|
||||
Utils.log("url = " + uri.scheme!!)
|
||||
val host = uri.host
|
||||
val path = uri.path
|
||||
var id = ""
|
||||
if (!TextUtils.isEmpty(path)) {
|
||||
id = path!!.substring(1)
|
||||
}
|
||||
return LinkEntity(type = host, link = id)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
@ -11,6 +11,7 @@ import com.gh.gamecenter.entity.TimeEntity
|
||||
import com.gh.gamecenter.retrofit.Response
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlin.concurrent.fixedRateTimer
|
||||
|
||||
object FixedRateJobHelper {
|
||||
@ -34,8 +35,8 @@ object FixedRateJobHelper {
|
||||
fixedRateTimer("Global-Fixed-Rate-Timer", initialDelay = 100, period = CHECKER_PERIOD) {
|
||||
// 时间校对,10分钟一次
|
||||
if ((mExecuteCount * CHECKER_PERIOD) % TIME_PERIOD == 0L) {
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application).api.time
|
||||
.subscribeOn(AppExecutor.cachedScheduler)
|
||||
RetrofitManager.getInstance().api.time
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : Response<TimeEntity>() {
|
||||
override fun onResponse(response: TimeEntity?) {
|
||||
val serverTime = response?.time
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
// ChatActivity.ACTION_DISMISS_FLOATING_WINDOW -> {
|
||||
// ImManager.dismissFloatingWindow()
|
||||
//
|
||||
// RetrofitManager.getInstance(HaloApp.getInstance().application).api.postImEnding(UserManager.getInstance().userId)
|
||||
// RetrofitManager.getInstance().api.postImEnding(UserManager.getInstance().userId)
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .subscribe()
|
||||
// }
|
||||
|
||||
@ -78,7 +78,7 @@ object PushManager {
|
||||
//
|
||||
// val body = RequestBody.create(MediaType.parse("application/json"), jsonObject.toString())
|
||||
//
|
||||
// RetrofitManager.getInstance(mApplication).api.getAlias(body)
|
||||
// RetrofitManager.getInstance().api.getAlias(body)
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .subscribe(
|
||||
// { setAlias(it) },
|
||||
|
||||
@ -5,6 +5,7 @@ import android.app.Application
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.gh.base.GHThreadFactory
|
||||
import com.halo.assistant.HaloApp
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
@ -29,31 +30,31 @@ class TimeElapsedHelper(val fragment: Fragment?, val activity: Activity?) {
|
||||
|
||||
init {
|
||||
activity?.application?.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
|
||||
override fun onActivityStarted(a: Activity?) {
|
||||
override fun onActivityStarted(a: Activity) {
|
||||
}
|
||||
|
||||
override fun onActivitySaveInstanceState(a: Activity?, outState: Bundle?) {
|
||||
override fun onActivitySaveInstanceState(a: Activity, outState: Bundle) {
|
||||
}
|
||||
|
||||
override fun onActivityStopped(a: Activity?) {
|
||||
override fun onActivityStopped(a: Activity) {
|
||||
}
|
||||
|
||||
override fun onActivityCreated(a: Activity?, savedInstanceState: Bundle?) {
|
||||
override fun onActivityCreated(a: Activity, savedInstanceState: Bundle?) {
|
||||
}
|
||||
|
||||
override fun onActivityPaused(a: Activity?) {
|
||||
override fun onActivityPaused(a: Activity) {
|
||||
if (activity == a) {
|
||||
pauseCounting()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResumed(a: Activity?) {
|
||||
override fun onActivityResumed(a: Activity) {
|
||||
if (activity == a) {
|
||||
resumeCounting()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityDestroyed(a: Activity?) {
|
||||
override fun onActivityDestroyed(a: Activity) {
|
||||
if (activity == a) {
|
||||
HaloApp.getInstance().application.unregisterActivityLifecycleCallbacks(this)
|
||||
}
|
||||
@ -114,7 +115,7 @@ class TimeElapsedHelper(val fragment: Fragment?, val activity: Activity?) {
|
||||
}
|
||||
|
||||
object TimeElapsedThreadHolder {
|
||||
val threadService: ExecutorService by lazy { Executors.newSingleThreadExecutor() }
|
||||
val threadService: ExecutorService by lazy { Executors.newSingleThreadExecutor(GHThreadFactory("TIME_ELAPSED_THREAD")) }
|
||||
}
|
||||
|
||||
interface TimeoutCallback {
|
||||
|
||||
@ -1,36 +1,49 @@
|
||||
package com.gh.common.constant;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.gh.common.util.EnvHelper;
|
||||
import com.gh.common.util.GsonUtils;
|
||||
import com.gh.common.util.PackageHelper;
|
||||
import com.gh.common.util.PackageUtils;
|
||||
import com.gh.common.util.SPUtils;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
import com.gh.gamecenter.SuggestionActivity;
|
||||
import com.gh.gamecenter.entity.GameGuidePopupEntity;
|
||||
import com.gh.gamecenter.entity.NewSettingsEntity;
|
||||
import com.gh.gamecenter.entity.NewsEntity;
|
||||
import com.gh.gamecenter.entity.SettingsEntity;
|
||||
import com.gh.gamecenter.eventbus.EBReuse;
|
||||
import com.gh.gamecenter.retrofit.BiResponse;
|
||||
import com.gh.gamecenter.retrofit.Response;
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager;
|
||||
import com.halo.assistant.HaloApp;
|
||||
import com.lightgame.utils.Utils;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
public class Config {
|
||||
|
||||
public static final String API_HOST = BuildConfig.API_HOST;
|
||||
public static final String SENSITIVE_API_HOST = BuildConfig.SENSITIVE_API_HOST;
|
||||
// 这个 API_HOST 在测试包里会随着选择的环境切换,正式包里会一直保持正式 host
|
||||
public static final String API_HOST = EnvHelper.getHost();
|
||||
public static final String NEW_API_HOST = EnvHelper.getNewHost();
|
||||
|
||||
/**
|
||||
* 需要配置的请使用{@link PreferenceManager#getDefaultSharedPreferences(Context)}
|
||||
@ -41,26 +54,20 @@ public class Config {
|
||||
public static final String WECHAT_SECRET = BuildConfig.WECHAT_SECRET;
|
||||
public static final String TENCENT_APPID = BuildConfig.TENCENT_APPID;
|
||||
public static final String WEIBO_APPKEY = BuildConfig.WEIBO_APPKEY;
|
||||
public static final String MIPUSH_APPID = BuildConfig.MIPUSH_APPID;
|
||||
public static final String MIPUSH_APPKEY = BuildConfig.MIPUSH_APPKEY;
|
||||
public static final String MTA_APPKEY = BuildConfig.MTA_APPKEY;
|
||||
public static final String TALKINGDATA_APPID = BuildConfig.TD_APPID;// TalkingData
|
||||
public static final String UMENG_APPKEY = BuildConfig.UMENG_APPKEY;
|
||||
public static final String UMENG_MESSAGE_SECRET = BuildConfig.UMENG_MESSAGE_SECRET;
|
||||
public static final String BUGLY_APPID = BuildConfig.BUGLY_APPID;
|
||||
public static final String LETO_APPID = BuildConfig.LETO_APPID;
|
||||
public static final String TTAD_APPID = BuildConfig.TTAD_APPID;
|
||||
public static final String DOUYIN_CLIENTKEY = BuildConfig.DOUYIN_CLIENTKEY;
|
||||
public static final String DOUYIN_CLIENTSECRET = BuildConfig.DOUYIN_CLIENTSECRET;
|
||||
public static final String QUICK_LOGIN_APPID = BuildConfig.QUICK_LOGIN_APPID;
|
||||
public static final String QUICK_LOGIN_APPKEY = BuildConfig.QUICK_LOGIN_APPKEY;
|
||||
// http://www.ghzs666.com/article/${articleId}.html
|
||||
public static final String URL_ARTICLE = "http://www.ghzs666.com/article/"; // ghzs/ghzs666 统一
|
||||
public static final String PATCHES = "patches";
|
||||
|
||||
public static final String DEFAULT_CHANNEL = "GH_TEST";
|
||||
public static final String DEFAULT_CHANNEL = "GH_TEST3";
|
||||
public static final String DEFAULT_CHANNEL_FOR_RELEASE = "GH_LOST"; // 正式包的缺省渠道,避免因渠道丢失而回落到测试渠道
|
||||
|
||||
private static String SETTINGS_KEY = "settingsKey";
|
||||
|
||||
private static SettingsEntity mSettingsEntity;
|
||||
private static NewSettingsEntity mNewSettingsEntity;
|
||||
private static GameGuidePopupEntity mGameGuidePopupEntity;
|
||||
private static SharedPreferences mDefaultSharedPreferences;
|
||||
|
||||
public static final String FIX_DOWNLOAD_KEY = "isFixDownload";
|
||||
public static final String FIX_PLUGIN_KEY = "isFixPlugin";
|
||||
@ -70,6 +77,8 @@ public class Config {
|
||||
public static final int VIDEO_PAGE_SIZE = 21; // 视频列表大多都是一行3个
|
||||
|
||||
public static boolean isShow() {
|
||||
if (SPUtils.getBoolean(Constants.SP_TEENAGER_MODE)) return false;
|
||||
|
||||
if (getPreferences().getBoolean(FIX_DOWNLOAD_KEY, false)) return true;
|
||||
|
||||
if (!isExistDownloadFilter()) return false;
|
||||
@ -203,6 +212,26 @@ public class Config {
|
||||
return mSettingsEntity;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static NewSettingsEntity getNewSettingsEntity() {
|
||||
if (mNewSettingsEntity == null) {
|
||||
try {
|
||||
String json = SPUtils.getString(Constants.SP_NEW_SETTINGS);
|
||||
if (!TextUtils.isEmpty(json)) {
|
||||
mNewSettingsEntity = GsonUtils.fromJson(json, NewSettingsEntity.class);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return mNewSettingsEntity;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static GameGuidePopupEntity getGameGuidePopupEntity() {
|
||||
return mGameGuidePopupEntity;
|
||||
}
|
||||
|
||||
private static boolean isExistDownloadFilter() {
|
||||
if (getSettings() == null || getSettings().getDownload() == null || getSettings().getDownload().size() == 0) {
|
||||
return false;
|
||||
@ -225,7 +254,11 @@ public class Config {
|
||||
}
|
||||
|
||||
public static SharedPreferences getPreferences() {
|
||||
return PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().getApplication());
|
||||
if (mDefaultSharedPreferences == null) {
|
||||
mDefaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().getApplication());
|
||||
}
|
||||
|
||||
return mDefaultSharedPreferences;
|
||||
}
|
||||
|
||||
public static boolean isExistHideFunction() {
|
||||
@ -255,10 +288,11 @@ public class Config {
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
public static void getGhzsSettings() {
|
||||
String channel = HaloApp.getInstance().getChannel();
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().getApplication())
|
||||
.getSensitiveApi().getSettings(PackageUtils.getVersionName(), channel)
|
||||
RetrofitManager.getInstance()
|
||||
.getApi().getSettings(PackageUtils.getGhVersionName(), channel)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Response<SettingsEntity>() {
|
||||
@ -275,8 +309,53 @@ public class Config {
|
||||
if (!getPreferences().getBoolean(Config.FIX_DOWNLOAD_KEY, false) && Config.isShow()) {
|
||||
getPreferences().edit().putBoolean(Config.FIX_DOWNLOAD_KEY, true).apply();
|
||||
}
|
||||
EventBus.getDefault().post(new EBReuse("Refresh"));
|
||||
if (!SPUtils.getBoolean(Constants.SP_TEENAGER_MODE)) {
|
||||
EventBus.getDefault().post(new EBReuse("Refresh"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
RetrofitManager.getInstance()
|
||||
.getApi().getNewSettings(Build.MANUFACTURER, Build.MODEL, channel, Build.VERSION.SDK_INT, BuildConfig.VERSION_NAME)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new BiResponse<NewSettingsEntity>() {
|
||||
@Override
|
||||
public void onSuccess(NewSettingsEntity data) {
|
||||
mNewSettingsEntity = data;
|
||||
SPUtils.setString(Constants.SP_NEW_SETTINGS, GsonUtils.toJson(data));
|
||||
}
|
||||
});
|
||||
|
||||
RetrofitManager.getInstance()
|
||||
.getApi().getGameGuidePopup(Build.MANUFACTURER, Build.VERSION.RELEASE, Build.MODEL, channel, BuildConfig.VERSION_NAME)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new BiResponse<GameGuidePopupEntity>() {
|
||||
@Override
|
||||
public void onSuccess(GameGuidePopupEntity data) {
|
||||
mGameGuidePopupEntity = data;
|
||||
}
|
||||
});
|
||||
|
||||
String manufacturer = Build.MANUFACTURER.toUpperCase(Locale.CHINA);
|
||||
if (manufacturer.equals("OPPO") || manufacturer.equals("VIVO")) {
|
||||
RetrofitManager.getInstance().getNewApi().getBrowserHintUrl(manufacturer)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new BiResponse<ResponseBody>() {
|
||||
@Override
|
||||
public void onSuccess(ResponseBody data) {
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(data.string());
|
||||
if (!TextUtils.isEmpty(jsonObject.getJSONObject("data").getString("link"))) {
|
||||
SPUtils.setString(Constants.SP_BROWSER_HINT_URL, jsonObject.getJSONObject("data").getString("link"));
|
||||
}
|
||||
} catch (JSONException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package com.gh.common.constant;
|
||||
|
||||
import com.gh.common.util.PackageUtils;
|
||||
import com.gh.common.util.TimeUtils;
|
||||
import com.halo.assistant.HaloApp;
|
||||
|
||||
public class Constants {
|
||||
@ -27,12 +28,16 @@ public class Constants {
|
||||
|
||||
public static final String GAME_DETAIL_COME_IN = "game_detail_come_in"; // 从游戏详情进入
|
||||
|
||||
public static final String SPLASH_AD = "splash_ad"; // 启动广告
|
||||
|
||||
public static final String XPOSED_INSTALLER_PACKAGE_NAME = "de.robv.android.xposed.installer";
|
||||
|
||||
public static final String EB_QUIT_LOGIN = "quit_login";
|
||||
|
||||
// 用于避免历史下载掺和到普通下载状态的 ID 修饰符
|
||||
public static final String GAME_ID_DIVIDER = ":";
|
||||
public static final String EB_SHOW_AD = "show_ad";
|
||||
|
||||
public static final String EB_GAME_DETAIL = "eb_game_detail";
|
||||
|
||||
// 用于避免历史下载影响到部分依赖名字作为数据更新条件的修饰符
|
||||
public static final String GAME_NAME_DECORATOR = " ";
|
||||
|
||||
@ -44,15 +49,26 @@ public class Constants {
|
||||
// 最近显示的弹窗信息
|
||||
public static final String SP_LAST_OPENING_ID = "last_opening_dialog_id";
|
||||
public static final String SP_LAST_OPENING_TIME = "last_opening_dialog_time";
|
||||
|
||||
public static final String SP_LAST_ACCEPTED_PRIVACY_DIALOG_ID = "last_accepted_privacy_dialog_id";
|
||||
|
||||
// 游戏图标和图标角标
|
||||
public static final String RAW_GAME_ICON = "raw_game_icon";
|
||||
public static final String GAME_ICON_SUBSCRIPT = "game_icon_subscript";
|
||||
|
||||
public static final String IS_PLATFORM_RECOMMEND = "isPlatformRecommend";
|
||||
|
||||
// 下载 id,一般来说跟下载文件名一样
|
||||
public static final String DOWNLOAD_ID = "download_id";
|
||||
|
||||
public static final String GHZS_GAME_ID = "5ae4462c2924bc7936438d07";
|
||||
|
||||
public static final String EXTRA_DOWNLOAD_TYPE = "extra_download_type";
|
||||
public static final String SILENT_UPDATE = "静默更新";
|
||||
public static final String SIMULATOR_DOWNLOAD = "下载模拟器";
|
||||
public static final String SIMULATOR_GAME = "simulator_game";
|
||||
public static final String SIMULATOR = "simulator";
|
||||
public static final String GAME_NAME = "game_name";
|
||||
public static final String SIMULATOR_DOWNLOAD_START_TIME = "simulator_download_start_time";
|
||||
public static final String LAST_GHZS_UPDATE_FILE_SIZE = "last_ghzs_update_file_size";
|
||||
|
||||
@ -62,6 +78,17 @@ public class Constants {
|
||||
public static final String SP_IMEI = "imei";
|
||||
public static final String SP_ANDROID_ID = "android_id";
|
||||
|
||||
// 安装类型
|
||||
public static final String SP_INSTALL_TYPE = "install_type";
|
||||
|
||||
public static final String LAST_INSTALL_GAME = "last_install_game";
|
||||
|
||||
// 骨架图配置
|
||||
public static final int SHIMMER_ANGLE = 18;
|
||||
public static final int SHIMMER_DURATION = 1200;
|
||||
public static final float MASK_WIDTH = 0.8F;
|
||||
public static final float GRADIENT_CENTER_COLOR_WIDTH = 0.1F;
|
||||
|
||||
//引导设置 “通知管理” 引导弹窗
|
||||
public static final String SP_SHOWED_NOTIFICATION_LOGIN = "show_notification_login_hint";
|
||||
public static final String SP_SHOWED_NOTIFICATION_QUESTION = "show_notification_question_hint";
|
||||
@ -77,11 +104,11 @@ public class Constants {
|
||||
// 今天是否已经触发了 “通知管理” 引导弹窗
|
||||
public static final String SP_IS_SHOWED_NOTIFICATION_TODAY = "show_is_notification_today";
|
||||
// v4.0.0已废弃,标记安装的游戏为已玩过弹窗,最多取消2次 (https://gitlab.ghzs.com/pm/halo-app-issues/issues/722 调整为版本相关) (不是常量了也放这里好像有点奇怪)
|
||||
public static final String SP_MARK_INSTALLED_GAME = "mark_installed_game" + PackageUtils.getVersionName();
|
||||
public static final String SP_MARK_INSTALLED_GAME = "mark_installed_game" + PackageUtils.getGhVersionName();
|
||||
// 标记安装的游戏为已玩过弹窗(个人主页最多弹一次)
|
||||
public static final String SP_MARK_INSTALLED_GAME_USER_HOME = "mark_installed_game_user_home" + PackageUtils.getVersionName();
|
||||
public static final String SP_MARK_INSTALLED_GAME_USER_HOME = "mark_installed_game_user_home" + PackageUtils.getGhVersionName();
|
||||
// 标记安装的游戏为已玩过弹窗(我的游戏最多弹一次)
|
||||
public static final String SP_MARK_INSTALLED_GAME_MY_GAME = "mark_installed_game_my_game" + PackageUtils.getVersionName();
|
||||
public static final String SP_MARK_INSTALLED_GAME_MY_GAME = "mark_installed_game_my_game" + PackageUtils.getGhVersionName();
|
||||
//视频详情滑动引导
|
||||
public static final String SP_SHOW_SLIDE_GUIDE = "show_slide_guide";
|
||||
//视频详情点击引导
|
||||
@ -106,8 +133,6 @@ public class Constants {
|
||||
public static final String SP_FILTER_TAGS = "filter_tags";
|
||||
//实名认证弹窗分类数据
|
||||
public static final String SP_AUTH_DIALOG = "auth_dialog";
|
||||
//顶部视频进度保存,重启恢复
|
||||
public static final String SP_TOP_VIDEO_SCHEDULE = "top_video_schedule";
|
||||
//我的光环小红点提示
|
||||
public static final String SP_GH_RED_POINT_REMIND = "gh_red_point_remind";
|
||||
//论坛首页引导
|
||||
@ -131,10 +156,100 @@ public class Constants {
|
||||
public static final String SP_BRAND_NEW_USER = "brand_new_user";
|
||||
//包名检测是否点击不再提示
|
||||
public static final String SP_PACKAGE_CHECK = "package_check";
|
||||
//游戏详情预约引导提示
|
||||
public static final String SP_GAME_DETAIL_RESERVE_GUIDE = "game_detail_reserve_guide";
|
||||
|
||||
public static final String SP_XAPK_UNZIP_ACTIVITY = "xapk_unzip_activity";
|
||||
public static final String SP_XAPK_URL = "xapk_url";
|
||||
|
||||
//游戏详情推荐弹窗
|
||||
public static final String SP_RECOMMEND_POPUP = "recommend_popup";
|
||||
|
||||
// 设备实名信息
|
||||
public static final String SP_DEVICE_CERTIFICATION_PREFIX = "device_certification_prefix";
|
||||
|
||||
// 使用浏览器安装开关
|
||||
public static final String SP_USE_BROWSER_TO_INSTALL = "use_browser_to_install";
|
||||
// 游戏详情页底部使用浏览器安装提示
|
||||
public static final String SP_SHOULD_SHOW_GAMEDETAIL_USE_BROWSER_TO_INSTALL_HINT = "should_show_gamedetail_use_browser_to_install_hint";
|
||||
// 第一次普通安装推荐使用浏览器安装提示
|
||||
public static final String SP_SHOULD_SHOW_USE_BROWSER_TO_INSTALL_HINT = "should_show_use_browser_to_install_hint";
|
||||
// 游戏详情切换安装方式显示开关
|
||||
public static final String SP_SWITCH_INSTALL_VISIBLE = "sp_switch_install_visible";
|
||||
|
||||
//模拟器管理引导
|
||||
public static final String SP_SIMULATOR_GUIDE = "simulator_guide";
|
||||
//模拟器游戏引导
|
||||
public static final String SP_SIMULATOR_GAME_GUIDE = "simulator_game_guide";
|
||||
|
||||
// 临时设备ID (本地生成,与GID不相关)
|
||||
public static final String SP_TEMPORARY_DEVICE_ID = "temporary_device_id";
|
||||
|
||||
// 光环助手最后的安装(更新)时间
|
||||
public static final String SP_GH_LAST_UPDATE_TIME = "gh_last_update_time";
|
||||
|
||||
//首页视频播放进度
|
||||
public static final String SP_HOME_VIDEO_PLAY_RECORD = "home_video_play_record";
|
||||
|
||||
// 论坛内容视频播放进度
|
||||
public static final String SP_CONTENT_VIDEO_PLAY_RECORD = "content_video_play_record";
|
||||
|
||||
// 用户是否曾经永久拒绝过存储权限
|
||||
public static final String SP_USER_HAS_PERMANENTLY_DENIED_STORAGE_PERMISSION = "user_has_permanently_denied_storage_permission";
|
||||
|
||||
// 是否已经填写邀请码
|
||||
public static final String SP_HAS_COMPLETE_INVITE_CODE = "has_complete_invite_code";
|
||||
|
||||
// 补充配置项
|
||||
public static final String SP_NEW_SETTINGS = "new_settings";
|
||||
|
||||
// 头像挂件ID
|
||||
public static final String SP_CHOOSE_AVATAR_ID = "choose_avatar_id";
|
||||
|
||||
// 是否第一次进入新分类2.0
|
||||
public static final String SP_FIRST_ENTER_CATEGORY_V2 = "first_enter_category_v2";
|
||||
|
||||
// 是否成功取过号
|
||||
public static final String SP_HAS_GET_PHONE_INFO = "has_get_phone_info";
|
||||
|
||||
// 是否点击过更换背景按钮
|
||||
public static final String SP_HAS_CLICK_CHANGE_BG = "has_click_change_bg";
|
||||
// 是否显示更换背景提示
|
||||
public static final String SP_SHOW_CHANGE_BG_TIPS = "show_change_bg_tips" + TimeUtils.getStartTimeOfToday();
|
||||
|
||||
// 新分类2.0引导
|
||||
public static final String SP_SHOW_CATEGORY_GUIDE = "show_category_guide";
|
||||
|
||||
// 用户是否需要 weibo x86 so
|
||||
public static final String SP_USER_NEED_WEIBO_X86_SO = "user_need_weibo_x86_so";
|
||||
|
||||
// 当前设备是不是模拟器
|
||||
public static final String SP_IS_EMULATOR = "is_emulator";
|
||||
|
||||
// 内容视频播放选项
|
||||
public static final String SP_CONTENT_VIDEO_OPTION = "content_video_option";
|
||||
// 首页/游戏详情页视频播放选项
|
||||
public static final String SP_HOME_OR_DETAIL_VIDEO_OPTION = "home_or_detail_video_option";
|
||||
// 是否默认静音播放视频
|
||||
public static final String SP_VIDEO_PLAY_MUTE = "video_play_mute";
|
||||
//帖子发布页上传视频引导
|
||||
public static final String SP_ARTICLE_VIDEO_GUIDE = "article_video_guide";
|
||||
//问题发布页上传视频引导
|
||||
public static final String SP_QUESTION_VIDEO_GUIDE = "question_video_guide";
|
||||
// 论坛详情申请版主引导
|
||||
public static final String SP_FORUM_DETAIL_MODERATOR_GUIDE = "forum_detail_moderator_guide";
|
||||
// 游戏详情安装引导
|
||||
public static final String SP_GAME_DETAIL_INSTALL_GUIDE = "game_detail_install_guide";
|
||||
public static final String SP_SHOULD_SHOW_GAME_DETAIL_INSTALL_GUIDE = "should_show_game_detail_install_guide";
|
||||
// 儿童/青少年模式
|
||||
public static final String SP_TEENAGER_MODE = "teenager_mode";
|
||||
// 我的游戏引导
|
||||
public static final String SP_MY_GAME_GUIDE = "my_game_guide";
|
||||
//微信绑定配置信息
|
||||
public static final String SP_WECHAT_CONFIG = "wechat_config";
|
||||
//游戏库导航栏小红点提示
|
||||
public static final String SP_GAME_NAVIGATION = "game_navigation";
|
||||
|
||||
//手机号码匹配规则
|
||||
public static final String REGEX_MOBILE = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$";
|
||||
public static final String REGEX_ACCOUNT = "^[a-zA-Z_]\\w{5,17}$";
|
||||
@ -144,8 +259,8 @@ public class Constants {
|
||||
public static final String INPUT_RULE = "0123456789abcdefghijklnmopqrstuvwxyzABCDEFGHIJKLNMOPQRSTUVWXYZ_";
|
||||
|
||||
// 微信绑定地址
|
||||
public static final String WECHAT_BIND_ADDRESS_DEV = "https://resource.ghzs.com/page/wechat_dev/index.html#/";
|
||||
public static final String WECHAT_BIND_ADDRESS = "https://resource.ghzs.com/page/wechat_pro/index.html#/";
|
||||
public static final String WECHAT_BIND_ADDRESS_DEV = "https://dev-and-static.ghzs.com/web/wechat_bind/index.html#/";
|
||||
public static final String WECHAT_BIND_ADDRESS = "https://and-static.ghzs.com/web/wechat_bind/index.html#/";
|
||||
|
||||
// 礼仪考试地址
|
||||
public static final String REGULATION_TEST_ADDRESS_DEV = "https://static-web.ghzs.com/etiquette-dev/index.html#/";
|
||||
@ -169,13 +284,99 @@ public class Constants {
|
||||
//版规声明
|
||||
public static final String FORUM_REGULATIONS_NEWS_ID = "5f4db9cc34d44d01b92fd670";
|
||||
|
||||
// 权限使用场景地址
|
||||
public static final String PERMISSION_SCENARIO_ADDRESS = "https://resource.ghzs.com/page/permissions/android.html";
|
||||
|
||||
//帮助内容详情
|
||||
public static final String HELP_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs_help_dev/help.html?content=";
|
||||
public static final String HELP_ADDRESS = "https://static-web.ghzs.com/ghzs_help/help.html?content=";
|
||||
|
||||
// 注销页面
|
||||
public static final String LOGOUT_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs_help_dev/help.html?content=5f6b1f02786564003944a693";
|
||||
public static final String LOGOUT_ADDRESS = "https://static-web.ghzs.com/ghzs_help/help.html?content=5f534111b1f72909fc225672";
|
||||
public static final String LOGOUT_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs_help_dev/help.html?content=5f6b1f02786564003944a693&from=ghzs";
|
||||
public static final String LOGOUT_ADDRESS = "https://static-web.ghzs.com/ghzs_help/help.html?content=5f534111b1f72909fc225672&from=ghzs";
|
||||
|
||||
// 商品详情
|
||||
public static final String COMMODITY_DETAIL_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/product?from=ghzs";
|
||||
public static final String COMMODITY_DETAIL_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/product?from=ghzs";
|
||||
|
||||
// 光能记录
|
||||
public static final String ENERGY_RECORD_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/record?from=ghzs";
|
||||
public static final String ENERGY_RECORD_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/record?from=ghzs";
|
||||
|
||||
// 订单中心
|
||||
public static final String ORDER_CENTER_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/orders?from=ghzs";
|
||||
public static final String ORDER_CENTER_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/orders?from=ghzs";
|
||||
|
||||
// 订单详情
|
||||
public static final String ORDER_DETAIL_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/order-detail?from=ghzs";
|
||||
public static final String ORDER_DETAIL_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/order-detail?from=ghzs";
|
||||
|
||||
// 邀请好友
|
||||
public static final String INVITE_FRIENDS_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs_activity_dev/inviteFriends.html#/invite";
|
||||
public static final String INVITE_FRIENDS_ADDRESS = "https://static-web.ghzs.com/ghzs_activity_prod/inviteFriends.html#/invite";
|
||||
|
||||
// 等级页面
|
||||
public static final String LEVEL_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs-userhome-dev/index.html#/level";
|
||||
public static final String LEVEL_ADDRESS = "https://static-web.ghzs.com/ghzs-userhome/index.html#/level";
|
||||
|
||||
// 兑换规则
|
||||
public static final String EXCHANGE_RULE_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/exchange-rule?from=ghzs";
|
||||
public static final String EXCHANGE_RULE_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/exchange-rule?from=ghzs";
|
||||
|
||||
// 光能规则
|
||||
public static final String ENERGY_RULE_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/energy-rule?from=ghzs";
|
||||
public static final String ENERGY_RULE_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/energy-rule?from=ghzs";
|
||||
|
||||
// 兑换商品
|
||||
public static final String EXCHANGE_COMMODITY_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/exchange-log?from=ghzs";
|
||||
public static final String EXCHANGE_COMMODITY_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/exchange-log?from=ghzs";
|
||||
|
||||
// 抽奖乐园
|
||||
public static final String LOTTERY_PARADISE_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/lottery-list?from=ghzs";
|
||||
public static final String LOTTERY_PARADISE_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/lottery-list?from=ghzs";
|
||||
|
||||
// 我的奖品
|
||||
public static final String MY_PRIZE_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/mywin?from=ghzs";
|
||||
public static final String MY_PRIZE_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/mywin?from=ghzs";
|
||||
|
||||
// 中奖订单详情
|
||||
public static final String WIN_ORDER_DETAIL_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/win-order-detail?from=ghzs";
|
||||
public static final String WIN_ORDER_DETAIL_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/win-order-detail?from=ghzs";
|
||||
|
||||
// 地址信息
|
||||
public static final String ADDRESS_INFO_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/address-list?from=ghzs";
|
||||
public static final String ADDRESS_INFO_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/address-list?from=ghzs";
|
||||
|
||||
// 领奖信息
|
||||
public static final String PRIZE_INFO_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/user-info?from=ghzs";
|
||||
public static final String PRIZE_INFO_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/user-info?from=ghzs";
|
||||
|
||||
// 提现信息
|
||||
public static final String WITHDRAW_INFO_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/cash?from=ghzs";
|
||||
public static final String WITHDRAW_INFO_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/cash?from=ghzs";
|
||||
|
||||
// 活动详情
|
||||
public static final String ACTIVITY_DETAIL_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs_activity_dev/common.html?from=ghzs";
|
||||
public static final String ACTIVITY_DETAIL_ADDRESS = "https://static-web.ghzs.com/ghzs_activity_prod/common.html?from=ghzs";
|
||||
|
||||
// 游戏单详情分享链接
|
||||
public static final String GAME_COLLECTION_SHARE_ADDRESS_DEV = "https://dev-and-static.ghzs.com/web/game_collection/index.html#/?from=ghzs";
|
||||
public static final String GAME_COLLECTION_SHARE_ADDRESS = "https://and-static.ghzs.com/web/game_collection/index.html#/?from=ghzs";
|
||||
|
||||
// 游戏单活动分享链接 https://git.shanqu.cc/pm/halo-app-issues/-/issues/1638
|
||||
public static final String GAME_COLLECTION_ACTIVITY_ADDRESS_DEV = "https://dev-and-static.ghzs.com/web/ghzs_activity/haoyouUnlock.html";
|
||||
public static final String GAME_COLLECTION_ACTIVITY_ADDRESS = "https://and-static.ghzs.com/web/ghzs_activity/haoyouUnlock.html";
|
||||
|
||||
// 青少年模式找回密码
|
||||
public static final String TEEN_MODE_RESET_PASSWORD = "https://resource.ghzs.com/page/privacy_policies/help_password.html";
|
||||
|
||||
// 儿童/青少年使用须知
|
||||
public static final String TEEN_MODE_RULE = "https://resource.ghzs.com/page/privacy_policies/teenager_privacy.html";
|
||||
|
||||
//游戏单管理规范
|
||||
public static final String GAME_COLLECTION_RULE = "https://resource.ghzs.com/page/privacy_policies/game_collection.html";
|
||||
|
||||
public static final String SP_IS_DEV_ENV = "is_dev_env";
|
||||
|
||||
//最少需要多少数据才能上传
|
||||
public static final int DATA_AMOUNT = 20;
|
||||
@ -197,7 +398,7 @@ public class Constants {
|
||||
//已收录包名更新 cd间隔
|
||||
public static final int PACKAGES_CD = 60 * 1000;
|
||||
|
||||
public static final String[] REPORT_LIST = new String[]{"垃圾广告营销", "恶意攻击谩骂", "淫秽色情信息", "违法有害信息", "其它"};
|
||||
public static final String[] REPORT_LIST = new String[]{"垃圾广告营销", "恶意攻击谩骂", "淫秽色情信息", "违法有害信息", "其他原因"};
|
||||
|
||||
public static final String ENTRANCE_UNKNOWN = "(unknown)";
|
||||
|
||||
@ -208,4 +409,19 @@ public class Constants {
|
||||
|
||||
// 标记下载重试标记(值为任务已下载大小,为空表示需要重试)
|
||||
public static final String MARK_RETRY_DOWNLOAD = "retry_download";
|
||||
|
||||
// 工具箱历史记录(最多4个)
|
||||
public static final String TOOLBOX_HISTORY = "toolbox_history";
|
||||
|
||||
// 浏览器安装说明url
|
||||
public static final String SP_BROWSER_HINT_URL = "browser_hint_url";
|
||||
public static final String DEFAULT_OPPO_BROWSER_HINT_URL = "https://static-web.ghzs.com/ghzs_help/help.html?content=5fa90fe143d91a022e0d33ff";
|
||||
public static final String DEFAULT_VIVO_BROWSER_HINT_URL = "https://static-web.ghzs.com/ghzs_help/help.html?content=618112ce04796e63e97643a4&from=ghzs";
|
||||
|
||||
public static final int FOLLOW_HINT_TRIGGER_HEIGHT = 10;
|
||||
|
||||
// 深色模式
|
||||
public static final String SP_NIGHT_MODE = "night_mode";
|
||||
// 跟随系统模式
|
||||
public static final String SP_SYSTEM_MODE = "system_mode";
|
||||
}
|
||||
|
||||
@ -35,6 +35,9 @@ public class ItemViewType {
|
||||
public static final int GALLERY_SLIDE = 27; // 首页自动滚动画廊专题
|
||||
public static final int GALLERY = 28; // 首页倾斜画廊专题
|
||||
public static final int BLANK_DIVIDER = 29; // 空白补充区域
|
||||
public static final int COMMON_LINK_COLLECTION = 30; // 通用链接合集
|
||||
public static final int RANK_COLLECTION = 31; // 排行榜样式专题合集
|
||||
public static final int GAME_COLLECTION_ITEM = 32; // 游戏单
|
||||
|
||||
/**
|
||||
* 普通列表
|
||||
|
||||
@ -0,0 +1,185 @@
|
||||
package com.gh.common.databind
|
||||
|
||||
import android.os.Build
|
||||
import android.text.Editable
|
||||
import android.text.Html
|
||||
import android.text.TextWatcher
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.PopupWindow
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.databinding.BindingAdapter
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.gh.base.OnViewClickListener
|
||||
import com.gh.common.util.dip2px
|
||||
import com.gh.common.util.toDrawable
|
||||
import com.gh.common.view.BugFixedPopupWindow
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.KaifuAddItemBinding
|
||||
import com.gh.gamecenter.databinding.LayoutAddKaifuPopupBinding
|
||||
import com.gh.gamecenter.databinding.LayoutPopupContainerBinding
|
||||
import com.gh.gamecenter.entity.ServerCalendarEntity
|
||||
import com.gh.gamecenter.servers.add.AddKaiFuPopupAdapter
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
object AddKaiFuBindingAdapter {
|
||||
private var popupWindow: BugFixedPopupWindow? = null
|
||||
|
||||
@JvmStatic
|
||||
@BindingAdapter("addKaiFuView", "clickListener")
|
||||
fun addKaiFuView(
|
||||
view: LinearLayout,
|
||||
list: List<ServerCalendarEntity?>?,
|
||||
listener: OnViewClickListener<*>?
|
||||
) {
|
||||
if (list == null) return
|
||||
view.removeAllViews()
|
||||
view.addView(LayoutInflater.from(view.context).inflate(R.layout.kaifu_new_add_item_title, null))
|
||||
for (i in list.indices) {
|
||||
val inflate = LayoutInflater.from(view.context).inflate(R.layout.kaifu_add_item, null)
|
||||
val binding = KaifuAddItemBinding.bind(inflate)
|
||||
binding.clickListener = listener
|
||||
binding.entity = list[i]
|
||||
binding.position = i
|
||||
binding.isCloseBottom = list.size - 1 == i
|
||||
view.addView(inflate)
|
||||
binding.kaifuAddFirstName.setOnFocusChangeListener { _, hasFocus ->
|
||||
if (hasFocus) {
|
||||
binding.kaifuAddFirstName.hint = ""
|
||||
if (binding.kaifuAddFirstName.text.isNullOrEmpty()) {
|
||||
showPopupOption(binding.kaifuAddFirstName) {
|
||||
binding.kaifuAddFirstName.setText(it)
|
||||
binding.kaifuAddFirstName.setSelection(it.length)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
binding.kaifuAddFirstName.hint = "点击填写"
|
||||
}
|
||||
}
|
||||
binding.kaifuAddServerName.setOnFocusChangeListener { _, hasFocus ->
|
||||
if (hasFocus) {
|
||||
binding.kaifuAddServerName.hint = ""
|
||||
} else {
|
||||
binding.kaifuAddServerName.hint = "点击填写"
|
||||
}
|
||||
}
|
||||
binding.kaifuAddFirstName.addTextChangedListener(object :TextWatcher{
|
||||
override fun beforeTextChanged(
|
||||
s: CharSequence?,
|
||||
start: Int,
|
||||
count: Int,
|
||||
after: Int
|
||||
) {
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||
if (!s.isNullOrEmpty()){
|
||||
popupWindow?.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
}
|
||||
|
||||
})
|
||||
if (i == list.size - 1) {
|
||||
binding.kaifuAddTime.background = R.drawable.bg_add_kaifu_bottom_left.toDrawable()
|
||||
binding.kaifuAddServerName.background = R.drawable.bg_add_kaifu_bottom_right.toDrawable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@BindingAdapter("addKaiFuTime", "addKaiFuPosition")
|
||||
fun addKaiFuTime(view: EditText, time: Long, position: Int) {
|
||||
if (time == 0L) {
|
||||
view.hint = "点击选择"
|
||||
view.setText("")
|
||||
} else {
|
||||
val pattern = "yyy-MM-dd HH:mm"
|
||||
val format = SimpleDateFormat(pattern, Locale.CHINA)
|
||||
view.setText(format.format(time * 1000))
|
||||
if (position == 0) {
|
||||
view.append(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) Html.fromHtml(
|
||||
String.format(
|
||||
"<font color='#2496FF'>%1\$s</font>",
|
||||
" +"
|
||||
), Html.FROM_HTML_MODE_LEGACY
|
||||
) else Html.fromHtml(String.format("<font color='#2496FF'>%1\$s</font>", " +")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@BindingAdapter("kaiFuTextColor", "kaiFuTextPosition")
|
||||
fun kaiFuTextColor(view: EditText, dataMark: Int, position: Int) {
|
||||
if (dataMark == 1 && view.id == R.id.kaifu_add_time || dataMark == 2 && view.id == R.id.kaifu_add_first_name || dataMark == 3 && view.id == R.id.kaifu_add_server_name || dataMark == 4) {
|
||||
view.setTextColor(ContextCompat.getColor(view.context, R.color.theme_red))
|
||||
view.setHintTextColor(ContextCompat.getColor(view.context, R.color.theme_red))
|
||||
} else if (position == 0) {
|
||||
view.setTextColor(ContextCompat.getColor(view.context, R.color.hint))
|
||||
view.setHintTextColor(ContextCompat.getColor(view.context, R.color.hint))
|
||||
} else {
|
||||
view.setTextColor(ContextCompat.getColor(view.context, R.color.text_title))
|
||||
view.setHintTextColor(ContextCompat.getColor(view.context, R.color.hint))
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
private fun showPopupOption(view: View, callback: (text: String) -> Unit) {
|
||||
val inflater = LayoutInflater.from(view.context)
|
||||
val mainBinding = LayoutPopupContainerBinding.inflate(inflater)
|
||||
popupWindow = BugFixedPopupWindow(
|
||||
mainBinding.root,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
val windowPos = IntArray(2)
|
||||
val anchorLoc = IntArray(2)
|
||||
// 获取锚点View在屏幕上的左上角坐标位置
|
||||
view.getLocationOnScreen(anchorLoc)
|
||||
val anchorHeight = view.height
|
||||
// 获取屏幕的高宽
|
||||
val screenHeight = view.context.resources.displayMetrics.heightPixels
|
||||
popupWindow?.let { popupWindow ->
|
||||
// 测量contentView
|
||||
popupWindow.contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
|
||||
val windowHeight = popupWindow.contentView.measuredHeight
|
||||
|
||||
// 判断需要向上弹出还是向下弹出显示
|
||||
val isNeedShowUp = screenHeight - anchorLoc[1] - anchorHeight < windowHeight
|
||||
windowPos[1] = if (isNeedShowUp) {
|
||||
anchorLoc[1] - windowHeight
|
||||
} else anchorLoc[1] + anchorHeight
|
||||
|
||||
LayoutAddKaifuPopupBinding.inflate(inflater, mainBinding.container, false).apply {
|
||||
root.layoutParams = root.layoutParams.apply {
|
||||
width = view.width + 16F.dip2px()
|
||||
}
|
||||
popupRv.adapter = AddKaiFuPopupAdapter(
|
||||
root.context,
|
||||
arrayListOf("安卓混服", "硬核专服", "官方专服", "官方渠道服", "腾讯QQ服", "腾讯微信服")
|
||||
) {
|
||||
callback.invoke(it)
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
popupRv.layoutManager = LinearLayoutManager(root.context)
|
||||
mainBinding.container.addView(root)
|
||||
}
|
||||
|
||||
popupWindow.isTouchable = true
|
||||
popupWindow.inputMethodMode = PopupWindow.INPUT_METHOD_NEEDED
|
||||
popupWindow.isOutsideTouchable = true
|
||||
popupWindow.animationStyle = R.style.popwindow_option_anim_style
|
||||
|
||||
// 设置偏移
|
||||
windowPos[1] = windowPos[1] - 12F.dip2px()
|
||||
windowPos[0] = anchorLoc[0] - 11F.dip2px()
|
||||
popupWindow.showAtLocation(view, Gravity.TOP or Gravity.START, windowPos[0], windowPos[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,14 +10,12 @@ import android.text.style.ForegroundColorSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.databinding.BindingAdapter;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
@ -25,7 +23,6 @@ import com.gh.base.OnViewClickListener;
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.dialog.CertificationDialog;
|
||||
import com.gh.common.dialog.PackageCheckDialogFragment;
|
||||
import com.gh.common.dialog.ReserveDialogFragment;
|
||||
import com.gh.common.exposure.ExposureEvent;
|
||||
import com.gh.common.history.HistoryHelper;
|
||||
import com.gh.common.repository.ReservationRepository;
|
||||
@ -36,6 +33,7 @@ import com.gh.common.util.DataUtils;
|
||||
import com.gh.common.util.DialogUtils;
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.common.util.DownloadDialogHelper;
|
||||
import com.gh.common.util.ExtensionsKt;
|
||||
import com.gh.common.util.GameUtils;
|
||||
import com.gh.common.util.GameViewUtils;
|
||||
import com.gh.common.util.ImageUtils;
|
||||
@ -46,17 +44,18 @@ import com.gh.common.util.NumberUtils;
|
||||
import com.gh.common.util.PackageInstaller;
|
||||
import com.gh.common.util.PackageUtils;
|
||||
import com.gh.common.util.PlatformUtils;
|
||||
import com.gh.common.util.RealNameHelper;
|
||||
import com.gh.common.util.ReservationHelper;
|
||||
import com.gh.common.view.DownloadProgressBar;
|
||||
import com.gh.common.view.DrawableView;
|
||||
import com.gh.common.view.GameIconView;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.gh.download.dialog.DownloadDialog;
|
||||
import com.gh.download.server.BrowserInstallHelper;
|
||||
import com.gh.gamecenter.DownloadManagerActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.WebActivity;
|
||||
import com.gh.gamecenter.baselist.LoadStatus;
|
||||
import com.gh.gamecenter.databinding.KaifuAddItemBinding;
|
||||
import com.gh.gamecenter.databinding.KaifuDetailItemRowBinding;
|
||||
import com.gh.gamecenter.entity.ApkEntity;
|
||||
import com.gh.gamecenter.entity.GameEntity;
|
||||
@ -66,6 +65,7 @@ import com.gh.gamecenter.entity.ServerCalendarEntity;
|
||||
import com.gh.gamecenter.entity.TagStyleEntity;
|
||||
import com.gh.gamecenter.entity.TestEntity;
|
||||
import com.gh.gamecenter.eventbus.EBReuse;
|
||||
import com.gh.gamecenter.gamedetail.dialog.GamePermissionDialogFragment;
|
||||
import com.gh.gamecenter.manager.PackagesManager;
|
||||
import com.gh.gamecenter.qa.entity.CommunityVideoEntity;
|
||||
import com.lightgame.download.DownloadEntity;
|
||||
@ -75,10 +75,8 @@ import com.lightgame.utils.Utils;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Created by khy on 12/02/18.
|
||||
@ -86,22 +84,18 @@ import java.util.Locale;
|
||||
|
||||
public class BindingAdapters {
|
||||
|
||||
@BindingAdapter("imageIcon")
|
||||
public static void loadIcon(SimpleDraweeView view, String imageUrl) {
|
||||
ImageUtils.displayIcon(view, imageUrl);
|
||||
}
|
||||
|
||||
@BindingAdapter("imageUrl")
|
||||
public static void loadImage(SimpleDraweeView view, String imageUrl) {
|
||||
ImageUtils.display(view, imageUrl);
|
||||
}
|
||||
|
||||
@BindingAdapter("setTextSize")
|
||||
public static void setTextSize(TextView view, int number) {
|
||||
view.setTextSize(number);
|
||||
}
|
||||
|
||||
@BindingAdapter("setTypeface")
|
||||
public static void setTypeface(TextView view, String type) {
|
||||
if (type == null) return;
|
||||
|
||||
@ -121,7 +115,6 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter({"addDetailKaiFuView", "addDetailKaiFuViewListener", "isReadyPatch"})
|
||||
public static void addDetailKaiFuView(LinearLayout view, List<ServerCalendarEntity> list
|
||||
, OnViewClickListener listener, Boolean isReadyPatch) {
|
||||
if (list == null) return;
|
||||
@ -129,13 +122,13 @@ public class BindingAdapters {
|
||||
for (int i = 0; i < list.size() + 1; i++) { // 1 is Title
|
||||
View inflate = LayoutInflater.from(view.getContext()).inflate(R.layout.kaifu_detail_item_row, null);
|
||||
KaifuDetailItemRowBinding binding = KaifuDetailItemRowBinding.bind(inflate);
|
||||
binding.setIsCloseBottom(i == list.size());
|
||||
binding.setIsReadyPatch(isReadyPatch);
|
||||
if (i == 0) {
|
||||
binding.setIsTitle(true);
|
||||
} else {
|
||||
ServerCalendarEntity serverEntity = list.get(i - 1);
|
||||
binding.setEntity(serverEntity);
|
||||
binding.getRoot().setBackgroundColor(isReadyPatch != null && isReadyPatch ? ExtensionsKt.toColor(R.color.theme) : ExtensionsKt.toColor(R.color.white));
|
||||
binding.getRoot().setPadding(DisplayUtils.dip2px(1), DisplayUtils.dip2px(1), DisplayUtils.dip2px(1), i == list.size() ? DisplayUtils.dip2px(1) : 0);
|
||||
ServerCalendarEntity serverEntity = list.get(i - 1);
|
||||
binding.timeTv.setText(i == 0 ? "时间" : serverEntity.getFormatTime("HH:mm"));
|
||||
binding.remarkTv.setText(i == 0 ? "备注" : serverEntity.getRemark());
|
||||
binding.nameTv.setText(i == 0 ? "名字" : (TextUtils.isEmpty(serverEntity.getNote()) ? "-" : serverEntity.getNote()));
|
||||
if (i != 0) {
|
||||
binding.getRoot().setOnClickListener(v -> {
|
||||
listener.onClick(v, isReadyPatch != null && isReadyPatch ? serverEntity : null);
|
||||
});
|
||||
@ -157,77 +150,10 @@ public class BindingAdapters {
|
||||
}
|
||||
|
||||
// 如果超过10000,则转换为1.0W
|
||||
@BindingAdapter("transSimpleCount")
|
||||
public static void transSimpleCount(TextView view, int count) {
|
||||
view.setText(NumberUtils.transSimpleCount(count));
|
||||
}
|
||||
|
||||
@BindingAdapter({"addKaiFuView", "clickListener"})
|
||||
public static void addKaiFuView(LinearLayout view, List<ServerCalendarEntity> list, OnViewClickListener listener) {
|
||||
if (list == null) return;
|
||||
view.removeAllViews();
|
||||
view.addView(LayoutInflater.from(view.getContext()).inflate(R.layout.kaifu_add_item_title, null));
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
View inflate = LayoutInflater.from(view.getContext()).inflate(R.layout.kaifu_add_item, null);
|
||||
KaifuAddItemBinding binding = KaifuAddItemBinding.bind(inflate);
|
||||
binding.setClickListener(listener);
|
||||
binding.setEntity(list.get(i));
|
||||
binding.setPosition(i);
|
||||
binding.setIsCloseBottom(list.size() - 1 == i);
|
||||
view.addView(inflate);
|
||||
|
||||
binding.kaifuAddName.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (hasFocus) {
|
||||
binding.kaifuAddName.setHint("");
|
||||
} else {
|
||||
binding.kaifuAddName.setHint("点击填写");
|
||||
}
|
||||
});
|
||||
binding.kaifuAddRemark.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (hasFocus) {
|
||||
binding.kaifuAddRemark.setHint("");
|
||||
} else {
|
||||
binding.kaifuAddRemark.setHint("点击填写");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter({"addKaiFuTime", "addKaiFuPosition"})
|
||||
public static void addKaiFuTime(EditText view, Long time, Integer position) {
|
||||
if (time == 0) {
|
||||
view.setHint("点击选择");
|
||||
view.setText("");
|
||||
} else {
|
||||
String pattern;
|
||||
if (position == 0) {
|
||||
pattern = "yyy-MM-dd HH:mm +";
|
||||
} else {
|
||||
pattern = "yyy-MM-dd HH:mm";
|
||||
}
|
||||
SimpleDateFormat format = new SimpleDateFormat(pattern, Locale.CHINA);
|
||||
view.setText(format.format(time * 1000));
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter({"kaiFuTextColor", "kaiFuTextPosition"})
|
||||
public static void kaiFuTextColor(EditText view, Integer dataMark, Integer position) {
|
||||
if (dataMark == 1 && view.getId() == R.id.kaifu_add_time
|
||||
|| dataMark == 2 && view.getId() == R.id.kaifu_add_name
|
||||
|| dataMark == 3 && view.getId() == R.id.kaifu_add_remark
|
||||
|| dataMark == 4 && (view.getId() == R.id.kaifu_add_time || view.getId() == R.id.kaifu_add_name)) {
|
||||
view.setTextColor(ContextCompat.getColor(view.getContext(), R.color.red));
|
||||
view.setHintTextColor(ContextCompat.getColor(view.getContext(), R.color.red));
|
||||
} else if (position == 0) {
|
||||
view.setTextColor(ContextCompat.getColor(view.getContext(), R.color.hint));
|
||||
view.setHintTextColor(ContextCompat.getColor(view.getContext(), R.color.hint));
|
||||
} else {
|
||||
view.setTextColor(ContextCompat.getColor(view.getContext(), R.color.title));
|
||||
view.setHintTextColor(ContextCompat.getColor(view.getContext(), R.color.hint));
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("textColorFromString")
|
||||
public static void textColorFromString(TextView tv, String hexString) {
|
||||
if (TextUtils.isEmpty(hexString)) return;
|
||||
|
||||
@ -238,7 +164,6 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("visibleGone")
|
||||
public static void showHide(View view, Boolean show) {
|
||||
if (show != null && show) {
|
||||
view.setVisibility(View.VISIBLE);
|
||||
@ -247,7 +172,6 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("goneIf")
|
||||
public static void goneIf(View view, Boolean gone) {
|
||||
if (gone != null && gone) {
|
||||
view.setVisibility(View.GONE);
|
||||
@ -259,7 +183,6 @@ public class BindingAdapters {
|
||||
/**
|
||||
* lazy 的 paddingTop
|
||||
*/
|
||||
@BindingAdapter("lazyPaddingLeft")
|
||||
public static void lazyPaddingLeft(View view, int paddingLeftInDp) {
|
||||
view.setPadding(DisplayUtils.dip2px(paddingLeftInDp), view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom());
|
||||
}
|
||||
@ -267,7 +190,6 @@ public class BindingAdapters {
|
||||
/**
|
||||
* lazy 的 paddingTop
|
||||
*/
|
||||
@BindingAdapter("lazyPaddingTop")
|
||||
public static void lazyPaddingTop(View view, int paddingTopInDp) {
|
||||
view.setPadding(view.getPaddingLeft(), DisplayUtils.dip2px(paddingTopInDp), view.getPaddingRight(), view.getPaddingBottom());
|
||||
}
|
||||
@ -275,12 +197,10 @@ public class BindingAdapters {
|
||||
/**
|
||||
* lazy 的 paddingBottom
|
||||
*/
|
||||
@BindingAdapter("lazyPaddingBottom")
|
||||
public static void lazyPaddingBottom(View view, int paddingBottomInDp) {
|
||||
view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), DisplayUtils.dip2px(paddingBottomInDp));
|
||||
}
|
||||
|
||||
@BindingAdapter("visibleInvisible")
|
||||
public static void visibleInvisible(View view, Boolean show) {
|
||||
if (show != null && show) {
|
||||
view.setVisibility(View.VISIBLE);
|
||||
@ -289,7 +209,6 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("messageUnread")
|
||||
public static void setMessageUnread(TextView view, int unreadCount) {
|
||||
if (unreadCount < 100) {
|
||||
view.setText(String.valueOf(unreadCount));
|
||||
@ -298,7 +217,6 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("serverTypePadding")
|
||||
public static void setServerTypePadding(TextView view, String serverType) {
|
||||
int paddRight = 0;
|
||||
if (TextUtils.isEmpty(serverType)) {
|
||||
@ -316,7 +234,6 @@ public class BindingAdapters {
|
||||
view.setPadding(0, 0, paddRight, 0);
|
||||
}
|
||||
|
||||
@BindingAdapter("serverType")
|
||||
public static void setServerType(TextView view, String serverType) {
|
||||
view.setText(serverType);
|
||||
if ("删档内测".equals(serverType) || "不删档内测".equals(serverType)) {
|
||||
@ -326,19 +243,22 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("game")
|
||||
public static void setGame(View view, GameEntity gameEntity) {
|
||||
if (gameEntity != null && view instanceof GameIconView) {
|
||||
((GameIconView) view).displayGameIcon(gameEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("articleType")
|
||||
public static void setGameIcon(View view, GameEntity gameEntity) {
|
||||
if (gameEntity != null && view instanceof GameIconView) {
|
||||
((GameIconView) view).displayGameIcon(gameEntity.getIcon(), gameEntity.getIconSubscript());
|
||||
}
|
||||
}
|
||||
|
||||
public static void setArticleType(TextView view, String articleType) {
|
||||
NewsUtils.setNewsType(view, articleType, 0, 0);
|
||||
}
|
||||
|
||||
@BindingAdapter("detailDownloadText")
|
||||
public static void setDetailDownloadText(TextView view, GameEntity gameEntity) {
|
||||
if (gameEntity == null || gameEntity.getApk().isEmpty()) {
|
||||
view.setBackgroundResource(R.drawable.game_item_btn_pause_style);
|
||||
@ -347,7 +267,6 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("liBaoBtn")
|
||||
public static void setLiBaoBtn(TextView view, String status) {
|
||||
if (TextUtils.isEmpty(status)) return;
|
||||
switch (status) {
|
||||
@ -412,7 +331,6 @@ public class BindingAdapters {
|
||||
}
|
||||
|
||||
// 大图下的进度条
|
||||
@BindingAdapter({"downloadButton", "traceEvent", "clickCallBack", "entrance", "location"})
|
||||
public static void setDownloadButton(DownloadProgressBar progressBar,
|
||||
GameEntity gameEntity,
|
||||
ExposureEvent traceEvent,
|
||||
@ -420,6 +338,9 @@ public class BindingAdapters {
|
||||
@Nullable String entrance,
|
||||
@Nullable String location) {
|
||||
|
||||
// 恢复DialogFragment
|
||||
restoreDialogFragment(progressBar);
|
||||
|
||||
// 判断是否显示按钮
|
||||
if (gameEntity != null
|
||||
&& Config.isShowDownload(gameEntity.getId())
|
||||
@ -456,27 +377,37 @@ public class BindingAdapters {
|
||||
return;
|
||||
}
|
||||
}
|
||||
PackageCheckDialogFragment.show((AppCompatActivity) v.getContext(), gameEntity.getPackageDialog(), () -> {
|
||||
DownloadDialogHelper.findAvailableDialogAndShow(v.getContext(), gameEntity, apk, () -> {
|
||||
CertificationDialog.showCertificationDialog(v.getContext(), gameEntity, () -> {
|
||||
DialogUtils.showVersionNumberDialog(v.getContext(), gameEntity, () -> {
|
||||
DialogUtils.showOverseaDownloadDialog(v.getContext(), gameEntity, () -> {
|
||||
DialogUtils.checkDownload(v.getContext(), apk.getSize(),
|
||||
isSubscribe -> download(progressBar, gameEntity, traceEvent, isSubscribe, entrance, location));
|
||||
RealNameHelper.checkIfAuth(v.getContext(), gameEntity, () -> {
|
||||
GamePermissionDialogFragment.show((AppCompatActivity) v.getContext(), gameEntity, gameEntity.getInfo(), () -> {
|
||||
BrowserInstallHelper.showBrowserInstallHintDialog(v.getContext(), () -> {
|
||||
PackageCheckDialogFragment.show((AppCompatActivity) v.getContext(), gameEntity, () -> {
|
||||
DownloadDialogHelper.findAvailableDialogAndShow(v.getContext(), gameEntity, apk, () -> {
|
||||
CertificationDialog.showCertificationDialog(v.getContext(), gameEntity, () -> {
|
||||
DialogUtils.showVersionNumberDialog(v.getContext(), gameEntity, () -> {
|
||||
DialogUtils.showOverseaDownloadDialog(v.getContext(), gameEntity, () -> {
|
||||
DialogUtils.checkDownload(v.getContext(), apk.getSize(),
|
||||
isSubscribe -> download(progressBar, gameEntity, traceEvent, isSubscribe, entrance, location));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
CertificationDialog.showCertificationDialog(v.getContext(), gameEntity, () -> {
|
||||
DialogUtils.showVersionNumberDialog(v.getContext(), gameEntity, () -> {
|
||||
DownloadDialog.showDownloadDialog(
|
||||
v.getContext(),
|
||||
gameEntity,
|
||||
traceEvent,
|
||||
entrance,
|
||||
location + ":" + gameEntity.getName());
|
||||
RealNameHelper.checkIfAuth(v.getContext(), gameEntity, () -> {
|
||||
GamePermissionDialogFragment.show((AppCompatActivity) v.getContext(), gameEntity, gameEntity.getInfo(), () -> {
|
||||
CertificationDialog.showCertificationDialog(v.getContext(), gameEntity, () -> {
|
||||
DialogUtils.showVersionNumberDialog(v.getContext(), gameEntity, () -> {
|
||||
DownloadDialog.showDownloadDialog(
|
||||
v.getContext(),
|
||||
gameEntity,
|
||||
traceEvent,
|
||||
entrance,
|
||||
location + ":" + gameEntity.getName());
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -511,19 +442,22 @@ public class BindingAdapters {
|
||||
case INSTALL_PLUGIN:
|
||||
case INSTALL_NORMAL:
|
||||
if (gameEntity.getApk().size() == 1) {
|
||||
DownloadEntity downloadEntity = DownloadManager.getInstance(progressBar.getContext()).getDownloadEntityByUrl(gameEntity.getApk().get(0).getUrl());
|
||||
DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByUrl(gameEntity.getApk().get(0).getUrl());
|
||||
if (downloadEntity != null) {
|
||||
PackageInstaller.install(v.getContext(), downloadEntity);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RESERVABLE:
|
||||
CheckLoginUtils.checkLogin(progressBar.getContext(), "", () -> {
|
||||
ReserveDialogFragment dialogFragment = ReserveDialogFragment.getInstance(gameEntity, () -> {
|
||||
LogUtils.logReservation(gameEntity, traceEvent);
|
||||
updateReservation(progressBar, gameEntity);
|
||||
RealNameHelper.checkIfAuth(v.getContext(), gameEntity, () -> {
|
||||
GamePermissionDialogFragment.show((AppCompatActivity) v.getContext(), gameEntity, gameEntity.getInfo(), () -> {
|
||||
CheckLoginUtils.checkLogin(progressBar.getContext(), "", () -> {
|
||||
ReservationHelper.reserve(v.getContext(), gameEntity.getId(), () -> {
|
||||
LogUtils.logReservation(gameEntity, traceEvent);
|
||||
updateReservation(progressBar, gameEntity);
|
||||
});
|
||||
});
|
||||
});
|
||||
dialogFragment.show(((AppCompatActivity) progressBar.getContext()).getSupportFragmentManager(), "reserve");
|
||||
});
|
||||
break;
|
||||
case RESERVED:
|
||||
@ -550,8 +484,10 @@ public class BindingAdapters {
|
||||
HistoryHelper.insertGameEntity(gameEntity);
|
||||
}
|
||||
|
||||
Intent i = new Intent(WebActivity.getIntentForWebGame(progressBar.getContext(), linkEntity.getLink(), gameEntity.getName(), isPlay, linkEntity.getCloseButton()));
|
||||
progressBar.getContext().startActivity(i);
|
||||
GamePermissionDialogFragment.show((AppCompatActivity) v.getContext(), gameEntity, gameEntity.getInfo(), () -> {
|
||||
Intent i = new Intent(WebActivity.getIntentForWebGame(progressBar.getContext(), linkEntity.getLink(), gameEntity.getName(), isPlay, linkEntity.getCloseButton()));
|
||||
progressBar.getContext().startActivity(i);
|
||||
});
|
||||
break;
|
||||
case UPDATING:
|
||||
Utils.toast(progressBar.getContext(), "正在加急更新版本,敬请后续留意");
|
||||
@ -614,7 +550,7 @@ public class BindingAdapters {
|
||||
|
||||
// 显示下载过程状态
|
||||
if (gameEntity.getApk().size() == 1) {
|
||||
DownloadEntity downloadEntity = DownloadManager.getInstance(progressBar.getContext()).getDownloadEntityByUrl(gameEntity.getApk().get(0).getUrl());
|
||||
DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByUrl(gameEntity.getApk().get(0).getUrl());
|
||||
if (downloadEntity != null) {
|
||||
progressBar.setProgress((int) (downloadEntity.getPercent() * 10));
|
||||
switch (downloadEntity.getStatus()) {
|
||||
@ -642,6 +578,9 @@ public class BindingAdapters {
|
||||
case cancel:
|
||||
case hijack:
|
||||
case notfound:
|
||||
case uncertificated:
|
||||
case unqualified:
|
||||
case unavailable:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -650,6 +589,18 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当页面完全重建时,若存在重建的DialogFragment,则需要手动恢复该DialogFragment之前配置的回调(因为DialogFragment重建时只会从arguments中获取之前的配置内容,
|
||||
* 而arguments无法传递回调),或者dismiss该DialogFragment
|
||||
*/
|
||||
private static void restoreDialogFragment(DownloadProgressBar progressBar) {
|
||||
GamePermissionDialogFragment gamePermissionDialogFragment =
|
||||
((GamePermissionDialogFragment) ((AppCompatActivity) progressBar.getContext()).getSupportFragmentManager().findFragmentByTag(GamePermissionDialogFragment.class.getSimpleName()));
|
||||
if (gamePermissionDialogFragment != null) {
|
||||
gamePermissionDialogFragment.dismissAllowingStateLoss();
|
||||
}
|
||||
}
|
||||
|
||||
/*private static void download(DownloadProgressBar progressBar, GameEntity gameEntity, ExposureEvent traceEvent, @Nullable String entrance, @Nullable String location, View v) {
|
||||
if (gameEntity.getApk().size() == 1) {
|
||||
ApkEntity apk = gameEntity.getApk().get(0);
|
||||
@ -720,18 +671,58 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("gameLabelList")
|
||||
public static void setGameLabelList(LinearLayout layout, List<TagStyleEntity> tagStyle) {
|
||||
GameViewUtils.setLabelList(layout.getContext(), layout, tagStyle);
|
||||
}
|
||||
|
||||
// 包含测试开服标签
|
||||
@BindingAdapter("setGameTags")
|
||||
public static void setGameTags(LinearLayout layout, GameEntity gameEntity) {
|
||||
try {
|
||||
if (layout.getVisibility() == View.GONE) return;
|
||||
ArrayList<TagStyleEntity> tagStyle = new ArrayList<>();
|
||||
TestEntity test = gameEntity.getTest();
|
||||
if (test != null
|
||||
// 这个判断用于开测表列表
|
||||
&& !"type_tag".equals(test.getGameTag())) {
|
||||
if ("custom".equals(test.getGameTag())) {
|
||||
TagStyleEntity typeTag = new TagStyleEntity();
|
||||
if (!TextUtils.isEmpty(test.getText())) {
|
||||
typeTag.setName(test.getText() != null ? test.getText() : "");
|
||||
} else {
|
||||
typeTag.setName(test.getType() != null ? test.getType() : "");
|
||||
}
|
||||
typeTag.setBackground("E8F3FF");
|
||||
typeTag.setColor("1383EB");
|
||||
tagStyle.add(typeTag);
|
||||
} else {
|
||||
TagStyleEntity typeTag = new TagStyleEntity();
|
||||
typeTag.setName(test.getType() != null ? test.getType() : "");
|
||||
typeTag.setBackground("FFF3E0");
|
||||
typeTag.setColor("FA8500");
|
||||
tagStyle.add(typeTag);
|
||||
|
||||
TagStyleEntity timeTag = new TagStyleEntity();
|
||||
timeTag.setName(GameViewUtils.getGameTestDate(test.getStart()));
|
||||
timeTag.setBackground("E0FFF9");
|
||||
timeTag.setColor("00A887");
|
||||
tagStyle.add(timeTag);
|
||||
}
|
||||
} else {
|
||||
tagStyle = gameEntity.getTagStyle();
|
||||
}
|
||||
GameViewUtils.setLabelList(layout.getContext(), layout, tagStyle);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setVideoDetailGameTags(LinearLayout layout, GameEntity gameEntity) {
|
||||
try {
|
||||
ArrayList<TagStyleEntity> tagStyle = new ArrayList<>();
|
||||
TestEntity test = gameEntity.getTest();
|
||||
if (test != null) {
|
||||
if (test != null
|
||||
// 这个判断用于开测表列表
|
||||
&& !"type_tag".equals(test.getGameTag())) {
|
||||
TagStyleEntity typeTag = new TagStyleEntity();
|
||||
typeTag.setName(test.getType() != null ? test.getType() : "");
|
||||
typeTag.setBackground("FFF3E0");
|
||||
@ -746,20 +737,18 @@ public class BindingAdapters {
|
||||
} else {
|
||||
tagStyle = gameEntity.getTagStyle();
|
||||
}
|
||||
GameViewUtils.setLabelList(layout.getContext(), layout, tagStyle);
|
||||
GameViewUtils.setLabelList(layout.getContext(), layout, tagStyle, 4);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("isRefreshing")
|
||||
public static void isRefreshing(SwipeRefreshLayout layout, LoadStatus status) {
|
||||
if (status != LoadStatus.INIT_LOADING && status != LoadStatus.LIST_LOADING) {
|
||||
layout.setRefreshing(false);
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter({"setGameName", "isShowPlatform", "isShowSuffix"})
|
||||
public static void setGameName(TextView view, GameEntity game, boolean isShowPlatform, @Nullable Boolean isShowSuffix) {
|
||||
if (isShowSuffix == null) isShowSuffix = true; // 默认显示
|
||||
if (isShowPlatform && game.getApk().size() > 0) {
|
||||
@ -772,7 +761,6 @@ public class BindingAdapters {
|
||||
|
||||
}
|
||||
|
||||
@BindingAdapter({"setCommunityImage", "setCommunityVideoImage"})
|
||||
public static void setCommunityImage(SimpleDraweeView imageView, List<String> images, List<CommunityVideoEntity> videos) {
|
||||
if (videos.size() > 0) {
|
||||
CommunityVideoEntity videoEntity = videos.get(0);
|
||||
@ -786,7 +774,6 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter({"setCommunityVideoDuration"})
|
||||
public static void setCommunityVideoDuration(TextView mVideoDuration, List<CommunityVideoEntity> videos) {
|
||||
if (videos != null && videos.size() > 0) {
|
||||
CommunityVideoEntity videoEntity = videos.get(0);
|
||||
@ -798,7 +785,6 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter({"setGameTags", "setMaxGameTags"})
|
||||
public static void setGameTags(TextView view, List<TagStyleEntity> tags, int maxTags) {
|
||||
if (tags == null) {
|
||||
view.setText("");
|
||||
@ -827,7 +813,6 @@ public class BindingAdapters {
|
||||
view.setText(span);
|
||||
}
|
||||
|
||||
@BindingAdapter({"setVideoData"})
|
||||
public static void setVideoData(TextView view, int count) {
|
||||
if (count > 0) {
|
||||
view.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(view.getContext(), R.drawable.ic_video_data_up), null, null, null);
|
||||
@ -835,7 +820,7 @@ public class BindingAdapters {
|
||||
view.setText(count + "");
|
||||
} else {
|
||||
view.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
|
||||
view.setTextColor(ContextCompat.getColor(view.getContext(), R.color.text_999999));
|
||||
view.setTextColor(ContextCompat.getColor(view.getContext(), R.color.text_subtitleDesc));
|
||||
view.setText("-");
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,94 @@
|
||||
package com.gh.common.dialog
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.gh.base.fragment.BaseDialogFragment
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.common.util.EntranceUtils
|
||||
import com.gh.common.util.SpanBuilder
|
||||
import com.gh.common.util.dip2px
|
||||
import com.gh.common.view.CustomLinkMovementMethod
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.DialogApplyModeratorBinding
|
||||
|
||||
|
||||
class ApplyModeratorDialogFragment : BaseDialogFragment() {
|
||||
private lateinit var binding: DialogApplyModeratorBinding
|
||||
private var mGroupNumber = ""
|
||||
private var mGroupKey = ""
|
||||
private var mParentTag = ""
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
requireArguments().run {
|
||||
mGroupNumber = getString(KEY_GROUP_NUMBER) ?: ""
|
||||
mGroupKey = getString(KEY_GROUP_KEY) ?: ""
|
||||
mParentTag = getString(EntranceUtils.KEY_PARENT_TAG) ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = DialogApplyModeratorBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val startText = "版主考核群:"
|
||||
val text = "$startText$mGroupNumber\n感谢你对论坛建设的支持\n请加入版主考核群并联系群主进行版主资格考核"
|
||||
binding.desTv.text = SpanBuilder(text)
|
||||
.click(startText.length, startText.length + mGroupNumber.length, R.color.theme_font,true) {
|
||||
DirectUtils.directToQqGroup(
|
||||
requireContext(),
|
||||
mGroupKey
|
||||
)
|
||||
}
|
||||
.build()
|
||||
binding.desTv.movementMethod = CustomLinkMovementMethod.getInstance()
|
||||
binding.confirmTv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
activity?.supportFragmentManager?.findFragmentByTag(mParentTag)
|
||||
?.onActivityResult(REQUEST_CODE, Activity.RESULT_OK, null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val width = requireContext().resources.displayMetrics.widthPixels - 60F.dip2px()
|
||||
val height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
dialog?.window?.setLayout(width, height)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY_GROUP_NUMBER = "group_number"
|
||||
const val KEY_GROUP_KEY = "group_key"
|
||||
const val REQUEST_CODE = 1103
|
||||
|
||||
@JvmStatic
|
||||
fun show(
|
||||
activity: AppCompatActivity,
|
||||
number: String,
|
||||
key: String,
|
||||
tag: String
|
||||
) {
|
||||
ApplyModeratorDialogFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(KEY_GROUP_NUMBER, number)
|
||||
putString(KEY_GROUP_KEY, key)
|
||||
putString(EntranceUtils.KEY_PARENT_TAG, tag)
|
||||
}
|
||||
}.show(
|
||||
activity.supportFragmentManager,
|
||||
ApplyModeratorDialogFragment::class.java.simpleName
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
package com.gh.common.dialog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import com.gh.base.fragment.BaseDialogFragment
|
||||
import com.gh.gamecenter.R
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
abstract class BaseDraggableDialogFragment : BaseDialogFragment(), View.OnTouchListener {
|
||||
private var mInitPositionY = 0f
|
||||
private lateinit var mGestureDetector: GestureDetector
|
||||
private lateinit var mRootView: View
|
||||
private lateinit var mDragCloseView: View
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mRootView = getRootView()
|
||||
mDragCloseView = getDragCloseView()
|
||||
mDragCloseView.setOnTouchListener(this)
|
||||
mGestureDetector = GestureDetector(requireContext(), SingleTapConfirm())
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val createDialog = super.onCreateDialog(savedInstanceState)
|
||||
createDialog.setCanceledOnTouchOutside(true)
|
||||
|
||||
val window = createDialog.window
|
||||
window?.setGravity(Gravity.BOTTOM)
|
||||
window?.setWindowAnimations(R.style.community_publication_animation)
|
||||
return createDialog
|
||||
}
|
||||
|
||||
// dialog drag animation
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouch(v: View, event: MotionEvent): Boolean {
|
||||
if (mGestureDetector.onTouchEvent(event) && mRootView.y == 0F) {
|
||||
v.performClick()
|
||||
return true
|
||||
}
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
mInitPositionY = mRootView.y - event.rawY
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val offsetY = event.rawY + mInitPositionY
|
||||
val dialogY = mRootView.y
|
||||
if (dialogY + offsetY > 0) {
|
||||
mRootView.animate()
|
||||
.y(offsetY)
|
||||
.setDuration(0)
|
||||
.start()
|
||||
} else {
|
||||
resetDialogPosition()
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_CANCEL,
|
||||
MotionEvent.ACTION_UP,
|
||||
MotionEvent.ACTION_OUTSIDE -> {
|
||||
if (mRootView.y >= mRootView.height / 2) {
|
||||
dismissAllowingStateLoss()
|
||||
} else {
|
||||
resetDialogPosition(300)
|
||||
}
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun resetDialogPosition(duration: Long = 0) {
|
||||
mRootView.animate()
|
||||
.y(0F)
|
||||
.setDuration(duration)
|
||||
.start()
|
||||
}
|
||||
|
||||
private class SingleTapConfirm : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onSingleTapUp(event: MotionEvent): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val width = HaloApp.getInstance().application.resources.displayMetrics.widthPixels
|
||||
val height = dialog?.window?.attributes?.height ?: ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
dialog?.window?.setLayout(width, height)
|
||||
}
|
||||
|
||||
abstract fun getRootView(): View
|
||||
abstract fun getDragCloseView(): View
|
||||
override fun getThemeRes(): Int = R.style.DialogFragmentDimAmount
|
||||
}
|
||||
@ -4,6 +4,8 @@ import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.gh.common.util.MtaHelper
|
||||
import com.lightgame.dialog.BaseDialogFragment
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
@ -63,4 +65,29 @@ abstract class BaseTrackableDialogFragment : BaseDialogFragment() {
|
||||
|
||||
open fun trackWithBasicDeviceInfo() = false
|
||||
|
||||
|
||||
override fun show(manager: FragmentManager, tag: String?) {
|
||||
val fragment = manager.findFragmentByTag(tag)
|
||||
if (fragment != null) {
|
||||
val transaction = manager.beginTransaction()
|
||||
transaction.show(fragment)
|
||||
transaction.commit()
|
||||
} else {
|
||||
try {
|
||||
val clazz: Class<*> = DialogFragment::class.java
|
||||
val dismissed = clazz.getDeclaredField("mDismissed")
|
||||
dismissed.isAccessible = true
|
||||
dismissed[this] = false
|
||||
val shownByMe = clazz.getDeclaredField("mShownByMe")
|
||||
shownByMe.isAccessible = true
|
||||
shownByMe[this] = true
|
||||
val transaction = manager.beginTransaction()
|
||||
transaction.add(this, tag)
|
||||
transaction.commitAllowingStateLoss()
|
||||
} catch (e: Exception) {
|
||||
super.show(manager, tag)
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17,18 +17,13 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import com.gh.common.avoidcallback.AvoidOnResultManager
|
||||
import com.gh.common.avoidcallback.Callback
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.CheckLoginUtils
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.util.GsonUtils
|
||||
import com.gh.common.util.SPUtils
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.UserInfoEditActivity
|
||||
import com.gh.gamecenter.WebActivity
|
||||
import com.gh.gamecenter.ShellActivity
|
||||
import com.gh.gamecenter.entity.AuthDialogEntity
|
||||
import com.gh.gamecenter.entity.AuthDialogLevel
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.user.UserViewModel
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.halo.assistant.fragment.user.UserInfoEditFragment
|
||||
import com.lightgame.utils.AppManager
|
||||
@ -56,7 +51,7 @@ class CertificationDialog(context: Context, private val authDialogEntity: AuthDi
|
||||
detailedDesTv.paint.isAntiAlias = true
|
||||
|
||||
detailedDesTv.setOnClickListener {
|
||||
context.startActivity(WebActivity.getIntentByUrl(context, authDialogEntity.link))
|
||||
DirectUtils.directToWebView(context, authDialogEntity.link)
|
||||
}
|
||||
|
||||
when (authDialogEntity.level) {
|
||||
@ -134,10 +129,17 @@ class CertificationDialog(context: Context, private val authDialogEntity: AuthDi
|
||||
val currentActivity = AppManager.getInstance().currentActivity() ?: return
|
||||
|
||||
AvoidOnResultManager.getInstance(currentActivity as AppCompatActivity)
|
||||
.startForResult(UserInfoEditActivity.getIntent(context, UserViewModel.TYPE_ID_CARD), object : Callback {
|
||||
.startForResult(
|
||||
ShellActivity.getIntent(
|
||||
context,
|
||||
ShellActivity.Type.REAL_NAME_INFO,
|
||||
).apply {
|
||||
putExtra(EntranceUtils.KEY_GAME_ID, gameId)
|
||||
}, object : Callback {
|
||||
override fun onActivityResult(resultCode: Int, data: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
val isAuthSuccess = data.getBooleanExtra(UserInfoEditFragment.AUTH_SUCCESS, false)
|
||||
val isAuthSuccess =
|
||||
data.getBooleanExtra(UserInfoEditFragment.AUTH_SUCCESS, false)
|
||||
if (isAuthSuccess) {
|
||||
listener.onConfirm()
|
||||
dismiss()
|
||||
|
||||
@ -23,22 +23,22 @@ import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.*
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.DialogDeviceRemindBinding
|
||||
import com.gh.gamecenter.entity.DeviceDialogEntity
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.halo.assistant.fragment.SettingsFragment.AUTO_INSTALL_SP_KEY
|
||||
import com.gh.gamecenter.setting.GameDownloadSettingFragment.Companion.AUTO_INSTALL_SP_KEY
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.download.DownloadStatus
|
||||
import io.reactivex.disposables.Disposable
|
||||
import kotlinx.android.synthetic.main.dialog_device_remind.view.*
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
* 设备提醒弹窗
|
||||
*/
|
||||
class DeviceRemindDialog(context: Context, val entity: DeviceDialogEntity, val gameEntity: GameEntity) : Dialog(context, R.style.GhAlertDialog) {
|
||||
private lateinit var view: View
|
||||
private val mBinding: DialogDeviceRemindBinding by lazy { DialogDeviceRemindBinding.inflate(layoutInflater) }
|
||||
private var currentPage = 0
|
||||
private var mSlideLooperInterval = 3000L
|
||||
private lateinit var mLooperHandle: LooperHandle
|
||||
@ -60,39 +60,48 @@ class DeviceRemindDialog(context: Context, val entity: DeviceDialogEntity, val g
|
||||
|
||||
companion object {
|
||||
fun showDeviceRemindDialog(context: Context, gameEntity: GameEntity) {
|
||||
val datas = SPUtils.getString(Constants.SP_DEVICE_REMIND)
|
||||
if (datas.isNotEmpty()) {
|
||||
val pair = shouldShowDeviceRemindDialog(gameEntity)
|
||||
if (pair.first) {
|
||||
val dialog = DeviceRemindDialog(context, pair.second!!, gameEntity)
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun shouldShowDeviceRemindDialog(gameEntity: GameEntity): Pair<Boolean, DeviceDialogEntity?> {
|
||||
val datas = SPUtils.getString(Constants.SP_DEVICE_REMIND)
|
||||
if (datas.isNotEmpty()) {
|
||||
val type = object : TypeToken<List<DeviceDialogEntity>>() {}.type
|
||||
val entitys = GsonUtils.gson.fromJson<List<DeviceDialogEntity>>(datas, type)
|
||||
val entities = GsonUtils.gson.fromJson<List<DeviceDialogEntity>>(datas, type)
|
||||
//1.判断设备是否匹配
|
||||
val entity = entitys.find { it.manufacturer.toLowerCase().startsWith(Build.MANUFACTURER.toLowerCase()) }
|
||||
?: return
|
||||
val entity = entities.find { it.manufacturer.toLowerCase().startsWith(Build.MANUFACTURER.toLowerCase()) }
|
||||
?: return Pair(false, null)
|
||||
//2.判断游戏不含剔除标签
|
||||
gameEntity.tagStyle.forEach {
|
||||
if (entity.excludeTags.contains(it.name)) {
|
||||
return
|
||||
return Pair(false, null)
|
||||
}
|
||||
}
|
||||
//3.不再弹出提示判断
|
||||
val isNoRemindAgain = SPUtils.getBoolean(Constants.SP_NO_REMIND_AGAIN, false)
|
||||
if (isNoRemindAgain) return
|
||||
|
||||
val dialog = DeviceRemindDialog(context, entity, gameEntity)
|
||||
dialog.show()
|
||||
if (isNoRemindAgain) return Pair(false, null)
|
||||
return Pair(true, entity)
|
||||
}
|
||||
return Pair(false, null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
view = LayoutInflater.from(context).inflate(R.layout.dialog_device_remind, null)
|
||||
setContentView(view)
|
||||
setContentView(mBinding.root)
|
||||
mDatas.addAll(entity.gallery)
|
||||
view.titleTv.text = entity.title
|
||||
view.contentTv.text = entity.content
|
||||
mBinding.titleTv.text = entity.title
|
||||
mBinding.contentTv.text = entity.content
|
||||
|
||||
view.bannerView.apply {
|
||||
mBinding.bannerView.apply {
|
||||
orientation = ViewPager2.ORIENTATION_HORIZONTAL
|
||||
mAdapter = BannerAdapter()
|
||||
val recyclerView = getChildAt(0) as RecyclerView
|
||||
@ -123,36 +132,36 @@ class DeviceRemindDialog(context: Context, val entity: DeviceDialogEntity, val g
|
||||
}
|
||||
val isFirst = SPUtils.getBoolean(Constants.SP_FIRST_DEVICE_REMIND, false)
|
||||
if (!isFirst) {
|
||||
view.cancelTv.isEnabled = false
|
||||
view.cancelTv.background = ContextCompat.getDrawable(context, R.drawable.button_round_f5f5f5)
|
||||
mBinding.cancelTv.isEnabled = false
|
||||
mBinding.cancelTv.background = ContextCompat.getDrawable(context, R.drawable.button_round_f5f5f5)
|
||||
disposable = countDownTimer(3) { finish, time ->
|
||||
if (finish) {
|
||||
view.cancelTv.isEnabled = true
|
||||
view.cancelTv.background = ContextCompat.getDrawable(context, R.drawable.button_blue_oval)
|
||||
view.cancelTv.text = "我知道了"
|
||||
view.cancelTv.setTextColor(ContextCompat.getColor(context, R.color.white))
|
||||
mBinding.cancelTv.isEnabled = true
|
||||
mBinding.cancelTv.background = ContextCompat.getDrawable(context, R.drawable.button_blue_oval)
|
||||
mBinding.cancelTv.text = "我知道了"
|
||||
mBinding.cancelTv.setTextColor(ContextCompat.getColor(context, R.color.white))
|
||||
} else {
|
||||
view.cancelTv.text = "我知道了(${time}S)"
|
||||
mBinding.cancelTv.text = "我知道了(${time}S)"
|
||||
}
|
||||
}
|
||||
|
||||
SPUtils.setBoolean(Constants.SP_FIRST_DEVICE_REMIND, true)
|
||||
} else {
|
||||
view.noRemindAgainCb.visibility = View.VISIBLE
|
||||
view.cancelTv.text = "我知道了"
|
||||
view.cancelTv.isEnabled = true
|
||||
view.cancelTv.setTextColor(ContextCompat.getColor(context, R.color.white))
|
||||
view.cancelTv.background = ContextCompat.getDrawable(context, R.drawable.button_blue_oval)
|
||||
mBinding.noRemindAgainCb.visibility = View.VISIBLE
|
||||
mBinding.cancelTv.text = "我知道了"
|
||||
mBinding.cancelTv.isEnabled = true
|
||||
mBinding.cancelTv.setTextColor(ContextCompat.getColor(context, R.color.white))
|
||||
mBinding.cancelTv.background = ContextCompat.getDrawable(context, R.drawable.button_blue_oval)
|
||||
}
|
||||
view.cancelTv.setOnClickListener {
|
||||
SPUtils.setBoolean(Constants.SP_NO_REMIND_AGAIN, view.noRemindAgainCb.isChecked)
|
||||
mBinding.cancelTv.setOnClickListener {
|
||||
SPUtils.setBoolean(Constants.SP_NO_REMIND_AGAIN, mBinding.noRemindAgainCb.isChecked)
|
||||
dismiss()
|
||||
}
|
||||
DownloadManager.getInstance(context).addObserver(dataWatcher)
|
||||
DownloadManager.getInstance().addObserver(dataWatcher)
|
||||
}
|
||||
|
||||
private fun addIndicator() {
|
||||
view.indicatorLl.removeAllViews()
|
||||
mBinding.indicatorLl.removeAllViews()
|
||||
mDatas.forEach { _ ->
|
||||
val indicatorView = ImageView(context).apply {
|
||||
setImageResource(R.drawable.selector_device_remind_indicator)
|
||||
@ -161,13 +170,13 @@ class DeviceRemindDialog(context: Context, val entity: DeviceDialogEntity, val g
|
||||
params.rightMargin = DisplayUtils.dip2px(1F)
|
||||
layoutParams = params
|
||||
}
|
||||
view.indicatorLl.addView(indicatorView)
|
||||
mBinding.indicatorLl.addView(indicatorView)
|
||||
}
|
||||
}
|
||||
|
||||
private fun slideIndicator(position: Int) {
|
||||
for (i in 0 until view.indicatorLl.childCount) {
|
||||
val childAt = view.indicatorLl.getChildAt(i)
|
||||
for (i in 0 until mBinding.indicatorLl.childCount) {
|
||||
val childAt = mBinding.indicatorLl.getChildAt(i)
|
||||
childAt.isSelected = i == position
|
||||
}
|
||||
}
|
||||
@ -200,7 +209,7 @@ class DeviceRemindDialog(context: Context, val entity: DeviceDialogEntity, val g
|
||||
|
||||
fun scrollToNextPage() {
|
||||
currentPage++
|
||||
view.bannerView.setCurrentItem(currentPage, true)
|
||||
mBinding.bannerView.setCurrentItem(currentPage, true)
|
||||
}
|
||||
|
||||
fun startScroll() {
|
||||
@ -215,7 +224,7 @@ class DeviceRemindDialog(context: Context, val entity: DeviceDialogEntity, val g
|
||||
|
||||
class LooperHandle(val mAdapter: BannerAdapter) : Handler() {
|
||||
private val mWeakReference: WeakReference<BannerAdapter> = WeakReference(mAdapter)
|
||||
override fun handleMessage(msg: Message?) {
|
||||
override fun handleMessage(msg: Message) {
|
||||
val adapter = mWeakReference.get()
|
||||
adapter?.scrollToNextPage()
|
||||
adapter?.startScroll()
|
||||
@ -228,6 +237,6 @@ class DeviceRemindDialog(context: Context, val entity: DeviceDialogEntity, val g
|
||||
disposable!!.dispose()
|
||||
disposable = null
|
||||
}
|
||||
DownloadManager.getInstance(context).removeObserver(dataWatcher)
|
||||
DownloadManager.getInstance().removeObserver(dataWatcher)
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package com.gh.common.dialog
|
||||
|
||||
import android.app.Dialog
|
||||
import android.graphics.Paint
|
||||
import android.os.Bundle
|
||||
import android.util.TypedValue
|
||||
@ -8,54 +9,75 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.text.HtmlCompat
|
||||
import com.gh.base.fragment.BaseDialogFragment
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.common.util.DisplayUtils
|
||||
import com.gh.common.util.MtaHelper
|
||||
import com.gh.common.util.dip2px
|
||||
import com.gh.common.util.toColor
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.DialogGameOffServiceBinding
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import kotlinx.android.synthetic.main.dialog_game_off_service.*
|
||||
|
||||
// 游戏关闭下载弹窗
|
||||
class GameOffServiceDialogFragment
|
||||
// : BaseTrackableDialogFragment()
|
||||
:BaseDialogFragment() {
|
||||
: BaseDialogFragment() {
|
||||
|
||||
private var mDialog: GameEntity.Dialog? = null
|
||||
private var mBinding: DialogGameOffServiceBinding? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mDialog = requireArguments().getParcelable(KEY_DIALOG)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.dialog_game_off_service, null)
|
||||
return DialogGameOffServiceBinding
|
||||
.inflate(inflater)
|
||||
.apply { mBinding = this }
|
||||
.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mDialog?.run {
|
||||
titleTv.text = title
|
||||
contentTv.text = HtmlCompat.fromHtml(content, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||
|
||||
for (site in sites) {
|
||||
val siteTv = TextView(context)
|
||||
siteTv.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
|
||||
topMargin = DisplayUtils.dip2px(12f)
|
||||
}
|
||||
siteTv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
|
||||
siteTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.theme_font))
|
||||
siteTv.text = site.text
|
||||
siteTv.paintFlags = siteTv.paintFlags or Paint.UNDERLINE_TEXT_FLAG
|
||||
siteTv.setOnClickListener {
|
||||
// MtaHelper.onEvent("游戏下载状态按钮", getKey(), site.text)
|
||||
DirectUtils.directToWebView(requireContext(), site.url, "(关闭下载弹窗)")
|
||||
mBinding?.run {
|
||||
mDialog?.run {
|
||||
titleTv.text = title
|
||||
contentTv.text = HtmlCompat.fromHtml(content, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||
okTv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
container.addView(siteTv)
|
||||
// 过滤内容为空的元素
|
||||
val notEmptySite = sites.filter { it.text.isNotBlank() }
|
||||
|
||||
notEmptySite.forEachIndexed { index, site ->
|
||||
val siteTv = TextView(context)
|
||||
siteTv.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
|
||||
topMargin = 24F.dip2px()
|
||||
if (index == notEmptySite.size - 1) bottomMargin = 8F.dip2px()
|
||||
}
|
||||
siteTv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14F)
|
||||
siteTv.setTextColor(R.color.theme_font.toColor())
|
||||
siteTv.text = site.text
|
||||
siteTv.paintFlags = siteTv.paintFlags or Paint.UNDERLINE_TEXT_FLAG
|
||||
siteTv.setOnClickListener {
|
||||
// MtaHelper.onEvent("游戏下载状态按钮", getKey(), site.text)
|
||||
DirectUtils.directToWebView(requireContext(), site.url, "(关闭下载弹窗)")
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
container.addView(siteTv)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return super.onCreateDialog(savedInstanceState).apply { setCanceledOnTouchOutside(true) }
|
||||
}
|
||||
|
||||
// override fun getEvent(): String {
|
||||
// return "游戏下载状态按钮"
|
||||
// }
|
||||
@ -65,9 +87,13 @@ class GameOffServiceDialogFragment
|
||||
// }
|
||||
|
||||
companion object {
|
||||
const val KEY_DIALOG = "dialog"
|
||||
|
||||
@JvmStatic
|
||||
fun getInstance(dialog: GameEntity.Dialog) = GameOffServiceDialogFragment().apply {
|
||||
mDialog = dialog
|
||||
arguments = Bundle().apply {
|
||||
putParcelable(KEY_DIALOG, dialog)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ import com.gh.common.util.PermissionHelper.INSTALL_PERMISSION_CODE
|
||||
import com.gh.common.xapk.XapkInstaller
|
||||
import com.gh.gamecenter.R
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.utils.Utils
|
||||
import kotlin.random.Random
|
||||
|
||||
class InstallPermissionDialogFragment : BaseTrackableDialogFragment() {
|
||||
@ -26,7 +27,7 @@ class InstallPermissionDialogFragment : BaseTrackableDialogFragment() {
|
||||
lateinit var mView: View
|
||||
var isXapk = false
|
||||
var url: String = ""
|
||||
var mCallBack: (() -> Unit)? = null
|
||||
var mCallBack: ((isFromPermissionGrantedCallback: Boolean) -> Unit)? = null
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
mView = inflater.inflate(R.layout.dialog_install_permission, null, false)
|
||||
@ -56,7 +57,7 @@ class InstallPermissionDialogFragment : BaseTrackableDialogFragment() {
|
||||
closeTv.setOnClickListener {
|
||||
MtaHelper.onEvent(getEvent(), getKey(), "文案样式_点击以后再说")
|
||||
if (isXapk) {
|
||||
mCallBack?.invoke()
|
||||
mCallBack?.invoke(false)
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
@ -77,9 +78,11 @@ class InstallPermissionDialogFragment : BaseTrackableDialogFragment() {
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == RESULT_OK && requestCode == INSTALL_PERMISSION_CODE) {
|
||||
SPUtils.setString(Constants.SP_XAPK_UNZIP_ACTIVITY, "")
|
||||
SPUtils.setString(Constants.SP_XAPK_URL, "")
|
||||
mCallBack?.invoke()
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
|
||||
SPUtils.setString(Constants.SP_XAPK_UNZIP_ACTIVITY, "")
|
||||
SPUtils.setString(Constants.SP_XAPK_URL, "")
|
||||
}
|
||||
mCallBack?.invoke(true)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
@ -90,27 +93,27 @@ class InstallPermissionDialogFragment : BaseTrackableDialogFragment() {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun show(activity: AppCompatActivity, downloadEntity: DownloadEntity, callBack: (() -> Unit)?) {
|
||||
fun show(activity: AppCompatActivity, downloadEntity: DownloadEntity, callBack: ((isFromPermissionGrantedCallback: Boolean) -> Unit)?) {
|
||||
val isXapk = XapkInstaller.XAPK_EXTENSION_NAME == downloadEntity.path.getExtension()
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
callBack?.invoke()
|
||||
callBack?.invoke(false)
|
||||
return
|
||||
}
|
||||
val haveInstallPermission = activity.packageManager.canRequestPackageInstalls()
|
||||
if (haveInstallPermission) {
|
||||
callBack?.invoke()
|
||||
callBack?.invoke(false)
|
||||
return
|
||||
}
|
||||
if (isXapk) {
|
||||
val xapkUnzipVersions = Config.getSettings()?.permissionPopupAppliedVersions?.xapkUnzip
|
||||
if (xapkUnzipVersions?.contains(Build.VERSION.SDK_INT.toString()) == false) {
|
||||
callBack?.invoke()
|
||||
callBack?.invoke(false)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
val installVersions = Config.getSettings()?.permissionPopupAppliedVersions?.install
|
||||
if (installVersions?.contains(Build.VERSION.SDK_INT.toString()) == false) {
|
||||
callBack?.invoke()
|
||||
callBack?.invoke(false)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package com.gh.common.dialog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
@ -15,10 +16,10 @@ import com.gh.common.util.PermissionHelper
|
||||
import com.gh.common.util.fromHtml
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.DialogNotificationHintBinding
|
||||
import com.gh.gamecenter.entity.NotificationStyleEntity
|
||||
import com.gh.gamecenter.entity.NotificationUgc
|
||||
import com.lightgame.utils.Utils
|
||||
import kotlinx.android.synthetic.main.dialog_notification_hint.*
|
||||
import org.json.JSONArray
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
@ -29,9 +30,10 @@ import kotlin.random.Random
|
||||
class NotificationHintDialogFragment : BaseTrackableDialogFragment() {
|
||||
|
||||
private var mNotificationUgc: NotificationUgc? = null
|
||||
private val mBinding: DialogNotificationHintBinding by lazy { DialogNotificationHintBinding.inflate(layoutInflater) }
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.dialog_notification_hint, null)
|
||||
return mBinding.root
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@ -54,35 +56,41 @@ class NotificationHintDialogFragment : BaseTrackableDialogFragment() {
|
||||
val styleEntityJson = jsonObj.getJSONObject(mNotificationUgc!!.value)
|
||||
val styleEntity = GsonUtils.fromJson(styleEntityJson.toString(), NotificationStyleEntity::class.java)
|
||||
val drawableId = resources.getIdentifier(styleEntity.image, "drawable", requireContext().packageName)
|
||||
notificationIv.setImageDrawable(ContextCompat.getDrawable(requireContext(), drawableId))
|
||||
notificationTitle.text = styleEntity.title
|
||||
notificationContent.text = styleEntity.content.fromHtml()
|
||||
if (index == 0) {
|
||||
closeIv.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_notification_close_1))
|
||||
} else {
|
||||
activateTv.background = ContextCompat.getDrawable(requireContext(), R.drawable.bg_notification_open_btn_style_1)
|
||||
activateTv.text = "优雅的开启"
|
||||
}
|
||||
|
||||
activateTv.setOnClickListener {
|
||||
MtaHelper.onEventWithBasicDeviceInfo(getEvent(), getKey(), "点击立即开启")
|
||||
MtaHelper.onEventWithBasicDeviceInfo(getEvent(), getKey(), "${styleEntity.scenes}_${styleEntity.styleNo}_点击立即开启")
|
||||
dismiss()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
//这种方案适用于 API 26, 即8.0(含8.0)以上可以用
|
||||
val intent = Intent()
|
||||
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID)
|
||||
startActivity(intent)
|
||||
mBinding.run {
|
||||
notificationIv.setImageDrawable(ContextCompat.getDrawable(requireContext(), drawableId))
|
||||
notificationTitle.text = styleEntity.title
|
||||
notificationContent.text = styleEntity.content.fromHtml()
|
||||
if (index == 0) {
|
||||
closeIv.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_notification_close_1))
|
||||
} else {
|
||||
PermissionHelper.toPermissionSetting(requireActivity())
|
||||
activateTv.background = ContextCompat.getDrawable(requireContext(), R.drawable.bg_notification_open_btn_style_1)
|
||||
activateTv.text = "优雅的开启"
|
||||
}
|
||||
}
|
||||
|
||||
closeIv.setOnClickListener {
|
||||
dismiss()
|
||||
MtaHelper.onEventWithBasicDeviceInfo(getEvent(), getKey(), "点击关闭")
|
||||
MtaHelper.onEventWithBasicDeviceInfo(getEvent(), getKey(), "${styleEntity.scenes}_${styleEntity.styleNo}_点击关闭")
|
||||
activateTv.setOnClickListener {
|
||||
MtaHelper.onEventWithBasicDeviceInfo(getEvent(), getKey(), "点击立即开启")
|
||||
MtaHelper.onEventWithBasicDeviceInfo(getEvent(), getKey(), "${styleEntity.scenes}_${styleEntity.styleNo}_点击立即开启")
|
||||
dismissAllowingStateLoss()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
//这种方案适用于 API 26, 即8.0(含8.0)以上可以用
|
||||
val intent = Intent()
|
||||
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID)
|
||||
try {
|
||||
startActivity(intent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
PermissionHelper.toPermissionSetting(requireActivity())
|
||||
}
|
||||
} else {
|
||||
PermissionHelper.toPermissionSetting(requireActivity())
|
||||
}
|
||||
}
|
||||
|
||||
closeIv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
MtaHelper.onEventWithBasicDeviceInfo(getEvent(), getKey(), "点击关闭")
|
||||
MtaHelper.onEventWithBasicDeviceInfo(getEvent(), getKey(), "${styleEntity.scenes}_${styleEntity.styleNo}_点击关闭")
|
||||
}
|
||||
}
|
||||
|
||||
dialog?.setCanceledOnTouchOutside(true)
|
||||
|
||||
@ -1,30 +1,39 @@
|
||||
package com.gh.common.dialog
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageInfo
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.base.BaseRecyclerViewHolder
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.view.CustomLinkMovementMethod
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.FragmentPackageCheckBinding
|
||||
import com.gh.gamecenter.databinding.PackageCheckItemBinding
|
||||
import com.gh.gamecenter.entity.DetectionObjectEntity
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.LinkEntity
|
||||
import com.gh.gamecenter.entity.PackageDialogEntity
|
||||
import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
import com.lightgame.dialog.BaseDialogFragment
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.download.DownloadStatus
|
||||
import io.reactivex.disposables.Disposable
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
@ -33,6 +42,7 @@ import org.greenrobot.eventbus.ThreadMode
|
||||
/**
|
||||
* 包名检测弹窗
|
||||
*/
|
||||
// TODO 将 gameEntity 放到 argument 里再取出,避免重建时为空
|
||||
class PackageCheckDialogFragment : BaseDialogFragment() {
|
||||
|
||||
private lateinit var binding: FragmentPackageCheckBinding
|
||||
@ -40,12 +50,35 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
|
||||
private val mDuration = 3000
|
||||
private var mDisposable: Disposable? = null
|
||||
private var mAdapter: PackageCheckAdapter? = null
|
||||
var packageDialogEntity: PackageDialogEntity? = null
|
||||
private var mAllInstalledPackages = PackageUtils.getInstalledPackages(HaloApp.getInstance().application, 0)
|
||||
var gameEntity: GameEntity? = null
|
||||
var callBack: DialogUtils.ConfirmListener? = null
|
||||
|
||||
private val dataWatcher = object : DataWatcher() {
|
||||
override fun onDataChanged(downloadEntity: DownloadEntity) {
|
||||
val packageName = downloadEntity.packageName
|
||||
val detectionObjects = gameEntity?.packageDialog?.detectionObjects
|
||||
if (DownloadStatus.add == downloadEntity.status || DownloadStatus.done == downloadEntity.status) {
|
||||
detectionObjects?.forEach { detectionObject ->
|
||||
if (detectionObject.packages.contains(packageName)) {
|
||||
val packageLink = gameEntity?.packageDialog?.links?.find { it.buttonLink }
|
||||
LogUtils.uploadPackageCheck(
|
||||
"pkg_check_pop_download", if (DownloadStatus.add == downloadEntity.status) "下载开始" else "下载完成",
|
||||
gameEntity, packageLink?.text ?: "", packageLink?.title
|
||||
?: "", downloadEntity.gameId, downloadEntity.getMetaExtra(Constants.GAME_NAME)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
EventBus.getDefault().register(this)
|
||||
gameEntity?.let {
|
||||
LogUtils.uploadPackageCheck("pkg_check_pop_click", "出现弹窗", it, "", "", "", "")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
@ -55,7 +88,7 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
packageDialogEntity?.let {
|
||||
gameEntity?.packageDialog?.let {
|
||||
changeParams(it.detectionObjects.size)
|
||||
|
||||
binding.packageRv.layoutManager = LinearLayoutManager(requireContext())
|
||||
@ -67,8 +100,11 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
|
||||
|
||||
val spanBuilder = SpanBuilder(it.linkHintText).build()
|
||||
it.links.forEachIndexed { index, link ->
|
||||
val linkSpan = SpanBuilder(link.title ?: "").click(0, (link.title
|
||||
?: "").length, R.color.theme_font, true) {
|
||||
val linkSpan = SpanBuilder(link.title ?: "").click(
|
||||
0, (link.title
|
||||
?: "").length, R.color.theme_font, true
|
||||
) {
|
||||
LogUtils.uploadPackageCheck("pkg_check_pop_click", "点击链接", gameEntity, link.text, link.title, "", "")
|
||||
DirectUtils.directToLinkPage(requireContext(), link, "包名检测弹窗", "")
|
||||
}.build()
|
||||
spanBuilder.append(linkSpan)
|
||||
@ -88,6 +124,10 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
|
||||
binding.cancelTv.text = "我知道了"
|
||||
binding.noRemindAgainCb.visibility = View.GONE
|
||||
}
|
||||
"OPTIONAL_CURRENT_HINT" -> {
|
||||
binding.cancelTv.text = "我知道了"
|
||||
binding.noRemindAgainCb.visibility = View.VISIBLE
|
||||
}
|
||||
else -> {
|
||||
binding.cancelTv.text = "我知道了"
|
||||
binding.noRemindAgainCb.visibility = View.VISIBLE
|
||||
@ -95,7 +135,9 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
|
||||
}
|
||||
initListener(it)
|
||||
}
|
||||
checkPackage()
|
||||
binding.root.post {
|
||||
checkPackage()
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeParams(size: Int) {
|
||||
@ -107,15 +149,19 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
|
||||
private fun initListener(entity: PackageDialogEntity) {
|
||||
binding.downloadBtn.setOnClickListener {
|
||||
if (binding.noRemindAgainCb.isChecked) {
|
||||
SPUtils.setBoolean(Constants.SP_PACKAGE_CHECK, true)
|
||||
saveRecord(entity)
|
||||
}
|
||||
val isAllPackageInstalled = isAllPackageInstalled(entity)
|
||||
val isAllPackageInstalled = isAllPackageInstalled(mAllInstalledPackages, entity)
|
||||
if (isAllPackageInstalled) {
|
||||
callBack?.onConfirm()
|
||||
dismissAllowingStateLoss()
|
||||
} else {
|
||||
val packageLink = entity.links.find { it.buttonLink }
|
||||
var packageLink = getNotInstalledLink(entity)
|
||||
if (packageLink == null) {
|
||||
packageLink = entity.links.find { it.buttonLink }
|
||||
}
|
||||
if (packageLink != null) {
|
||||
LogUtils.uploadPackageCheck("pkg_check_pop_click", "点击前往下载", gameEntity, packageLink.text, packageLink.title, "", "")
|
||||
DirectUtils.directToLinkPage(requireContext(), packageLink, "包名检测弹窗", "")
|
||||
}
|
||||
}
|
||||
@ -126,22 +172,26 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
|
||||
callBack?.onConfirm()
|
||||
}
|
||||
if (binding.noRemindAgainCb.isChecked) {
|
||||
SPUtils.setBoolean(Constants.SP_PACKAGE_CHECK, true)
|
||||
saveRecord(entity)
|
||||
LogUtils.uploadPackageCheck("pkg_check_pop_click", "不再提示", gameEntity, "", "", "", "")
|
||||
}
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveRecord(entity: PackageDialogEntity) {
|
||||
if (entity.level == "OPTIONAL_CURRENT_HINT") {
|
||||
SPUtils.setBoolean("${Constants.SP_PACKAGE_CHECK}:${gameEntity?.id}", true)
|
||||
} else {
|
||||
SPUtils.setBoolean("${Constants.SP_PACKAGE_CHECK}:${gameEntity?.packageDialog?.id}", true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkPackage() {
|
||||
var index = 0
|
||||
mTotalWidth = (DisplayUtils.getScreenWidth() - 108f.dip2px()).toFloat()
|
||||
mDisposable = rxTimer(1) {
|
||||
val width = (mTotalWidth / mDuration) * it
|
||||
val params = binding.progressView.layoutParams as RelativeLayout.LayoutParams
|
||||
params.width = width.toInt()
|
||||
binding.progressView.layoutParams = params
|
||||
|
||||
packageDialogEntity?.detectionObjects?.let { objects ->
|
||||
gameEntity?.packageDialog?.detectionObjects?.let { objects ->
|
||||
if (objects.isNotEmpty()) {
|
||||
val averageTime = if (objects.size == 1) {
|
||||
mDuration
|
||||
@ -159,24 +209,50 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
|
||||
if (it >= mDuration) {
|
||||
mDisposable?.dispose()
|
||||
binding.downloadBtn.isEnabled = true
|
||||
binding.progressText.text = "检测完成"
|
||||
binding.progressView.background = ContextCompat.getDrawable(requireContext(), R.drawable.package_check_complete_bg)
|
||||
binding.downloadBtn.background = R.drawable.bg_notification_open_btn_style_2.toDrawable()
|
||||
}
|
||||
}
|
||||
val animator = ValueAnimator.ofInt(0, 100)
|
||||
animator.duration = mDuration.toLong()
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.addUpdateListener {
|
||||
binding.progressBar.progress = it.animatedValue as Int
|
||||
}
|
||||
animator.start()
|
||||
}
|
||||
|
||||
private fun getNotInstalledLink(packageDialogEntity: PackageDialogEntity): LinkEntity? {
|
||||
val links = LinkedHashSet<LinkEntity>()
|
||||
packageDialogEntity.detectionObjects.forEach { obj ->
|
||||
if (!checkDetectionsInstalled(mAllInstalledPackages, obj.packages)) {
|
||||
obj.assignDownload.forEach {
|
||||
links.add(packageDialogEntity.links[it])
|
||||
}
|
||||
}
|
||||
}
|
||||
var link: LinkEntity? = null
|
||||
if (links.size > 1) {
|
||||
link = links.find { it.buttonLink } ?: links.toList()[0]
|
||||
} else if (links.size == 1) {
|
||||
link = links.toList()[0]
|
||||
}
|
||||
return link
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val width = requireContext().resources.displayMetrics.widthPixels - 60F.dip2px()
|
||||
val height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
dialog?.window?.setLayout(width, height)
|
||||
dialog?.setCanceledOnTouchOutside(true)
|
||||
requireDialog().window?.setLayout(width, height)
|
||||
requireDialog().setCanceledOnTouchOutside(true)
|
||||
DownloadManager.getInstance().addObserver(dataWatcher)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
packageDialogEntity?.let {
|
||||
if (isAllPackageInstalled(it)) {
|
||||
mAllInstalledPackages = PackageUtils.getInstalledPackages(HaloApp.getInstance().application, 0)
|
||||
gameEntity?.packageDialog?.let {
|
||||
if (isAllPackageInstalled(mAllInstalledPackages, it)) {
|
||||
callBack?.onConfirm()
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
@ -189,26 +265,30 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
|
||||
if (mDisposable?.isDisposed == false) {
|
||||
mDisposable?.dispose()
|
||||
}
|
||||
LogUtils.uploadPackageCheck("pkg_check_pop_click", "关闭弹窗", gameEntity, "", "", "", "")
|
||||
DownloadManager.getInstance().removeObserver(dataWatcher)
|
||||
}
|
||||
|
||||
//安装、卸载事件
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(busFour: EBPackage) {
|
||||
if ("安装" == busFour.type || "卸载" == busFour.type) {
|
||||
mAllInstalledPackages = PackageUtils.getInstalledPackages(HaloApp.getInstance().application, 0)
|
||||
mAdapter?.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
class PackageCheckAdapter(val context: Context, val entities: ArrayList<DetectionObjectEntity>) : BaseRecyclerAdapter<RecyclerView.ViewHolder>(context) {
|
||||
inner class PackageCheckAdapter(val context: Context, val entities: ArrayList<DetectionObjectEntity>) :
|
||||
BaseRecyclerAdapter<RecyclerView.ViewHolder>(context) {
|
||||
private var index = -1
|
||||
|
||||
fun notifyPackages() {
|
||||
index++
|
||||
notifyDataSetChanged()
|
||||
notifyItemChanged(index)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return PackageCheckViewHolder(PackageCheckItemBinding.bind(LayoutInflater.from(context).inflate(R.layout.package_check_item, parent, false)))
|
||||
return PackageCheckViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = entities.size
|
||||
@ -216,22 +296,15 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (holder is PackageCheckViewHolder) {
|
||||
val entity = entities[position]
|
||||
holder.binding.entity = entity
|
||||
holder.binding.gameNameTv.text = entity.text
|
||||
if (position <= index) {
|
||||
var isAllInstalled = false
|
||||
entity.packages.forEach { packageName ->
|
||||
val isInstalled = PackageUtils.getInstalledPackages(context, 0).find { it.packageName == packageName } != null
|
||||
if (isInstalled) {
|
||||
isAllInstalled = true
|
||||
return@forEach
|
||||
}
|
||||
}
|
||||
val isAllInstalled = checkDetectionsInstalled(mAllInstalledPackages, entity.packages)
|
||||
if (isAllInstalled) {
|
||||
holder.binding.statusTv.text = "已安装"
|
||||
holder.binding.statusTv.setTextColor(ContextCompat.getColor(context, R.color.theme_font))
|
||||
} else {
|
||||
holder.binding.statusTv.text = "未安装"
|
||||
holder.binding.statusTv.setTextColor(ContextCompat.getColor(context, R.color.text_FF4147))
|
||||
holder.binding.statusTv.setTextColor(ContextCompat.getColor(context, R.color.theme_red))
|
||||
}
|
||||
holder.binding.statusTv.visibility = View.VISIBLE
|
||||
} else {
|
||||
@ -245,56 +318,66 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun show(activity: AppCompatActivity, packageDialogEntity: PackageDialogEntity?, callBack: DialogUtils.ConfirmListener) {
|
||||
fun show(activity: AppCompatActivity, gameEntity: GameEntity, callBack: DialogUtils.ConfirmListener) {
|
||||
val packageDialogEntity = gameEntity.packageDialog
|
||||
if (packageDialogEntity == null) {
|
||||
callBack.onConfirm()
|
||||
return
|
||||
}
|
||||
if (isAllPackageInstalled(packageDialogEntity)) {
|
||||
|
||||
val allInstalledPackages = PackageUtils.getInstalledPackages(HaloApp.getInstance().application, 0)
|
||||
if (isAllPackageInstalled(allInstalledPackages, packageDialogEntity)) {
|
||||
callBack.onConfirm()
|
||||
return
|
||||
}
|
||||
val isChoose = SPUtils.getBoolean(Constants.SP_PACKAGE_CHECK, false)
|
||||
|
||||
val isChoose = SPUtils.getBoolean("${Constants.SP_PACKAGE_CHECK}:${packageDialogEntity.id}", false)
|
||||
if (packageDialogEntity.level == "OPTIONAL_HINT" && isChoose) {
|
||||
callBack.onConfirm()
|
||||
return
|
||||
}
|
||||
|
||||
var dialogFragment = activity.supportFragmentManager.findFragmentByTag(PackageCheckDialogFragment::class.java.simpleName) as? PackageCheckDialogFragment
|
||||
val isCurrentGameChoose = SPUtils.getBoolean("${Constants.SP_PACKAGE_CHECK}:${gameEntity.id}", false)
|
||||
if (packageDialogEntity.level == "OPTIONAL_CURRENT_HINT" && isCurrentGameChoose) {
|
||||
callBack.onConfirm()
|
||||
return
|
||||
}
|
||||
|
||||
if (!activity.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) return
|
||||
|
||||
var dialogFragment =
|
||||
activity.supportFragmentManager.findFragmentByTag(PackageCheckDialogFragment::class.java.simpleName) as? PackageCheckDialogFragment
|
||||
if (dialogFragment == null) {
|
||||
dialogFragment = PackageCheckDialogFragment()
|
||||
dialogFragment.packageDialogEntity = packageDialogEntity
|
||||
dialogFragment.gameEntity = gameEntity
|
||||
dialogFragment.callBack = callBack
|
||||
|
||||
dialogFragment.show(activity.supportFragmentManager, PackageCheckDialogFragment::class.java.simpleName)
|
||||
} else {
|
||||
dialogFragment.packageDialogEntity = packageDialogEntity
|
||||
dialogFragment.gameEntity = gameEntity
|
||||
dialogFragment.callBack = callBack
|
||||
|
||||
val transaction: FragmentTransaction = activity.supportFragmentManager.beginTransaction()
|
||||
transaction.show(dialogFragment)
|
||||
transaction.commit()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun isAllPackageInstalled(packageDialogEntity: PackageDialogEntity): Boolean {
|
||||
var isAllInstalled = true
|
||||
|
||||
val isPackagesInstall: (ArrayList<String>) -> Boolean = { packages ->
|
||||
var isPackagesInstalled = false
|
||||
packages.forEach {packageName->
|
||||
val isInstalled = PackageUtils.getInstalledPackages(HaloApp.getInstance().application, 0).find { it.packageName == packageName } != null
|
||||
if (isInstalled) {
|
||||
isPackagesInstalled = true
|
||||
return@forEach
|
||||
}
|
||||
private fun checkDetectionsInstalled(allInstalledPackages: List<PackageInfo>, packages: ArrayList<String>): Boolean {
|
||||
var isPackagesInstalled = false
|
||||
packages.forEach { packageName ->
|
||||
val isInstalled = allInstalledPackages.find { it.packageName == packageName } != null
|
||||
if (isInstalled) {
|
||||
isPackagesInstalled = true
|
||||
return@forEach
|
||||
}
|
||||
isPackagesInstalled
|
||||
}
|
||||
return isPackagesInstalled
|
||||
}
|
||||
|
||||
|
||||
fun isAllPackageInstalled(allInstalledPackages: List<PackageInfo>, packageDialogEntity: PackageDialogEntity): Boolean {
|
||||
var isAllInstalled = true
|
||||
packageDialogEntity.detectionObjects.forEach loop@{ obj ->
|
||||
if (!isPackagesInstall(obj.packages)) {
|
||||
if (!checkDetectionsInstalled(allInstalledPackages, obj.packages)) {
|
||||
isAllInstalled = false
|
||||
return isAllInstalled
|
||||
}
|
||||
|
||||
@ -1,155 +0,0 @@
|
||||
package com.gh.common.dialog
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned
|
||||
import android.text.TextPaint
|
||||
import android.text.method.ScrollingMovementMethod
|
||||
import android.text.style.ClickableSpan
|
||||
import android.view.*
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import com.gh.base.fragment.BaseDialogFragment
|
||||
import com.gh.common.util.DirectUtils.directToExternalBrowser
|
||||
import com.gh.common.util.dip2px
|
||||
import com.gh.common.view.CustomLinkMovementMethod
|
||||
import com.gh.gamecenter.R
|
||||
|
||||
class PrivacyDialogFragment : BaseDialogFragment() {
|
||||
|
||||
// private val mLocalPrivacyHtml = "file:///android_asset/privacy_policies.html"
|
||||
// private val mLocalRegulationHtml = "file:///android_asset/user_regulation.html"
|
||||
|
||||
var containerView: View? = null
|
||||
var mCallBack: ((isSuccess: Boolean) -> Unit)? = null
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
containerView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_privacy_protocol, null, false)
|
||||
return containerView
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
// val mWebViewPrivacy = containerView?.findViewById<WebView>(R.id.webView)
|
||||
// val mWebViewRegulation = containerView?.findViewById<WebView>(R.id.webView2)
|
||||
//
|
||||
// mWebViewPrivacy?.isHorizontalScrollBarEnabled = false
|
||||
// mWebViewRegulation?.isHorizontalScrollBarEnabled = false
|
||||
|
||||
val contentTv = containerView?.findViewById<TextView>(R.id.contentTv)
|
||||
val descTv = containerView?.findViewById<TextView>(R.id.descTv)
|
||||
contentTv?.movementMethod = ScrollingMovementMethod()
|
||||
|
||||
val skipText = SpannableStringBuilder("查看完整版的隐私政策和用户协议")
|
||||
skipText.setSpan(object : ClickableSpan() {
|
||||
override fun updateDrawState(ds: TextPaint) {
|
||||
super.updateDrawState(ds)
|
||||
ds.color = ContextCompat.getColor(requireContext(), R.color.theme_font)
|
||||
ds.isUnderlineText = false
|
||||
}
|
||||
|
||||
override fun onClick(widget: View) {
|
||||
directToExternalBrowser(context!!, context!!.getString(R.string.privacy_policy_url))
|
||||
}
|
||||
}, skipText.length - 9, skipText.length - 5, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
|
||||
skipText.setSpan(object : ClickableSpan() {
|
||||
override fun updateDrawState(ds: TextPaint) {
|
||||
super.updateDrawState(ds)
|
||||
ds.color = ContextCompat.getColor(requireContext(), R.color.theme_font)
|
||||
ds.isUnderlineText = false
|
||||
}
|
||||
|
||||
override fun onClick(widget: View) {
|
||||
directToExternalBrowser(requireContext(), requireContext().getString(R.string.disclaimer_url))
|
||||
}
|
||||
}, skipText.length - 4, skipText.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
descTv?.movementMethod = CustomLinkMovementMethod()
|
||||
descTv?.text = skipText
|
||||
|
||||
// val mWebViewPrivacy = containerView?.findViewById<WebView>(R.id.webView)
|
||||
//
|
||||
// mWebViewPrivacy?.isHorizontalScrollBarEnabled = false
|
||||
//
|
||||
// val settingsArrayList = arrayListOf(mWebViewPrivacy?.settings, mWebViewRegulation?.settings)
|
||||
//
|
||||
// for (settings in settingsArrayList) {
|
||||
// settings?.javaScriptEnabled = true
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
// settings?.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
|
||||
// }
|
||||
// // 避免提示网页有害信息不能访问
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// settings?.safeBrowsingEnabled = false
|
||||
// }
|
||||
//
|
||||
// // 适配大于屏幕宽度的页面
|
||||
// settings?.useWideViewPort = true
|
||||
// settings?.loadWithOverviewMode = true
|
||||
// settings?.domStorageEnabled = true
|
||||
//
|
||||
// // 自适应屏幕
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
// settings?.layoutAlgorithm = WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
|
||||
// }
|
||||
// }
|
||||
|
||||
// mWebViewPrivacy?.webViewClient = client
|
||||
// mWebViewRegulation?.webViewClient = client
|
||||
|
||||
containerView?.findViewById<View>(R.id.refuseTv)?.setOnClickListener {
|
||||
mCallBack?.invoke(false)
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
containerView?.findViewById<View>(R.id.agreeTv)?.setOnClickListener {
|
||||
mCallBack?.invoke(true)
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val createDialog = super.onCreateDialog(savedInstanceState)
|
||||
createDialog.setCanceledOnTouchOutside(false)
|
||||
createDialog.setOnKeyListener(object : DialogInterface.OnKeyListener {
|
||||
override fun onKey(dialog: DialogInterface?, keyCode: Int, event: KeyEvent?): Boolean {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
val window = createDialog.window
|
||||
window?.setGravity(Gravity.CENTER)
|
||||
return createDialog
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val width = requireContext().resources.displayMetrics.widthPixels - 60F.dip2px()
|
||||
val height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
dialog?.window?.setLayout(width, height)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun show(activity: AppCompatActivity, callBack: ((isSuccess: Boolean) -> Unit)?) {
|
||||
var privacyDialogFragment = activity.supportFragmentManager.findFragmentByTag(PrivacyDialogFragment::class.java.simpleName) as? PrivacyDialogFragment
|
||||
if (privacyDialogFragment != null) {
|
||||
privacyDialogFragment.mCallBack = callBack
|
||||
val transaction: FragmentTransaction = activity.supportFragmentManager.beginTransaction()
|
||||
transaction.show(privacyDialogFragment)
|
||||
transaction.commit()
|
||||
} else {
|
||||
privacyDialogFragment = PrivacyDialogFragment().apply {
|
||||
mCallBack = callBack
|
||||
}
|
||||
privacyDialogFragment.show(activity.supportFragmentManager, PrivacyDialogFragment::class.java.simpleName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,190 @@
|
||||
package com.gh.common.dialog
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned
|
||||
import android.text.TextPaint
|
||||
import android.text.method.ScrollingMovementMethod
|
||||
import android.text.style.ClickableSpan
|
||||
import android.view.*
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import com.gh.base.fragment.BaseDialogFragment
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.SPUtils
|
||||
import com.gh.common.util.dip2px
|
||||
import com.gh.common.util.fromHtml
|
||||
import com.gh.common.view.CustomLinkMovementMethod
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.WebActivity
|
||||
import com.gh.gamecenter.databinding.DialogPrivacyProtocolBinding
|
||||
import com.gh.gamecenter.entity.DialogEntity
|
||||
import com.lightgame.utils.AppManager
|
||||
import splitties.bundle.put
|
||||
|
||||
class PrivacyPolicyDialogFragment : BaseDialogFragment() {
|
||||
|
||||
private var mCallBack: ((isSuccess: Boolean) -> Unit)? = null
|
||||
private val mBinding by lazy { DialogPrivacyProtocolBinding.inflate(layoutInflater) }
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
return mBinding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mBinding.contentTv.movementMethod = ScrollingMovementMethod()
|
||||
|
||||
val privacyPolicyEntity = arguments?.get(KEY_DATA) as? DialogEntity.PrivacyPolicyEntity
|
||||
if (privacyPolicyEntity == null) {
|
||||
showPreLaunchStyle()
|
||||
} else {
|
||||
showUpdatedStyle(privacyPolicyEntity)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showUpdatedStyle(privacyPolicyEntity: DialogEntity.PrivacyPolicyEntity) {
|
||||
mBinding.titleIv.visibility = View.VISIBLE
|
||||
mBinding.privacyTitleTv.text = "光环助手《隐私协议》更新"
|
||||
mBinding.contentTv.text = privacyPolicyEntity.content.fromHtml()
|
||||
|
||||
val skipText = SpannableStringBuilder("查看隐私政策详情")
|
||||
skipText.setSpan(object : ClickableSpan() {
|
||||
override fun updateDrawState(ds: TextPaint) {
|
||||
super.updateDrawState(ds)
|
||||
ds.color = ContextCompat.getColor(requireContext(), R.color.theme_font)
|
||||
ds.isUnderlineText = false
|
||||
}
|
||||
|
||||
override fun onClick(widget: View) {
|
||||
val intent = WebActivity.getIntent(requireContext(), context!!.getString(R.string.privacy_policy_url), true)
|
||||
context?.startActivity(intent)
|
||||
}
|
||||
}, skipText.length - 6, skipText.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
mBinding.descTv.movementMethod = CustomLinkMovementMethod()
|
||||
mBinding.descTv.text = skipText
|
||||
|
||||
if (privacyPolicyEntity.alertType == "INFORM") {
|
||||
mBinding.refuseTv.visibility = View.GONE
|
||||
mBinding.agreeTv.text = "我知道了"
|
||||
mBinding.agreeTv.setOnClickListener {
|
||||
SPUtils.setString(Constants.SP_LAST_ACCEPTED_PRIVACY_DIALOG_ID, privacyPolicyEntity.id)
|
||||
mCallBack?.invoke(true)
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
} else {
|
||||
mBinding.refuseTv.text = "不同意退出"
|
||||
mBinding.agreeTv.text = "同意"
|
||||
mBinding.refuseTv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
AppManager.getInstance().finishAllActivity()
|
||||
}
|
||||
mBinding.agreeTv.setOnClickListener {
|
||||
SPUtils.setString(Constants.SP_LAST_ACCEPTED_PRIVACY_DIALOG_ID, privacyPolicyEntity.id)
|
||||
mCallBack?.invoke(true)
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showPreLaunchStyle() {
|
||||
val skipText = SpannableStringBuilder("查看完整版的隐私政策和用户协议")
|
||||
|
||||
skipText.setSpan(object : ClickableSpan() {
|
||||
override fun updateDrawState(ds: TextPaint) {
|
||||
super.updateDrawState(ds)
|
||||
ds.color = ContextCompat.getColor(requireContext(), R.color.theme_font)
|
||||
ds.isUnderlineText = false
|
||||
}
|
||||
|
||||
override fun onClick(widget: View) {
|
||||
val intent = WebActivity.getIntent(requireContext(), context!!.getString(R.string.privacy_policy_url), true)
|
||||
context?.startActivity(intent)
|
||||
}
|
||||
}, skipText.length - 9, skipText.length - 5, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
|
||||
skipText.setSpan(object : ClickableSpan() {
|
||||
override fun updateDrawState(ds: TextPaint) {
|
||||
super.updateDrawState(ds)
|
||||
ds.color = ContextCompat.getColor(requireContext(), R.color.theme_font)
|
||||
ds.isUnderlineText = false
|
||||
}
|
||||
|
||||
override fun onClick(widget: View) {
|
||||
val intent = WebActivity.getIntent(requireContext(), context!!.getString(R.string.disclaimer_url), true)
|
||||
context?.startActivity(intent)
|
||||
}
|
||||
}, skipText.length - 4, skipText.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
mBinding.descTv.movementMethod = CustomLinkMovementMethod()
|
||||
mBinding.descTv.text = skipText
|
||||
|
||||
mBinding.refuseTv.setOnClickListener {
|
||||
mCallBack?.invoke(false)
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
mBinding.agreeTv.setOnClickListener {
|
||||
mCallBack?.invoke(true)
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val createDialog = super.onCreateDialog(savedInstanceState)
|
||||
createDialog.setCanceledOnTouchOutside(false)
|
||||
createDialog.setOnKeyListener(object : DialogInterface.OnKeyListener {
|
||||
override fun onKey(dialog: DialogInterface?, keyCode: Int, event: KeyEvent?): Boolean {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
val window = createDialog.window
|
||||
window?.setGravity(Gravity.CENTER)
|
||||
return createDialog
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val width = requireContext().resources.displayMetrics.widthPixels - 60F.dip2px()
|
||||
val height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
dialog?.window?.setLayout(width, height)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY_DATA = "data"
|
||||
|
||||
@JvmStatic
|
||||
fun show(activity: FragmentActivity,
|
||||
privacyPolicyEntity: DialogEntity.PrivacyPolicyEntity? = null,
|
||||
callBack: ((isSuccess: Boolean) -> Unit)?) {
|
||||
var privacyDialogFragment = activity.supportFragmentManager.findFragmentByTag(PrivacyPolicyDialogFragment::class.java.simpleName) as? PrivacyPolicyDialogFragment
|
||||
if (privacyDialogFragment != null) {
|
||||
privacyDialogFragment.mCallBack = callBack
|
||||
val transaction: FragmentTransaction = activity.supportFragmentManager.beginTransaction()
|
||||
transaction.show(privacyDialogFragment)
|
||||
transaction.commit()
|
||||
} else {
|
||||
privacyDialogFragment = PrivacyPolicyDialogFragment().apply {
|
||||
mCallBack = callBack
|
||||
}
|
||||
}
|
||||
privacyDialogFragment.arguments = Bundle().apply {
|
||||
put(KEY_DATA, privacyPolicyEntity)
|
||||
}
|
||||
privacyDialogFragment.show(
|
||||
activity.supportFragmentManager,
|
||||
PrivacyPolicyDialogFragment::class.java.simpleName
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,379 +1,384 @@
|
||||
package com.gh.common.dialog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.os.Bundle
|
||||
import android.text.Html
|
||||
import android.view.*
|
||||
import android.view.animation.AnimationUtils
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import butterknife.OnClick
|
||||
import com.gh.base.fragment.BaseDialogFragment
|
||||
import com.gh.common.AppExecutor
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.common.history.HistoryHelper
|
||||
import com.gh.common.repository.ReservationRepository
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.NotificationUgc
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
||||
// 预约弹窗
|
||||
class ReserveDialogFragment
|
||||
: BaseDialogFragment(), KeyboardHeightObserver {
|
||||
// : BaseTrackableDialogFragment() {
|
||||
|
||||
@BindView(R.id.reserve_hint_tv)
|
||||
lateinit var reserveHintTv: TextView
|
||||
|
||||
@BindView(R.id.reserve_content_tv)
|
||||
lateinit var reserveContentTv: TextView
|
||||
|
||||
@BindView(R.id.reserve_completed_content_tv)
|
||||
lateinit var reserveCompletedContentTv: TextView
|
||||
|
||||
@BindView(R.id.mobile_et)
|
||||
lateinit var mobileEt: EditText
|
||||
|
||||
@BindView(R.id.reserve_container)
|
||||
lateinit var reserveContainer: View
|
||||
|
||||
@BindView(R.id.reserve_completed_container)
|
||||
lateinit var reserveCompletedContainer: View
|
||||
|
||||
@BindView(R.id.customizable_btn)
|
||||
lateinit var customizableBtn: TextView
|
||||
|
||||
@BindView(R.id.content_container)
|
||||
lateinit var contentContainer: View
|
||||
|
||||
@BindView(R.id.mobile_index_container)
|
||||
lateinit var mobileIndexContainer: View
|
||||
|
||||
@BindView(R.id.mobile_index_reserve)
|
||||
lateinit var mobileIndexReserve: TextView
|
||||
|
||||
@BindView(R.id.mobile_index_user)
|
||||
lateinit var mobileIndexUser: TextView
|
||||
|
||||
@BindView(R.id.mobile_et_delete)
|
||||
lateinit var mobileEtDelete: View
|
||||
|
||||
@BindView(R.id.layout_container)
|
||||
lateinit var layoutContainer: View
|
||||
|
||||
private lateinit var mViewModel: ReserveViewModel
|
||||
|
||||
private var mSuccessCallback: SuccessCallback? = null
|
||||
|
||||
private var mGame: GameEntity? = null
|
||||
private var mGameId: String = ""
|
||||
private var mGameName: String = ""
|
||||
|
||||
private var mKeyboardHeightProvider: KeyboardHeightProvider? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
mViewModel = viewModelProvider()
|
||||
mKeyboardHeightProvider = KeyboardHeightProvider(activity)
|
||||
mKeyboardHeightProvider?.start()
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.dialog_reserve_game, null)
|
||||
}
|
||||
|
||||
// override fun getEvent(): String {
|
||||
// return "预约游戏"
|
||||
//package com.gh.common.dialog
|
||||
//
|
||||
//import android.annotation.SuppressLint
|
||||
//import android.app.Application
|
||||
//import android.os.Bundle
|
||||
//import android.text.Html
|
||||
//import android.view.*
|
||||
//import android.view.animation.AnimationUtils
|
||||
//import android.widget.EditText
|
||||
//import android.widget.TextView
|
||||
//import androidx.lifecycle.AndroidViewModel
|
||||
//import androidx.lifecycle.MutableLiveData
|
||||
//import androidx.lifecycle.Observer
|
||||
//import butterknife.BindView
|
||||
//import butterknife.ButterKnife
|
||||
//import butterknife.OnClick
|
||||
//import com.gh.base.fragment.BaseDialogFragment
|
||||
//import com.gh.common.AppExecutor
|
||||
//import com.gh.common.constant.Config
|
||||
//import com.gh.common.history.HistoryHelper
|
||||
//import com.gh.common.repository.ReservationRepository
|
||||
//import com.gh.common.util.*
|
||||
//import com.gh.gamecenter.R
|
||||
//import com.gh.gamecenter.entity.GameEntity
|
||||
//import com.gh.gamecenter.entity.NotificationUgc
|
||||
//import com.gh.gamecenter.manager.UserManager
|
||||
//import com.gh.gamecenter.retrofit.BiResponse
|
||||
//import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
//import com.halo.assistant.HaloApp
|
||||
//import com.lightgame.utils.Utils
|
||||
//import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
//import io.reactivex.schedulers.Schedulers
|
||||
//import okhttp3.ResponseBody
|
||||
//import org.json.JSONArray
|
||||
//import org.json.JSONObject
|
||||
//
|
||||
//// 预约弹窗
|
||||
//@Deprecated("v5.6.0废弃")
|
||||
//class ReserveDialogFragment
|
||||
// : BaseDialogFragment(), KeyboardHeightObserver {
|
||||
//// : BaseTrackableDialogFragment() {
|
||||
//
|
||||
// @BindView(R.id.reserve_hint_tv)
|
||||
// lateinit var reserveHintTv: TextView
|
||||
//
|
||||
// @BindView(R.id.reserve_content_tv)
|
||||
// lateinit var reserveContentTv: TextView
|
||||
//
|
||||
// @BindView(R.id.reserve_completed_content_tv)
|
||||
// lateinit var reserveCompletedContentTv: TextView
|
||||
//
|
||||
// @BindView(R.id.mobile_et)
|
||||
// lateinit var mobileEt: EditText
|
||||
//
|
||||
// @BindView(R.id.reserve_container)
|
||||
// lateinit var reserveContainer: View
|
||||
//
|
||||
// @BindView(R.id.reserve_completed_container)
|
||||
// lateinit var reserveCompletedContainer: View
|
||||
//
|
||||
// @BindView(R.id.customizable_btn)
|
||||
// lateinit var customizableBtn: TextView
|
||||
//
|
||||
// @BindView(R.id.content_container)
|
||||
// lateinit var contentContainer: View
|
||||
//
|
||||
// @BindView(R.id.mobile_index_container)
|
||||
// lateinit var mobileIndexContainer: View
|
||||
//
|
||||
// @BindView(R.id.mobile_index_reserve)
|
||||
// lateinit var mobileIndexReserve: TextView
|
||||
//
|
||||
// @BindView(R.id.mobile_index_user)
|
||||
// lateinit var mobileIndexUser: TextView
|
||||
//
|
||||
// @BindView(R.id.mobile_et_delete)
|
||||
// lateinit var mobileEtDelete: View
|
||||
//
|
||||
// @BindView(R.id.layout_container)
|
||||
// lateinit var layoutContainer: View
|
||||
//
|
||||
// private lateinit var mViewModel: ReserveViewModel
|
||||
//
|
||||
// var successCallback: SuccessCallback? = null
|
||||
//
|
||||
// private var mGame: GameEntity? = null
|
||||
// private var mGameId: String = ""
|
||||
// private var mGameName: String = ""
|
||||
//
|
||||
// private var mKeyboardHeightProvider: KeyboardHeightProvider? = null
|
||||
//
|
||||
// override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// super.onCreate(savedInstanceState)
|
||||
//
|
||||
// mGame = requireArguments().getParcelable(EntranceUtils.KEY_GAME)
|
||||
// mGameId = mGame?.id ?:""
|
||||
// mGameName = mGame?.name ?:""
|
||||
//
|
||||
// mViewModel = viewModelProvider()
|
||||
// mKeyboardHeightProvider = KeyboardHeightProvider(activity)
|
||||
// mKeyboardHeightProvider?.start()
|
||||
// }
|
||||
//
|
||||
// override fun getKey(): String {
|
||||
// return "预约功能操作"
|
||||
// override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
// super.onActivityCreated(savedInstanceState)
|
||||
// dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||
// }
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
ButterKnife.bind(this, view)
|
||||
|
||||
val reserveContent = "游戏上线,您将收到<font color='#1383EB'>免费短信</font>提醒"
|
||||
reserveContentTv.text = Html.fromHtml(reserveContent)
|
||||
|
||||
|
||||
mobileEt.setTextChangedListener { s, _, _, _ ->
|
||||
mobileIndexContainer.visibility = View.GONE
|
||||
mobileEtDelete.goneIf(s.trim().isEmpty())
|
||||
}
|
||||
|
||||
mViewModel.reservation.observeNonNull(this) {
|
||||
if (it.success) {
|
||||
showSuccessDialog(it.withMobile, it.boundWechat)
|
||||
mSuccessCallback?.onSuccess()
|
||||
HistoryHelper.insertGameEntity(mGame!!)
|
||||
}
|
||||
}
|
||||
|
||||
mViewModel.reserveMobile.observe(viewLifecycleOwner, Observer {
|
||||
setMobileIndexHint(it)
|
||||
})
|
||||
|
||||
dialog?.setCanceledOnTouchOutside(true)
|
||||
}
|
||||
|
||||
private fun showSuccessDialog(withMobile: Boolean, boundWechat: Boolean) {
|
||||
reserveHintTv.text = "游戏预约成功"
|
||||
reserveContainer.visibility = View.GONE
|
||||
reserveCompletedContainer.visibility = View.VISIBLE
|
||||
|
||||
val reservation = Config.getSettings()?.appointment
|
||||
val dialogConfig = if (withMobile) reservation?.withMobile else reservation?.withoutMobile
|
||||
|
||||
reserveCompletedContentTv.text = dialogConfig?.htmlContent?.fromHtml()
|
||||
if (dialogConfig?.text.isNullOrEmpty()
|
||||
|| (dialogConfig?.type == "wechat_bind" && boundWechat)) {
|
||||
customizableBtn.visibility = View.GONE
|
||||
} else {
|
||||
customizableBtn.text = dialogConfig?.text
|
||||
customizableBtn.setOnClickListener {
|
||||
// MtaHelper.onEvent("预约游戏", "预约功能操作", "点击跳转按钮")
|
||||
DirectUtils.directToLinkPage(
|
||||
requireContext(),
|
||||
dialogConfig!!.toLinkEntity(),
|
||||
"(游戏预约)",
|
||||
"")
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setMobileIndexHint(reserveMobile: String?) {
|
||||
var userMobile = UserManager.getInstance().userInfoEntity?.mobile
|
||||
if (reserveMobile == userMobile) userMobile = null
|
||||
|
||||
if (!reserveMobile.isNullOrEmpty()) {
|
||||
mobileIndexReserve.visibility = View.VISIBLE
|
||||
mobileIndexReserve.text = reserveMobile
|
||||
} else {
|
||||
mobileIndexReserve.visibility = View.GONE
|
||||
}
|
||||
|
||||
if (!userMobile.isNullOrEmpty()) {
|
||||
mobileIndexUser.visibility = View.VISIBLE
|
||||
mobileIndexUser.text = userMobile
|
||||
} else {
|
||||
mobileIndexUser.visibility = View.GONE
|
||||
}
|
||||
mobileIndexContainer.goneIf(mobileIndexUser.visibility == View.GONE && mobileIndexReserve.visibility == View.GONE)
|
||||
if (mobileIndexContainer.visibility ==View.VISIBLE) {
|
||||
mobileIndexContainer.animation = AnimationUtils.loadAnimation(requireContext(), R.anim.reserve_dialog_index_anim)
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.reserve_with_mobile_btn,
|
||||
R.id.reserve_without_mobile_btn,
|
||||
R.id.content_container,
|
||||
R.id.close_btn,
|
||||
R.id.customizable_btn,
|
||||
R.id.mobile_index_reserve,
|
||||
R.id.mobile_index_user,
|
||||
R.id.mobile_et_delete,
|
||||
R.id.mobile_et,
|
||||
R.id.layout_container)
|
||||
fun onClick(view: View) {
|
||||
when (view.id) {
|
||||
R.id.reserve_without_mobile_btn -> {
|
||||
// MtaHelper.onEvent("预约游戏", "预约功能操作", "点击无手机号预约")
|
||||
if (mobileIndexContainer.visibility == View.VISIBLE) {
|
||||
mobileIndexContainer.visibility = View.GONE
|
||||
return
|
||||
}
|
||||
|
||||
mViewModel.reserve(gameId = mGameId, gameName = mGameName)
|
||||
}
|
||||
|
||||
R.id.reserve_with_mobile_btn -> {
|
||||
if (mobileIndexContainer.visibility == View.VISIBLE) {
|
||||
mobileIndexContainer.visibility = View.GONE
|
||||
return
|
||||
}
|
||||
|
||||
val mobile = mobileEt.text.toString()
|
||||
if (mobile.length < 11 || !mobile.startsWith("1")) {
|
||||
Utils.toast(context, "手机号格式错误,请检查并重新输入")
|
||||
return
|
||||
}
|
||||
|
||||
// MtaHelper.onEvent("预约游戏", "预约功能操作", "点击立即预约")
|
||||
mViewModel.reserve(gameId = mGameId, gameName = mGameName, mobile = mobile)
|
||||
}
|
||||
|
||||
R.id.close_btn -> {
|
||||
// MtaHelper.onEvent("预约游戏", "预约功能操作", "点击关闭")
|
||||
dismissAllowingStateLoss()
|
||||
AppExecutor.uiExecutor.executeWithDelay(Runnable {
|
||||
NotificationHelper.showNotificationHintDialog(NotificationUgc.RESERVE_GAME)
|
||||
}, 1000)
|
||||
}
|
||||
R.id.content_container -> {
|
||||
mobileIndexContainer.visibility = View.GONE
|
||||
}
|
||||
R.id.mobile_index_reserve -> {
|
||||
mobileEt.setText(mobileIndexReserve.text.toString())
|
||||
mobileEt.setSelection(mobileEt.text.length)
|
||||
mobileIndexContainer.visibility = View.GONE
|
||||
}
|
||||
R.id.mobile_index_user -> {
|
||||
mobileEt.setText(mobileIndexUser.text.toString())
|
||||
mobileEt.setSelection(mobileEt.text.length)
|
||||
mobileIndexContainer.visibility = View.GONE
|
||||
}
|
||||
R.id.mobile_et_delete -> {
|
||||
mobileEt.setText("")
|
||||
}
|
||||
R.id.mobile_et -> {
|
||||
mobileIndexContainer.visibility = View.GONE
|
||||
}
|
||||
R.id.layout_container -> {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (HaloApp.getInstance().mCacheKeyboardHeight > 0) {
|
||||
val attributes = dialog?.window?.attributes
|
||||
val heightPixels = requireContext().resources.displayMetrics.heightPixels
|
||||
val mCacheKeyboardHeight = HaloApp.getInstance().mCacheKeyboardHeight
|
||||
val statusBarHeight = DisplayUtils.getStatusBarHeight(requireContext().resources)
|
||||
dialog?.window?.attributes?.height = heightPixels - mCacheKeyboardHeight - statusBarHeight
|
||||
attributes?.gravity = Gravity.TOP
|
||||
dialog?.window?.attributes = attributes
|
||||
}
|
||||
mKeyboardHeightProvider?.setKeyboardHeightObserver(this)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
mKeyboardHeightProvider?.setKeyboardHeightObserver(null)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
mKeyboardHeightProvider?.close()
|
||||
}
|
||||
|
||||
override fun onKeyboardHeightChanged(height: Int, orientation: Int) {
|
||||
if (height > 0) {
|
||||
val attributes = dialog?.window?.attributes
|
||||
attributes?.gravity = Gravity.CENTER
|
||||
dialog?.window?.attributes = attributes
|
||||
HaloApp.getInstance().mCacheKeyboardHeight = height
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(gameEntity: GameEntity, successCallback: SuccessCallback) = ReserveDialogFragment().apply {
|
||||
this.mGame = gameEntity
|
||||
this.mGameId = gameEntity.id
|
||||
this.mGameName = gameEntity.name ?: ""
|
||||
this.mSuccessCallback = successCallback
|
||||
}
|
||||
}
|
||||
|
||||
interface SuccessCallback {
|
||||
fun onSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
class ReserveViewModel(application: Application) : AndroidViewModel(application) {
|
||||
val reservation = MutableLiveData<Reservation>()
|
||||
|
||||
val reserveMobile = MutableLiveData<String>()
|
||||
|
||||
init {
|
||||
getAppointmentMobile()
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun reserve(gameId: String, gameName: String, mobile: String = "") {
|
||||
|
||||
val requestMap = hashMapOf<String, String>()
|
||||
requestMap["game_id"] = gameId
|
||||
if (mobile.isNotEmpty()) {
|
||||
requestMap["mobile"] = mobile
|
||||
}
|
||||
|
||||
RetrofitManager.getInstance(getApplication()).api
|
||||
.createNewGameReservation(requestMap.createRequestBody())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
var boundWechat = false
|
||||
tryWithDefaultCatch {
|
||||
boundWechat = JSONObject(data.string() ?: "").getBoolean("wechat_bind")
|
||||
}
|
||||
|
||||
reservation.postValue(Reservation(success = true, withMobile = mobile.isNotEmpty(), boundWechat = boundWechat))
|
||||
ReservationRepository.addReservationToMemoryAndRefresh(gameId)
|
||||
|
||||
// MtaHelper.onEvent("预约游戏", "预约", gameName)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
Utils.toast(getApplication(), exception.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun getAppointmentMobile() {
|
||||
RetrofitManager.getInstance(getApplication()).api
|
||||
.getAppointmentMobile(UserManager.getInstance().userId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
var mobile: String? = null
|
||||
tryCatchInRelease {
|
||||
val jsonArray = JSONArray(data.string())
|
||||
if (jsonArray.length() > 0) {
|
||||
mobile = jsonArray.get(0).toString()
|
||||
}
|
||||
}
|
||||
|
||||
reserveMobile.postValue(mobile)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
reserveMobile.postValue(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
class Reservation(var success: Boolean = false, var withMobile: Boolean = false, var boundWechat: Boolean = false)
|
||||
}
|
||||
//
|
||||
// override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
// return inflater.inflate(R.layout.dialog_reserve_game, null)
|
||||
// }
|
||||
//
|
||||
//// override fun getEvent(): String {
|
||||
//// return "预约游戏"
|
||||
//// }
|
||||
////
|
||||
//// override fun getKey(): String {
|
||||
//// return "预约功能操作"
|
||||
//// }
|
||||
//
|
||||
// @Suppress("DEPRECATION")
|
||||
// @SuppressLint("SetTextI18n")
|
||||
// override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// super.onViewCreated(view, savedInstanceState)
|
||||
// ButterKnife.bind(this, view)
|
||||
//
|
||||
// val reserveContent = "游戏上线,您将收到<font color='#1383EB'>免费短信</font>提醒"
|
||||
// reserveContentTv.text = Html.fromHtml(reserveContent)
|
||||
//
|
||||
//
|
||||
// mobileEt.setTextChangedListener { s, _, _, _ ->
|
||||
// mobileIndexContainer.visibility = View.GONE
|
||||
// mobileEtDelete.goneIf(s.trim().isEmpty())
|
||||
// }
|
||||
//
|
||||
// mViewModel.reservation.observeNonNull(this) {
|
||||
// if (it.success) {
|
||||
// showSuccessDialog(it.withMobile, it.boundWechat)
|
||||
// successCallback?.onSuccess()
|
||||
// HistoryHelper.insertGameEntity(mGame!!)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// mViewModel.reserveMobile.observe(viewLifecycleOwner, Observer {
|
||||
// setMobileIndexHint(it)
|
||||
// })
|
||||
//
|
||||
// dialog?.setCanceledOnTouchOutside(true)
|
||||
// }
|
||||
//
|
||||
// private fun showSuccessDialog(withMobile: Boolean, boundWechat: Boolean) {
|
||||
// reserveHintTv.text = "游戏预约成功"
|
||||
// reserveContainer.visibility = View.GONE
|
||||
// reserveCompletedContainer.visibility = View.VISIBLE
|
||||
//
|
||||
// val reservation = Config.getSettings()?.appointment
|
||||
// val dialogConfig = if (withMobile) reservation?.withMobile else reservation?.withoutMobile
|
||||
//
|
||||
// reserveCompletedContentTv.text = dialogConfig?.htmlContent?.fromHtml()
|
||||
// if (dialogConfig?.text.isNullOrEmpty()
|
||||
// || (dialogConfig?.type == "wechat_bind" && boundWechat)) {
|
||||
// customizableBtn.visibility = View.GONE
|
||||
// } else {
|
||||
// customizableBtn.text = dialogConfig?.text
|
||||
// customizableBtn.setOnClickListener {
|
||||
//// MtaHelper.onEvent("预约游戏", "预约功能操作", "点击跳转按钮")
|
||||
// DirectUtils.directToLinkPage(
|
||||
// requireContext(),
|
||||
// dialogConfig!!.toLinkEntity(),
|
||||
// "(游戏预约)",
|
||||
// "")
|
||||
// dismissAllowingStateLoss()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun setMobileIndexHint(reserveMobile: String?) {
|
||||
// var userMobile = UserManager.getInstance().userInfoEntity?.mobile
|
||||
// if (reserveMobile == userMobile) userMobile = null
|
||||
//
|
||||
// if (!reserveMobile.isNullOrEmpty()) {
|
||||
// mobileIndexReserve.visibility = View.VISIBLE
|
||||
// mobileIndexReserve.text = reserveMobile
|
||||
// } else {
|
||||
// mobileIndexReserve.visibility = View.GONE
|
||||
// }
|
||||
//
|
||||
// if (!userMobile.isNullOrEmpty()) {
|
||||
// mobileIndexUser.visibility = View.VISIBLE
|
||||
// mobileIndexUser.text = userMobile
|
||||
// } else {
|
||||
// mobileIndexUser.visibility = View.GONE
|
||||
// }
|
||||
// mobileIndexContainer.goneIf(mobileIndexUser.visibility == View.GONE && mobileIndexReserve.visibility == View.GONE)
|
||||
// if (mobileIndexContainer.visibility ==View.VISIBLE) {
|
||||
// mobileIndexContainer.animation = AnimationUtils.loadAnimation(requireContext(), R.anim.reserve_dialog_index_anim)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @OnClick(R.id.reserve_with_mobile_btn,
|
||||
// R.id.reserve_without_mobile_btn,
|
||||
// R.id.content_container,
|
||||
// R.id.close_btn,
|
||||
// R.id.customizable_btn,
|
||||
// R.id.mobile_index_reserve,
|
||||
// R.id.mobile_index_user,
|
||||
// R.id.mobile_et_delete,
|
||||
// R.id.mobile_et,
|
||||
// R.id.layout_container)
|
||||
// fun onClick(view: View) {
|
||||
// when (view.id) {
|
||||
// R.id.reserve_without_mobile_btn -> {
|
||||
//// MtaHelper.onEvent("预约游戏", "预约功能操作", "点击无手机号预约")
|
||||
// if (mobileIndexContainer.visibility == View.VISIBLE) {
|
||||
// mobileIndexContainer.visibility = View.GONE
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// mViewModel.reserve(gameId = mGameId, gameName = mGameName)
|
||||
// }
|
||||
//
|
||||
// R.id.reserve_with_mobile_btn -> {
|
||||
// if (mobileIndexContainer.visibility == View.VISIBLE) {
|
||||
// mobileIndexContainer.visibility = View.GONE
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// val mobile = mobileEt.text.toString()
|
||||
// if (mobile.length < 11 || !mobile.startsWith("1")) {
|
||||
// Utils.toast(context, "手机号格式错误,请检查并重新输入")
|
||||
// return
|
||||
// }
|
||||
//
|
||||
//// MtaHelper.onEvent("预约游戏", "预约功能操作", "点击立即预约")
|
||||
// mViewModel.reserve(gameId = mGameId, gameName = mGameName, mobile = mobile)
|
||||
// }
|
||||
//
|
||||
// R.id.close_btn -> {
|
||||
//// MtaHelper.onEvent("预约游戏", "预约功能操作", "点击关闭")
|
||||
// dismissAllowingStateLoss()
|
||||
// AppExecutor.uiExecutor.executeWithDelay(Runnable {
|
||||
// NotificationHelper.showNotificationHintDialog(NotificationUgc.RESERVE_GAME)
|
||||
// }, 1000)
|
||||
// }
|
||||
// R.id.content_container -> {
|
||||
// mobileIndexContainer.visibility = View.GONE
|
||||
// }
|
||||
// R.id.mobile_index_reserve -> {
|
||||
// mobileEt.setText(mobileIndexReserve.text.toString())
|
||||
// mobileEt.setSelection(mobileEt.text.length)
|
||||
// mobileIndexContainer.visibility = View.GONE
|
||||
// }
|
||||
// R.id.mobile_index_user -> {
|
||||
// mobileEt.setText(mobileIndexUser.text.toString())
|
||||
// mobileEt.setSelection(mobileEt.text.length)
|
||||
// mobileIndexContainer.visibility = View.GONE
|
||||
// }
|
||||
// R.id.mobile_et_delete -> {
|
||||
// mobileEt.setText("")
|
||||
// }
|
||||
// R.id.mobile_et -> {
|
||||
// mobileIndexContainer.visibility = View.GONE
|
||||
// }
|
||||
// R.id.layout_container -> {
|
||||
// dismissAllowingStateLoss()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun onResume() {
|
||||
// super.onResume()
|
||||
// if (HaloApp.getInstance().mCacheKeyboardHeight > 0) {
|
||||
// val attributes = dialog?.window?.attributes
|
||||
// val heightPixels = requireContext().resources.displayMetrics.heightPixels
|
||||
// val mCacheKeyboardHeight = HaloApp.getInstance().mCacheKeyboardHeight
|
||||
// val statusBarHeight = DisplayUtils.getStatusBarHeight(requireContext().resources)
|
||||
// dialog?.window?.attributes?.height = heightPixels - mCacheKeyboardHeight - statusBarHeight
|
||||
// attributes?.gravity = Gravity.TOP
|
||||
// dialog?.window?.attributes = attributes
|
||||
// }
|
||||
// mKeyboardHeightProvider?.setKeyboardHeightObserver(this)
|
||||
// }
|
||||
//
|
||||
// override fun onPause() {
|
||||
// super.onPause()
|
||||
// mKeyboardHeightProvider?.setKeyboardHeightObserver(null)
|
||||
// }
|
||||
//
|
||||
// override fun onDestroy() {
|
||||
// super.onDestroy()
|
||||
// mKeyboardHeightProvider?.close()
|
||||
// }
|
||||
//
|
||||
// override fun onKeyboardHeightChanged(height: Int, orientation: Int) {
|
||||
// if (height > 0) {
|
||||
// val attributes = dialog?.window?.attributes
|
||||
// attributes?.gravity = Gravity.CENTER
|
||||
// dialog?.window?.attributes = attributes
|
||||
// HaloApp.getInstance().mCacheKeyboardHeight = height
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// companion object {
|
||||
// @JvmStatic
|
||||
// fun getInstance(gameEntity: GameEntity, successCallback: SuccessCallback) = ReserveDialogFragment().apply {
|
||||
// arguments = Bundle().apply {
|
||||
// putParcelable(EntranceUtils.KEY_GAME, gameEntity)
|
||||
// }
|
||||
// this.successCallback = successCallback
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// interface SuccessCallback {
|
||||
// fun onSuccess()
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//class ReserveViewModel(application: Application) : AndroidViewModel(application) {
|
||||
// val reservation = MutableLiveData<Reservation>()
|
||||
//
|
||||
// val reserveMobile = MutableLiveData<String>()
|
||||
//
|
||||
// init {
|
||||
// getAppointmentMobile()
|
||||
// }
|
||||
//
|
||||
// @SuppressLint("CheckResult")
|
||||
// fun reserve(gameId: String, gameName: String, mobile: String = "") {
|
||||
//
|
||||
// val requestMap = hashMapOf<String, String>()
|
||||
// requestMap["game_id"] = gameId
|
||||
// if (mobile.isNotEmpty()) {
|
||||
// requestMap["mobile"] = mobile
|
||||
// }
|
||||
//
|
||||
// RetrofitManager.getInstance().api
|
||||
// .createNewGameReservation(requestMap.createRequestBody())
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .subscribe(object : BiResponse<ResponseBody>() {
|
||||
// override fun onSuccess(data: ResponseBody) {
|
||||
// var boundWechat = false
|
||||
// tryWithDefaultCatch {
|
||||
// boundWechat = JSONObject(data.string() ?: "").getBoolean("wechat_bind")
|
||||
// }
|
||||
//
|
||||
// reservation.postValue(Reservation(success = true, withMobile = mobile.isNotEmpty(), boundWechat = boundWechat))
|
||||
// ReservationRepository.addReservationToMemoryAndRefresh(gameId)
|
||||
//
|
||||
//// MtaHelper.onEvent("预约游戏", "预约", gameName)
|
||||
// }
|
||||
//
|
||||
// override fun onFailure(exception: Exception) {
|
||||
// Utils.toast(getApplication(), exception.message)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// @SuppressLint("CheckResult")
|
||||
// private fun getAppointmentMobile() {
|
||||
// RetrofitManager.getInstance().api
|
||||
// .getAppointmentMobile(UserManager.getInstance().userId)
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(object : BiResponse<ResponseBody>() {
|
||||
// override fun onSuccess(data: ResponseBody) {
|
||||
// var mobile: String? = null
|
||||
// tryCatchInRelease {
|
||||
// val jsonArray = JSONArray(data.string())
|
||||
// if (jsonArray.length() > 0) {
|
||||
// mobile = jsonArray.get(0).toString()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// reserveMobile.postValue(mobile)
|
||||
// }
|
||||
//
|
||||
// override fun onFailure(exception: Exception) {
|
||||
// reserveMobile.postValue(null)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// class Reservation(var success: Boolean = false, var withMobile: Boolean = false, var boundWechat: Boolean = false)
|
||||
//}
|
||||
@ -3,27 +3,88 @@ package com.gh.common.exposure
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Keep
|
||||
@Parcelize
|
||||
data class ExposureEntity(
|
||||
@SerializedName("game_id")
|
||||
val gameId: String? = "",
|
||||
val gameName: String? = "",
|
||||
val gameVersion: String? = "",
|
||||
val sequence: Int? = 0,
|
||||
val platform: String? = "",
|
||||
val downloadType: String? = "",
|
||||
val downloadCompleteType: String? = "",
|
||||
@SerializedName("game_id")
|
||||
val gameId: String? = "",
|
||||
@SerializedName("subject_id")
|
||||
val subjectId: String? = null, // 专题 id
|
||||
@SerializedName("carousel_id")
|
||||
val carouselId: String? = "", // 轮播图 id
|
||||
val gameName: String? = "",
|
||||
val gameVersion: String? = "",
|
||||
val sequence: Int? = 0,
|
||||
val outerSequence: Int? = 0,
|
||||
val platform: String? = null,
|
||||
var isMirrorData: Boolean = false,
|
||||
@SerializedName("is_web_download")
|
||||
var isWebDownload: Boolean = false,
|
||||
val downloadType: String? = null,
|
||||
val downloadCompleteType: String? = null,
|
||||
@SerializedName("display_type")
|
||||
val displayType: String? = "",
|
||||
@SerializedName("is_platform_recommend")
|
||||
val isPlatformRecommend: Boolean? = false,
|
||||
|
||||
// 下载地址的 host 和 path
|
||||
var host: String? = "",
|
||||
var path: String? = "",
|
||||
// 外层内容 id (包括)
|
||||
// 1. "test_server_id" : "", // 开测表 ID
|
||||
// 2. "block_id" : "", // 板块 ID
|
||||
// 3. "category_id" : "", // 新分类1.0 ID
|
||||
// 4. "category_v2_id" : "", // 新分类2.0 ID
|
||||
var containerId: String? = null,
|
||||
var containerType: String? = null,
|
||||
|
||||
// 统计启动弹窗相关数据用的 (ugly)
|
||||
@SerializedName("dialog_id")
|
||||
var welcomeDialogId: String? = "",
|
||||
@SerializedName("link_title")
|
||||
var welcomeDialogLinkTitle: String? = ""
|
||||
) : Parcelable
|
||||
@SerializedName("test_server_id")
|
||||
var testServerId: String? = null,
|
||||
@SerializedName("block_id")
|
||||
var blockId: String? = null,
|
||||
@SerializedName("category_id")
|
||||
var categoryId: String? = null,
|
||||
@SerializedName("category_v2_id")
|
||||
var categoryV2Id: String? = null,
|
||||
@SerializedName("navigation_id")
|
||||
var navigationId: String? = null,
|
||||
|
||||
// 专题详情的来源页面和来源 id
|
||||
@SerializedName("source_page")
|
||||
var sourcePage: String? = null,
|
||||
@SerializedName("source_page_id")
|
||||
var sourcePageId: String? = null,
|
||||
@SerializedName("source_page_name")
|
||||
var sourcePageName: String? = null,
|
||||
|
||||
// 下载地址的 host 和 path
|
||||
var host: String? = null,
|
||||
var path: String? = null,
|
||||
|
||||
// 统计启动弹窗相关数据用的 (ugly)
|
||||
@SerializedName("dialog_id")
|
||||
var welcomeDialogId: String? = null,
|
||||
@SerializedName("link_title")
|
||||
var welcomeDialogLinkTitle: String? = null
|
||||
) : Parcelable {
|
||||
|
||||
fun setContainerInfo(id: String?, type: String?) {
|
||||
when (type) {
|
||||
TEST_SERVER_ID -> testServerId = id
|
||||
BLOCK_ID -> blockId = id
|
||||
CATEGORY_ID -> categoryId = id
|
||||
CATEGORY_V2_ID -> categoryV2Id = id
|
||||
NAVIGATION_ID -> navigationId = id
|
||||
}
|
||||
|
||||
containerId = null
|
||||
containerType = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TEST_SERVER_ID = "test_server_id"
|
||||
const val BLOCK_ID = "block_id"
|
||||
const val CATEGORY_ID = "category_id"
|
||||
const val CATEGORY_V2_ID = "category_v2_id"
|
||||
const val NAVIGATION_ID = "navigation_id"
|
||||
}
|
||||
}
|
||||
@ -9,45 +9,102 @@ import com.gh.common.exposure.meta.Meta
|
||||
import com.gh.common.exposure.meta.MetaUtil
|
||||
import com.gh.common.exposure.time.TimeUtil
|
||||
import com.gh.common.util.getFirstElementDividedByDivider
|
||||
import com.gh.download.server.BrowserInstallHelper
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
@Keep
|
||||
@Parcelize
|
||||
@Entity(tableName = "exposureEvent")
|
||||
data class ExposureEvent(
|
||||
var payload: ExposureEntity,
|
||||
val source: List<ExposureSource>,
|
||||
var eTrace: List<ExposureEvent>? = arrayListOf(),
|
||||
val event: ExposureType,
|
||||
val meta: Meta = MetaUtil.getMeta(),
|
||||
val time: Int = TimeUtil.currentTime(),
|
||||
@PrimaryKey
|
||||
val id: String = UUID.randomUUID().toString()) : Parcelable {
|
||||
var payload: ExposureEntity,
|
||||
val source: List<ExposureSource>,
|
||||
var eTrace: List<ExposureEvent>? = arrayListOf(),
|
||||
val event: ExposureType,
|
||||
val meta: Meta = MetaUtil.getMeta(),
|
||||
val time: Int = TimeUtil.currentTime(),
|
||||
@PrimaryKey
|
||||
val id: String = UUID.randomUUID().toString()
|
||||
) : Parcelable {
|
||||
companion object {
|
||||
|
||||
// TODO 建一个 exposureEvent 池规避反复生成对象
|
||||
@JvmStatic
|
||||
fun createEvent(gameEntity: GameEntity?, source: List<ExposureSource>, eTrace: List<ExposureEvent>? = null, event: ExposureType = ExposureType.EXPOSURE): ExposureEvent {
|
||||
fun createEvent(
|
||||
gameEntity: GameEntity?,
|
||||
source: List<ExposureSource>,
|
||||
eTrace: List<ExposureEvent>? = null,
|
||||
event: ExposureType = ExposureType.EXPOSURE
|
||||
): ExposureEvent {
|
||||
if (gameEntity?.getApk()?.size == 1) {
|
||||
gameEntity.gameVersion = gameEntity.getApk().elementAtOrNull(0)?.version ?: ""
|
||||
}
|
||||
return ExposureEvent(
|
||||
payload = ExposureEntity(
|
||||
gameId = gameEntity?.id?.getFirstElementDividedByDivider(Constants.GAME_ID_DIVIDER),
|
||||
gameName = gameEntity?.name?.removeSuffix(Constants.GAME_NAME_DECORATOR),
|
||||
gameVersion = gameEntity?.gameVersion,
|
||||
sequence = gameEntity?.sequence,
|
||||
platform = gameEntity?.platform,
|
||||
downloadType = gameEntity?.downloadType,
|
||||
downloadCompleteType = gameEntity?.downloadCompleteType,
|
||||
// ugly
|
||||
welcomeDialogId = gameEntity?.welcomeDialogId ?: eTrace?.firstOrNull()?.payload?.welcomeDialogId,
|
||||
welcomeDialogLinkTitle = gameEntity?.welcomeDialogTitle ?: eTrace?.firstOrNull()?.payload?.welcomeDialogLinkTitle),
|
||||
source = source,
|
||||
eTrace = eTrace,
|
||||
event = event).apply { gameEntity?.exposureEvent = this }
|
||||
payload = ExposureEntity(
|
||||
gameId = gameEntity?.id?.getFirstElementDividedByDivider(DownloadEntity.GAME_ID_DIVIDER),
|
||||
gameName = eTrace?.firstOrNull()?.payload?.gameName
|
||||
?: gameEntity?.name?.removeSuffix(Constants.GAME_NAME_DECORATOR),
|
||||
gameVersion = eTrace?.firstOrNull()?.payload?.gameVersion
|
||||
?: gameEntity?.gameVersion,
|
||||
subjectId = eTrace?.firstOrNull()?.payload?.subjectId
|
||||
?: gameEntity?.subjectId
|
||||
?: gameEntity?.subjectData?.id,
|
||||
isMirrorData = eTrace?.firstOrNull()?.payload?.isMirrorData
|
||||
?: gameEntity?.shouldUseMirrorInfo() ?: false,
|
||||
isWebDownload = BrowserInstallHelper.isUseBrowserToInstallEnabled() && BrowserInstallHelper.shouldUseBrowserToInstall(), // 实时的值,不用从 eTrace 里取
|
||||
sequence = eTrace?.firstOrNull()?.payload?.sequence ?: gameEntity?.sequence,
|
||||
outerSequence = eTrace?.firstOrNull()?.payload?.outerSequence
|
||||
?: gameEntity?.outerSequence,
|
||||
platform = eTrace?.firstOrNull()?.payload?.platform ?: gameEntity?.platform,
|
||||
downloadType = gameEntity?.downloadType,
|
||||
downloadCompleteType = gameEntity?.downloadCompleteType,
|
||||
displayType = eTrace?.firstOrNull()?.payload?.displayType
|
||||
?: gameEntity?.displayContent,
|
||||
isPlatformRecommend = gameEntity?.isPlatformRecommend,
|
||||
// ugly
|
||||
welcomeDialogId = gameEntity?.welcomeDialogId
|
||||
?: eTrace?.firstOrNull()?.payload?.welcomeDialogId,
|
||||
welcomeDialogLinkTitle = gameEntity?.welcomeDialogTitle
|
||||
?: eTrace?.firstOrNull()?.payload?.welcomeDialogLinkTitle
|
||||
),
|
||||
source = source,
|
||||
eTrace = eTrace,
|
||||
event = event
|
||||
).also {
|
||||
it.payload.categoryId = eTrace?.firstOrNull()?.payload?.categoryId
|
||||
it.payload.categoryV2Id = eTrace?.firstOrNull()?.payload?.categoryV2Id
|
||||
it.payload.testServerId = eTrace?.firstOrNull()?.payload?.testServerId
|
||||
it.payload.blockId = eTrace?.firstOrNull()?.payload?.blockId
|
||||
|
||||
it.payload.setContainerInfo(
|
||||
eTrace?.firstOrNull()?.payload?.containerId ?: gameEntity?.containerId,
|
||||
eTrace?.firstOrNull()?.payload?.containerType ?: gameEntity?.containerType
|
||||
)
|
||||
|
||||
it.payload.sourcePage = eTrace?.firstOrNull()?.payload?.sourcePage
|
||||
it.payload.sourcePageId = eTrace?.firstOrNull()?.payload?.sourcePageId
|
||||
it.payload.sourcePageName = eTrace?.firstOrNull()?.payload?.sourcePageName
|
||||
|
||||
gameEntity?.exposureEvent = it
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun createEventWithSourceConcat(
|
||||
gameEntity: GameEntity?,
|
||||
basicSource: List<ExposureSource>,
|
||||
source: List<ExposureSource>,
|
||||
eTrace: List<ExposureEvent>? = null,
|
||||
event: ExposureType = ExposureType.EXPOSURE
|
||||
): ExposureEvent {
|
||||
val concatSourceList = ArrayList<ExposureSource>().apply {
|
||||
addAll(basicSource)
|
||||
addAll(source)
|
||||
}
|
||||
return createEvent(gameEntity, concatSourceList, eTrace, event)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -11,24 +11,24 @@ import io.reactivex.functions.Consumer
|
||||
*/
|
||||
class ExposureListener(var fragment: Fragment, var exposable: IExposable) : RecyclerView.OnScrollListener() {
|
||||
|
||||
var throttleBus: ExposureThrottleBus? = null
|
||||
val throttleBus: ExposureThrottleBus by lazy { ExposureThrottleBus(Consumer { commitExposure(it) }, Consumer(Throwable::printStackTrace)) }
|
||||
var layoutManager: LinearLayoutManager? = null
|
||||
var visibleState: ExposureThrottleBus.VisibleState? = null
|
||||
|
||||
init {
|
||||
fragment.fragmentManager?.registerFragmentLifecycleCallbacks(
|
||||
object : FragmentManager.FragmentLifecycleCallbacks() {
|
||||
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
|
||||
throttleBus = ExposureThrottleBus(Consumer { commitExposure(it) }, Consumer(Throwable::printStackTrace))
|
||||
}
|
||||
|
||||
override fun onFragmentPaused(fm: FragmentManager, f: Fragment) {
|
||||
visibleState?.let { commitExposure(it) }
|
||||
throttleBus?.clear()
|
||||
if (fragment == f) {
|
||||
visibleState?.let { commitExposure(it) }
|
||||
throttleBus.clear()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFragmentViewDestroyed(fm: FragmentManager, f: Fragment) {
|
||||
fragment.fragmentManager?.unregisterFragmentLifecycleCallbacks(this)
|
||||
if (fragment == f) {
|
||||
fragment.fragmentManager?.unregisterFragmentLifecycleCallbacks(this)
|
||||
}
|
||||
}
|
||||
}, false)
|
||||
}
|
||||
@ -44,7 +44,7 @@ class ExposureListener(var fragment: Fragment, var exposable: IExposable) : Recy
|
||||
|
||||
layoutManager?.run {
|
||||
visibleState = ExposureThrottleBus.VisibleState(findFirstVisibleItemPosition(), findLastVisibleItemPosition())
|
||||
throttleBus?.postVisibleState(visibleState!!)
|
||||
throttleBus.postVisibleState(visibleState!!)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,15 +1,13 @@
|
||||
package com.gh.common.exposure
|
||||
|
||||
import com.aliyun.sls.android.sdk.model.LogGroup
|
||||
import com.gh.common.exposure.time.TimeUtil
|
||||
import com.aliyun.sls.android.producer.Log
|
||||
import com.gh.common.loghub.LoghubHelper
|
||||
import com.gh.common.util.toJson
|
||||
import com.gh.common.util.tryWithDefaultCatch
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.loghub.LgLOG
|
||||
import com.gh.loghub.LoghubHelper
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ExecutorService
|
||||
|
||||
/**
|
||||
* A handful tool for committing logs to aliyun loghub.
|
||||
@ -22,26 +20,23 @@ import java.util.concurrent.Executors
|
||||
*/
|
||||
object ExposureManager {
|
||||
|
||||
private const val ENDPOINT = "cn-qingdao.log.aliyuncs.com"
|
||||
private const val PROJECT = "ghzs"
|
||||
private const val STORE_SIZE = 100
|
||||
private const val LOG_STORE = BuildConfig.EXPOSURE_REPO
|
||||
|
||||
private val loghubHelper = LoghubHelper.getInstance()
|
||||
|
||||
// exposureCache 用来过滤掉具有相同 id 的曝光事件,避免重复发送事件
|
||||
private val exposureSet by lazy { hashSetOf<ExposureEvent>() }
|
||||
private val exposureExecutor by lazy { Executors.newSingleThreadExecutor() }
|
||||
private var exposureExecutor: ExecutorService? = null
|
||||
private val exposureCache by lazy { FixedSizeLinkedHashSet<String>(300) }
|
||||
private val exposureDao by lazy { ExposureDatabase.buildDatabase(HaloApp.getInstance().application).logHubEventDao() }
|
||||
|
||||
@JvmStatic
|
||||
fun init() {
|
||||
loghubHelper.init(HaloApp.getInstance().application, ENDPOINT, PROJECT, LOG_STORE) { TimeUtil.currentTimeMillis() }
|
||||
|
||||
exposureExecutor.execute {
|
||||
val eventList = exposureDao.getAll()
|
||||
exposureSet.addAll(eventList)
|
||||
fun init(excutor: ExecutorService) {
|
||||
exposureExecutor = excutor
|
||||
exposureExecutor?.execute {
|
||||
tryWithDefaultCatch {
|
||||
val eventList = exposureDao.getAll()
|
||||
exposureSet.addAll(eventList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,7 +44,7 @@ object ExposureManager {
|
||||
* Log a single exposure event.
|
||||
*/
|
||||
fun log(event: ExposureEvent) {
|
||||
exposureExecutor.execute {
|
||||
exposureExecutor?.execute {
|
||||
try {
|
||||
if (!exposureCache.contains(event.id)) {
|
||||
// Catch `android.database.sqlite.SQLiteFullException: database or disk is full` exception.
|
||||
@ -71,7 +66,7 @@ object ExposureManager {
|
||||
* Log a collection of exposure event.
|
||||
*/
|
||||
fun log(eventList: List<ExposureEvent>) {
|
||||
exposureExecutor.execute {
|
||||
exposureExecutor?.execute {
|
||||
for (event in eventList) {
|
||||
try {
|
||||
if (!exposureCache.contains(event.id)) {
|
||||
@ -94,14 +89,13 @@ object ExposureManager {
|
||||
* @param forced Ignore all restrictions.
|
||||
*/
|
||||
fun commitSavedExposureEvents(forced: Boolean = false) {
|
||||
exposureExecutor.execute {
|
||||
exposureExecutor?.execute {
|
||||
tryWithDefaultCatch {
|
||||
// TODO 初始化 loghubHelper 去掉这个 tryCatch 块
|
||||
if (exposureSet.size < STORE_SIZE && !forced || exposureSet.size == 0) return@execute
|
||||
|
||||
val exposureList = exposureSet.toList()
|
||||
// uploadLogGroup 是一个异步方法,LoghubHelper 里面实现了重传功能,所以这里交给它就好了
|
||||
loghubHelper.uploadLogGroup(buildLogGroup(exposureList))
|
||||
uploadExposures(exposureList)
|
||||
|
||||
Utils.log("Exposure", "提交了${exposureList.size}条曝光记录")
|
||||
exposureSet.removeAll(exposureList)
|
||||
@ -114,31 +108,25 @@ object ExposureManager {
|
||||
return jsonWithMultipleBracket.replace("[[", "[").replace("]]", "]")
|
||||
}
|
||||
|
||||
private fun buildLogGroup(eventList: List<ExposureEvent>): LogGroup {
|
||||
val logGroup = LogGroup("sls android", "no ip")
|
||||
|
||||
eventList.forEach { logGroup.PutLog(buildLog(it)) }
|
||||
|
||||
return logGroup
|
||||
private fun uploadExposures(eventList: List<ExposureEvent>) {
|
||||
eventList.forEach {
|
||||
LoghubHelper.uploadLog(buildLog(it), LOG_STORE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildLog(event: ExposureEvent): LgLOG {
|
||||
val log = LgLOG(TimeUtil.currentTime())
|
||||
|
||||
log.PutContent("id", event.id)
|
||||
log.PutContent("payload", event.payload.toJson())
|
||||
log.PutContent("event", event.event.toString())
|
||||
log.PutContent("source", eliminateMultipleBrackets(event.source.toJson()))
|
||||
log.PutContent("meta", event.meta.toJson())
|
||||
log.PutContent("e-traces", if (event.eTrace != null) {
|
||||
private fun buildLog(event: ExposureEvent) = Log().apply {
|
||||
putContent("id", event.id)
|
||||
putContent("payload", event.payload.toJson())
|
||||
putContent("event", event.event.toString())
|
||||
putContent("source", eliminateMultipleBrackets(event.source.toJson()))
|
||||
putContent("meta", event.meta.toJson())
|
||||
putContent("e-traces", if (event.eTrace != null) {
|
||||
eliminateMultipleBrackets(event.eTrace?.toJson() ?: "")
|
||||
} else "")
|
||||
log.PutTime(event.time)
|
||||
|
||||
return log
|
||||
logTime = event.time.toLong()
|
||||
}
|
||||
|
||||
internal class FixedSizeLinkedHashSet<T>(var maxSize: Int) : LinkedHashSet<T>() {
|
||||
class FixedSizeLinkedHashSet<T>(var maxSize: Int) : LinkedHashSet<T>() {
|
||||
override fun add(element: T): Boolean {
|
||||
if (size == maxSize) {
|
||||
pop()
|
||||
|
||||
@ -2,7 +2,7 @@ package com.gh.common.exposure
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.Keep
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Keep
|
||||
@Parcelize
|
||||
|
||||
@ -2,53 +2,75 @@ package com.gh.common.exposure
|
||||
|
||||
import android.text.TextUtils
|
||||
import com.g00fy2.versioncompare.Version
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.PackageUtils
|
||||
import com.gh.common.util.toObject
|
||||
import com.gh.gamecenter.entity.ApkEntity
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.halo.assistant.HaloApp
|
||||
import java.util.*
|
||||
import com.lightgame.download.DownloadEntity
|
||||
|
||||
object ExposureUtils {
|
||||
|
||||
private val mDownloadCompleteTraceEventIdSet = ExposureManager.FixedSizeLinkedHashSet<String>(3)
|
||||
|
||||
@JvmStatic
|
||||
fun logADownloadExposureEvent(entity: GameEntity,
|
||||
platform: String?,
|
||||
traceEvent: ExposureEvent?,
|
||||
downloadType: DownloadType): ExposureEvent {
|
||||
fun logADownloadExposureEvent(
|
||||
entity: GameEntity,
|
||||
platform: String?,
|
||||
traceEvent: ExposureEvent?,
|
||||
downloadType: DownloadType
|
||||
): ExposureEvent {
|
||||
val gameEntity = entity.clone()
|
||||
gameEntity.id = if (entity.id.contains(Constants.GAME_ID_DIVIDER)) {
|
||||
entity.id.split(Constants.GAME_ID_DIVIDER).toTypedArray()[0]
|
||||
gameEntity.id = if (entity.id.contains(DownloadEntity.GAME_ID_DIVIDER)) {
|
||||
entity.id.split(DownloadEntity.GAME_ID_DIVIDER).toTypedArray()[0]
|
||||
} else {
|
||||
entity.id
|
||||
}
|
||||
gameEntity.gameVersion = entity.getApk().elementAtOrNull(0)?.version ?: gameEntity.gameVersion
|
||||
gameEntity.gameVersion = entity.getApk().elementAtOrNull(0)?.version
|
||||
?: gameEntity.gameVersion
|
||||
gameEntity.platform = platform
|
||||
gameEntity.downloadType = downloadType.toString()
|
||||
val exposureEvent = ExposureEvent.createEvent(gameEntity = gameEntity,
|
||||
source = traceEvent?.source ?: ArrayList(),
|
||||
eTrace = ExposureTraceUtils.appendTrace(traceEvent),
|
||||
event = ExposureType.DOWNLOAD)
|
||||
ExposureManager.log(exposureEvent)
|
||||
val exposureEvent = ExposureEvent.createEvent(
|
||||
gameEntity = gameEntity,
|
||||
source = traceEvent?.source ?: ArrayList(),
|
||||
eTrace = ExposureTraceUtils.appendTrace(traceEvent),
|
||||
event = ExposureType.DOWNLOAD
|
||||
)
|
||||
if (!TextUtils.isEmpty(entity.id)) {
|
||||
ExposureManager.log(exposureEvent)
|
||||
}
|
||||
return exposureEvent
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun logADownloadCompleteExposureEvent(entity: GameEntity,
|
||||
platform: String?,
|
||||
trace: String?,
|
||||
host: String? = "unknown",
|
||||
path: String? = "unknown",
|
||||
downloadType: DownloadType) {
|
||||
fun logADownloadCompleteExposureEvent(
|
||||
entity: GameEntity,
|
||||
platform: String?,
|
||||
trace: String?,
|
||||
host: String? = "unknown",
|
||||
path: String? = "unknown",
|
||||
downloadType: DownloadType
|
||||
) {
|
||||
val gameEntity = entity.clone()
|
||||
gameEntity.platform = platform
|
||||
gameEntity.downloadCompleteType = downloadType.toString()
|
||||
val traceEvent = trace?.toObject<ExposureEvent>()
|
||||
val exposureEvent = ExposureEvent.createEvent(gameEntity = gameEntity,
|
||||
source = traceEvent?.source ?: ArrayList(),
|
||||
eTrace = ExposureTraceUtils.appendTrace(traceEvent),
|
||||
event = ExposureType.DOWNLOAD_COMPLETE)
|
||||
|
||||
if (TextUtils.isEmpty(entity.id)) return
|
||||
|
||||
// 避免生成 trace 相同的下载完成事件,根据日志看下载完成的同一秒有可能生成两条
|
||||
if (mDownloadCompleteTraceEventIdSet.contains(traceEvent?.id)) {
|
||||
return
|
||||
}
|
||||
|
||||
traceEvent?.payload?.gameId?.let { mDownloadCompleteTraceEventIdSet.add(it) }
|
||||
|
||||
val exposureEvent = ExposureEvent.createEvent(
|
||||
gameEntity = gameEntity,
|
||||
source = traceEvent?.source ?: ArrayList(),
|
||||
eTrace = ExposureTraceUtils.appendTrace(traceEvent),
|
||||
event = ExposureType.DOWNLOAD_COMPLETE
|
||||
)
|
||||
exposureEvent.payload.host = host
|
||||
exposureEvent.payload.path = path
|
||||
ExposureManager.log(exposureEvent)
|
||||
@ -57,12 +79,25 @@ object ExposureUtils {
|
||||
|
||||
@JvmStatic
|
||||
fun getDownloadType(apkEntity: ApkEntity, gameId: String): DownloadType {
|
||||
return if (PackageUtils.isInstalled(HaloApp.getInstance().application, apkEntity.packageName)) {
|
||||
if (PackageUtils.isSignedByGh(HaloApp.getInstance().application, apkEntity.packageName)) {
|
||||
return if (PackageUtils.isInstalled(
|
||||
HaloApp.getInstance().application,
|
||||
apkEntity.packageName
|
||||
)
|
||||
) {
|
||||
if (PackageUtils.isSignedByGh(
|
||||
HaloApp.getInstance().application,
|
||||
apkEntity.packageName
|
||||
)
|
||||
) {
|
||||
if (PackageUtils.isCanUpdate(apkEntity, gameId)) {
|
||||
DownloadType.PLUGIN_UPDATE
|
||||
} else {
|
||||
if (Version(apkEntity.version).isHigherThan(PackageUtils.getVersionByPackage(apkEntity.packageName))) {
|
||||
if (Version(apkEntity.version).isHigherThan(
|
||||
PackageUtils.getVersionNameByPackageName(
|
||||
apkEntity.packageName
|
||||
)
|
||||
)
|
||||
) {
|
||||
DownloadType.UPDATE
|
||||
} else {
|
||||
DownloadType.DOWNLOAD
|
||||
@ -80,6 +115,26 @@ object ExposureUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为游戏实体添加位置序号和上层专题 id (若存在)
|
||||
*/
|
||||
@JvmStatic
|
||||
fun updateExposureInfo(
|
||||
gameList: List<GameEntity>?,
|
||||
subjectId: String? = null,
|
||||
containerId: String? = null,
|
||||
containerType: String? = null
|
||||
) {
|
||||
gameList?.let {
|
||||
for ((index, game) in it.withIndex()) {
|
||||
game.sequence = index
|
||||
game.subjectId = subjectId
|
||||
game.containerId = containerId
|
||||
game.containerType = containerType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class DownloadType {
|
||||
DOWNLOAD,
|
||||
|
||||
|
||||
@ -2,16 +2,16 @@ package com.gh.common.exposure.meta
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.Keep
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Keep
|
||||
@Parcelize
|
||||
data class Meta(
|
||||
val mac: String? = "",
|
||||
val imei: String? = "",
|
||||
val jnfj: String? = "",
|
||||
val model: String? = "",
|
||||
val manufacturer: String? = "",
|
||||
val android_id: String? = "",
|
||||
val dia: String? = "",
|
||||
val android_sdk: Int? = -1,
|
||||
val android_version: String? = "",
|
||||
val network: String? = "",
|
||||
|
||||
@ -11,10 +11,10 @@ import android.telephony.TelephonyManager
|
||||
import android.text.TextUtils
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.SPUtils
|
||||
import com.gh.common.util.tryWithDefaultCatch
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.leon.channel.helper.ChannelReaderUtil
|
||||
import com.walkud.rom.checker.RomIdentifier
|
||||
|
||||
object MetaUtil {
|
||||
@ -26,13 +26,25 @@ object MetaUtil {
|
||||
private var imei: String? = null
|
||||
private var base64EncodedImei: String? = null
|
||||
private var androidId: String? = null
|
||||
private var base64EncodedAndroidId: String? = null
|
||||
|
||||
private var romName: String? = null
|
||||
private var romVersion: String? = null
|
||||
|
||||
fun refreshMeta() {
|
||||
m = Meta(mac = "",
|
||||
imei = getIMEI(),
|
||||
|
||||
if (romName == null) {
|
||||
tryWithDefaultCatch {
|
||||
romName = RomIdentifier.getRom().name
|
||||
romVersion = RomIdentifier.getRom().versionName
|
||||
}
|
||||
}
|
||||
|
||||
m = Meta(mac = null,
|
||||
jnfj = getBase64EncodedIMEI(),
|
||||
model = getModel(),
|
||||
manufacturer = getManufacturer(),
|
||||
android_id = getAndroidId(),
|
||||
dia = getBase64EncodedAndroidId(),
|
||||
android_sdk = getAndroidSDK(),
|
||||
android_version = getAndroidVersion(),
|
||||
network = getNetwork(),
|
||||
@ -43,7 +55,7 @@ object MetaUtil {
|
||||
appVersion = BuildConfig.VERSION_NAME,
|
||||
userId = UserManager.getInstance().userId,
|
||||
exposureVersion = BuildConfig.EXPOSURE_VERSION,
|
||||
rom = RomIdentifier.getRom().name + "" + RomIdentifier.getRom().versionName)
|
||||
rom = romName + "" + romVersion)
|
||||
}
|
||||
|
||||
fun getMeta(): Meta {
|
||||
@ -54,10 +66,7 @@ object MetaUtil {
|
||||
}
|
||||
|
||||
private fun getChannel(): String? {
|
||||
if (TextUtils.isEmpty(channel)) {
|
||||
channel = if (ChannelReaderUtil.getChannel(application) != null) ChannelReaderUtil.getChannel(application) else ""
|
||||
}
|
||||
return channel
|
||||
return HaloApp.getInstance().channel
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,6 +116,10 @@ object MetaUtil {
|
||||
|
||||
@JvmStatic
|
||||
fun getBase64EncodedIMEI(): String {
|
||||
if (imei == null) {
|
||||
getIMEI()
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(base64EncodedImei) && imei != null) {
|
||||
try {
|
||||
base64EncodedImei = android.util.Base64.encodeToString(getIMEI().trim().toByteArray(), android.util.Base64.NO_WRAP)
|
||||
@ -119,6 +132,24 @@ object MetaUtil {
|
||||
return base64EncodedImei ?: ""
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getBase64EncodedAndroidId(): String {
|
||||
if (androidId == null) {
|
||||
getAndroidId()
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(base64EncodedAndroidId) && androidId != null) {
|
||||
try {
|
||||
base64EncodedAndroidId = android.util.Base64.encodeToString(getAndroidId().trim().toByteArray(), android.util.Base64.NO_WRAP)
|
||||
} catch (e: java.lang.Exception) {
|
||||
e.printStackTrace()
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
return base64EncodedAndroidId ?: ""
|
||||
}
|
||||
|
||||
fun getModel(): String? {
|
||||
return Build.MODEL
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package com.gh.common.filter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import com.gh.common.AppExecutor
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.SPUtils
|
||||
import com.gh.common.util.debounceActionWithInterval
|
||||
@ -12,8 +11,8 @@ import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.functions.Function
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
||||
object RegionSettingHelper {
|
||||
|
||||
@ -62,13 +61,12 @@ object RegionSettingHelper {
|
||||
@SuppressLint("CheckResult")
|
||||
@JvmStatic
|
||||
fun getRegionSetting() {
|
||||
debounceActionWithInterval(R.string.app_name, 3000) {
|
||||
debounceActionWithInterval(R.string.app_name, 5000) {
|
||||
// 使用默认的 Schdulers.io() 可能会触发 OOM
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application)
|
||||
.sensitiveApi
|
||||
RetrofitManager.getInstance()
|
||||
.api
|
||||
.getRegionSetting(HaloApp.getInstance().channel)
|
||||
.subscribeOn(AppExecutor.cachedScheduler)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : BiResponse<RegionSetting>() {
|
||||
override fun onSuccess(data: RegionSetting) {
|
||||
updateSettingsInMemory(data)
|
||||
@ -76,7 +74,7 @@ object RegionSettingHelper {
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
SPUtils.getString(SP_SETTING).toObject<RegionSetting>()?.let {
|
||||
SPUtils.getString(SP_SETTING)?.toObject<RegionSetting>()?.let {
|
||||
updateSettingsInMemory(it)
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.entity.HistoryGameEntity
|
||||
import com.gh.gamecenter.entity.MyVideoEntity
|
||||
import com.gh.gamecenter.entity.NewsEntity
|
||||
@ -15,7 +16,7 @@ import com.gh.gamecenter.room.converter.*
|
||||
import com.gh.gamecenter.room.dao.*
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
@Database(entities = [AnswerEntity::class, ArticleEntity::class, NewsEntity::class, HistoryGameEntity::class, MyVideoEntity::class], version = 7, exportSchema = false)
|
||||
@Database(entities = [AnswerEntity::class, ArticleEntity::class, NewsEntity::class, HistoryGameEntity::class, MyVideoEntity::class, GamesCollectionEntity::class], version = 11, exportSchema = false)
|
||||
@TypeConverters(CountConverter::class,
|
||||
CommunityConverter::class,
|
||||
TimeConverter::class,
|
||||
@ -23,8 +24,16 @@ import com.halo.assistant.HaloApp
|
||||
ThumbnailConverter::class,
|
||||
TagStyleListConverter::class,
|
||||
StringArrayListConverter::class,
|
||||
ListStringConverter::class,
|
||||
CommunityVideoConverter::class,
|
||||
UserConverter::class)
|
||||
UserConverter::class,
|
||||
ImageInfoConverter::class,
|
||||
VideoInfoConverter::class,
|
||||
QuestionsConverter::class,
|
||||
MeConverter::class,
|
||||
SimpleGameListConverter::class,
|
||||
TagInfoListConverter::class,
|
||||
ActivityLabelListConverter::class)
|
||||
|
||||
abstract class HistoryDatabase : RoomDatabase() {
|
||||
|
||||
@ -33,6 +42,7 @@ abstract class HistoryDatabase : RoomDatabase() {
|
||||
abstract fun newsDao(): NewsHistoryDao
|
||||
abstract fun gameDao(): GameDao
|
||||
abstract fun videoHistoryDao(): VideoHistoryDao
|
||||
abstract fun gamesCollectionDao(): GamesCollectionDao
|
||||
|
||||
companion object {
|
||||
|
||||
@ -69,6 +79,47 @@ abstract class HistoryDatabase : RoomDatabase() {
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_7_8: Migration = object : Migration(7, 8) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("Alter TABLE ArticleEntity add images TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE ArticleEntity add imagesInfo TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE AnswerEntity add imagesInfo TEXT NOT NULL DEFAULT ''")
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_8_9: Migration = object : Migration(8, 9) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("Alter TABLE ArticleEntity add des TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE ArticleEntity add url TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE ArticleEntity add videoInfo TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE ArticleEntity add poster TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE ArticleEntity add length INTEGER NOT NULL DEFAULT 0")
|
||||
database.execSQL("Alter TABLE ArticleEntity add status TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE ArticleEntity add content TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE ArticleEntity add questions TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE AnswerEntity add des TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE AnswerEntity add url TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE AnswerEntity add videoInfo TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE AnswerEntity add poster TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE AnswerEntity add length INTEGER NOT NULL DEFAULT 0")
|
||||
database.execSQL("Alter TABLE AnswerEntity add status TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE AnswerEntity add content TEXT NOT NULL DEFAULT ''")
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_9_10: Migration = object : Migration(9, 10) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE GamesCollectionEntity (id TEXT NOT NULL PRIMARY KEY, tags TEXT, games TEXT, title TEXT NOT NULL, intro TEXT NOT NULL, cover TEXT NOT NULL, display TEXT NOT NULL, stamp TEXT NOT NULL, count TEXT, user TEXT, me TEXT, orderTag INTEGER NOT NULL)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_10_11: Migration = object : Migration(10, 11) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("Alter TABLE GamesCollectionEntity add activityTags TEXT DEFAULT ''")
|
||||
}
|
||||
}
|
||||
|
||||
val instance by lazy {
|
||||
Room.databaseBuilder(HaloApp.getInstance().application, HistoryDatabase::class.java, "USER_TRACK_HISTORY_DATABASE")
|
||||
.addMigrations(MIGRATION_2_3)
|
||||
@ -76,6 +127,10 @@ abstract class HistoryDatabase : RoomDatabase() {
|
||||
.addMigrations(MIGRATION_4_5)
|
||||
.addMigrations(MIGRATION_5_6)
|
||||
.addMigrations(MIGRATION_6_7)
|
||||
.addMigrations(MIGRATION_7_8)
|
||||
.addMigrations(MIGRATION_8_9)
|
||||
.addMigrations(MIGRATION_9_10)
|
||||
.addMigrations(MIGRATION_10_11)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +24,11 @@ object HistoryHelper {
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.articleDao().addArticle(articleEntity) } }
|
||||
}
|
||||
|
||||
fun insertGamesCollectionEntity(gamesCollectionDetailEntity: GamesCollectionDetailEntity) {
|
||||
val gamesCollectionEntity = convertGamesCollectionDetailToGamesCollection(gamesCollectionDetailEntity)
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.gamesCollectionDao().addGamesCollection(gamesCollectionEntity) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun insertGameEntity(gameEntity: GameEntity) {
|
||||
val historyGameEntity = convertGameEntityToHistoryGameEntity(gameEntity)
|
||||
@ -44,10 +49,9 @@ object HistoryHelper {
|
||||
historyGame.brief = updateEntity.brief
|
||||
historyGame.des = ""
|
||||
historyGame.icon = updateEntity.rawIcon ?: updateEntity.icon
|
||||
historyGame.iconSubscript = historyGame.iconSubscript
|
||||
historyGame.iconSubscript = updateEntity.iconSubscript
|
||||
historyGame.name = updateEntity.name
|
||||
historyGame.tagStyle = updateEntity.tagStyle
|
||||
historyGame.tag = updateEntity.tag
|
||||
return historyGame
|
||||
}
|
||||
|
||||
@ -69,48 +73,55 @@ object HistoryHelper {
|
||||
@JvmStatic
|
||||
fun insertNewsEntity(newsEntity: NewsEntity) {
|
||||
newsEntity.orderTag = System.currentTimeMillis()
|
||||
runOnIoThread { HistoryDatabase.instance.newsDao().addNews(newsEntity) }
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.newsDao().addNews(newsEntity) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deleteNewsEntity(newsId: String) {
|
||||
runOnIoThread { HistoryDatabase.instance.newsDao().deleteNews(NewsEntity().apply { id = newsId }) }
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.newsDao().deleteNews(NewsEntity().apply { id = newsId }) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deleteGameEntity(gameId: String) {
|
||||
runOnIoThread { HistoryDatabase.instance.gameDao().deleteGame(HistoryGameEntity(id = gameId)) }
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.gameDao().deleteGame(HistoryGameEntity(id = gameId)) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deleteArticleEntity(articleId: String) {
|
||||
runOnIoThread { HistoryDatabase.instance.articleDao().deleteArticle(ArticleEntity(id = articleId)) }
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.articleDao().deleteArticle(ArticleEntity(id = articleId)) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deleteAnswerEntity(answerId: String) {
|
||||
runOnIoThread { HistoryDatabase.instance.answerDao().deleteAnswer(AnswerEntity().apply { primaryKey = answerId }) }
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.answerDao().deleteAnswer(AnswerEntity().apply { primaryKey = answerId }) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deleteVideoEntity(videoId: String) {
|
||||
runOnIoThread { HistoryDatabase.instance.videoHistoryDao().deleteVideo(MyVideoEntity().apply { id = videoId }) }
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.videoHistoryDao().deleteVideo(MyVideoEntity().apply { id = videoId }) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deleteAttentionVideoRecord() {
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.videoHistoryDao().deleteAttentionVideoRecord() } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deleteGamesCollectionEntity(gameCollectionId: String) {
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.gamesCollectionDao().deleteGamesCollection(GamesCollectionEntity(id = gameCollectionId)) }}
|
||||
}
|
||||
|
||||
|
||||
@JvmStatic
|
||||
fun emptyDatabase() {
|
||||
runOnIoThread { HistoryDatabase.instance.clearAllTables() }
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.clearAllTables() } }
|
||||
}
|
||||
|
||||
private fun convertArticleDetailToArticle(articleDetailEntity: ArticleDetailEntity): ArticleEntity {
|
||||
val articleEntity = ArticleEntity()
|
||||
|
||||
articleEntity.id = articleDetailEntity.id
|
||||
articleEntity.brief = articleDetailEntity.content.
|
||||
removeVideoContent().
|
||||
removeInsertedContent().
|
||||
clearHtmlFormatCompletely().
|
||||
replace(" +".toRegex()," ")
|
||||
articleEntity.brief = articleDetailEntity.content.removeVideoContent().removeInsertedContent().clearHtmlFormatCompletely().replace(" +".toRegex(), " ")
|
||||
articleEntity.count = articleDetailEntity.count
|
||||
articleDetailEntity.community.id = articleDetailEntity.communityId
|
||||
articleEntity.community = articleDetailEntity.community
|
||||
@ -118,6 +129,9 @@ object HistoryHelper {
|
||||
articleEntity.title = articleDetailEntity.title
|
||||
articleEntity.user = articleDetailEntity.user
|
||||
articleEntity.orderTag = System.currentTimeMillis()
|
||||
articleEntity.images = articleDetailEntity.images
|
||||
articleEntity.imagesInfo = articleDetailEntity.imagesInfo
|
||||
articleEntity.videos = articleDetailEntity.videos
|
||||
|
||||
return articleEntity
|
||||
}
|
||||
@ -132,14 +146,41 @@ object HistoryHelper {
|
||||
answerEntity.vote = answerDetailEntity.vote
|
||||
answerEntity.user = answerDetailEntity.user
|
||||
answerEntity.orderTag = System.currentTimeMillis()
|
||||
answerEntity.brief = answerDetailEntity.content.
|
||||
removeVideoContent().
|
||||
removeInsertedContent().
|
||||
clearHtmlFormatCompletely().
|
||||
replace(" +".toRegex(), " ")
|
||||
answerEntity.brief = answerDetailEntity.content.removeVideoContent().removeInsertedContent().clearHtmlFormatCompletely().replace(" +".toRegex(), " ")
|
||||
answerEntity.time = answerDetailEntity.time
|
||||
answerEntity.images = answerDetailEntity.images
|
||||
answerEntity.imagesInfo = answerDetailEntity.imagesInfo
|
||||
answerEntity.videos = answerDetailEntity.videos
|
||||
|
||||
return answerEntity
|
||||
}
|
||||
|
||||
private fun convertGamesCollectionDetailToGamesCollection(gamesCollectionDetailEntity: GamesCollectionDetailEntity): GamesCollectionEntity {
|
||||
val gamesCollectionEntity = GamesCollectionEntity()
|
||||
|
||||
gamesCollectionEntity.id = gamesCollectionDetailEntity.id
|
||||
gamesCollectionEntity.tags = gamesCollectionDetailEntity.tags
|
||||
gamesCollectionEntity.activityTags = gamesCollectionDetailEntity.activityTags
|
||||
gamesCollectionDetailEntity.games?.take(3)?.map { it.toSimpleGame() }?.run {
|
||||
gamesCollectionEntity.games = ArrayList(this)
|
||||
}
|
||||
gamesCollectionEntity.title = gamesCollectionDetailEntity.title
|
||||
gamesCollectionEntity.intro = gamesCollectionDetailEntity.intro
|
||||
gamesCollectionEntity.cover = gamesCollectionDetailEntity.cover
|
||||
gamesCollectionEntity.display = gamesCollectionDetailEntity.display
|
||||
gamesCollectionEntity.orderTag = System.currentTimeMillis()
|
||||
gamesCollectionEntity.stamp = gamesCollectionDetailEntity.stamp
|
||||
gamesCollectionEntity.count = gamesCollectionDetailEntity.count
|
||||
gamesCollectionDetailEntity.user?.run {
|
||||
gamesCollectionEntity.user = User(
|
||||
id = id ?: "",
|
||||
name = name ?: "",
|
||||
icon = icon ?: "",
|
||||
badge = badge)
|
||||
}
|
||||
gamesCollectionEntity.me = gamesCollectionDetailEntity.me
|
||||
|
||||
return gamesCollectionEntity
|
||||
}
|
||||
|
||||
}
|
||||
38
app/src/main/java/com/gh/common/image/EmptyDecoder.kt
Normal file
38
app/src/main/java/com/gh/common/image/EmptyDecoder.kt
Normal file
@ -0,0 +1,38 @@
|
||||
package com.gh.common.image
|
||||
|
||||
import com.facebook.imagepipeline.common.ImageDecodeOptions
|
||||
import com.facebook.imagepipeline.decoder.ImageDecoder
|
||||
import com.facebook.imagepipeline.image.CloseableImage
|
||||
import com.facebook.imagepipeline.image.EncodedImage
|
||||
import com.facebook.imagepipeline.image.QualityInfo
|
||||
|
||||
class EmptyDecoder : ImageDecoder {
|
||||
override fun decode(
|
||||
encodedImage: EncodedImage,
|
||||
length: Int,
|
||||
qualityInfo: QualityInfo,
|
||||
options: ImageDecodeOptions
|
||||
): CloseableImage {
|
||||
return object : CloseableImage() {
|
||||
override fun close() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
override fun getWidth(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun getHeight(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun getSizeInBytes(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun isClosed(): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
32
app/src/main/java/com/gh/common/json/JsonBuilder.kt
Normal file
32
app/src/main/java/com/gh/common/json/JsonBuilder.kt
Normal file
@ -0,0 +1,32 @@
|
||||
package com.gh.common.json
|
||||
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.util.*
|
||||
|
||||
//Source: https://stackoverflow.com/questions/41861449/kotlin-dsl-for-creating-json-objects-without-creating-garbage
|
||||
|
||||
fun json(build: JsonObjectBuilder.() -> Unit): JSONObject {
|
||||
return JsonObjectBuilder().json(build)
|
||||
}
|
||||
|
||||
class JsonObjectBuilder {
|
||||
private val deque: Deque<JSONObject> = ArrayDeque()
|
||||
|
||||
fun json(build: JsonObjectBuilder.() -> Unit): JSONObject {
|
||||
deque.push(JSONObject())
|
||||
this.build()
|
||||
return deque.pop()
|
||||
}
|
||||
|
||||
infix fun <T> String.to(value: T) {
|
||||
// wrap value into json block if it is a lambda
|
||||
val wrapped = when (value) {
|
||||
is Function0<*> -> json { value.invoke() }
|
||||
is Array<*> -> JSONArray().apply { value.forEach { put(it) } }
|
||||
else -> value
|
||||
}
|
||||
|
||||
deque.peek().put(this, wrapped)
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,7 @@ import android.os.Parcelable
|
||||
import androidx.annotation.Keep
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.util.*
|
||||
|
||||
@Keep
|
||||
|
||||
105
app/src/main/java/com/gh/common/loghub/LoghubHelper.kt
Normal file
105
app/src/main/java/com/gh/common/loghub/LoghubHelper.kt
Normal file
@ -0,0 +1,105 @@
|
||||
package com.gh.common.loghub
|
||||
|
||||
import com.aliyun.sls.android.producer.*
|
||||
import com.gh.common.util.PackageFlavorHelper
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
|
||||
/**
|
||||
* 上传阿里云日志辅助类
|
||||
*/
|
||||
object LoghubHelper {
|
||||
|
||||
private const val ACCESS_KEY_ID = "LTAIV3i0sNc4TPK1"
|
||||
private const val ACCESS_KEY_SECRET = "8dKtTPeE5WYA6ZCeuIBcIVp7eB0ir4"
|
||||
private const val ENDPOINT = "cn-qingdao.log.aliyuncs.com"
|
||||
private const val PROJECT = "ghzs"
|
||||
|
||||
private val mClientMaps by lazy { hashMapOf<String, LogProducerClient>() }
|
||||
|
||||
fun uploadLog(log: Log, logStore: String) {
|
||||
getClient(logStore)?.addLog(log)
|
||||
}
|
||||
|
||||
fun uploadLogs(logs: List<Log>, logStore: String) {
|
||||
logs.forEach {
|
||||
uploadLog(it, logStore)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun getClient(logStore: String): LogProducerClient? {
|
||||
if (!mClientMaps.containsKey(logStore)) {
|
||||
mClientMaps[logStore] = createClient(logStore)
|
||||
}
|
||||
return mClientMaps[logStore]
|
||||
}
|
||||
|
||||
private fun createClient(logStore: String): LogProducerClient {
|
||||
val config = LogProducerConfig(
|
||||
HaloApp.getInstance().applicationContext,
|
||||
ENDPOINT,
|
||||
PROJECT,
|
||||
logStore,
|
||||
ACCESS_KEY_ID,
|
||||
ACCESS_KEY_SECRET
|
||||
).apply {
|
||||
// 1 开启断点续传功能, 0 关闭
|
||||
// 每次发送前会把日志保存到本地的binlog文件,只有发送成功才会删除,保证日志上传At Least Once
|
||||
setPersistent(1)
|
||||
// 持久化的文件名,需要保证文件所在的文件夹已创建。配置多个客户端时,不应设置相同文件
|
||||
setPersistentFilePath(HaloApp.getInstance().application.filesDir.absolutePath + "/${logStore}.dat")
|
||||
// 是否每次AddLog强制刷新,高可靠性场景建议打开
|
||||
setPersistentForceFlush(1)
|
||||
// 持久化文件滚动个数,建议设置成10。
|
||||
setPersistentMaxFileCount(10)
|
||||
// 每个持久化文件的大小,建议设置成1-10M
|
||||
setPersistentMaxFileSize(1024 * 1024)
|
||||
// 本地最多缓存的日志数,不建议超过1M,通常设置为65536即可
|
||||
setPersistentMaxLogCount(65536)
|
||||
|
||||
//网络连接超时时间,整数,单位秒,默认为10
|
||||
setConnectTimeoutSec(15)
|
||||
//日志发送超时时间,整数,单位秒,默认为15
|
||||
setSendTimeoutSec(15)
|
||||
//flusher线程销毁最大等待时间,整数,单位秒,默认为1
|
||||
setDestroyFlusherWaitSec(2)
|
||||
//sender线程池销毁最大等待时间,整数,单位秒,默认为1
|
||||
setDestroySenderWaitSec(2)
|
||||
//日志时间与本机时间之差,超过该大小后会根据 `drop_delay_log` 选项进行处理。
|
||||
//一般此种情况只会在设置persistent的情况下出现,即设备下线后,超过几天/数月启动,发送退出前未发出的日志
|
||||
//整数,单位秒,默认为7*24*3600,即7天
|
||||
setMaxLogDelayTime(7 * 24 * 3600)
|
||||
//对于超过 `max_log_delay_time` 日志的处理策略
|
||||
//0 不丢弃,把日志时间修改为当前时间; 1 丢弃,默认为 1 (丢弃)
|
||||
setDropDelayLog(0)
|
||||
//是否丢弃鉴权失败的日志,0 不丢弃,1丢弃
|
||||
//默认为 0,即不丢弃
|
||||
setDropUnauthorizedLog(0)
|
||||
}
|
||||
|
||||
return if (!PackageFlavorHelper.IS_TEST_FLAVOR) {
|
||||
LogProducerClient(config)
|
||||
} else {
|
||||
LogProducerClient(config) { resultCode, reqId, errorMessage, logBytes, compressedBytes ->
|
||||
// resultCode 返回结果代码
|
||||
// reqId 请求id
|
||||
// errorMessage 错误信息,没有为null
|
||||
// logBytes 日志大小
|
||||
// compressedBytes 压缩后日志大小
|
||||
Utils.log(
|
||||
"LoghubHelper -> ${
|
||||
String.format(
|
||||
"%s %s %s %s %s",
|
||||
LogProducerResult.fromInt(resultCode),
|
||||
reqId,
|
||||
errorMessage,
|
||||
logBytes,
|
||||
compressedBytes
|
||||
)
|
||||
}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,14 +2,12 @@ package com.gh.common.loghub
|
||||
|
||||
import android.app.Application
|
||||
import androidx.annotation.Keep
|
||||
import com.aliyun.sls.android.sdk.model.Log
|
||||
import com.aliyun.sls.android.sdk.model.LogGroup
|
||||
import com.aliyun.sls.android.producer.Log
|
||||
import com.gh.common.exposure.ExposureEntity
|
||||
import com.gh.common.exposure.meta.Meta
|
||||
import com.gh.common.util.tryWithDefaultCatch
|
||||
import com.gh.loghub.LoghubHelper
|
||||
import org.json.JSONObject
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ExecutorService
|
||||
|
||||
object LoghubUtils {
|
||||
|
||||
@ -17,22 +15,25 @@ object LoghubUtils {
|
||||
private lateinit var mApplication: Application
|
||||
|
||||
private val loghubEventSet by lazy { hashSetOf<LoghubEvent>() }
|
||||
private val loghubEventExecutor by lazy { Executors.newSingleThreadExecutor() }
|
||||
private var loghubEventExecutor: ExecutorService? = null
|
||||
private val loghubEventDao by lazy { LoghubDatabase.buildDatabase(mApplication).logHubEventDao() }
|
||||
|
||||
@JvmStatic
|
||||
fun init(application: Application) {
|
||||
fun init(application: Application, executor: ExecutorService) {
|
||||
mApplication = application
|
||||
|
||||
loghubEventExecutor.execute {
|
||||
val eventList = loghubEventDao.getAll()
|
||||
loghubEventSet.addAll(eventList)
|
||||
loghubEventExecutor = executor
|
||||
loghubEventExecutor?.execute {
|
||||
tryWithDefaultCatch {
|
||||
val eventList = loghubEventDao.getAll()
|
||||
loghubEventSet.addAll(eventList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun log(logJson: JSONObject, logStore: String, forcedUpload: Boolean) {
|
||||
loghubEventExecutor.execute {
|
||||
loghubEventExecutor?.execute {
|
||||
try {
|
||||
val event = LoghubEvent(time = (System.currentTimeMillis() / 1000L).toString(), content = logJson.toString(), logStore = logStore)
|
||||
loghubEventSet.add(event)
|
||||
@ -49,7 +50,7 @@ object LoghubUtils {
|
||||
|
||||
@JvmStatic
|
||||
fun log(jsonString: String, logStore: String, forcedUpload: Boolean) {
|
||||
loghubEventExecutor.execute {
|
||||
loghubEventExecutor?.execute {
|
||||
try {
|
||||
val event = LoghubEvent(time = (System.currentTimeMillis() / 1000L).toString(), content = jsonString, logStore = logStore)
|
||||
loghubEventSet.add(event)
|
||||
@ -64,49 +65,37 @@ object LoghubUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private fun uploadLogGroup(logGroup: LogGroup, logStore: String) {
|
||||
LoghubHelper.getInstance().uploadLogGroup(logGroup, logStore)
|
||||
}
|
||||
|
||||
fun commitSavedLoghubEvents() {
|
||||
loghubEventExecutor.execute {
|
||||
loghubEventExecutor?.execute {
|
||||
// TODO 初始化 loghubHelper 去掉这个 tryCatch 块
|
||||
tryWithDefaultCatch {
|
||||
if (loghubEventSet.isEmpty()) return@execute
|
||||
|
||||
val exposureList = loghubEventSet.toList()
|
||||
|
||||
createLogGroupAndUpload()
|
||||
uploadEvents()
|
||||
loghubEventSet.removeAll(exposureList)
|
||||
loghubEventDao.deleteMany(exposureList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createLogGroupAndUpload() {
|
||||
val logGroupHashMap = hashMapOf<String, LogGroup>()
|
||||
|
||||
private fun uploadEvents() {
|
||||
for (event in loghubEventSet) {
|
||||
if (!logGroupHashMap.containsKey(event.logStore)) {
|
||||
logGroupHashMap[event.logStore] = LogGroup("sls android", "no ip")
|
||||
}
|
||||
|
||||
val log = Log()
|
||||
// 特殊处理,以下logStore不需要用content包裹数据
|
||||
if (event.logStore == "collection" || event.logStore == "common" || event.logStore == "halo-api-device-installed") {
|
||||
val contentJson = JSONObject(event.content)
|
||||
for (key in contentJson.keys()) {
|
||||
log.PutContent(key, contentJson.get(key).toString())
|
||||
log.putContent(key, contentJson.get(key).toString())
|
||||
}
|
||||
} else {
|
||||
log.PutContent("current time ", event.time)
|
||||
log.PutContent("content", event.content)
|
||||
log.putContent("current time ", event.time)
|
||||
log.putContent("content", event.content)
|
||||
}
|
||||
logGroupHashMap[event.logStore]?.PutLog(log)
|
||||
}
|
||||
|
||||
for ((logStore, logGroup) in logGroupHashMap) {
|
||||
uploadLogGroup(logGroup, logStore)
|
||||
LoghubHelper.uploadLog(log, event.logStore)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -12,13 +12,15 @@ import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.core.view.ViewCompat
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.gh.common.util.DisplayUtils
|
||||
import com.gh.common.util.ImageUtils
|
||||
import com.gh.common.util.doOnEnd
|
||||
import com.gh.common.util.doOnStart
|
||||
import com.gh.gamecenter.R
|
||||
import kotlinx.android.synthetic.main.view_notifier.view.*
|
||||
|
||||
class NotifierView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0)
|
||||
: FrameLayout(context, attrs, defStyle) {
|
||||
@ -65,13 +67,21 @@ class NotifierView @JvmOverloads constructor(context: Context, attrs: AttributeS
|
||||
|
||||
var showVerticalTranslateAnimation: Boolean = true
|
||||
|
||||
private var mCardView: CardView
|
||||
private var mContentTv: TextView
|
||||
private var mIconIv: SimpleDraweeView
|
||||
|
||||
init {
|
||||
inflate(context, R.layout.view_notifier, this)
|
||||
|
||||
ViewCompat.setTranslationZ(this, Integer.MAX_VALUE.toFloat())
|
||||
|
||||
cardView.scaleX = SCALE_MINI
|
||||
cardView.scaleY = SCALE_MINI
|
||||
mCardView = findViewById(R.id.cardView)
|
||||
mContentTv = findViewById(R.id.tvText)
|
||||
mIconIv = findViewById(R.id.ivIcon)
|
||||
|
||||
mCardView.scaleX = SCALE_MINI
|
||||
mCardView.scaleY = SCALE_MINI
|
||||
|
||||
verticalAnimationOffset = dp2px(100F)
|
||||
|
||||
@ -113,7 +123,7 @@ class NotifierView @JvmOverloads constructor(context: Context, attrs: AttributeS
|
||||
expandAnimator.duration = DEFAULT_DURATION
|
||||
expandAnimator.addUpdateListener { a ->
|
||||
val progress = a?.animatedValue as Float
|
||||
tvText.width = (textWidth * progress).toInt()
|
||||
mContentTv.width = (textWidth * progress).toInt()
|
||||
}
|
||||
expandAnimator.doOnEnd {
|
||||
enableSwipeToDismiss()
|
||||
@ -124,46 +134,46 @@ class NotifierView @JvmOverloads constructor(context: Context, attrs: AttributeS
|
||||
shrinkAnimator.duration = DEFAULT_DURATION
|
||||
shrinkAnimator.addUpdateListener { a ->
|
||||
val progress = a?.animatedValue as Float
|
||||
tvText.width = (textWidth * progress).toInt()
|
||||
mContentTv.width = (textWidth * progress).toInt()
|
||||
}
|
||||
shrinkAnimator.doOnEnd {
|
||||
val lp = FrameLayout.LayoutParams(cardView.layoutParams)
|
||||
val lp = FrameLayout.LayoutParams(mCardView.layoutParams)
|
||||
lp.gravity = Gravity.NO_GRAVITY
|
||||
cardView.layoutParams = lp
|
||||
mCardView.layoutParams = lp
|
||||
|
||||
disableSwipeToDismiss()
|
||||
}
|
||||
|
||||
translateToLeftAnimator = ObjectAnimator.ofFloat(cardView, "translationX", veryRight, centerX)
|
||||
translateToLeftAnimator = ObjectAnimator.ofFloat(mCardView, "translationX", veryRight, centerX)
|
||||
translateToLeftAnimator.duration = DEFAULT_DURATION
|
||||
translateToLeftAnimator.doOnEnd {
|
||||
onShowListener?.onShow()
|
||||
|
||||
val lp = FrameLayout.LayoutParams(cardView.layoutParams)
|
||||
val lp = FrameLayout.LayoutParams(mCardView.layoutParams)
|
||||
lp.gravity = Gravity.CENTER_HORIZONTAL
|
||||
cardView.layoutParams = lp
|
||||
cardView.translationX = 0f
|
||||
mCardView.layoutParams = lp
|
||||
mCardView.translationX = 0f
|
||||
|
||||
expandAnimator.start()
|
||||
}
|
||||
|
||||
translateToRightAnimator = ObjectAnimator.ofFloat(cardView, "translationX", centerX, veryRight)
|
||||
translateToRightAnimator = ObjectAnimator.ofFloat(mCardView, "translationX", centerX, veryRight)
|
||||
translateToRightAnimator.duration = DEFAULT_DURATION
|
||||
|
||||
translateUpAnimator = ObjectAnimator.ofFloat(cardView, "translationY", veryBottom + verticalAnimationOffset, veryBottom)
|
||||
translateUpAnimator = ObjectAnimator.ofFloat(mCardView, "translationY", veryBottom + verticalAnimationOffset, veryBottom)
|
||||
translateUpAnimator.duration = DEFAULT_DURATION
|
||||
translateUpAnimator.doOnStart { cardView.translationX = veryRight }
|
||||
translateUpAnimator.doOnStart { mCardView.translationX = veryRight }
|
||||
|
||||
translateDownAnimator = ObjectAnimator.ofFloat(cardView, "translationY", veryBottom, veryBottom + verticalAnimationOffset)
|
||||
translateDownAnimator = ObjectAnimator.ofFloat(mCardView, "translationY", veryBottom, veryBottom + verticalAnimationOffset)
|
||||
translateDownAnimator.duration = DEFAULT_DURATION
|
||||
|
||||
zoomInAnimator = ObjectAnimator.ofPropertyValuesHolder(cardView, PropertyValuesHolder.ofFloat("scaleX", SCALE_DEFAULT),
|
||||
zoomInAnimator = ObjectAnimator.ofPropertyValuesHolder(mCardView, PropertyValuesHolder.ofFloat("scaleX", SCALE_DEFAULT),
|
||||
PropertyValuesHolder.ofFloat("scaleY", SCALE_DEFAULT))
|
||||
zoomInAnimator.duration = DEFAULT_DURATION
|
||||
zoomInAnimator.doOnStart { cardView.translationX = veryRight }
|
||||
zoomInAnimator.doOnStart { cardView.translationY = veryBottom }
|
||||
zoomInAnimator.doOnStart { mCardView.translationX = veryRight }
|
||||
zoomInAnimator.doOnStart { mCardView.translationY = veryBottom }
|
||||
|
||||
zoomOutAnimator = ObjectAnimator.ofPropertyValuesHolder(cardView, PropertyValuesHolder.ofFloat("scaleX", SCALE_MINI),
|
||||
zoomOutAnimator = ObjectAnimator.ofPropertyValuesHolder(mCardView, PropertyValuesHolder.ofFloat("scaleX", SCALE_MINI),
|
||||
PropertyValuesHolder.ofFloat("scaleY", SCALE_MINI))
|
||||
zoomOutAnimator.duration = DEFAULT_DURATION
|
||||
zoomOutAnimator.doOnEnd { removeFromParent() }
|
||||
@ -177,7 +187,7 @@ class NotifierView @JvmOverloads constructor(context: Context, attrs: AttributeS
|
||||
}
|
||||
|
||||
private fun enableSwipeToDismiss() {
|
||||
cardView?.setOnTouchListener(SwipeDismissTouchListener(cardView, object : SwipeDismissTouchListener.DismissCallbacks {
|
||||
mCardView?.setOnTouchListener(SwipeDismissTouchListener(mCardView, object : SwipeDismissTouchListener.DismissCallbacks {
|
||||
override fun canDismiss(): Boolean {
|
||||
return true
|
||||
}
|
||||
@ -193,7 +203,7 @@ class NotifierView @JvmOverloads constructor(context: Context, attrs: AttributeS
|
||||
}
|
||||
|
||||
private fun disableSwipeToDismiss() {
|
||||
cardView?.setOnTouchListener(null)
|
||||
mCardView?.setOnTouchListener(null)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
@ -264,13 +274,13 @@ class NotifierView @JvmOverloads constructor(context: Context, attrs: AttributeS
|
||||
|
||||
fun setText(text: String?) {
|
||||
if (!TextUtils.isEmpty(text)) {
|
||||
tvText.text = text
|
||||
tvText.measure(0, 0)
|
||||
textWidth = tvText.measuredWidth
|
||||
tvText.width = 0
|
||||
mContentTv.text = text
|
||||
mContentTv.measure(0, 0)
|
||||
textWidth = mContentTv.measuredWidth
|
||||
mContentTv.width = 0
|
||||
|
||||
cardView.measure(0, 0)
|
||||
cardViewWidth = cardView.measuredWidth
|
||||
mCardView.measure(0, 0)
|
||||
cardViewWidth = mCardView.measuredWidth
|
||||
}
|
||||
}
|
||||
|
||||
@ -284,7 +294,7 @@ class NotifierView @JvmOverloads constructor(context: Context, attrs: AttributeS
|
||||
}
|
||||
|
||||
fun setIcon(url: String) {
|
||||
ImageUtils.display(ivIcon, url)
|
||||
ImageUtils.display(mIconIv, url)
|
||||
}
|
||||
|
||||
private fun dp2px(dp: Float): Int {
|
||||
|
||||
@ -5,13 +5,14 @@ import android.database.ContentObserver
|
||||
import android.media.AudioManager
|
||||
import android.os.Handler
|
||||
import com.gh.common.util.tryCatchInRelease
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
class VolumeObserver(var context: Context, handler: Handler, var callback: MuteCallback? = null)
|
||||
: ContentObserver(handler) {
|
||||
class VolumeObserver(var callback: MuteCallback? = null)
|
||||
: ContentObserver(Handler()) {
|
||||
var previousVolume: Int = 0
|
||||
|
||||
init {
|
||||
val audio = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
val audio = HaloApp.getInstance().application.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
// 部分设备的 audioManager getStreamVolume 内部会触发空指针 :(
|
||||
tryCatchInRelease { previousVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC) }
|
||||
}
|
||||
@ -19,8 +20,13 @@ class VolumeObserver(var context: Context, handler: Handler, var callback: MuteC
|
||||
override fun onChange(selfChange: Boolean) {
|
||||
super.onChange(selfChange)
|
||||
|
||||
val audio = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
val currentVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC)
|
||||
val audio = HaloApp.getInstance().application.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
var currentVolume = 0
|
||||
|
||||
tryCatchInRelease {
|
||||
// 部分设备(Meizu 7.1.2 M6 Note)的 audioManager getStreamVolume 内部会触发空指针 :(
|
||||
currentVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC)
|
||||
}
|
||||
|
||||
val delta = previousVolume - currentVolume
|
||||
|
||||
|
||||
@ -5,7 +5,6 @@ import com.gh.common.util.ApkActiveUtils
|
||||
import com.gh.common.util.RandomUtils
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.Observable
|
||||
|
||||
// 热门卡牌的仓库
|
||||
@ -16,7 +15,7 @@ object RemenkapaiRepository {
|
||||
@JvmStatic
|
||||
fun getRemenkapai(size: Int): Observable<List<GameEntity>> {
|
||||
return if (remenkapaiList.isEmpty()) {
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application).sensitiveApi.remenkapai
|
||||
RetrofitManager.getInstance().api.remenkapai
|
||||
.map(RegionSettingHelper.filterGame)
|
||||
.map { gameList -> filterEntityWithoutApk(gameList) }
|
||||
.map { pickRandomSizeEntity(size) }
|
||||
|
||||
@ -39,7 +39,7 @@ object ReservationRepository {
|
||||
@JvmStatic
|
||||
fun refreshReservations() {
|
||||
if (CheckLoginUtils.isLogin()) {
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application).api
|
||||
RetrofitManager.getInstance().api
|
||||
.getAllTheGameReservations(UserManager.getInstance().userId, Utils.getTime(HaloApp.getInstance().application))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
||||
@ -2,19 +2,17 @@ package com.gh.common.simulator
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.g00fy2.versioncompare.Version
|
||||
import com.gh.common.AppExecutor.uiExecutor
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.dialog.TrackableDialog
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.util.PackageInstaller.getDownloadPath
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.ApkEntity
|
||||
@ -23,6 +21,7 @@ import com.gh.gamecenter.entity.TrackableEntity
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.download.*
|
||||
import com.lightgame.utils.Utils
|
||||
import java.lang.ref.WeakReference
|
||||
import java.text.DecimalFormat
|
||||
|
||||
class SimulatorDownloadManager private constructor() {
|
||||
@ -34,6 +33,7 @@ class SimulatorDownloadManager private constructor() {
|
||||
private var appProgressFilling: View? = null
|
||||
private var appProgressAnchor: View? = null
|
||||
private var downloadDialog: Dialog? = null
|
||||
private var mContextRef: WeakReference<Context>? = null
|
||||
|
||||
private var simulatorLocation: SimulatorLocation? = null
|
||||
private var simulator: SimulatorEntity? = null
|
||||
@ -70,8 +70,11 @@ class SimulatorDownloadManager private constructor() {
|
||||
val fileName = downloadEntity.path.substring(downloadEntity.path.lastIndexOf('/') + 1).removeSuffix(".apk")
|
||||
val startTime = downloadEntity.getMetaExtra(Constants.SIMULATOR_DOWNLOAD_START_TIME)
|
||||
LogUtils.uploadSimulatorDownload("simulator_download_complete", fileName, simulator?.id, downloadEntity.name, gameId, locationStr, downloadType, startTime)
|
||||
DownloadManager.getInstance(HaloApp.getInstance().application).cancel(downloadEntity.url, false, true)
|
||||
downloadDialog?.dismiss()
|
||||
DownloadManager.getInstance().cancel(downloadEntity.url, false, true)
|
||||
val activity = mContextRef?.get() as? AppCompatActivity
|
||||
if (activity?.isFinishing == false) {
|
||||
downloadDialog?.dismiss()
|
||||
}
|
||||
}
|
||||
DownloadStatus.neterror == downloadEntity.status -> {
|
||||
ToastUtils.showToast("网络不稳定,下载任务已暂停")
|
||||
@ -82,6 +85,15 @@ class SimulatorDownloadManager private constructor() {
|
||||
DownloadStatus.notfound == downloadEntity.status -> {
|
||||
ToastUtils.showToast("下载链接异常,请稍后重试")
|
||||
}
|
||||
DownloadStatus.uncertificated == downloadEntity.status -> {
|
||||
ToastUtils.showToast("请先进行实名认证")
|
||||
}
|
||||
DownloadStatus.unqualified == downloadEntity.status -> {
|
||||
ToastUtils.showToast("未成年人暂不允许在此时间下载游戏")
|
||||
}
|
||||
DownloadStatus.unavailable == downloadEntity.status -> {
|
||||
ToastUtils.showToast("该游戏未接入防沉迷系统,暂不支持下载")
|
||||
}
|
||||
DownloadStatus.hijack == downloadEntity.status -> {
|
||||
ToastUtils.showToast("网络劫持,请稍后重试")
|
||||
}
|
||||
@ -94,7 +106,9 @@ class SimulatorDownloadManager private constructor() {
|
||||
showDownloadDialog(context, simulator, location, "", "", null)
|
||||
}
|
||||
|
||||
fun showDownloadDialog(context: Context, simulator: SimulatorEntity?, location: SimulatorLocation, gameId: String = "", gameName: String = "", cancelCallback: (() -> Unit)? = null) {
|
||||
fun showDownloadDialog(context: Context?, simulator: SimulatorEntity?, location: SimulatorLocation, gameId: String = "", gameName: String = "", cancelCallback: (() -> Unit)? = null) {
|
||||
if (context == null) return
|
||||
mContextRef = WeakReference(context)
|
||||
this.simulatorLocation = location
|
||||
this.simulator = simulator
|
||||
this.gameId = gameId
|
||||
@ -105,7 +119,7 @@ class SimulatorDownloadManager private constructor() {
|
||||
return
|
||||
}
|
||||
val isInstalled = PackageUtils.isInstalledFromAllPackage(context, simulator?.apk?.packageName)
|
||||
val versionFromInstalledApp = PackageUtils.getVersionByPackage(simulator?.apk?.packageName)
|
||||
val versionFromInstalledApp = PackageUtils.getVersionNameByPackageName(simulator?.apk?.packageName)
|
||||
val shouldShowUpdate = Version(simulator?.apk?.version).isHigherThan(versionFromInstalledApp)
|
||||
val title = if (shouldShowUpdate && isInstalled) "更新模拟器" else "安装模拟器"
|
||||
val message = if (shouldShowUpdate && isInstalled) "检测到模拟器存在更高版本,是否前往更新" else "模拟器游戏需要先下载安装对应的模拟器,才可以运行"
|
||||
@ -117,15 +131,30 @@ class SimulatorDownloadManager private constructor() {
|
||||
key = if (shouldShowUpdate && isInstalled) "更新弹窗" else "下载弹窗",
|
||||
logShowEvent = true
|
||||
)
|
||||
DialogUtils.showNewAlertDialog(context, title, message, negativeText, positiveText, trackableEntity, Gravity.LEFT, {
|
||||
if (shouldShowUpdate && isInstalled) {
|
||||
cancelCallback?.invoke()
|
||||
MtaHelper.onEvent(trackableEntity.event, trackableEntity.key, "点击下次再说")
|
||||
}
|
||||
}, {
|
||||
showDownloadingDialog(context, simulator)
|
||||
MtaHelper.onEvent(trackableEntity.event, trackableEntity.key, if (shouldShowUpdate && isInstalled) "点击更新" else "点击下载")
|
||||
})
|
||||
DialogHelper.showDialog(
|
||||
context,
|
||||
title,
|
||||
message,
|
||||
positiveText,
|
||||
negativeText,
|
||||
trackMtaEvent = true,
|
||||
cancelClickCallback = {
|
||||
if (shouldShowUpdate && isInstalled) {
|
||||
cancelCallback?.invoke()
|
||||
MtaHelper.onEvent(trackableEntity.event, trackableEntity.key, "点击下次再说")
|
||||
}
|
||||
},
|
||||
confirmClickCallback = {
|
||||
showDownloadingDialog(context, simulator)
|
||||
MtaHelper.onEvent(
|
||||
trackableEntity.event,
|
||||
trackableEntity.key,
|
||||
if (shouldShowUpdate && isInstalled) "点击更新" else "点击下载"
|
||||
)
|
||||
},
|
||||
mtaEvent = trackableEntity.event, mtaKey = trackableEntity.key,
|
||||
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
|
||||
)
|
||||
}
|
||||
|
||||
private fun showDownloadingDialog(context: Context, simulator: SimulatorEntity?) {
|
||||
@ -151,13 +180,16 @@ class SimulatorDownloadManager private constructor() {
|
||||
appProgressFilling = view.findViewById(R.id.progress_filling)
|
||||
|
||||
view.findViewById<View>(R.id.app_tv_cancel).setOnClickListener {
|
||||
DownloadManager.getInstance(context).pause(simulator?.apk?.url)
|
||||
DownloadManager.getInstance().pause(simulator?.apk?.url)
|
||||
MtaHelper.onEvent("模拟器下载", "下载中弹窗", "点击关闭")
|
||||
downloadDialog?.dismiss()
|
||||
val activity = mContextRef?.get() as? AppCompatActivity
|
||||
if (activity?.isFinishing == false) {
|
||||
downloadDialog?.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
downloadDialog?.setOnDismissListener {
|
||||
DownloadManager.getInstance(context).removeObserver(dataWatcher)
|
||||
DownloadManager.getInstance().removeObserver(dataWatcher)
|
||||
}
|
||||
|
||||
downloadDialog?.setCanceledOnTouchOutside(false)
|
||||
@ -169,51 +201,62 @@ class SimulatorDownloadManager private constructor() {
|
||||
params?.width = context.resources.displayMetrics.widthPixels - DisplayUtils.dip2px(60f)
|
||||
downloadDialog?.window?.attributes = params
|
||||
|
||||
download(context, simulator)
|
||||
download(simulator)
|
||||
}
|
||||
|
||||
private fun showNoneEmulatorDialog(context: Context) {
|
||||
val dialog = DialogUtils.showNewAlertDialog(context, "安装模拟器", "模拟器游戏需要先下载安装对应的模拟器,才可以运行", "取消", "暂无下载", null, {
|
||||
ToastUtils.showToast("该模拟器暂未提供下载")
|
||||
})
|
||||
dialog?.window?.findViewById<TextView>(R.id.confirm)?.setTextColor(ContextCompat.getColor(context, R.color.text_cccccc))
|
||||
DialogHelper.showDialog(
|
||||
context,
|
||||
"安装模拟器",
|
||||
"模拟器游戏需要先下载安装对应的模拟器,才可以运行",
|
||||
"暂无下载",
|
||||
"取消",
|
||||
confirmClickCallback = {
|
||||
ToastUtils.showToast("该模拟器暂未提供下载")
|
||||
},
|
||||
uiModificationCallback = { binding ->
|
||||
binding.confirmTv.setTextColor(R.color.text_body.toColor())
|
||||
},
|
||||
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||||
}
|
||||
|
||||
private fun download(context: Context, simulator: SimulatorEntity?) {
|
||||
private fun download(simulator: SimulatorEntity?) {
|
||||
val apkEntity = simulator?.apk ?: return
|
||||
DownloadManager.getInstance(context).pauseAll()
|
||||
|
||||
val entity = DownloadManager.getInstance(context).getDownloadEntityByUrl(apkEntity.url)
|
||||
val entity = DownloadManager.getInstance().getDownloadEntityByUrl(apkEntity.url)
|
||||
HaloApp.put(simulator.name, simulator)
|
||||
if (entity != null) {
|
||||
when (entity.status) {
|
||||
DownloadStatus.pause, DownloadStatus.subscribe,
|
||||
DownloadStatus.neterror, DownloadStatus.timeout -> {
|
||||
DownloadManager.getInstance(context).addObserver(dataWatcher)
|
||||
uiExecutor.executeWithDelay(Runnable { DownloadManager.getInstance(context).resume(entity, true) }, 200)
|
||||
DownloadManager.getInstance().addObserver(dataWatcher)
|
||||
uiExecutor.executeWithDelay(Runnable { DownloadManager.getInstance().resume(entity, true) }, 200)
|
||||
downloadDialog?.show()
|
||||
}
|
||||
DownloadStatus.done -> DataChanger.notifyDataChanged(entity)
|
||||
|
||||
else -> createDownload(context, apkEntity, simulator)
|
||||
else -> createDownload(apkEntity, simulator)
|
||||
}
|
||||
} else {
|
||||
createDownload(context, apkEntity, simulator)
|
||||
createDownload(apkEntity, simulator)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDownload(context: Context, apkEntity: ApkEntity, simulator: SimulatorEntity) {
|
||||
DownloadManager.getInstance(context).addObserver(dataWatcher)
|
||||
private fun createDownload(apkEntity: ApkEntity, simulator: SimulatorEntity) {
|
||||
DownloadManager.getInstance().addObserver(dataWatcher)
|
||||
val downloadId = PackageInstaller.createDownloadId(simulator.name)
|
||||
val downloadEntity = DownloadEntity()
|
||||
downloadEntity.url = apkEntity.url
|
||||
downloadEntity.name = simulator.name
|
||||
downloadEntity.path = getDownloadPath(simulator.name, apkEntity.format)
|
||||
downloadEntity.path = PackageInstaller.getDownloadPathWithId(downloadId, apkEntity.format)
|
||||
downloadEntity.platform = apkEntity.getPlatform()
|
||||
downloadEntity.packageName = apkEntity.packageName
|
||||
downloadEntity.versionName = apkEntity.version
|
||||
|
||||
downloadEntity.addMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE, Constants.SIMULATOR_DOWNLOAD)
|
||||
downloadEntity.addMetaExtra(Constants.SIMULATOR_DOWNLOAD_START_TIME, (System.currentTimeMillis() / 1000).toString())
|
||||
uiExecutor.executeWithDelay(Runnable { DownloadManager.getInstance(context).add(downloadEntity) }, 200)
|
||||
downloadEntity.addMetaExtra(Constants.DOWNLOAD_ID, downloadId)
|
||||
uiExecutor.executeWithDelay(Runnable { DownloadManager.getInstance().add(downloadEntity) }, 200)
|
||||
|
||||
val locationStr = if (simulatorLocation == SimulatorLocation.LAUNCH) "${simulatorLocation?.value}《${gameName}》" else simulatorLocation?.value
|
||||
val fileName = downloadEntity.path.substring(downloadEntity.path.lastIndexOf('/') + 1).removeSuffix(".apk")
|
||||
|
||||
@ -3,37 +3,44 @@ package com.gh.common.simulator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.text.TextUtils
|
||||
import com.g00fy2.versioncompare.Version
|
||||
import com.gh.common.json.json
|
||||
import com.gh.common.util.*
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.SimulatorGameRecordEntity
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.EmptyResponse
|
||||
import com.gh.gamecenter.retrofit.Response
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.gamecenter.room.AppDatabase
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.download.DownloadDao
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.download.FileUtils
|
||||
import com.lightgame.utils.AppManager
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
import org.json.JSONArray
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
|
||||
|
||||
object SimulatorGameManager {
|
||||
|
||||
private val gamePath = FileUtils.getDownloadPath(HaloApp.getInstance().application, "emulator_game")
|
||||
private val gamePath by lazy { FileUtils.getDownloadPath(HaloApp.getInstance().application, "emulator_game") }
|
||||
|
||||
@JvmStatic
|
||||
fun getPathByType(type: String) = "${gamePath}/${type}"
|
||||
|
||||
@JvmStatic
|
||||
fun deleteLocalGames(names: List<String>) {
|
||||
val downloadEntityList = DownloadDao.getInstance(HaloApp.getInstance().application).all
|
||||
val downloadEntityList = DownloadManager.getInstance().allDownloadEntity
|
||||
names.forEach { name ->
|
||||
val downloadEntity = downloadEntityList.find { it.name == name }
|
||||
if (downloadEntity != null) {
|
||||
@ -48,7 +55,7 @@ object SimulatorGameManager {
|
||||
|
||||
@JvmStatic
|
||||
fun deleteLocalGame(name: String) {
|
||||
val downloadEntityList = DownloadDao.getInstance(HaloApp.getInstance().application).all
|
||||
val downloadEntityList = DownloadManager.getInstance().allDownloadEntity
|
||||
val downloadEntity = downloadEntityList.find { it.name == name }
|
||||
if (downloadEntity != null) {
|
||||
val file = File(downloadEntity.path)
|
||||
@ -63,7 +70,7 @@ object SimulatorGameManager {
|
||||
fun findDownloadEntityByUrl(url: String?): DownloadEntity? {
|
||||
val downloadEntity = DownloadDao.getInstance(HaloApp.getInstance().application).get(url)
|
||||
if (downloadEntity != null) {
|
||||
val isFileCompleted = DownloadManager.getInstance(HaloApp.getInstance().application).isFileCompleted(url)
|
||||
val isFileCompleted = DownloadManager.getInstance().isDownloadCompleted(url)
|
||||
if (downloadEntity.isSimulatorGame() && isFileCompleted) {
|
||||
return downloadEntity
|
||||
}
|
||||
@ -77,8 +84,10 @@ object SimulatorGameManager {
|
||||
|
||||
@JvmStatic
|
||||
fun launchSimulatorGame(downloadEntity: DownloadEntity, gameEntity: GameEntity) {
|
||||
val versionFromInstalledApp = PackageUtils.getVersionByPackage(gameEntity.simulator?.apk?.packageName)
|
||||
val versionFromInstalledApp = PackageUtils.getVersionNameByPackageName(gameEntity.simulator?.apk?.packageName)
|
||||
val shouldShowUpdate = Version(gameEntity.simulator?.apk?.version).isHigherThan(versionFromInstalledApp)
|
||||
|
||||
updateSimulatorConfigFile(gameId = gameEntity.id)
|
||||
if (shouldShowUpdate) {
|
||||
SimulatorDownloadManager.getInstance().showDownloadDialog(AppManager.getInstance().recentActiveActivity, gameEntity.simulator,
|
||||
SimulatorDownloadManager.SimulatorLocation.LAUNCH, gameEntity.id, gameEntity.name
|
||||
@ -90,31 +99,63 @@ object SimulatorGameManager {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun jumpToSimulator(downloadEntity: DownloadEntity, gameEntity: GameEntity) {
|
||||
val intent = Intent()
|
||||
intent.data = Uri.fromFile(File(downloadEntity.path))
|
||||
if (gameEntity.simulatorType == "FBA") {
|
||||
val apkEntity = gameEntity.getApk()[0]
|
||||
intent.putExtra("rom_name", apkEntity.packageName)
|
||||
}
|
||||
intent.putExtra("default_path", downloadEntity.path.substring(0, downloadEntity.path.lastIndexOf('/')))
|
||||
intent.putExtra("game_type", gameEntity.simulatorType)
|
||||
intent.putExtra("title", downloadEntity.name)
|
||||
intent.putExtra("icon", gameEntity.icon ?: "")
|
||||
intent.putExtra("meta", LogUtils.getMetaObject().toString())
|
||||
intent.putExtra("simulatorId", gameEntity.simulator?.id)
|
||||
intent.putExtra("simulatorName", gameEntity.simulator?.name)
|
||||
intent.putExtra("gameId", gameEntity.id)
|
||||
val gameIcon = gameEntity.icon ?: gameEntity.rawIcon ?: ""
|
||||
Single.just(ImageUtils.getCachedUrl(gameIcon))
|
||||
.flatMap {
|
||||
getBitmapFormCache(it)
|
||||
}.map {
|
||||
BitmapUtils.compressBitmap(it, 100)
|
||||
}.map {
|
||||
val baos = ByteArrayOutputStream()
|
||||
it.compress(Bitmap.CompressFormat.WEBP, 100, baos)
|
||||
baos.toByteArray()
|
||||
}
|
||||
.compose(singleToMain())
|
||||
.subscribe({
|
||||
val intent = Intent()
|
||||
intent.data = Uri.fromFile(File(downloadEntity.path))
|
||||
if (gameEntity.simulatorType == "FBA" || gameEntity.simulatorType == "FBN") {
|
||||
val apkEntity = gameEntity.getApk()[0]
|
||||
intent.putExtra("rom_name", apkEntity.packageName)
|
||||
}
|
||||
intent.putExtra("default_path", downloadEntity.path.substring(0, downloadEntity.path.lastIndexOf('/')))
|
||||
intent.putExtra("game_type", gameEntity.simulatorType)
|
||||
intent.putExtra("title", downloadEntity.name)
|
||||
intent.putExtra("icon", gameIcon)
|
||||
intent.putExtra("iconStream", it)
|
||||
intent.putExtra("meta", LogUtils.getMetaObject().toString())
|
||||
intent.putExtra("simulatorId", gameEntity.simulator?.id)
|
||||
intent.putExtra("simulatorName", gameEntity.simulator?.name)
|
||||
intent.putExtra("gameId", gameEntity.id)
|
||||
|
||||
val destActivity = "com.gh.emu.RequestPermissionActivity"
|
||||
intent.setClassName(gameEntity.simulator?.apk?.packageName ?: "", destActivity)
|
||||
try {
|
||||
AppManager.getInstance().recentActiveActivity.startActivity(intent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
ToastUtils.showToast("模拟器安装错误")
|
||||
}
|
||||
val destActivity = "com.gh.emu.RequestPermissionActivity"
|
||||
intent.setClassName(gameEntity.simulator?.apk?.packageName ?: "", destActivity)
|
||||
try {
|
||||
AppManager.getInstance().recentActiveActivity?.startActivity(intent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
ToastUtils.showToast("模拟器安装错误")
|
||||
}
|
||||
|
||||
recordPlaySimulatorGames(gameEntity.id)
|
||||
recordPlaySimulatorGames(gameEntity.id)
|
||||
}, {
|
||||
ToastUtils.showToast("跳转失败")
|
||||
})
|
||||
}
|
||||
|
||||
private fun getBitmapFormCache(url: String): Single<Bitmap> {
|
||||
return Single.create {
|
||||
ImageUtils.getBitmap(url, object : BiCallback<Bitmap, Boolean> {
|
||||
override fun onFirst(first: Bitmap) {
|
||||
it.onSuccess(first)
|
||||
}
|
||||
|
||||
override fun onSecond(second: Boolean) {
|
||||
it.onError(Throwable("获取bitmap失败"))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -123,18 +164,69 @@ object SimulatorGameManager {
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
@JvmStatic
|
||||
fun recordDownloadSimulatorGames(gameId: String) {
|
||||
fun recordDownloadSimulatorGame(gameId: String, type: String) {
|
||||
val requestMap = hashMapOf<String, Any>()
|
||||
requestMap["game_id"] = gameId
|
||||
requestMap["package"] = "-"
|
||||
val body = requestMap.createRequestBodyAny()
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application).api
|
||||
RetrofitManager.getInstance().api
|
||||
.downloadSimulatorGames(HaloApp.getInstance().gid, body)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
refreshSimulatorGame(gameId, type)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量记录设备下载的模拟器游戏
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
@JvmStatic
|
||||
fun recordDownloadSimulatorGames() {
|
||||
val gameArray = JSONArray()
|
||||
val allGameList = AppDatabase.getInstance().simulatorGameDao().getAllSimulatorGame()
|
||||
for (game in allGameList) {
|
||||
if (!game.isDeleted) {
|
||||
gameArray.put(json {
|
||||
"game_id" to game.id
|
||||
"package" to "-"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (gameArray.length() == 0) return
|
||||
|
||||
RetrofitManager.getInstance().api
|
||||
.putDownloadSimulatorGames(HaloApp.getInstance().gid, gameArray.toRequestBody())
|
||||
.compose(singleToMain())
|
||||
.subscribe(EmptyResponse())
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun refreshSimulatorGame(gameId: String, type: String) {
|
||||
val simulatorGameDao = AppDatabase.getInstance().simulatorGameDao()
|
||||
RetrofitManager.getInstance().api
|
||||
.getSimulatorGames(HaloApp.getInstance().gid, 1, getFilter(type))
|
||||
.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<List<GameEntity>>() {
|
||||
override fun onSuccess(data: List<GameEntity>) {
|
||||
val simulatorGameRecordList = ArrayList<SimulatorGameRecordEntity>()
|
||||
data.forEach {
|
||||
val entity = it.convertSimulatorGameRecordEntity()
|
||||
entity.isRecentlyPlayed = it.id == gameId
|
||||
simulatorGameRecordList.add(entity)
|
||||
}
|
||||
simulatorGameDao.addSimulatorGameList(simulatorGameRecordList)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun getFilter(type: String): String {
|
||||
return UrlFilterUtils.getFilterQuery("type", type)
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记已玩过
|
||||
*/
|
||||
@ -145,10 +237,9 @@ object SimulatorGameManager {
|
||||
requestMap["game_id"] = gameId
|
||||
requestMap["package"] = packageName
|
||||
val body = requestMap.toRequestBody()
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application).api
|
||||
RetrofitManager.getInstance().api
|
||||
.postPlayedGame(UserManager.getInstance().userId, body)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.compose(singleToMain())
|
||||
.subscribe(EmptyResponse())
|
||||
}
|
||||
}
|
||||
@ -163,7 +254,7 @@ object SimulatorGameManager {
|
||||
requestMap["game_id"] = gameId
|
||||
requestMap["package"] = "-"
|
||||
val body = requestMap.createRequestBodyAny()
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application).api
|
||||
RetrofitManager.getInstance().api
|
||||
.playedSimulatorGames(HaloApp.getInstance().gid, body)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
@ -175,7 +266,7 @@ object SimulatorGameManager {
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
fun deleteSimulatorGame(gameId: String, callback: () -> Unit) {
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application).api
|
||||
RetrofitManager.getInstance().api
|
||||
.deleteSimulatorGame(HaloApp.getInstance().gid, gameId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
@ -189,11 +280,12 @@ object SimulatorGameManager {
|
||||
/**
|
||||
* 批量删除设备下载模拟器游戏的记录
|
||||
*/
|
||||
@JvmStatic
|
||||
@SuppressLint("CheckResult")
|
||||
fun deleteSimulatorGames(gameIds: List<String>, callback: () -> Unit) {
|
||||
val requestMap = hashMapOf<String, Any>()
|
||||
requestMap["game_ids"] = gameIds
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application).api
|
||||
RetrofitManager.getInstance().api
|
||||
.deleteSimulatorGames(HaloApp.getInstance().gid, requestMap.toRequestBody())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
@ -210,10 +302,28 @@ object SimulatorGameManager {
|
||||
@JvmStatic
|
||||
@SuppressLint("CheckResult")
|
||||
fun deleteAllSimulatorGame() {
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application).api
|
||||
RetrofitManager.getInstance().api
|
||||
.deleteAllSimulatorGame(HaloApp.getInstance().gid)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(EmptyResponse())
|
||||
}
|
||||
|
||||
private fun updateSimulatorConfigFile(gameId: String) {
|
||||
RetrofitManager.getInstance()
|
||||
.api
|
||||
.getGameDigest(gameId)
|
||||
.map(ApkActiveUtils.filterMapper)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : Response<GameEntity>() {
|
||||
override fun onResponse(game: GameEntity?) {
|
||||
game?.let {
|
||||
if (!TextUtils.isEmpty(game.simulatorGameConfig)) {
|
||||
val configFilePath = getPathByType(game.simulatorType + "/cheat/" + game.getApk().firstOrNull()?.packageName + ".ini")
|
||||
FileUtils.downloadAndUpdateFile(game.simulatorGameConfig, configFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ object SyncFieldConstants {
|
||||
const val ANSWER_VOTE = "ANSWER_VOTE"
|
||||
const val ARTICLE_VOTE = "ARTICLE_VOTE"
|
||||
const val ARTICLE_COMMENT_VOTE = "ARTICLE_COMMENT_VOTE"
|
||||
const val GAME_COLLECTION_VOTE = "GAME_COLLECTION_VOTE"
|
||||
|
||||
// 赞同数量
|
||||
const val ANSWER_VOTE_COUNT = "ANSWER_VOTE_COUNT"
|
||||
@ -16,8 +17,12 @@ object SyncFieldConstants {
|
||||
const val ANSWER_COMMENT_COUNT = "ANSWER_COMMENT_COUNT"
|
||||
const val ARTICLE_COMMENT_COUNT = "ARTICLE_COMMENT_COUNT"
|
||||
const val ARTICLE_COMMENT_REPLY_COUNT = "ARTICLE_COMMENT_REPLY_COUNT"
|
||||
const val ANSWER_COMMENT_REPLY_COUNT = "ANSWER_COMMENT_REPLY_COUNT"
|
||||
|
||||
// 回答数量
|
||||
const val ANSWER_COUNT = "ANSWER_COUNT"
|
||||
|
||||
// 是否关注
|
||||
const val IS_FOLLOWER = "IS_FOLLOWER"
|
||||
|
||||
}
|
||||
@ -16,7 +16,6 @@ import com.gh.gamecenter.qa.answer.detail.AnswerDetailActivity
|
||||
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
|
||||
import com.gh.gamecenter.qa.entity.AnswerEntity
|
||||
import com.gh.gamecenter.qa.entity.Questions
|
||||
import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity
|
||||
|
||||
class ExampleAdapter(context: Context) : ListAdapter<AnswerEntity>(context), ISyncAdapterHandler {
|
||||
|
||||
@ -63,10 +62,11 @@ class ExampleAdapter(context: Context) : ListAdapter<AnswerEntity>(context), ISy
|
||||
binding.title.setOnClickListener {
|
||||
if ("community_article" == answer.type) {
|
||||
mContext.startActivity(ArticleDetailActivity.getIntent(mContext, UserManager.getInstance().community, answer.id!!, "", getPath()))
|
||||
} else {
|
||||
val questions = answer.questions
|
||||
mContext.startActivity(QuestionsDetailActivity.getIntent(mContext, questions.id, "", getPath()))
|
||||
}
|
||||
// else {
|
||||
// val questions = answer.questions
|
||||
// mContext.startActivity(QuestionsDetailActivity.getIntent(mContext, questions.id, "", getPath()))
|
||||
// }
|
||||
}
|
||||
|
||||
answerViewHolder.itemView.setOnClickListener {
|
||||
@ -80,7 +80,7 @@ class ExampleAdapter(context: Context) : ListAdapter<AnswerEntity>(context), ISy
|
||||
ItemViewType.ITEM_FOOTER -> {
|
||||
val footerViewHolder = holder as FooterViewHolder
|
||||
footerViewHolder.initItemPadding()
|
||||
footerViewHolder.initFooterViewHolder(mIsLoading, mIsNetworkError, mIsOver, R.string.ask_loadover_hint)
|
||||
footerViewHolder.initFooterViewHolder(mIsLoading, mIsNetworkError, mIsOver)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ class ExampleFragment : ListFragment<AnswerEntity, NormalListViewModel<AnswerEnt
|
||||
}
|
||||
|
||||
override fun provideDataObservable(page: Int): Observable<MutableList<AnswerEntity>> {
|
||||
return RetrofitManager.getInstance(context).api.getCommunitiesRecommendNewest(UserManager.getInstance().community.id, page)
|
||||
return RetrofitManager.getInstance().api.getCommunitiesRecommendNewest(UserManager.getInstance().community.id, page)
|
||||
}
|
||||
|
||||
override fun provideListViewModel(): NormalListViewModel<AnswerEntity> {
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
package com.gh.common.tracker
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.os.Bundle
|
||||
|
||||
// FYI 快速启动并且不设置 contentView 马上结束的 SplashActivity 没有 onStart 和 onStop 回调
|
||||
class ActivityLifecycleWatcher(private val mTrack: ITrack) : Application.ActivityLifecycleCallbacks {
|
||||
|
||||
override fun onActivityStarted(activity: Activity) {
|
||||
mTrack.onActivityStarted(activity)
|
||||
}
|
||||
|
||||
override fun onActivityStopped(activity: Activity) {
|
||||
mTrack.onActivityStopped(activity)
|
||||
}
|
||||
|
||||
override fun onActivityDestroyed(activity: Activity) {}
|
||||
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
|
||||
override fun onActivityPaused(activity: Activity) {}
|
||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
|
||||
override fun onActivityResumed(activity: Activity) {}
|
||||
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
package com.gh.common.tracker
|
||||
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import kotlin.concurrent.fixedRateTimer
|
||||
|
||||
/**
|
||||
* 应用生命周期观察者
|
||||
*/
|
||||
class AppLifecycleWatcher(private val mTrack: ITrack) : DefaultLifecycleObserver {
|
||||
|
||||
private var mWatchVisibleCount = 0 // 检查是否满足上传可见的次数
|
||||
|
||||
private val mLastVisibleTime by lazy { AtomicLong(0L) } // 最后可见时间
|
||||
private val mIsVisible by lazy { AtomicBoolean(false) } // 当前是否可见
|
||||
private val mWatchInterval by lazy { AtomicLong(MIN_WATCH_VISIBLE_INTERVAL) } // 与上次上报可见的间隔时间
|
||||
|
||||
init {
|
||||
fixedRateTimer(FIXED_RATE_TIMER_NAME, initialDelay = MIN_WATCH_VISIBLE_INTERVAL, period = MIN_WATCH_VISIBLE_INTERVAL) {
|
||||
if (mIsVisible.get()) {
|
||||
// 单位间隔发送
|
||||
val meetLogVisibleInterval = (mWatchVisibleCount++) * MIN_WATCH_VISIBLE_INTERVAL == mWatchInterval.get()
|
||||
|
||||
val equalToMinVisibleInterval = mWatchInterval.get() == MIN_WATCH_VISIBLE_INTERVAL
|
||||
val equalToMaxVisibleInterval = mWatchInterval.get() == MAX_WATCH_VISIBLE_INTERVAL
|
||||
|
||||
if (meetLogVisibleInterval || equalToMinVisibleInterval) {
|
||||
// 上报完重置计数
|
||||
mWatchVisibleCount = 0
|
||||
mTrack.onAppVisible(mWatchInterval.get() / 1000)
|
||||
if (!equalToMaxVisibleInterval) {
|
||||
mWatchInterval.set(mWatchInterval.get() * 2) // 间隔递增两倍
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart(owner: LifecycleOwner) {
|
||||
super.onStart(owner)
|
||||
|
||||
if (mLastVisibleTime.get() == 0L
|
||||
|| (mLastVisibleTime.get() + SESSION_ALIVE_INTERVAL) < System.currentTimeMillis() / 1000) {
|
||||
mTrack.onSessionChanged(UUID.randomUUID().toString())
|
||||
}
|
||||
mIsVisible.set(true)
|
||||
|
||||
mTrack.onAppStarted()
|
||||
}
|
||||
|
||||
override fun onStop(owner: LifecycleOwner) {
|
||||
super.onStop(owner)
|
||||
|
||||
mIsVisible.set(false)
|
||||
mLastVisibleTime.set(System.currentTimeMillis() / 1000)
|
||||
mWatchInterval.set(MIN_WATCH_VISIBLE_INTERVAL)
|
||||
|
||||
mTrack.onAppStopped()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val FIXED_RATE_TIMER_NAME = "APP_TRACKER_TIMER"
|
||||
|
||||
private const val SESSION_ALIVE_INTERVAL = 30L // 30s
|
||||
private const val MIN_WATCH_VISIBLE_INTERVAL = 5000L // 5s
|
||||
private const val MAX_WATCH_VISIBLE_INTERVAL = 320000L // 320s
|
||||
}
|
||||
|
||||
}
|
||||
8
app/src/main/java/com/gh/common/tracker/IBusiness.kt
Normal file
8
app/src/main/java/com/gh/common/tracker/IBusiness.kt
Normal file
@ -0,0 +1,8 @@
|
||||
package com.gh.common.tracker
|
||||
|
||||
import androidx.annotation.Nullable
|
||||
|
||||
interface IBusiness {
|
||||
@Nullable
|
||||
fun getBusinessId(): Pair<String, String>
|
||||
}
|
||||
14
app/src/main/java/com/gh/common/tracker/ITrack.kt
Normal file
14
app/src/main/java/com/gh/common/tracker/ITrack.kt
Normal file
@ -0,0 +1,14 @@
|
||||
package com.gh.common.tracker
|
||||
|
||||
import android.app.Activity
|
||||
|
||||
interface ITrack {
|
||||
fun onActivityStarted(activity: Activity)
|
||||
fun onActivityStopped(activity: Activity)
|
||||
|
||||
fun onAppStarted()
|
||||
fun onAppVisible(interval: Long)
|
||||
fun onAppStopped()
|
||||
|
||||
fun onSessionChanged(sessionId: String)
|
||||
}
|
||||
73
app/src/main/java/com/gh/common/tracker/Tracker.kt
Normal file
73
app/src/main/java/com/gh/common/tracker/Tracker.kt
Normal file
@ -0,0 +1,73 @@
|
||||
package com.gh.common.tracker
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import com.gh.common.runOnUiThread
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* launch_id 当前启动的唯一 id (每次应用冷启动或被内存回收时变更)
|
||||
* session_id 当前会话的唯一 id (应用切换至后台超30秒时变更)
|
||||
*/
|
||||
object Tracker : ITrack {
|
||||
|
||||
private var mSessionId: String = ""
|
||||
private val mLaunchId by lazy { UUID.randomUUID().toString() }
|
||||
|
||||
val sessionId: String
|
||||
get() = mSessionId
|
||||
|
||||
val launchId: String
|
||||
get() = mLaunchId
|
||||
|
||||
private var mIsInitialized = false
|
||||
|
||||
@JvmStatic
|
||||
fun init(application: Application) {
|
||||
if (mIsInitialized) {
|
||||
return
|
||||
}
|
||||
|
||||
runOnUiThread {
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleWatcher(this))
|
||||
application.registerActivityLifecycleCallbacks(ActivityLifecycleWatcher(this))
|
||||
}
|
||||
mIsInitialized = true
|
||||
}
|
||||
|
||||
override fun onActivityStarted(activity: Activity) {
|
||||
val businessId = if (activity is IBusiness) activity.getBusinessId() else null
|
||||
|
||||
TrackerLogger.logActivityStart(
|
||||
System.identityHashCode(activity).toString(),
|
||||
activity::class.java.simpleName,
|
||||
businessId)
|
||||
}
|
||||
|
||||
override fun onActivityStopped(activity: Activity) {
|
||||
val businessId = if (activity is IBusiness) activity.getBusinessId() else null
|
||||
|
||||
TrackerLogger.logActivityStop(
|
||||
System.identityHashCode(activity).toString(),
|
||||
activity::class.java.simpleName,
|
||||
businessId)
|
||||
}
|
||||
|
||||
override fun onAppStarted() {
|
||||
TrackerLogger.logAppStart()
|
||||
}
|
||||
|
||||
override fun onAppVisible(interval: Long) {
|
||||
TrackerLogger.logAppVisible(interval)
|
||||
}
|
||||
|
||||
override fun onAppStopped() {
|
||||
TrackerLogger.logAppStop()
|
||||
}
|
||||
|
||||
override fun onSessionChanged(sessionId: String) {
|
||||
mSessionId = sessionId
|
||||
}
|
||||
|
||||
}
|
||||
188
app/src/main/java/com/gh/common/tracker/TrackerLogger.kt
Normal file
188
app/src/main/java/com/gh/common/tracker/TrackerLogger.kt
Normal file
@ -0,0 +1,188 @@
|
||||
package com.gh.common.tracker
|
||||
|
||||
import android.content.Context
|
||||
import com.gh.common.exposure.meta.MetaUtil
|
||||
import com.gh.common.exposure.meta.MetaUtil.getBase64EncodedAndroidId
|
||||
import com.gh.common.exposure.meta.MetaUtil.getBase64EncodedIMEI
|
||||
import com.gh.common.loghub.LoghubUtils
|
||||
import com.gh.common.util.PackageUtils
|
||||
import com.gh.common.util.tryCatchInRelease
|
||||
import com.gh.gamecenter.R
|
||||
import com.lightgame.utils.Utils
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
||||
object TrackerLogger {
|
||||
|
||||
private const val LOG_STORE = "launch_activity"
|
||||
|
||||
fun logAppStart() {
|
||||
logAppVisible(0)
|
||||
}
|
||||
|
||||
fun logAppVisible(interval: Long) {
|
||||
val jsonObject = JSONObject()
|
||||
val payloadObject = JSONObject()
|
||||
tryCatchInRelease {
|
||||
payloadObject.put("launch_id", Tracker.launchId)
|
||||
payloadObject.put("session_id", Tracker.sessionId)
|
||||
payloadObject.put("interval", interval)
|
||||
|
||||
jsonObject.put("event", "app_visible")
|
||||
jsonObject.put("payload", payloadObject)
|
||||
jsonObject.put("meta", getMeta())
|
||||
}
|
||||
uploadToLoghub(jsonObject, true)
|
||||
}
|
||||
|
||||
fun logAppStop() {
|
||||
val jsonObject = JSONObject()
|
||||
val payloadObject = JSONObject()
|
||||
tryCatchInRelease {
|
||||
payloadObject.put("launch_id", Tracker.launchId)
|
||||
payloadObject.put("session_id", Tracker.sessionId)
|
||||
|
||||
jsonObject.put("event", "app_invisible")
|
||||
jsonObject.put("payload", payloadObject)
|
||||
jsonObject.put("meta", getMeta())
|
||||
}
|
||||
uploadToLoghub(jsonObject, true)
|
||||
}
|
||||
|
||||
fun logActivityStart(activityId: String,
|
||||
activityName: String,
|
||||
activityBusinessId: Pair<String, String>? = null) {
|
||||
val jsonObject = JSONObject()
|
||||
val payloadObject = JSONObject()
|
||||
tryCatchInRelease {
|
||||
payloadObject.put("launch_id", Tracker.launchId)
|
||||
payloadObject.put("session_id", Tracker.sessionId)
|
||||
payloadObject.put("activity_id", activityId)
|
||||
payloadObject.put("activity_name", activityName)
|
||||
if (activityBusinessId != null) {
|
||||
payloadObject.put("activity_primary_business_id", activityBusinessId.first)
|
||||
if (activityBusinessId.second.isNotEmpty()) {
|
||||
payloadObject.put("activity_secondary_business_id", activityBusinessId.second)
|
||||
}
|
||||
}
|
||||
|
||||
jsonObject.put("event", "activity_visible")
|
||||
jsonObject.put("payload", payloadObject)
|
||||
jsonObject.put("meta", getMeta())
|
||||
}
|
||||
uploadToLoghub(jsonObject, false)
|
||||
}
|
||||
|
||||
fun logActivityStop(activityId: String,
|
||||
activityName: String,
|
||||
activityBusinessId: Pair<String, String>? = null) {
|
||||
val jsonObject = JSONObject()
|
||||
val payloadObject = JSONObject()
|
||||
tryCatchInRelease {
|
||||
payloadObject.put("launch_id", Tracker.launchId)
|
||||
payloadObject.put("session_id", Tracker.sessionId)
|
||||
payloadObject.put("activity_id", activityId)
|
||||
payloadObject.put("activity_name", activityName)
|
||||
if (activityBusinessId != null) {
|
||||
payloadObject.put("activity_primary_business_id", activityBusinessId.first)
|
||||
if (activityBusinessId.second.isNotEmpty()) {
|
||||
payloadObject.put("activity_secondary_business_id", activityBusinessId.second)
|
||||
}
|
||||
}
|
||||
|
||||
jsonObject.put("event", "activity_invisible")
|
||||
jsonObject.put("payload", payloadObject)
|
||||
jsonObject.put("meta", getMeta())
|
||||
}
|
||||
uploadToLoghub(jsonObject, false)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun logHomeTabSelected(tabPosition: Int,
|
||||
tabContent: String) {
|
||||
val jsonObject = JSONObject()
|
||||
val payloadObject = JSONObject()
|
||||
tryCatchInRelease {
|
||||
payloadObject.put("launch_id", Tracker.launchId)
|
||||
payloadObject.put("session_id", Tracker.sessionId)
|
||||
payloadObject.put("tab_position", tabPosition)
|
||||
payloadObject.put("tab_content", tabContent)
|
||||
|
||||
jsonObject.put("event", "home_tab_select")
|
||||
jsonObject.put("payload", payloadObject)
|
||||
jsonObject.put("meta", getMeta())
|
||||
}
|
||||
uploadToLoghub(jsonObject, false)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun logAppLaunch(context: Context) {
|
||||
val jsonObject = JSONObject()
|
||||
val payloadObject = JSONObject()
|
||||
val signatureHash = PackageUtils.getApkSignatureByPackageName(context, context.packageName)[0]
|
||||
val sideLoadInfo = PackageUtils.getSideLoadedInfo()
|
||||
|
||||
tryCatchInRelease {
|
||||
payloadObject.put("launch_id", Tracker.launchId)
|
||||
payloadObject.put("session_id", Tracker.sessionId)
|
||||
payloadObject.put("signature", signatureHash)
|
||||
payloadObject.put("package_name", context.packageName)
|
||||
payloadObject.put("app_name", context.getString(R.string.app_name))
|
||||
|
||||
sideLoadInfo?.let {
|
||||
payloadObject.put("is_side_loaded", sideLoadInfo["is_side_loaded"])
|
||||
sideLoadInfo["installer_store"]?.let {
|
||||
payloadObject.put("installer_store", it)
|
||||
}
|
||||
}
|
||||
|
||||
jsonObject.put("event", "app_launch")
|
||||
jsonObject.put("payload", payloadObject)
|
||||
jsonObject.put("meta", getMeta())
|
||||
}
|
||||
uploadToLoghub(jsonObject, true)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun logAppLaunchSuccessful() {
|
||||
val jsonObject = JSONObject()
|
||||
val payloadObject = JSONObject()
|
||||
tryCatchInRelease {
|
||||
payloadObject.put("launch_id", Tracker.launchId)
|
||||
payloadObject.put("session_id", Tracker.sessionId)
|
||||
|
||||
jsonObject.put("event", "app_launch_successful")
|
||||
jsonObject.put("payload", payloadObject)
|
||||
jsonObject.put("meta", getMeta())
|
||||
}
|
||||
uploadToLoghub(jsonObject, true)
|
||||
}
|
||||
|
||||
private fun uploadToLoghub(jsonObject: JSONObject, uploadImmediately: Boolean) {
|
||||
jsonObject.put("timestamp", System.currentTimeMillis() / 1000)
|
||||
|
||||
Utils.log("Tracker -> $jsonObject")
|
||||
LoghubUtils.log(jsonObject, LOG_STORE, uploadImmediately)
|
||||
}
|
||||
|
||||
private fun getMeta(): JSONObject {
|
||||
val (_, _, model, manufacturer, _, _, android_version, network, _, gid, _, channel, appVersion, userId) = MetaUtil.getMeta()
|
||||
val metaObject = JSONObject()
|
||||
try {
|
||||
metaObject.put("dia", getBase64EncodedAndroidId())
|
||||
metaObject.put("android_version", android_version)
|
||||
metaObject.put("app_version", appVersion)
|
||||
metaObject.put("channel", channel)
|
||||
metaObject.put("gid", gid)
|
||||
metaObject.put("jnfj", getBase64EncodedIMEI())
|
||||
metaObject.put("manufacturer", manufacturer)
|
||||
metaObject.put("model", model)
|
||||
metaObject.put("network", network)
|
||||
metaObject.put("user_id", userId)
|
||||
} catch (e: JSONException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return metaObject
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user