Compare commits
1013 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3cdc76b548 | |||
| b56ecfbf8f | |||
| 27c0b4c497 | |||
| de1558d58f | |||
| 0197355126 | |||
| e66698d2bd | |||
| def5996c49 | |||
| 700fd2e390 | |||
| 21a8eaafeb | |||
| 4391891999 | |||
| 8302e8b5e0 | |||
| a8de52d5cd | |||
| 46a42c9212 | |||
| 9ed97ea32c | |||
| 4f087cdcc7 | |||
| 4c4fcba2d2 | |||
| 69e9bfa7a9 | |||
| c3b29697bc | |||
| f0937e8d1e | |||
| 85f713dc2d | |||
| cd146366c0 | |||
| 08054ad6e5 | |||
| 011b4b536a | |||
| d2e118180c | |||
| 93856925ba | |||
| da0a33798b | |||
| 33f4f2e60b | |||
| 5827583edc | |||
| 2b33d95329 | |||
| 7f0be97170 | |||
| b6a0fa9196 | |||
| 52c624e94b | |||
| de847ac0d5 | |||
| 2bb579fec7 | |||
| 6a6b15fb14 | |||
| 81e6778148 | |||
| 309a6a6425 | |||
| 65e5ca64a1 | |||
| 69825d33ff | |||
| ec4acd0043 | |||
| 22bb168e49 | |||
| b7bea01eb1 | |||
| 82c3591d25 | |||
| 20d8de32ec | |||
| 3976646f39 | |||
| c849d4e0b0 | |||
| 671fab2fee | |||
| 8491609da7 | |||
| 67257748f9 | |||
| 312a3fe7bb | |||
| 2c16c54c96 | |||
| bbae5f0b9c | |||
| 95022da700 | |||
| 6471e95077 | |||
| 707e6f5d11 | |||
| 66dfcc8064 | |||
| 0400b7d396 | |||
| 96d4ca43ff | |||
| d2c0699b66 | |||
| 0e4cf2cd3a | |||
| 8144a7e857 | |||
| ea38698ac3 | |||
| d00b34ebdd | |||
| a4c285df6f | |||
| 6dfd9d5b7a | |||
| 876f7317a8 | |||
| 430a975e98 | |||
| f277319019 | |||
| 6b9fedd289 | |||
| d0ad48ada2 | |||
| a6b7beed75 | |||
| fa257e4a04 | |||
| 8d827b7275 | |||
| 27d73b545c | |||
| 7802d961a3 | |||
| d5d63371e3 | |||
| fcf1d38578 | |||
| fe17f817da | |||
| bfa76c0fda | |||
| 2e1a99f905 | |||
| e7cb8e41b5 | |||
| f88d4bde45 | |||
| b470194807 | |||
| ba5e3cb3eb | |||
| d5440b5d01 | |||
| a8f3f27fb3 | |||
| bec06bfbf4 | |||
| 9c510dc132 | |||
| 8302c99d57 | |||
| 5209e5b463 | |||
| 663e5124fe | |||
| dea2f569a1 | |||
| 03d25eaeae | |||
| e84f56975d | |||
| a3a21efba3 | |||
| 1daa4f91b3 | |||
| ee1ddbcdce | |||
| 772549e543 | |||
| db537e756a | |||
| f9c236fd63 | |||
| f414ecee32 | |||
| 5b1d0d5821 | |||
| 1c80ba56a5 | |||
| a92044f815 | |||
| 00f94bf950 | |||
| e27f061a92 | |||
| 40d28948e1 | |||
| 9b6e29d5f2 | |||
| 0ee000f08a | |||
| 6bdd021856 | |||
| 301e14268a | |||
| 825ea594f4 | |||
| 8fcb072abb | |||
| f17a4e6372 | |||
| a5b1202e5e | |||
| b5e025984c | |||
| 33dba4e9bf | |||
| 92492b21ae | |||
| 1c8ca4b8c2 | |||
| 466061222d | |||
| 905e229142 | |||
| 6906b96ef9 | |||
| fb748df3ce | |||
| 844de786d0 | |||
| 7dfe55014a | |||
| 24f19e2a61 | |||
| f947040241 | |||
| 569bb61545 | |||
| 78ea8b3e73 | |||
| 6b5056da41 | |||
| 2a86546f1a | |||
| d669fec902 | |||
| 3fc14b5194 | |||
| 4a3a039d0e | |||
| 1f43523f04 | |||
| 26e23fa736 | |||
| 958bce8d42 | |||
| 8f15e52eb5 | |||
| 4bedede646 | |||
| f66ee1721e | |||
| 138c5bb4c2 | |||
| d1245a9a1e | |||
| 561d6f7ea2 | |||
| d5992f7ed3 | |||
| d941aa0c71 | |||
| 7fa1d1e11b | |||
| 3edff028bf | |||
| 78d3c529c6 | |||
| f0af2e280b | |||
| 34623163e6 | |||
| c90bd007c7 | |||
| 5f9637ec50 | |||
| 1a05e13b03 | |||
| c835f88695 | |||
| 5674fe76a2 | |||
| e6933b4ded | |||
| 8a10dac03d | |||
| de71ebad52 | |||
| 310d1a2d5c | |||
| 30fcaf190b | |||
| f004b15cf0 | |||
| 480c91feb5 | |||
| 2c70b7eefc | |||
| 2be2513081 | |||
| 48c0d5d2cd | |||
| 3c5a6bcdec | |||
| bd2a3d529a | |||
| 6c0ac67cfa | |||
| cb07b8c9f4 | |||
| e22be3f233 | |||
| fd02f120c2 | |||
| 6496b3dad6 | |||
| 375b57cd3d | |||
| 269265b054 | |||
| d74154080b | |||
| 7471825a7c | |||
| 90580316e8 | |||
| 61e0ef2960 | |||
| 1125bb9b4c | |||
| 1e424a3791 | |||
| a901d95895 | |||
| 1a0a92c542 | |||
| 6ba8344292 | |||
| 38d2429183 | |||
| 36c755f781 | |||
| 093c731ba7 | |||
| eae9c11d72 | |||
| 3faae4b34c | |||
| 04eef35cbd | |||
| 6b08cb2159 | |||
| 0777ca90e0 | |||
| 4a322d07d9 | |||
| df49f98e46 | |||
| 122e3f6aa0 | |||
| b902837b1c | |||
| 62639a9c79 | |||
| 06dc7fc566 | |||
| ece6e099ec | |||
| f3ce82275a | |||
| 4000dd8383 | |||
| d67053ce7c | |||
| 37bca1ee1c | |||
| 229d3e9c88 | |||
| c5d79a014b | |||
| 7d609b9c2a | |||
| 97eabcc600 | |||
| 932863a73f | |||
| 4014fc27af | |||
| fdf7be4a9f | |||
| b0fd801fec | |||
| 34c0bbe434 | |||
| 92c5697565 | |||
| 445759e511 | |||
| a5b32cc099 | |||
| 7a5f24bfe7 | |||
| b40d58f1ae | |||
| ef28fc3616 | |||
| 80d2e2c488 | |||
| ca07f10c9d | |||
| 8a7fa127ba | |||
| 8ee6039a2b | |||
| aac58029f6 | |||
| 640a8d4e0f | |||
| 9f621696c1 | |||
| a45fff7cca | |||
| 8ba45deac9 | |||
| 0c9f741847 | |||
| 696f862633 | |||
| f85e3c2602 | |||
| d2e3635034 | |||
| 8f37cbdfde | |||
| 3ffd6f74f1 | |||
| 72aa1474d3 | |||
| 3f9fb27642 | |||
| 6d134edb0a | |||
| 7834e9d55a | |||
| 36ba95e2fd | |||
| 00deba962e | |||
| 4713594c7e | |||
| 688a169e4e | |||
| 3ace50b526 | |||
| a9dbb4850f | |||
| 910cda8dbc | |||
| 7d29c0d883 | |||
| 3f274cb4ea | |||
| 56d378b6dc | |||
| 03eebf2c07 | |||
| cf41572db7 | |||
| a2361254da | |||
| 233f786ee0 | |||
| 8590614272 | |||
| e179de3d0d | |||
| defe3bb3ff | |||
| 24ad3e0265 | |||
| e54646afcc | |||
| 4c62032c66 | |||
| 627affe0f7 | |||
| b5e21bef1e | |||
| 95999791bd | |||
| 07cba611cf | |||
| 29c2dfc1e5 | |||
| 6b904b9213 | |||
| 7e15307b46 | |||
| 626913c6ff | |||
| 00b98e4da9 | |||
| 67f565bee0 | |||
| 9eedf46294 | |||
| 810783da9c | |||
| 39dfe334ed | |||
| 1a985f2a7e | |||
| 35af46e3eb | |||
| 3eefadd83b | |||
| 0fbbd79bbd | |||
| 84eee14363 | |||
| 8f7ce5d148 | |||
| 5ed2fa4a0c | |||
| e5a06f7a3f | |||
| f0ef1c7a76 | |||
| 76ea9a4ba4 | |||
| 33454005c3 | |||
| 37b6e6bce0 | |||
| aaa9aab066 | |||
| e8669cc0cf | |||
| ddb226823f | |||
| 76eca47979 | |||
| bcac6944a0 | |||
| d9aa1c428d | |||
| 85c779ecd2 | |||
| 87a67470fd | |||
| be4142ad96 | |||
| 5a26f7735d | |||
| 11ec77ad99 | |||
| 2da4182ee1 | |||
| d17ad6a497 | |||
| 1abc0ecc79 | |||
| 51ee8c841b | |||
| 0636d2276b | |||
| bffee14882 | |||
| 93f2ed3a09 | |||
| c7b0ed26ae | |||
| e75ade83cb | |||
| 3e4f5d6361 | |||
| 999bc9f720 | |||
| cca068821b | |||
| 6d05a00488 | |||
| ba3450f2b3 | |||
| 57c084d512 | |||
| c956f35e07 | |||
| c44353fa13 | |||
| 8ebdc924f3 | |||
| 3923dd377d | |||
| 82eea64035 | |||
| 10229e59f6 | |||
| 8c2583eab3 | |||
| f9ef4851fb | |||
| d07b99fd98 | |||
| b23ce2adc3 | |||
| c4f2899e07 | |||
| ebb20f5c38 | |||
| 08aaae1b02 | |||
| 4aaef11f9c | |||
| 2d0d253149 | |||
| 875c0b8c16 | |||
| 3e285a4466 | |||
| 76e91265d5 | |||
| 47482ba793 | |||
| 685c38dcf7 | |||
| 240d97cdd5 | |||
| cd891d4378 | |||
| b9c05483d0 | |||
| 190c534dba | |||
| 5cc13d8161 | |||
| 81ac870c62 | |||
| 9eab5c3d4c | |||
| f4a4ae7eb3 | |||
| 4b2715e97d | |||
| 983e546a64 | |||
| d7161578b0 | |||
| d8683068c1 | |||
| efcea45819 | |||
| 28f89aa0ad | |||
| 309d42fdc1 | |||
| f8f45182ce | |||
| 5f08dee0e0 | |||
| deea0f3f83 | |||
| 2090c5950f | |||
| 02dcbdba02 | |||
| ee44b1986f | |||
| 16bc7b1e75 | |||
| 65bc06b195 | |||
| 06734d6274 | |||
| e1dcd72a79 | |||
| 3429a27354 | |||
| 7af99a5ed5 | |||
| d7833cd07f | |||
| a9b885c97f | |||
| 5eb815dba7 | |||
| 9797841280 | |||
| a2382be034 | |||
| 081ce01664 | |||
| eddb8a933c | |||
| 7a019cc465 | |||
| 08e8846af8 | |||
| 67d6cc6f05 | |||
| 1946478ce4 | |||
| 5689cdbafe | |||
| 44adfdede8 | |||
| 2992e3ca64 | |||
| ffbbd44beb | |||
| 3990515dc5 | |||
| 2bd218fd95 | |||
| 1a02239dd2 | |||
| a5a5e38d5c | |||
| 9ebf2be016 | |||
| 3e488d28cd | |||
| ce311d9b17 | |||
| 843a263b3f | |||
| 7e350cac41 | |||
| 447b7b35ef | |||
| 6e9edabd94 | |||
| 68cfdc57c8 | |||
| 81e0f1d9cc | |||
| 5436775b31 | |||
| b8f5c2ddbd | |||
| da433bfb5b | |||
| 50209aa6ed | |||
| 665fbb0f6c | |||
| fd0921870d | |||
| 568ff5cb1e | |||
| 0cc78b1055 | |||
| 6dc3b5c382 | |||
| 9ac373bf0e | |||
| 02f96f9083 | |||
| c74997f306 | |||
| 58d4f22a6b | |||
| c5cf1c168e | |||
| 36638a63ca | |||
| 07de30ceea | |||
| 35e15263b0 | |||
| 420867378b | |||
| 0cc183df22 | |||
| eb557ca9bd | |||
| 72b39a5982 | |||
| b96ee6bc19 | |||
| 684fa318ec | |||
| 29faea14c6 | |||
| c25f7a13db | |||
| 715edd77e0 | |||
| 25cc825ea6 | |||
| f7e7819b84 | |||
| d305315d7e | |||
| 965d756cba | |||
| 29a19b5df6 | |||
| cb2cdd98e8 | |||
| da79a972f7 | |||
| 3c13eff2cf | |||
| 7050070310 | |||
| 1dca4e8c0a | |||
| 1163e04aaf | |||
| 73bc256ce6 | |||
| df78db5686 | |||
| b64ceaf5d4 | |||
| eb9b4cd132 | |||
| b9eec94644 | |||
| 4e388ec3d7 | |||
| a5069789b3 | |||
| 3797fd85ad | |||
| f0b0423872 | |||
| e5daf1d29f | |||
| 2b9f40a976 | |||
| 05fbf28d2b | |||
| e54d06def9 | |||
| 840eff3cc4 | |||
| a0737d0010 | |||
| 9d93df888e | |||
| 90fbb235c3 | |||
| 9be884c672 | |||
| 550a4cdecf | |||
| 0d883b76fe | |||
| e3c3101cab | |||
| cce289253d | |||
| 2102dd729c | |||
| 347a83cf0f | |||
| 1f8d573fad | |||
| 72b6c2a176 | |||
| 743e3660e9 | |||
| 2ab6a49bec | |||
| 1e31e13384 | |||
| f77fbe680f | |||
| 53c55a3f36 | |||
| 18d11e3421 | |||
| bf42834051 | |||
| ee6337276c | |||
| cdc001b43e | |||
| f3eeaddec8 | |||
| b8e1e4e21c | |||
| 64f2c83894 | |||
| f9211e17a1 | |||
| f0c952848a | |||
| c736068d0e | |||
| e0c9d43f02 | |||
| 0a4b657a85 | |||
| 0121d9601c | |||
| 371a450f5a | |||
| 2796145d26 | |||
| 5a662ac72b | |||
| 6cd62cbd0a | |||
| 299fa1a107 | |||
| c39ba2e17e | |||
| 414b5b3861 | |||
| 6f89f4acce | |||
| 5f09fd1fa3 | |||
| 00f1b0e8a3 | |||
| 4473d0f787 | |||
| d5255f91d0 | |||
| 57a3479144 | |||
| 82c4ebf375 | |||
| 23ca884684 | |||
| cdfa999b05 | |||
| 224092b30a | |||
| 8daefb2e42 | |||
| c0c0ef979d | |||
| 6f0738cebb | |||
| 3c352921bb | |||
| 4df8f18cc4 | |||
| 120acdefc6 | |||
| 7eb31085b0 | |||
| 17a3446be4 | |||
| d713c67c9b | |||
| 76d788ee66 | |||
| 1cf13fc2a1 | |||
| de08d4d32d | |||
| dce3a91156 | |||
| 38bf17c932 | |||
| 5d0ccb9a34 | |||
| b557afde2c | |||
| ea9375c20e | |||
| fa464a9f1d | |||
| b3ff6ea991 | |||
| f9dbdd4aa8 | |||
| 21d06d97ef | |||
| fb28a49537 | |||
| d124504d5b | |||
| 0c69e0c5e5 | |||
| 39aee1ce89 | |||
| b3fe87b79a | |||
| 2dfdc17523 | |||
| c208e7f72d | |||
| 8722d0a65b | |||
| 54f044dfa0 | |||
| b854a6dfef | |||
| badb99da59 | |||
| 30d99dbe54 | |||
| c44c4e8b2d | |||
| 656b2f469b | |||
| 39e17842cc | |||
| 57f7c0bb31 | |||
| 76b03fa68b | |||
| c99d6fb16f | |||
| d29ccc0aca | |||
| bf51be498b | |||
| 6d941975e2 | |||
| 0e075d28a5 | |||
| 4b5e6b574f | |||
| fc79581f8c | |||
| 6176c47e2f | |||
| 9abe0eb158 | |||
| 4d7cedb8a6 | |||
| 2b02fe06ac | |||
| cd5f530fc6 | |||
| b9085c7091 | |||
| 679ee71f89 | |||
| 536f0e038a | |||
| b087e35b30 | |||
| db3df649ce | |||
| bb708277b1 | |||
| c3b694dc6c | |||
| b9ff0b1c88 | |||
| 52be8db71f | |||
| 0e791133dd | |||
| b4f760d69f | |||
| 9bba3c9560 | |||
| bcd61e87d3 | |||
| e8c0e523e6 | |||
| af9e9a87b4 | |||
| 2d1bb6435f | |||
| 5680b8508d | |||
| df0e9197dc | |||
| 79964160ff | |||
| 0d46554b66 | |||
| 2c674896ff | |||
| 3ce1f28ae6 | |||
| 5ef145ed47 | |||
| b478cd6812 | |||
| 7e86187132 | |||
| 58b7a9515f | |||
| 370b2122aa | |||
| c6f1a7d212 | |||
| c04e425550 | |||
| 19543d7d29 | |||
| 12af602066 | |||
| 000515cda7 | |||
| 645e5a94a6 | |||
| c2f69eb03d | |||
| 93a9c41bee | |||
| 02dffc6e22 | |||
| 2658b7ca93 | |||
| 8c3335a7bf | |||
| c0e7b8e3ee | |||
| 9ef3d65aa4 | |||
| 8d188d4798 | |||
| 7bfc84c156 | |||
| 18bc47efe1 | |||
| 4593952cc3 | |||
| 1d3e30f8d3 | |||
| 6fef655e44 | |||
| 552b81c558 | |||
| 6c9a4daf9b | |||
| fbc46c54c4 | |||
| bc7aaa8b08 | |||
| 95af9902e7 | |||
| f340a13212 | |||
| 3d8f17d641 | |||
| 68f7b0f20f | |||
| c3a3060b92 | |||
| c01b96298d | |||
| 26655da185 | |||
| 9d0345303a | |||
| 33d02a0b2e | |||
| f301ab694c | |||
| baa6c0db3c | |||
| d118f54bd3 | |||
| 4a7384d371 | |||
| 6a91b8ae0f | |||
| b5fddfbbd0 | |||
| cfa3ceaae7 | |||
| 7a8f75abfc | |||
| 3db70fcbbe | |||
| 7b74d9fe47 | |||
| 553c211f24 | |||
| 3b153f5967 | |||
| 926b2d070f | |||
| 3e37731d84 | |||
| 24b5b81279 | |||
| 0534294b82 | |||
| a314404b61 | |||
| 71b81b2255 | |||
| c8cb61436e | |||
| 1006bf25c1 | |||
| cb6f03231e | |||
| 8ee2308ee1 | |||
| 719dbeef76 | |||
| 4c34333956 | |||
| bff677d607 | |||
| ba1461c874 | |||
| 7a0e885444 | |||
| 72d4ab6db5 | |||
| dfd6b61123 | |||
| 352d7d032e | |||
| ae6ba7832d | |||
| 9d63a6d0c4 | |||
| e905fb366e | |||
| 27b325f7d9 | |||
| 5c2f8c60dd | |||
| 3ac5905994 | |||
| 0e460b0778 | |||
| 4eea5b99d6 | |||
| 34c60d09f6 | |||
| 48e1ee4e7b | |||
| 46863d09a2 | |||
| ade08dcbee | |||
| e9a381068e | |||
| f5a068937a | |||
| c2bc52e474 | |||
| cd4ce2aa6b | |||
| 257e82e825 | |||
| a705f0ec53 | |||
| 46699a05ce | |||
| 9fd1855daa | |||
| 9a96fffd18 | |||
| 00d6f5e4b1 | |||
| a3adb87a6a | |||
| d0c01f1873 | |||
| 725d0b217a | |||
| 1958c6aefb | |||
| 05be092d69 | |||
| 40fccea23a | |||
| a022526e7e | |||
| 765b4c3afa | |||
| f704a29b38 | |||
| 309bb98036 | |||
| bf7b0b0a82 | |||
| 98629c5e85 | |||
| 461acbd376 | |||
| e0b70b26f3 | |||
| 4a831f4cb9 | |||
| b58bbbe705 | |||
| dc186e2e6b | |||
| 44deb59624 | |||
| 886007eae5 | |||
| 27fc39cb70 | |||
| 5480caf44e | |||
| 877ecbaef1 | |||
| 434bfde292 | |||
| 40c55e716d | |||
| a448a974f1 | |||
| 1738fac5fd | |||
| 7b74318ca5 | |||
| 19e394abb8 | |||
| 6dabbe19dd | |||
| 7b95d991d4 | |||
| e286be7f1e | |||
| 4214c08e76 | |||
| 180c49f9d3 | |||
| ef040f68c7 | |||
| 65e098e1c8 | |||
| 3978ab95f4 | |||
| b134d077cf | |||
| d537cb2383 | |||
| dba866e5c9 | |||
| ce36dd4012 | |||
| ea2b9ef4df | |||
| 69fe3b3d76 | |||
| a34b101a05 | |||
| c134c4bf9e | |||
| b0fe7595da | |||
| 6a7ef44bdd | |||
| 633acb9236 | |||
| cf84205571 | |||
| 5aced2c03c | |||
| 6cfa949493 | |||
| 4131396117 | |||
| e5f8d959ed | |||
| 6f0c9e0b59 | |||
| dcfe3dee0e | |||
| 8348568ea7 | |||
| 1d5dece894 | |||
| 051e0751a5 | |||
| 64594c4457 | |||
| 5f19f55e03 | |||
| da6cfdc4e8 | |||
| c2274d6b6f | |||
| adbdc55bc4 | |||
| 165059ca2d | |||
| 6c1da15ae4 | |||
| ecf05bcd00 | |||
| 3baec32ff8 | |||
| cb95204fac | |||
| 89e05d1a29 | |||
| 41b940a8e9 | |||
| 3d8d612897 | |||
| b3297e3fcd | |||
| 9f00cab409 | |||
| e8a795f97b | |||
| c8c93cac6e | |||
| 96c233ebd0 | |||
| 36ef13b237 | |||
| f54fdd6016 | |||
| a43947077a | |||
| a863b7be13 | |||
| ab1ce8e179 | |||
| d391ab20b7 | |||
| 8403bc3a64 | |||
| 038e736eb1 | |||
| a215192175 | |||
| 22a1895050 | |||
| 2df42c772b | |||
| e68e47e132 | |||
| 72f9e8ed79 | |||
| 3a2c67152e | |||
| 82fea04460 | |||
| 6ccd70dad2 | |||
| ec2d14bffe | |||
| 1d544ff558 | |||
| 867cd29e0f | |||
| d1cf660af6 | |||
| adabe3a227 | |||
| b57ef6c3b4 | |||
| 1b50823843 | |||
| c4c499532a | |||
| 18ddad522a | |||
| 85b034ecfb | |||
| 5a04749ef2 | |||
| bcd32dde85 | |||
| 7832cb810b | |||
| cb652721a7 | |||
| 4ad005fb53 | |||
| ce312aea72 | |||
| 6c5dc556e8 | |||
| 95dc18f1d0 | |||
| f284cc8676 | |||
| 0cc456c4a1 | |||
| 0666146374 | |||
| 5860ca720e | |||
| 91ee8362ba | |||
| d8f043ad71 | |||
| 1706ead392 | |||
| dd81460826 | |||
| 5180916d9f | |||
| 4d92ddcc75 | |||
| f1f0900e13 | |||
| 6edf68d03c | |||
| c643ae507b | |||
| fdc843a93a | |||
| 383c06c72a | |||
| a255ce3e36 | |||
| fa58e820c1 | |||
| fad011e2fc | |||
| 9fad040e14 | |||
| 8d0ab82f15 | |||
| 3d7e994b93 | |||
| 4d40c0f431 | |||
| d9bb60e66b | |||
| 52433ba347 | |||
| 4290846698 | |||
| 6bbd7ec00d | |||
| dc535ddb38 | |||
| dfe087e702 | |||
| 930e0b92eb | |||
| 3199bc6118 | |||
| b1384375a3 | |||
| 81dd98cefd | |||
| 02108b3273 | |||
| c5deb87c23 | |||
| 572274a2e3 | |||
| 7f303b362d | |||
| 41ce6cce05 | |||
| f6e1c55749 | |||
| d24bcf8e6e | |||
| 2b497ff679 | |||
| 627c20f6fd | |||
| cfab918bea | |||
| 89746203fe | |||
| 8bc6947c94 | |||
| 5a264804da | |||
| 1f5e025fea | |||
| 3201e220a0 | |||
| d41129011b | |||
| 6eb1aefb1c | |||
| 433646b455 | |||
| 257e7d1d27 | |||
| f668d6492a | |||
| b8ea968a58 | |||
| 5b361afc67 | |||
| 0947cd1e24 | |||
| f8ab1f108b | |||
| 84f53bbf81 | |||
| ad2656a24b | |||
| 4734fbd651 | |||
| a947755383 | |||
| f4a1283073 | |||
| f37a2c5b24 | |||
| cca7d770f1 | |||
| 2c7f3f2321 | |||
| cc92e025f2 | |||
| ce0e28dad6 | |||
| b483f7cdb6 | |||
| 8ce0694099 | |||
| 910b356a05 | |||
| d74e0310f8 | |||
| ee19879577 | |||
| 5e3d7d2f2b | |||
| f292eb0fc6 | |||
| 475de2281b | |||
| 949c8331d2 | |||
| a5e0fa8402 | |||
| f0066b3dac | |||
| 10480a533c | |||
| d36dcac817 | |||
| d6d49ac7ad | |||
| 573e127fc7 | |||
| 9cb6e599a4 | |||
| f42dd1013e | |||
| 70441658e5 | |||
| 0ca71f25f6 | |||
| 5d3a4e24f9 | |||
| 3b8faf47c4 | |||
| ac21776d3b | |||
| e5127c9040 | |||
| 8533d06943 | |||
| 066dd6103f | |||
| 670f9c280c | |||
| 5ea3c58b77 | |||
| 6e15571ef4 | |||
| ad46fced4f | |||
| 5acf42423e | |||
| 2a2bef8a47 | |||
| 2ffb0f59a2 | |||
| 400bdaa6f5 | |||
| fa449966f3 | |||
| f4078493f6 | |||
| 5007f7e83d | |||
| ca1a8e15cb | |||
| b9f299805f | |||
| acde49012f | |||
| 76c69f0d19 | |||
| c0b91b4a63 | |||
| f6a194ae92 | |||
| 9e7d178972 | |||
| 17b2e2dca7 | |||
| eb73bcd25f | |||
| f96c090120 | |||
| aefdaee424 | |||
| 27ceee37c0 | |||
| e1c2e3c984 | |||
| 48e3718fc2 | |||
| ee20f65a9b | |||
| 235a13855b | |||
| acec42c6af | |||
| 6d009eddf5 | |||
| b0dde281ea | |||
| 1e7d43bc72 | |||
| 8576a1edaf | |||
| 5dc97d2191 | |||
| a32b45e067 | |||
| ff8210c72b | |||
| 85031e89b6 | |||
| f2676532ce | |||
| 7a996ee8f4 | |||
| d09e28d653 | |||
| e446f4190f | |||
| f53534b803 | |||
| fb6792f71b | |||
| 5d66d55db1 | |||
| d3bdd48425 | |||
| c89f675ccf | |||
| f0b989d20e | |||
| 3b950b6b28 | |||
| d10a0f956e | |||
| 2317228642 | |||
| 78a4571a8a | |||
| c7b16ce91b | |||
| 2e84aeaccf | |||
| 49e9a532b8 | |||
| 47f7d70cfb | |||
| a3b03efcbe | |||
| 814d4faf3e | |||
| c487dc2f03 | |||
| f552a06bd7 | |||
| 4de5593802 | |||
| 4294e9f98d | |||
| 05a911f1e3 | |||
| 464f8d7851 | |||
| 370b07be31 | |||
| cb49c71279 | |||
| f637fb9596 | |||
| 5c8072cc9e | |||
| 40cd057c28 | |||
| 0bcd37c1f1 | |||
| 6fce6fd80b | |||
| bc13a6056d | |||
| 00879964e4 | |||
| 64711d6175 | |||
| 0d3704f55d | |||
| 0856eed7fc | |||
| 8efaa9e724 | |||
| 98983ff4eb | |||
| 0ae36ed7a7 | |||
| 8e8b20c3dd | |||
| 82e2aa45c6 | |||
| e4be47e7e7 | |||
| 404c605a1e | |||
| 4966d85634 | |||
| 043eebf013 | |||
| 3b521ddd08 | |||
| bd74d3a1a3 | |||
| 0dc867ce64 | |||
| dde08e2d67 | |||
| 3c5871526d | |||
| 49f4ba2da8 | |||
| 77b660576a | |||
| a15ab62f03 | |||
| c65250973c | |||
| d5872aad55 | |||
| 9d27bba6fd | |||
| caeecce50b | |||
| f6c82dd767 | |||
| f88205f957 | |||
| 3a0a4726b9 | |||
| a113fc1c73 | |||
| 92d122b0a3 | |||
| d96a2d22e0 | |||
| 8d96b9fe7f | |||
| fdf7bdd64d | |||
| 4f4c90f792 | |||
| f42674ed6f | |||
| e66b697663 | |||
| a21e40a4f1 | |||
| 03dd58ca41 | |||
| 6f3827db15 | |||
| fd2b333bc7 | |||
| ba3e882f47 | |||
| b9a80e12a8 | |||
| 979ee209dd | |||
| 10b1380aad | |||
| b239407c25 | |||
| 475221a011 | |||
| 658ee78213 | |||
| 5244fe73ea | |||
| 243e024d1a | |||
| 5930996e1c | |||
| a05e6917b8 | |||
| d05e9e57c3 | |||
| 1b88e9c238 | |||
| ce32b8d63a | |||
| 2bc26c9046 | |||
| 714a64975f | |||
| 7a6354e781 | |||
| 5f11c2d0b6 | |||
| 9c6814482d | |||
| 94a5736939 | |||
| bc496d8277 | |||
| 2b23665cb5 | |||
| 1d1c9666dc | |||
| 1b14f3f608 | |||
| d370e817c0 | |||
| ea4c6c95a1 | |||
| f97ccd5c5d | |||
| 0b2a672f2d | |||
| f960b07ee1 | |||
| 5cbf06f6a6 | |||
| 70ea517a3a | |||
| 957305d6f5 | |||
| bbd1d429a6 | |||
| 56ba47a844 | |||
| 8101d394a8 | |||
| 496144f8ad | |||
| 4cbee71636 | |||
| 7aed643eba | |||
| 829da52101 | |||
| 9dd449c2ef | |||
| f78bdaa358 | |||
| 16236e1361 | |||
| 8e52d45037 | |||
| 2246670487 | |||
| cde325198e | |||
| faa0710fd5 | |||
| d1bb27ad6b | |||
| 8f1f679077 | |||
| b632cbba0f | |||
| de438e90c4 | |||
| 7a3b1d4dbe | |||
| 44e9b4da4d | |||
| 559710b4b4 | |||
| a05c5b4976 | |||
| 51cfe767ee | |||
| 0891e78b8d | |||
| 5c834ec09c | |||
| fc5b6ae4e4 | |||
| 5e668d9e13 | |||
| b3d63c5698 | |||
| 079c160268 | |||
| f5425c2bea |
4
.gitignore
vendored
4
.gitignore
vendored
@ -5,4 +5,6 @@ local.properties
|
||||
# sign.properties
|
||||
.DS_Store
|
||||
captures/
|
||||
build/
|
||||
build/
|
||||
release-app/
|
||||
scripts/apk-channel/
|
||||
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
[submodule "libraries/LGLibrary"]
|
||||
path = libraries/LGLibrary
|
||||
url = git@gitlab.ghzhushou.com:client/client-common.git
|
||||
branch = master
|
||||
17
CHANGELOG.md
Normal file
17
CHANGELOG.md
Normal file
@ -0,0 +1,17 @@
|
||||
### Ver 3.1
|
||||
|
||||
### Ver 3.0
|
||||
* 升级账号系统(登录流程/用户信息相关/用户账号相关操作(评论,礼包...))
|
||||
* 新增收藏功能(文章/工具箱)
|
||||
* 删除用户相关的所有本地数据库
|
||||
* 重做总开服表
|
||||
* 重做首页插件化模块
|
||||
* 礼包重复领取机制改变(可重复领取的礼包,领取后立刻显示再领一个/再淘一个)
|
||||
* 游戏下载平台面板修改(加快弹出速度,不再读取本地平台图片)
|
||||
* 接入bugly(tinker)
|
||||
|
||||
### Ver 2.6
|
||||
* xx
|
||||
|
||||
### Ver 2.5
|
||||
* 此处写本次更新所做的业务和代码修改
|
||||
57
README.md
57
README.md
@ -1,23 +1,66 @@
|
||||
# 光环助手Android客户端
|
||||
|
||||
### 多渠道打包配置
|
||||
* 使用[ApkChannelPackage](https://github.com/ltlovezh/ApkChannelPackage)的方案
|
||||
### APK打包配置
|
||||
|
||||
* 正式打包命令:请使用./gradlew channelRelease打包渠道包
|
||||
* 使用[ApkChannelPackage](https://github.com/ltlovezh/ApkChannelPackage)的方案
|
||||
* 打包命令,视情况使用:
|
||||
|
||||
> 打包Tinker基准包:`./scripts/tinker_release_base.sh`
|
||||
|
||||
> 以Tinker基准包打渠道包:`./scripts/tinker_release_channel.sh`
|
||||
|
||||
> 以Tinker基准包打补丁包:`./scripts/tinker_release_patch.sh`
|
||||
|
||||
### 混淆配置
|
||||
|
||||
* 配置文件:Android默认配置+proguard-rules.txt等
|
||||
* 参考libraries下每个项目独立的配置文件``proguard-project.txt``
|
||||
* 参考libraries下每个项目独立的配置文件`proguard-project.txt`
|
||||
|
||||
### apk大小优化
|
||||
|
||||
* 限制resConfig资源集
|
||||
* 开启ShrinkResources
|
||||
* 开启混淆,使用minifyEnabled(仅在release开启)
|
||||
* pngquant对png压缩、png/jpg->webp(未尝试)
|
||||
|
||||
### 第三方appkey等配置
|
||||
* 修改``gradle.properties``文件将各种key填入其中,实现统一管理
|
||||
|
||||
* 修改`gradle.properties`文件将各种key填入其中,实现统一管理
|
||||
* 通过gradle文件内的resValue/buildConfigField/manifestPlaceHolder方式实现编译期间修改,具体情况请参考``./build.gradle``和``./app/build.gradle``配置
|
||||
|
||||
### Ver 2.5
|
||||
* 此处写本次更新所做的业务和代码修改
|
||||
### 拉取代码步骤
|
||||
|
||||
1. 拉取主项目代码: `git clone -b dev git@gitlab.ghzhushou.com:halo/assistant-android.git`
|
||||
2. 初始化公用类库: `bash ./scripts/submodules_init.sh`
|
||||
|
||||
### submodule管理方式(只拉取master)
|
||||
|
||||
* 提交代码,需要cd到submodule文件夹去做修改
|
||||
* 更新远端代码,`bash ./scripts/submodules_update.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 导致布局出现异常(问题编辑标签选择弹窗)
|
||||
298
app/build.gradle
298
app/build.gradle
@ -1,29 +1,40 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
apply plugin: 'com.neenbedankt.android-apt'
|
||||
|
||||
//tinker插件
|
||||
//apply plugin: 'com.tencent.tinker.patch'
|
||||
apply plugin: 'org.jetbrains.kotlin.android.extensions'
|
||||
apply plugin: 'kotlin-android' // kotlin
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
// apkChannelPackage
|
||||
apply plugin: 'channel'
|
||||
|
||||
apply from: 'tinker-support.gradle'
|
||||
|
||||
android {
|
||||
|
||||
androidExtensions {
|
||||
experimental = true
|
||||
}
|
||||
|
||||
dataBinding {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
dexOptions {
|
||||
jumboMode = true
|
||||
// jumboMode = true
|
||||
javaMaxHeapSize "4g"
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
|
||||
multiDexEnabled true
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
arguments = [ eventBusIndex : 'com.bandeng.MyEventBusIndex' ]
|
||||
arguments = [eventBusIndex: 'com.gh.EventBusIndex']
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,35 +51,24 @@ android {
|
||||
// 由于app只针对中文用户,所以仅保留zh资源,其他删掉
|
||||
resConfigs "zh"
|
||||
|
||||
// jackOptions {
|
||||
// enabled true
|
||||
// }
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode rootProject.ext.versionCode
|
||||
versionName rootProject.ext.versionName
|
||||
applicationId rootProject.ext.applicationId
|
||||
|
||||
multiDexEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-fresco.txt'
|
||||
|
||||
/**
|
||||
* All third-party appid/appkey
|
||||
*/
|
||||
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", "MIPUSH_APPID", "\"${MIPUSH_APPID}\""
|
||||
buildConfigField "String", "MIPUSH_APPKEY", "\"${MIPUSH_APPKEY}\""
|
||||
buildConfigField "String", "MTA_APPKEY", "\"${MTA_APPKEY}\""
|
||||
buildConfigField "String", "TD_APPID", "\"${TD_APPID}\""
|
||||
buildConfigField "String", "UMENG_APPKEY", "\"${UMENG_APPKEY}\""
|
||||
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${UMENG_MESSAGE_SECRET}\""
|
||||
|
||||
buildConfigField "String", "DEBUG_UMENG_APPKEY", "\"${DEBUG_UMENG_APPKEY}\""
|
||||
buildConfigField "String", "DEBUG_UMENG_MESSAGE_SECRET", "\"${DEBUG_UMENG_MESSAGE_SECRET}\""
|
||||
buildConfigField "String", "DEBUG_MIPUSH_APPID", "\"${DEBUG_MIPUSH_APPID}\""
|
||||
buildConfigField "String", "DEBUG_MIPUSH_APPKEY", "\"${DEBUG_MIPUSH_APPKEY}\""
|
||||
buildConfigField "String", "PATCH_VERSION_NAME", "\"${PATCH_VERSION_NAME}\""
|
||||
|
||||
}
|
||||
|
||||
@ -89,7 +89,10 @@ android {
|
||||
debuggable true
|
||||
minifyEnabled false
|
||||
zipAlignEnabled false
|
||||
versionNameSuffix "-debug"
|
||||
signingConfig signingConfigs.debug
|
||||
|
||||
buildConfigField "String", "EXPOSURE_REPO", "\"test\""
|
||||
buildConfigField "String", "EXPOSURE_VERSION", "\"E1\""
|
||||
}
|
||||
release {
|
||||
debuggable false
|
||||
@ -97,45 +100,61 @@ android {
|
||||
zipAlignEnabled true
|
||||
shrinkResources true
|
||||
signingConfig signingConfigs.release
|
||||
|
||||
buildConfigField "String", "EXPOSURE_REPO", "\"exposure\""
|
||||
buildConfigField "String", "EXPOSURE_VERSION", "\"E1\""
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions "nonsense"
|
||||
|
||||
/**
|
||||
* 多渠道打包,所有渠道值均通过java code设置
|
||||
* 多渠道打包,渠道请参考"channel.txt"文件,所有渠道值均通过java code设置
|
||||
*/
|
||||
// productFlavors {
|
||||
// GH_100 {}
|
||||
// GH_101 {}
|
||||
// GH_102 {}
|
||||
// GH_103 {}
|
||||
// GH_104 {}
|
||||
// GH_106 {}
|
||||
// GH_107 {}
|
||||
// GH_108 {}
|
||||
// GH_109 {}
|
||||
// GH_110 {}
|
||||
// GH_111 {}
|
||||
// GH_113 {}
|
||||
// GH_114 {}
|
||||
// GH_115 {}
|
||||
// GH_116 {}
|
||||
// GH_117 {}
|
||||
// GH_118 {}
|
||||
// GH_119 {}
|
||||
// GH_120 {}
|
||||
// GH_121 {}
|
||||
// GH_123 {}
|
||||
// GH_127 {}
|
||||
// GH_200 {}
|
||||
// GH_201 {}
|
||||
// GH_202 {}
|
||||
// GH_203 {}
|
||||
// GH_204 {}
|
||||
// GH_205 {}
|
||||
// GH_222 {}
|
||||
// GH_307 {}
|
||||
// GH_TEST {}
|
||||
// }
|
||||
productFlavors {
|
||||
// publish release host
|
||||
publish {
|
||||
dimension "nonsense"
|
||||
buildConfigField "String", "API_HOST", "\"${API_HOST}\""
|
||||
buildConfigField "String", "USER_HOST", "\"${USER_HOST}\""
|
||||
buildConfigField "String", "COMMENT_HOST", "\"${COMMENT_HOST}\""
|
||||
buildConfigField "String", "LIBAO_HOST", "\"${LIBAO_HOST}\""
|
||||
buildConfigField "String", "MESSAGE_HOST", "\"${MESSAGE_HOST}\""
|
||||
buildConfigField "String", "DATA_HOST", "\"${DATA_HOST}\""
|
||||
|
||||
buildConfigField "String", "UMENG_APPKEY", "\"${UMENG_APPKEY}\""
|
||||
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${UMENG_MESSAGE_SECRET}\""
|
||||
buildConfigField "String", "MIPUSH_APPID", "\"${MIPUSH_APPID}\""
|
||||
buildConfigField "String", "MIPUSH_APPKEY", "\"${MIPUSH_APPKEY}\""
|
||||
buildConfigField "String", "MEIZUPUSH_APPID", "\"${MEIZUPUSH_APPID}\""
|
||||
buildConfigField "String", "MEIZUPUSH_APPKEY", "\"${MEIZUPUSH_APPKEY}\""
|
||||
|
||||
buildConfigField "String", "BUGLY_APPID", "\"${BUGLY_APPID}\""
|
||||
|
||||
}
|
||||
// internal test dev host
|
||||
internal {
|
||||
dimension "nonsense"
|
||||
versionNameSuffix "-debug"
|
||||
|
||||
buildConfigField "String", "API_HOST", "\"${DEV_API_HOST}\""
|
||||
buildConfigField "String", "USER_HOST", "\"${DEV_USER_HOST}\""
|
||||
buildConfigField "String", "COMMENT_HOST", "\"${DEV_COMMENT_HOST}\""
|
||||
buildConfigField "String", "LIBAO_HOST", "\"${DEV_LIBAO_HOST}\""
|
||||
buildConfigField "String", "MESSAGE_HOST", "\"${DEV_MESSAGE_HOST}\""
|
||||
buildConfigField "String", "DATA_HOST", "\"${DEV_DATA_HOST}\""
|
||||
|
||||
buildConfigField "String", "UMENG_APPKEY", "\"${DEBUG_UMENG_APPKEY}\""
|
||||
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${DEBUG_UMENG_MESSAGE_SECRET}\""
|
||||
buildConfigField "String", "MIPUSH_APPID", "\"${DEBUG_MIPUSH_APPID}\""
|
||||
buildConfigField "String", "MIPUSH_APPKEY", "\"${DEBUG_MIPUSH_APPKEY}\""
|
||||
buildConfigField "String", "MEIZUPUSH_APPID", "\"${DEBUG_MEIZUPUSH_APPID}\""
|
||||
buildConfigField "String", "MEIZUPUSH_APPKEY", "\"${DEBUG_MEIZUPUSH_APPKEY}\""
|
||||
|
||||
buildConfigField "String", "BUGLY_APPID", "\"${DEBUG_BUGLY_APPID}\""
|
||||
}
|
||||
}
|
||||
|
||||
// productFlavors.all { flavor ->
|
||||
// flavor.manifestPlaceholders = [CHANNEL_VALUE: name]//命令 gradlew assembleRelease
|
||||
// }
|
||||
@ -150,12 +169,6 @@ channel {
|
||||
apkNameFormat = '${appName}-${versionName}-${versionCode}-${flavorName}-${buildType}'
|
||||
}
|
||||
|
||||
apt {
|
||||
arguments {
|
||||
eventBusIndex "com.bandeng.MyEventBusIndex"
|
||||
}
|
||||
}
|
||||
|
||||
rebuildChannel {
|
||||
// baseDebugApk = 已有Debug APK
|
||||
// baseReleaseApk = 已有Release APK
|
||||
@ -166,83 +179,108 @@ rebuildChannel {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testCompile test.junit
|
||||
compile fileTree(include: '*.jar', dir: 'libs')
|
||||
compile libs.supportMultidex
|
||||
compile libs.supportDesign
|
||||
compile libs.supportAppCompat
|
||||
compile libs.supportAnnotation
|
||||
compile libs.supportPercent
|
||||
compile libs.supportDesign
|
||||
|
||||
compile libs.switchButton
|
||||
implementation fileTree(include: '*.jar', dir: 'libs')
|
||||
|
||||
compile libs.systemBarTint
|
||||
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}"
|
||||
|
||||
compile libs.fresco
|
||||
compile libs.frescoAnimatedGif
|
||||
implementation "com.android.support:multidex:${multidex}"
|
||||
implementation "com.android.support:design:${androidSupport}"
|
||||
implementation "com.android.support:support-v4:${androidSupport}"
|
||||
implementation "com.android.support:appcompat-v7:${androidSupport}"
|
||||
implementation "com.android.support:cardview-v7:${androidSupport}"
|
||||
implementation "com.android.support:support-annotations:${androidSupport}"
|
||||
implementation "com.android.support:percent:${androidSupport}"
|
||||
implementation "com.android.support.constraint:constraint-layout:${constraintLayout}"
|
||||
implementation "com.kyleduo.switchbutton:library:${switchButton}"
|
||||
|
||||
compile libs.okHttp
|
||||
compile libs.okHttpLogInterceptor
|
||||
implementation "com.facebook.fresco:fresco:${fresco}"
|
||||
implementation "com.facebook.fresco:animated-gif:${fresco}"
|
||||
|
||||
compile libs.apkChannelPackage
|
||||
implementation "com.squareup.okhttp3:okhttp:${okHttp}"
|
||||
|
||||
// debugCompile libs.stetho
|
||||
// debugCompile libs.stethoWithOkHttp
|
||||
implementation "com.leon.channel:helper:${apkChannelPackage}"
|
||||
|
||||
compile libs.retrofit
|
||||
compile libs.retrofitWithGson // include gson 2.7
|
||||
compile libs.retrofitWithRxJava
|
||||
// compile libs.gson
|
||||
implementation "com.squareup.retrofit2:retrofit:${retrofit}"
|
||||
implementation "com.squareup.retrofit2:converter-gson:${retrofit}" // include gson 2.7
|
||||
implementation "com.squareup.retrofit2:adapter-rxjava2:${retrofit}"
|
||||
|
||||
compile libs.ormliteAndroid
|
||||
compile libs.ormliteCore
|
||||
implementation "com.j256.ormlite:ormlite-android:${ormlite}"
|
||||
implementation "com.j256.ormlite:ormlite-core:${ormlite}"
|
||||
|
||||
compile libs.butterKnife
|
||||
apt libs.butterKnifeApt
|
||||
implementation "com.jakewharton:butterknife:${butterKnife}"
|
||||
annotationProcessor "com.jakewharton:butterknife-compiler:${butterKnife}"
|
||||
kapt "com.jakewharton:butterknife-compiler:${butterKnife}"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}"
|
||||
|
||||
compile libs.rxJava
|
||||
compile libs.rxAndroid
|
||||
compile libs.rxBinding
|
||||
compile libs.zxing
|
||||
compile libs.zxingAndroid
|
||||
implementation "org.greenrobot:eventbus:${eventbus}"
|
||||
annotationProcessor "org.greenrobot:eventbus-annotation-processor:${eventbusApt}"
|
||||
|
||||
compile libs.swipeLayout
|
||||
compile(libs.autoScrollViewPager) {
|
||||
implementation "io.reactivex.rxjava2:rxjava:${rxJava2}"
|
||||
implementation "io.reactivex.rxjava2:rxandroid:${rxAndroid2}"
|
||||
implementation "com.jakewharton.rxbinding2:rxbinding:${rxBinding2}"
|
||||
|
||||
implementation "com.google.zxing:core:${zxing}"
|
||||
implementation "com.google.zxing:android-core:${zxing}"
|
||||
|
||||
implementation "com.daimajia.swipelayout:library:${swipeLayout}"
|
||||
implementation("cn.trinea.android.view.autoscrollviewpager:android-auto-scroll-view-pager:${autoScrollViewPager}") {
|
||||
exclude module: 'support-v4'
|
||||
}
|
||||
|
||||
// tinker
|
||||
// provided libs.tinker_anno
|
||||
// compile libs.tinker_lib
|
||||
implementation "com.sina.weibo.sdk:core:${weiboSDK}"
|
||||
|
||||
compile libs.eventbus
|
||||
apt libs.eventbusApt
|
||||
// compile project(':libraries:EventBus')
|
||||
compile project(':libraries:MiPush')
|
||||
compile project(':libraries:MTA')
|
||||
compile project(':libraries:QQShare')
|
||||
compile project(':libraries:TalkingData')
|
||||
compile project(':libraries:UmengPush')
|
||||
compile project(':libraries:WechatShare')
|
||||
compile project(':libraries:WeiboShare')
|
||||
compile project(':libraries:iosched')
|
||||
// bugly with tinker support
|
||||
implementation "com.tencent.bugly:crashreport_upgrade:${buglyTinkerSupport}"
|
||||
|
||||
implementation "pub.devrel:easypermissions:${easypermissions}"
|
||||
// mvvm
|
||||
implementation "android.arch.lifecycle:runtime:${archLifecycleVersion}"
|
||||
kapt "android.arch.lifecycle:compiler:${archLifecycleVersion}"
|
||||
implementation "android.arch.lifecycle:extensions:${archLifecycleVersion}"
|
||||
implementation "android.arch.persistence.room:runtime:${archRoomVersion}"
|
||||
kapt "android.arch.persistence.room:compiler:${archRoomVersion}"
|
||||
|
||||
implementation 'com.google.android:flexbox:0.2.2'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
kapt 'com.android.databinding:compiler:3.1.3'
|
||||
|
||||
implementation 'com.contrarywind:Android-PickerView:4.1.3'
|
||||
|
||||
implementation "com.scwang.smartrefresh:SmartRefreshLayout:${smartRefreshLayout}"
|
||||
implementation "net.cachapa.expandablelayout:expandablelayout:${expandableLayout}"
|
||||
|
||||
implementation "top.zibin:Luban:${luban}"
|
||||
|
||||
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:iosched')
|
||||
implementation project(':libraries:LogHub')
|
||||
implementation project(':libraries:im')
|
||||
}
|
||||
File propFile = file('sign.properties');
|
||||
File propFile = file('sign.properties')
|
||||
if (propFile.exists()) {
|
||||
def Properties props = new Properties()
|
||||
Properties props = new Properties()
|
||||
props.load(new FileInputStream(propFile))
|
||||
|
||||
if (props.containsKey('keyAlias') && props.containsKey('keyPassword') &&
|
||||
props.containsKey('storeFile') && props.containsKey('storePassword')) {
|
||||
android.signingConfigs {
|
||||
// debug {
|
||||
// keyAlias props.get('keyAlias')
|
||||
// keyPassword props.get('keyPassword')
|
||||
// storeFile file(props.get('storeFile'))
|
||||
// storePassword props.get('storePassword')
|
||||
// }
|
||||
// debug 不要使用正式签名,这样tinker才不会打补丁。
|
||||
debug {
|
||||
keyAlias props.get('keyAlias')
|
||||
keyPassword props.get('keyPassword')
|
||||
storeFile file(props.get('storeFile'))
|
||||
storePassword props.get('storePassword')
|
||||
}
|
||||
release {
|
||||
keyAlias props.get('keyAlias')
|
||||
keyPassword props.get('keyPassword')
|
||||
@ -256,3 +294,31 @@ if (propFile.exists()) {
|
||||
} else {
|
||||
android.buildTypes.release.signingConfig = null
|
||||
}
|
||||
|
||||
// 用于测试读取 META-INF 里的 JSON 的代码
|
||||
//task generateMetaJson {
|
||||
// def resDir = new File(buildDir, 'generated/FILES_FOR_META_INF/')
|
||||
// def destDir = new File(resDir, 'META-INF/')
|
||||
// // Add resDir as a resource directory so that it is automatically included in the APK.
|
||||
// android {
|
||||
// sourceSets {
|
||||
// main.resources {
|
||||
// srcDir resDir
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// doLast {
|
||||
// if (!destDir.exists()) destDir.mkdirs()
|
||||
// copy {
|
||||
// into destDir
|
||||
// from new File('generated/FILES_FOR_META_INF/META-INF/halo_skip.json')
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//// Specify when put_files_in_META_INF should run
|
||||
//project.afterEvaluate {
|
||||
// tasks.findAll { task ->
|
||||
// task.name.startsWith('merge') && task.name.endsWith('Resources')
|
||||
// }.each { t -> t.dependsOn generateMetaJson }
|
||||
//}
|
||||
|
||||
@ -16,9 +16,23 @@
|
||||
# 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
|
||||
-dontoptimize
|
||||
|
||||
# OrmLite uses reflection
|
||||
-keep class com.j256.**
|
||||
@ -117,11 +131,16 @@
|
||||
<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 * extends rx.Subscriber
|
||||
@ -157,4 +176,25 @@
|
||||
|
||||
-keepclassmembers enum * { *; }
|
||||
|
||||
##---------------End: proguard configuration for Gson ----------
|
||||
##---------------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 @android.support.annotation.Keep class *
|
||||
-keepclassmembers class ** {
|
||||
@android.support.annotation.Keep *;
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
package com.gh.gamecenter;
|
||||
|
||||
import android.app.Application;
|
||||
import android.test.ApplicationTestCase;
|
||||
|
||||
/**
|
||||
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
|
||||
*/
|
||||
public class ApplicationTest extends ApplicationTestCase<Application> {
|
||||
public ApplicationTest() {
|
||||
super(Application.class);
|
||||
}
|
||||
}
|
||||
46
app/src/debug/java/com/gh/gamecenter/Injection.java
Normal file
46
app/src/debug/java/com/gh/gamecenter/Injection.java
Normal file
@ -0,0 +1,46 @@
|
||||
package com.gh.gamecenter;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import com.facebook.stetho.Stetho;
|
||||
import com.facebook.stetho.okhttp3.StethoInterceptor;
|
||||
import com.squareup.leakcanary.LeakCanary;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
|
||||
/**
|
||||
* @author CsHeng
|
||||
* @Date 03/09/2017
|
||||
* @Time 4:34 PM
|
||||
*/
|
||||
|
||||
public class Injection {
|
||||
|
||||
public static boolean appInit(Application application) {
|
||||
|
||||
// init leakcanary
|
||||
if (LeakCanary.isInAnalyzerProcess(application)) {
|
||||
// This process is dedicated to LeakCanary for heap analysis.
|
||||
// You should not init your app in this process.
|
||||
return false;
|
||||
}
|
||||
LeakCanary.install(application);
|
||||
|
||||
// init stetho
|
||||
Stetho.initializeWithDefaults(application);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static OkHttpClient.Builder provideRetrofitBuilder() {
|
||||
OkHttpClient.Builder builder = new OkHttpClient.Builder();
|
||||
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
|
||||
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
|
||||
builder.addNetworkInterceptor(interceptor);
|
||||
builder.addNetworkInterceptor(new StethoInterceptor());
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android = "http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools = "http://schemas.android.com/tools"
|
||||
package = "com.gh.gamecenter" >
|
||||
|
||||
<!-- 允许应用程序访问网络连接 -->
|
||||
@ -39,6 +40,14 @@
|
||||
<!-- 修改系统设置的权限 -->
|
||||
<uses-permission android:name = "android.permission.WRITE_SETTINGS" />
|
||||
|
||||
<!-- bugly with tinker -->
|
||||
<uses-permission android:name = "android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name = "android.permission.INTERNET" />
|
||||
<uses-permission android:name = "android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name = "android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name = "android.permission.READ_LOGS" />
|
||||
<uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<supports-screens
|
||||
android:anyDensity = "true"
|
||||
android:largeScreens = "true"
|
||||
@ -48,35 +57,42 @@
|
||||
|
||||
<!--android:largeHeap = "true"-->
|
||||
<application
|
||||
android:name = "com.gh.base.AppController"
|
||||
android:name = "com.halo.assistant.TinkerApp"
|
||||
android:allowBackup = "true"
|
||||
android:icon = "@drawable/logo"
|
||||
android:label = "@string/app_name"
|
||||
android:theme = "@style/AppCompatTheme" >
|
||||
android:resizeableActivity = "true"
|
||||
android:theme = "@style/AppCompatTheme.APP"
|
||||
tools:targetApi = "n" >
|
||||
|
||||
<!--android:launchMode = "singleTask"-->
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.SplashScreenActivity"
|
||||
android:configChanges = "keyboardHidden|orientation|screenSize"
|
||||
android:noHistory = "true"
|
||||
android:screenOrientation = "portrait"
|
||||
android:theme = "@style/AppGuideTheme"
|
||||
android:uiOptions = "splitActionBarWhenNarrow" >
|
||||
android:theme = "@style/AppGuideTheme" >
|
||||
<intent-filter >
|
||||
<action android:name = "android.intent.action.MAIN" />
|
||||
|
||||
<category android:name = "android.intent.category.LAUNCHER" />
|
||||
</intent-filter >
|
||||
|
||||
</activity >
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.MainActivity"
|
||||
android:launchMode = "singleTop"
|
||||
android:launchMode = "singleTask"
|
||||
android:screenOrientation = "portrait"
|
||||
android:windowSoftInputMode = "stateAlwaysHidden|adjustResize" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.DownloadManagerActivity"
|
||||
android:launchMode = "singleTask"
|
||||
android:screenOrientation = "portrait" />
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.ViewImageActivity"
|
||||
android:theme = "@android:style/Theme.Black.NoTitleBar.Fullscreen" />
|
||||
|
||||
<!--android:theme = "@android:style/Theme.Black.NoTitleBar.Fullscreen" 退出时屏幕抖动 -->
|
||||
<activity android:name = "com.gh.gamecenter.ViewImageActivity" />
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.SearchActivity"
|
||||
android:configChanges = "keyboardHidden"
|
||||
@ -91,7 +107,7 @@
|
||||
android:name = "com.gh.gamecenter.ConcernActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.SubjectActivity"
|
||||
android:name = "com.gh.gamecenter.subject.refactor.SubjectActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.PluginActivity"
|
||||
@ -99,7 +115,7 @@
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.NewsSearchActivity"
|
||||
android:screenOrientation = "portrait"
|
||||
android:windowSoftInputMode="stateHidden"/>
|
||||
android:windowSoftInputMode = "stateHidden" />
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.GameNewsActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
@ -121,7 +137,7 @@
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.LibaoActivity"
|
||||
android:screenOrientation = "portrait"
|
||||
android:windowSoftInputMode="stateHidden"/>
|
||||
android:windowSoftInputMode = "stateHidden" />
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.LibaoDetailActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
@ -155,9 +171,6 @@
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.AboutActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.KaiFuActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.CommentDetailActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
@ -169,8 +182,7 @@
|
||||
android:screenOrientation = "portrait" />
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.SuggestionActivity"
|
||||
android:screenOrientation = "portrait"
|
||||
android:windowSoftInputMode = "stateHidden|adjustResize" />
|
||||
android:screenOrientation = "portrait" />
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.VoteActivity"
|
||||
android:screenOrientation = "portrait"
|
||||
@ -178,7 +190,166 @@
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.ToolBoxActivity"
|
||||
android:screenOrientation = "portrait"
|
||||
android:windowSoftInputMode="stateHidden"/>
|
||||
android:windowSoftInputMode = "stateHidden" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.WeiBoShareActivity"
|
||||
android:screenOrientation = "portrait"
|
||||
android:windowSoftInputMode = "stateHidden" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.InstallActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
android:name = ".category.CategoryDirectoryActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
android:name = ".category.CategoryListActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.LoginActivity"
|
||||
android:screenOrientation = "portrait"
|
||||
android:windowSoftInputMode = "stateHidden" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.UserInfoActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.UserRegionActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.CollectionActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.MessageActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.UserInfoEditActivity"
|
||||
android:screenOrientation = "portrait"
|
||||
android:windowSoftInputMode = "stateHidden" />
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.KaiFuActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<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" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.qa.answer.edit.AnswerEditActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.ConcernInfoActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.InfoActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
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" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.MessageVoteActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
android:name = ".qa.questions.invite.QuestionsInviteActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
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" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.kaifu.add.AddKaiFuActivity"
|
||||
android:screenOrientation = "portrait"
|
||||
android:windowSoftInputMode = "stateHidden" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.kaifu.patch.PatchKaifuActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.BlockActivity"
|
||||
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" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.personalhome.fans.FansActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.personalhome.fans.FollowersActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.PersonalHomeActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.personalhome.answer.PersonalAnswerActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.personalhome.question.PersonalQuestionActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
|
||||
<!-- 使用小米/华为推送弹窗功能提高推送成功率-->
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.PushProxyActivity"
|
||||
android:exported = "true"
|
||||
android:theme = "@android:style/Theme.Translucent" />
|
||||
|
||||
<activity
|
||||
android:name = "com.gh.gamecenter.SkipActivity"
|
||||
android:theme = "@style/Theme.AppCompat.Light.Fullscreen.Transparent" >
|
||||
@ -191,6 +362,10 @@
|
||||
</intent-filter >
|
||||
</activity >
|
||||
|
||||
<activity
|
||||
android:name = ".CommonActivity"
|
||||
android:screenOrientation = "portrait" />
|
||||
|
||||
<receiver android:name = "com.gh.gamecenter.receiver.InstallAndUninstallReceiver" >
|
||||
<intent-filter >
|
||||
<action android:name = "android.intent.action.PACKAGE_ADDED" />
|
||||
@ -200,13 +375,7 @@
|
||||
<data android:scheme = "package" />
|
||||
</intent-filter >
|
||||
</receiver >
|
||||
<receiver
|
||||
android:name = "com.gh.gamecenter.receiver.NotificationReceiver"
|
||||
android:exported = "false" >
|
||||
<intent-filter >
|
||||
<action android:name = "com.gh.gamecenter.NOTIFICATION" />
|
||||
</intent-filter >
|
||||
</receiver >
|
||||
|
||||
<receiver
|
||||
android:name = "com.gh.gamecenter.receiver.DownloadReceiver"
|
||||
android:exported = "false" >
|
||||
@ -235,9 +404,41 @@
|
||||
</intent-filter >
|
||||
</receiver >
|
||||
|
||||
<service android:name = "com.gh.download.DownloadService" />
|
||||
<receiver android:name = "com.gh.gamecenter.receiver.UmengMessageReceiver" >
|
||||
<intent-filter >
|
||||
<action android:name = "com.gh.gamecenter.UMENG" />
|
||||
</intent-filter >
|
||||
</receiver >
|
||||
|
||||
<service android:name = "com.gh.gamecenter.statistics.AppStaticService" />
|
||||
<!--魅族push应用定义消息receiver声明 -->
|
||||
<receiver android:name="com.gh.gamecenter.receiver.MeizuPushReceiver">
|
||||
<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>
|
||||
|
||||
<service android:name = "com.gh.base.GHUmengNotificationService" />
|
||||
|
||||
<!--<service android:name = "com.gh.gamecenter.statistics.AppStaticService" />-->
|
||||
|
||||
</application >
|
||||
|
||||
|
||||
1365
app/src/main/assets/LocList.txt
Normal file
1365
app/src/main/assets/LocList.txt
Normal file
File diff suppressed because it is too large
Load Diff
28
app/src/main/assets/content.js
Normal file
28
app/src/main/assets/content.js
Normal file
@ -0,0 +1,28 @@
|
||||
function requestContentFocus() {
|
||||
$('#editor').focus();
|
||||
}
|
||||
|
||||
function setupWhenContentEditable() {
|
||||
var editor = $('#editor');
|
||||
if (!editor[0].hasAttribute('contenteditable')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// paste 回调只会获取粘贴之前的光标位置,需要自己手动加上粘贴文本的长度,并保证粘贴的是纯文本
|
||||
editor.on('paste', function(e) {
|
||||
e.preventDefault();
|
||||
var text = (e.originalEvent || e).clipboardData.getData('text/plain');
|
||||
text = text.replace(/\n/g, '<br>');
|
||||
if("" != text) {
|
||||
document.execCommand("insertHTML", false, text);
|
||||
} else {
|
||||
window.onPasteListener.onPaste();
|
||||
}
|
||||
});
|
||||
|
||||
requestContentFocus();
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
setupWhenContentEditable();
|
||||
});
|
||||
16
app/src/main/assets/editor.html
Normal file
16
app/src/main/assets/editor.html
Normal file
@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="user-scalable=no">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<link rel="stylesheet" type="text/css" href="normalize.css">
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="editor" contenteditable="true"></div>
|
||||
<script type="text/javascript" src="zepto.min.js"></script>
|
||||
<script type="text/javascript" src="rich_editor.js"></script>
|
||||
<script type="text/javascript" src="content.js"></script>
|
||||
<!--<script src="https://cdnjs.cloudflare.com/ajax/libs/vanilla-lazyload/10.15.0/lazyload.min.js"></script>-->
|
||||
</body>
|
||||
</html>
|
||||
40
app/src/main/assets/emojikf
Normal file
40
app/src/main/assets/emojikf
Normal file
@ -0,0 +1,40 @@
|
||||
emoji_kf_1.png,:smile:
|
||||
emoji_kf_2.png,:smiley:
|
||||
emoji_kf_3.png,:laughing:
|
||||
emoji_kf_4.png,:blush:
|
||||
emoji_kf_5.png,:heart_eyes:
|
||||
emoji_kf_6.png,:smirk:
|
||||
emoji_kf_7.png,:flushed:
|
||||
emoji_kf_8.png,:kissing_heart:
|
||||
emoji_kf_9.png,:grin:
|
||||
emoji_kf_10.png,:wink:
|
||||
emoji_kf_11.png,:stuck_out_tongue_winking_eye:
|
||||
emoji_kf_12.png,:stuck_out_tongue_closed eyes:
|
||||
emoji_kf_13.png,:worried:
|
||||
emoji_kf_14.png,:sleeping:
|
||||
emoji_kf_15.png,:expressionless:
|
||||
emoji_kf_16.png,:sweat_smile:
|
||||
emoji_kf_17.png,:joy:
|
||||
emoji_kf_18.png,:cold_sweat:
|
||||
emoji_kf_19.png,:sob:
|
||||
emoji_kf_20.png,:angry:
|
||||
emoji_kf_21.png,:mask:
|
||||
emoji_kf_22.png,:scream:
|
||||
emoji_kf_23.png,:sunglasses:
|
||||
emoji_kf_24.png,:heart:
|
||||
emoji_kf_25.png,:broken_heart:
|
||||
emoji_kf_26.png,:star:
|
||||
emoji_kf_27.png,:anger:
|
||||
emoji_kf_28.png,:exclamation:
|
||||
emoji_kf_29.png,:question:
|
||||
emoji_kf_30.png,:zzz:
|
||||
emoji_kf_31.png,:thumbsup:
|
||||
emoji_kf_32.png,:thumbsdown:
|
||||
emoji_kf_33.png,:ok_hand:
|
||||
emoji_kf_34.png,:punch:
|
||||
emoji_kf_35.png,:yeah:
|
||||
emoji_kf_36.png,:clap:
|
||||
emoji_kf_37.png,:muscle:
|
||||
emoji_kf_38.png,:pray:
|
||||
emoji_kf_39.png,:skull:
|
||||
emoji_kf_40.png,:trollface:
|
||||
BIN
app/src/main/assets/iattest.wav
Normal file
BIN
app/src/main/assets/iattest.wav
Normal file
Binary file not shown.
5
app/src/main/assets/microlog.properties
Normal file
5
app/src/main/assets/microlog.properties
Normal file
@ -0,0 +1,5 @@
|
||||
# This is a simple Microlog configuration file
|
||||
microlog.level=DEBUG
|
||||
microlog.appender=LogCatAppender;FileAppender
|
||||
microlog.formatter=PatternFormatter
|
||||
microlog.formatter.PatternFormatter.pattern=%c [%P] %m %T
|
||||
413
app/src/main/assets/normalize.css
vendored
Normal file
413
app/src/main/assets/normalize.css
vendored
Normal file
@ -0,0 +1,413 @@
|
||||
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
|
||||
|
||||
/**
|
||||
* 1. Set default font family to sans-serif.
|
||||
* 2. Prevent iOS text size adjust after orientation change, without disabling
|
||||
* user zoom.
|
||||
*/
|
||||
|
||||
html {
|
||||
font-family: sans-serif; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove default margin.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* HTML5 display definitions
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Correct `block` display not defined for any HTML5 element in IE 8/9.
|
||||
* Correct `block` display not defined for `details` or `summary` in IE 10/11
|
||||
* and Firefox.
|
||||
* Correct `block` display not defined for `main` in IE 11.
|
||||
*/
|
||||
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
main,
|
||||
menu,
|
||||
nav,
|
||||
section,
|
||||
summary {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct `inline-block` display not defined in IE 8/9.
|
||||
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
audio,
|
||||
canvas,
|
||||
progress,
|
||||
video {
|
||||
display: inline-block; /* 1 */
|
||||
vertical-align: baseline; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent modern browsers from displaying `audio` without controls.
|
||||
* Remove excess height in iOS 5 devices.
|
||||
*/
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address `[hidden]` styling not present in IE 8/9/10.
|
||||
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
|
||||
*/
|
||||
|
||||
[hidden],
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Links
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the gray background color from active links in IE 10.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Improve readability when focused and also mouse hovered in all browsers.
|
||||
*/
|
||||
|
||||
a:active,
|
||||
a:hover {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address styling not present in Safari and Chrome.
|
||||
*/
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address variable `h1` font-size and margin within `section` and `article`
|
||||
* contexts in Firefox 4+, Safari, and Chrome.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address styling not present in IE 8/9.
|
||||
*/
|
||||
|
||||
mark {
|
||||
background: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address inconsistent and variable font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove border when inside `a` element in IE 8/9/10.
|
||||
*/
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct overflow not hidden in IE 9/10/11.
|
||||
*/
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Address margin not present in IE 8/9 and Safari.
|
||||
*/
|
||||
|
||||
figure {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address differences between Firefox and other browsers.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contain overflow in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address odd `em`-unit font size rendering in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family: monospace, monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Known limitation: by default, Chrome and Safari on OS X allow very limited
|
||||
* styling of `select`, unless a `border` property is set.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 1. Correct color not being inherited.
|
||||
* Known issue: affects color of disabled elements.
|
||||
* 2. Correct font properties not being inherited.
|
||||
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
color: inherit; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
margin: 0; /* 3 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Address `overflow` set to `hidden` in IE 8/9/10/11.
|
||||
*/
|
||||
|
||||
button {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address inconsistent `text-transform` inheritance for `button` and `select`.
|
||||
* All other form control elements do not inherit `text-transform` values.
|
||||
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
|
||||
* Correct `select` style inheritance in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
|
||||
* and `video` controls.
|
||||
* 2. Correct inability to style clickable `input` types in iOS.
|
||||
* 3. Improve usability and consistency of cursor style between image-type
|
||||
* `input` and others.
|
||||
*/
|
||||
|
||||
button,
|
||||
html input[type="button"], /* 1 */
|
||||
input[type="reset"],
|
||||
input[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
cursor: pointer; /* 3 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-set default cursor for disabled elements.
|
||||
*/
|
||||
|
||||
button[disabled],
|
||||
html input[disabled] {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
|
||||
* the UA stylesheet.
|
||||
*/
|
||||
|
||||
input {
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* It's recommended that you don't attempt to style these elements.
|
||||
* Firefox's implementation doesn't respect box-sizing, padding, or width.
|
||||
*
|
||||
* 1. Address box sizing set to `content-box` in IE 8/9/10.
|
||||
* 2. Remove excess padding in IE 8/9/10.
|
||||
*/
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
|
||||
* `font-size` values of the `input`, it causes the cursor style of the
|
||||
* decrement button to change from `default` to `text`.
|
||||
*/
|
||||
|
||||
input[type="number"]::-webkit-inner-spin-button,
|
||||
input[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
|
||||
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
|
||||
*/
|
||||
|
||||
input[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
-webkit-box-sizing: content-box; /* 2 */
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
|
||||
* Safari (but not Chrome) clips the cancel button when the search input has
|
||||
* padding (and `textfield` appearance).
|
||||
*/
|
||||
|
||||
input[type="search"]::-webkit-search-cancel-button,
|
||||
input[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define consistent border, margin, and padding.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #c0c0c0;
|
||||
margin: 0 2px;
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct `color` not being inherited in IE 8/9/10/11.
|
||||
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
|
||||
*/
|
||||
|
||||
legend {
|
||||
border: 0; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove default vertical scrollbar in IE 8/9/10/11.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't inherit the `font-weight` (applied by a rule above).
|
||||
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
|
||||
*/
|
||||
|
||||
optgroup {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tables
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove most spacing between table cells.
|
||||
*/
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
padding: 0;
|
||||
}
|
||||
434
app/src/main/assets/rich_editor.js
Normal file
434
app/src/main/assets/rich_editor.js
Normal file
@ -0,0 +1,434 @@
|
||||
/**
|
||||
* Copyright (C) 2017 Wasabeef
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var RE = {};
|
||||
|
||||
RE.currentSelection = {
|
||||
"startContainer": 0,
|
||||
"startOffset": 0,
|
||||
"endContainer": 0,
|
||||
"endOffset": 0};
|
||||
|
||||
RE.editor = document.getElementById('editor');
|
||||
|
||||
document.addEventListener("selectionchange", function() { RE.backuprange(); });
|
||||
|
||||
// Initializations
|
||||
RE.callback = function() {
|
||||
window.location.href = "re-callback://" + encodeURI(RE.getHtml());
|
||||
}
|
||||
|
||||
RE.setHtml = function(contents) {
|
||||
RE.editor.innerHTML = decodeURIComponent(contents.replace(/\+/g, '%20'));
|
||||
}
|
||||
|
||||
RE.getHtml = function() {
|
||||
return RE.editor.innerHTML;
|
||||
}
|
||||
|
||||
RE.getText = function() {
|
||||
return RE.editor.innerText;
|
||||
}
|
||||
|
||||
RE.setBaseTextColor = function(color) {
|
||||
RE.editor.style.color = color;
|
||||
}
|
||||
|
||||
RE.setBaseFontSize = function(size) {
|
||||
RE.editor.style.fontSize = size;
|
||||
}
|
||||
|
||||
RE.setPadding = function(left, top, right, bottom) {
|
||||
RE.editor.style.paddingLeft = left;
|
||||
RE.editor.style.paddingTop = top;
|
||||
RE.editor.style.paddingRight = right;
|
||||
RE.editor.style.paddingBottom = bottom;
|
||||
}
|
||||
|
||||
RE.setBackgroundColor = function(color) {
|
||||
document.body.style.backgroundColor = color;
|
||||
}
|
||||
|
||||
RE.setBackgroundImage = function(image) {
|
||||
RE.editor.style.backgroundImage = image;
|
||||
}
|
||||
|
||||
RE.setWidth = function(size) {
|
||||
RE.editor.style.minWidth = size;
|
||||
}
|
||||
|
||||
RE.setHeight = function(size) {
|
||||
RE.editor.style.height = size;
|
||||
}
|
||||
|
||||
RE.setTextAlign = function(align) {
|
||||
RE.editor.style.textAlign = align;
|
||||
}
|
||||
|
||||
RE.setVerticalAlign = function(align) {
|
||||
RE.editor.style.verticalAlign = align;
|
||||
}
|
||||
|
||||
RE.setPlaceholder = function(placeholder) {
|
||||
RE.editor.setAttribute("placeholder", placeholder);
|
||||
}
|
||||
|
||||
RE.setEditorFocus = function() {
|
||||
RE.editor.focus();
|
||||
}
|
||||
|
||||
RE.setInputEnabled = function(inputEnabled) {
|
||||
RE.editor.contentEditable = String(inputEnabled);
|
||||
}
|
||||
|
||||
RE.setFocusByEnd = function() {
|
||||
//alert("111111")
|
||||
// var txt =RE.editor.createTextRange();
|
||||
// ("22222")
|
||||
// txt.moveStart('character',-1);
|
||||
// ("333333")
|
||||
// txt.collapse(true);
|
||||
// ("444444")
|
||||
// txt.select();
|
||||
// alert("ddddddd")
|
||||
}
|
||||
|
||||
RE.undo = function() {
|
||||
document.execCommand('undo', false, null);
|
||||
}
|
||||
|
||||
RE.redo = function() {
|
||||
document.execCommand('redo', false, null);
|
||||
}
|
||||
|
||||
RE.setBold = function() {
|
||||
document.execCommand('bold', false, null);
|
||||
}
|
||||
|
||||
RE.setItalic = function() {
|
||||
document.execCommand('italic', false, null);
|
||||
}
|
||||
|
||||
RE.setSubscript = function() {
|
||||
document.execCommand('subscript', false, null);
|
||||
}
|
||||
|
||||
RE.setSuperscript = function() {
|
||||
document.execCommand('superscript', false, null);
|
||||
}
|
||||
|
||||
RE.setStrikeThrough = function() {
|
||||
document.execCommand('strikeThrough', false, null);
|
||||
}
|
||||
|
||||
RE.setUnderline = function() {
|
||||
document.execCommand('underline', false, null);
|
||||
}
|
||||
|
||||
RE.setBullets = function() {
|
||||
document.execCommand('insertUnorderedList', false, null);
|
||||
}
|
||||
|
||||
RE.setNumbers = function() {
|
||||
document.execCommand('insertOrderedList', false, null);
|
||||
}
|
||||
|
||||
RE.setTextColor = function(color) {
|
||||
RE.restorerange();
|
||||
document.execCommand("styleWithCSS", null, true);
|
||||
document.execCommand('foreColor', false, color);
|
||||
document.execCommand("styleWithCSS", null, false);
|
||||
}
|
||||
|
||||
RE.setTextBackgroundColor = function(color) {
|
||||
RE.restorerange();
|
||||
document.execCommand("styleWithCSS", null, true);
|
||||
document.execCommand('hiliteColor', false, color);
|
||||
document.execCommand("styleWithCSS", null, false);
|
||||
}
|
||||
|
||||
RE.setFontSize = function(fontSize){
|
||||
document.execCommand("fontSize", false, fontSize);
|
||||
}
|
||||
|
||||
RE.setHeading = function(heading) {
|
||||
document.execCommand('formatBlock', false, '<h'+heading+'>');
|
||||
}
|
||||
|
||||
RE.setIndent = function() {
|
||||
document.execCommand('indent', false, null);
|
||||
}
|
||||
|
||||
RE.setOutdent = function() {
|
||||
document.execCommand('outdent', false, null);
|
||||
}
|
||||
|
||||
RE.setJustifyLeft = function() {
|
||||
document.execCommand('justifyLeft', false, null);
|
||||
}
|
||||
|
||||
RE.setJustifyCenter = function() {
|
||||
document.execCommand('justifyCenter', false, null);
|
||||
}
|
||||
|
||||
RE.setJustifyRight = function() {
|
||||
document.execCommand('justifyRight', false, null);
|
||||
}
|
||||
|
||||
RE.setBlockquote = function() {
|
||||
document.execCommand('formatBlock', false, '<blockquote>');
|
||||
}
|
||||
|
||||
RE.insertImage = function(url) {
|
||||
var html = "<div><img src =\"" + url + "\" style=\" max-width: 100%; display:block; margin:15px auto; height: auto;\"></div><br>"
|
||||
RE.insertHTML(html);
|
||||
}
|
||||
|
||||
//RE.lazyLoad = function() {
|
||||
// var myLazyLoad = new LazyLoad({
|
||||
// elements_selector: ".lazy"
|
||||
// })
|
||||
//}
|
||||
|
||||
// 替换成缩略图
|
||||
RE.replaceTbImage = function(imgRuleFlag, gifRuleFlag) {
|
||||
var imgs = document.getElementsByTagName("img");
|
||||
for (var i = 0; i < imgs.length; i++) {
|
||||
var img = imgs[i];
|
||||
if(img.src.indexOf("?") > 0) continue;
|
||||
|
||||
var tbImg
|
||||
if(img.src.indexOf(".gif") > 0) {
|
||||
tbImg = img.src + gifRuleFlag
|
||||
} else {
|
||||
tbImg = img.src + imgRuleFlag
|
||||
}
|
||||
|
||||
img.style.cssText = "max-width: 60%; display:block; margin:15px auto; height: auto;"
|
||||
img.src = tbImg;
|
||||
|
||||
if (i == 0) {
|
||||
var bigImg = document.createElement('img');
|
||||
bigImg.src = "file:///android_asset/web_load_dfimg_icon.png";
|
||||
bigImg.style.cssText = "max-width: 20%; margin:15px 0 0 0; height: auto;"
|
||||
img.parentNode.insertBefore(bigImg, img.parentNode.childNodes[0]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 替换成默认图
|
||||
RE.replaceAllDfImage = function(imgRuleFlag, gifRuleFlag) {
|
||||
var imgs = document.getElementsByTagName("img");
|
||||
for (var i = 0; i < imgs.length; i++) {
|
||||
var img = imgs[i];
|
||||
if(img.src.indexOf("web_load_dfimg_icon") > 0) {
|
||||
img.parentNode.removeChild(img.parentNode.childNodes[0]);
|
||||
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
|
||||
}
|
||||
} else {
|
||||
img.style.cssText = "max-width: 100%; display:block; margin:8px auto; height: auto;"
|
||||
img.src = img.src.split("?")[0] + imgRuleFlag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 去除显示大图
|
||||
RE.hideShowBigPic = function() {
|
||||
var imgs = document.getElementsByTagName("img");
|
||||
var j = 0;
|
||||
for (var i = 0; i < imgs.length; i++) {
|
||||
var img = imgs[i];
|
||||
if(img.src.indexOf(",thumbnail") > 0 && img.src.indexOf(".gif") == -1) {
|
||||
j++;
|
||||
}
|
||||
}
|
||||
// 去除显示大图
|
||||
if (j == 0) {
|
||||
var imgs = document.getElementsByTagName("img");
|
||||
for (var i = 0; i < imgs.length; i++) {
|
||||
var img = imgs[i];
|
||||
if(img.src.indexOf("web_load_dfimg_icon") > 0) {
|
||||
img.parentNode.removeChild(img.parentNode.childNodes[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RE.replaceDfImageByUrl = function(imgUrl, imgRuleFlag, gifRuleFlag) {
|
||||
var imgs = document.getElementsByTagName("img");
|
||||
for (var i = 0; i < imgs.length; i++) {
|
||||
var img = imgs[i];
|
||||
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 {
|
||||
img.src = img.src.split("?")[0] + imgRuleFlag
|
||||
}
|
||||
}
|
||||
}
|
||||
RE.hideShowBigPic();
|
||||
}
|
||||
|
||||
RE.ImageClickListener = function() {
|
||||
var imgs = document.getElementsByTagName("img");
|
||||
for (var i = 0; i < imgs.length; i++) {
|
||||
var img = imgs[i];
|
||||
window.imagelistener.imageArr(img.src);
|
||||
img.onclick = function() {
|
||||
window.imagelistener.imageClick(this.src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RE.insertHTML = function(html) {
|
||||
RE.restorerange();
|
||||
document.execCommand('insertHTML', false, html);
|
||||
}
|
||||
|
||||
RE.insertLink = function(url, title) {
|
||||
RE.restorerange();
|
||||
var sel = document.getSelection();
|
||||
if (sel.toString().length == 0) {
|
||||
document.execCommand("insertHTML",false,"<a href='"+url+"'>"+title+"</a>");
|
||||
} else if (sel.rangeCount) {
|
||||
var el = document.createElement("a");
|
||||
el.setAttribute("href", url);
|
||||
el.setAttribute("title", title);
|
||||
|
||||
var range = sel.getRangeAt(0).cloneRange();
|
||||
range.surroundContents(el);
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}
|
||||
RE.callback();
|
||||
}
|
||||
|
||||
RE.setTodo = function(text) {
|
||||
var html = '<input type="checkbox" name="'+ text +'" value="'+ text +'"/> ';
|
||||
document.execCommand('insertHTML', false, html);
|
||||
}
|
||||
|
||||
RE.prepareInsert = function() {
|
||||
RE.backuprange();
|
||||
}
|
||||
|
||||
RE.backuprange = function(){
|
||||
var selection = window.getSelection();
|
||||
if (selection.rangeCount > 0) {
|
||||
var range = selection.getRangeAt(0);
|
||||
RE.currentSelection = {
|
||||
"startContainer": range.startContainer,
|
||||
"startOffset": range.startOffset,
|
||||
"endContainer": range.endContainer,
|
||||
"endOffset": range.endOffset};
|
||||
}
|
||||
}
|
||||
|
||||
RE.restorerange = function(){
|
||||
var selection = window.getSelection();
|
||||
selection.removeAllRanges();
|
||||
var range = document.createRange();
|
||||
range.setStart(RE.currentSelection.startContainer, RE.currentSelection.startOffset);
|
||||
range.setEnd(RE.currentSelection.endContainer, RE.currentSelection.endOffset);
|
||||
selection.addRange(range);
|
||||
}
|
||||
|
||||
RE.enabledEditingItems = function(e) {
|
||||
var items = [];
|
||||
if (document.queryCommandState('bold')) {
|
||||
items.push('bold');
|
||||
}
|
||||
if (document.queryCommandState('italic')) {
|
||||
items.push('italic');
|
||||
}
|
||||
if (document.queryCommandState('subscript')) {
|
||||
items.push('subscript');
|
||||
}
|
||||
if (document.queryCommandState('superscript')) {
|
||||
items.push('superscript');
|
||||
}
|
||||
if (document.queryCommandState('strikeThrough')) {
|
||||
items.push('strikeThrough');
|
||||
}
|
||||
if (document.queryCommandState('underline')) {
|
||||
items.push('underline');
|
||||
}
|
||||
if (document.queryCommandState('insertOrderedList')) {
|
||||
items.push('orderedList');
|
||||
}
|
||||
if (document.queryCommandState('insertUnorderedList')) {
|
||||
items.push('unorderedList');
|
||||
}
|
||||
if (document.queryCommandState('justifyCenter')) {
|
||||
items.push('justifyCenter');
|
||||
}
|
||||
if (document.queryCommandState('justifyFull')) {
|
||||
items.push('justifyFull');
|
||||
}
|
||||
if (document.queryCommandState('justifyLeft')) {
|
||||
items.push('justifyLeft');
|
||||
}
|
||||
if (document.queryCommandState('justifyRight')) {
|
||||
items.push('justifyRight');
|
||||
}
|
||||
if (document.queryCommandState('insertHorizontalRule')) {
|
||||
items.push('horizontalRule');
|
||||
}
|
||||
var formatBlock = document.queryCommandValue('formatBlock');
|
||||
if (formatBlock.length > 0) {
|
||||
items.push(formatBlock);
|
||||
}
|
||||
|
||||
window.location.href = "re-state://" + encodeURI(items.join(','));
|
||||
}
|
||||
|
||||
RE.focus = function() {
|
||||
var range = document.createRange();
|
||||
range.selectNodeContents(RE.editor);
|
||||
range.collapse(false);
|
||||
var selection = window.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
RE.editor.focus();
|
||||
}
|
||||
|
||||
RE.blurFocus = function() {
|
||||
RE.editor.blur();
|
||||
}
|
||||
|
||||
RE.removeFormat = function() {
|
||||
document.execCommand('removeFormat', false, null);
|
||||
}
|
||||
|
||||
// Event Listeners
|
||||
RE.editor.addEventListener("input", RE.callback);
|
||||
RE.editor.addEventListener("keyup", function(e) {
|
||||
var KEY_LEFT = 37, KEY_RIGHT = 39;
|
||||
if (e.which == KEY_LEFT || e.which == KEY_RIGHT) {
|
||||
RE.enabledEditingItems(e);
|
||||
}
|
||||
});
|
||||
RE.editor.addEventListener("click", RE.enabledEditingItems);
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 544 KiB |
43
app/src/main/assets/style.css
Normal file
43
app/src/main/assets/style.css
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (C) 2017 Wasabeef
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@charset "UTF-8";
|
||||
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: scroll;
|
||||
display: table;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
min-height:100%;
|
||||
}
|
||||
|
||||
#editor {
|
||||
display: table-cell;
|
||||
outline: 0px solid transparent;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
#editor[placeholder]:empty:not(:focus):before {
|
||||
content: attr(placeholder);
|
||||
opacity: .5;
|
||||
}}
|
||||
BIN
app/src/main/assets/web_load_dfimg_icon.png
Normal file
BIN
app/src/main/assets/web_load_dfimg_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
2
app/src/main/assets/zepto.min.js
vendored
Normal file
2
app/src/main/assets/zepto.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -1,242 +0,0 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.RunningAppProcessInfo;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.os.Process;
|
||||
import android.support.multidex.MultiDex;
|
||||
import android.support.v4.util.ArrayMap;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.bandeng.MyEventBusIndex;
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.util.DataUtils;
|
||||
import com.gh.common.util.HttpsUtils;
|
||||
import com.gh.common.util.StringUtils;
|
||||
import com.gh.common.util.TokenUtils;
|
||||
import com.gh.common.util.Utils;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
import com.leon.channel.helper.ChannelReaderUtil;
|
||||
import com.umeng.message.IUmengRegisterCallback;
|
||||
import com.umeng.message.PushAgent;
|
||||
import com.umeng.message.UTrack;
|
||||
import com.xiaomi.channel.commonutils.logger.LoggerInterface;
|
||||
import com.xiaomi.mipush.sdk.Logger;
|
||||
import com.xiaomi.mipush.sdk.MiPushClient;
|
||||
import com.xiaomi.mipush.sdk.MiPushCommandMessage;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class AppController extends Application {
|
||||
|
||||
public static final String TAG = AppController.class.getSimpleName();
|
||||
public static final String KEY_FILE_INFO = "FileInfo";
|
||||
//快传文件发送单线程
|
||||
public static Executor FILE_SENDER_EXECUTOR = Executors.newSingleThreadExecutor();
|
||||
//快传文件发送主要的线程池
|
||||
public static Executor MAIN_EXECUTOR = Executors.newCachedThreadPool();
|
||||
private static AppController mInstance;
|
||||
private static ArrayMap<String, Object> objectMap = new ArrayMap<>();
|
||||
|
||||
private String mChannel;
|
||||
|
||||
public static void put(String key, Object object) {
|
||||
if (objectMap == null) {
|
||||
objectMap = new ArrayMap<>();
|
||||
}
|
||||
objectMap.put(key, object);
|
||||
}
|
||||
|
||||
public static Object get(String key, boolean isRemove) {
|
||||
if (objectMap == null) {
|
||||
return null;
|
||||
}
|
||||
if (isRemove) {
|
||||
return objectMap.remove(key);
|
||||
} else {
|
||||
return objectMap.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
public static void remove(String key) {
|
||||
if (objectMap == null) {
|
||||
return;
|
||||
}
|
||||
objectMap.remove(key);
|
||||
}
|
||||
|
||||
public static String getProcessName(Context cxt, int pid) {
|
||||
ActivityManager am = (ActivityManager) cxt.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
|
||||
if (runningApps == null) {
|
||||
return null;
|
||||
}
|
||||
for (RunningAppProcessInfo procInfo : runningApps) {
|
||||
if (procInfo.pid == pid) {
|
||||
return procInfo.processName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static synchronized AppController getInstance() {
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
public String getChannel() {
|
||||
return mChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
MultiDex.install(this);
|
||||
mInstance = this;
|
||||
|
||||
//TODO 强烈不建议开发阶段开启这个Handler,必须处理错误
|
||||
if (!BuildConfig.DEBUG) {
|
||||
AppUncaughtHandler appUncaughtHandler = new AppUncaughtHandler(this);
|
||||
Thread.setDefaultUncaughtExceptionHandler(appUncaughtHandler);
|
||||
}
|
||||
|
||||
mChannel = ChannelReaderUtil.getChannel(this);
|
||||
if (TextUtils.isEmpty(mChannel)) {
|
||||
//默认用Android Studio run时并没有写入channel magic number到apk包里面,所以需要fallback
|
||||
mChannel = "GH_TEST";
|
||||
}
|
||||
|
||||
Log.e("CHANNEL_ID", mChannel);
|
||||
|
||||
//初始化Fresco
|
||||
Fresco.initialize(this);
|
||||
|
||||
DataUtils.init(this, BuildConfig.DEBUG, mChannel);
|
||||
|
||||
//测试MTA崩溃的,坑爹
|
||||
// if (BuildConfig.DEBUG) {
|
||||
// StatConfig.setDebugEnable(true);
|
||||
// StatConfig.setStatSendStrategy(StatReportStrategy.DEVELOPER);
|
||||
// throw new RuntimeException("test again");
|
||||
// }
|
||||
|
||||
HttpsUtils.initHttpsUrlConnection(this);
|
||||
|
||||
// if (BuildConfig.DEBUG) {
|
||||
// Stetho.initializeWithDefaults(this);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 注册push服务,注册成功后会向{@link GHPushMessageReceiver}发送广播
|
||||
* 可以从{@link GHPushMessageReceiver#onCommandResult(Context, MiPushCommandMessage)}
|
||||
* 的{@link MiPushCommandMessage} 对象参数中获取注册信息
|
||||
*/
|
||||
if (shouldInit()) {
|
||||
MiPushClient.registerPush(this, Config.MIPUSH_APPID, Config.MIPUSH_APPKEY);
|
||||
if (BuildConfig.DEBUG) {
|
||||
MiPushClient.registerPush(this, Config.DEBUG_MIPUSH_APPID, Config.DEBUG_MIPUSH_APPKEY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
LoggerInterface newLogger = new LoggerInterface() {
|
||||
|
||||
@Override
|
||||
public void setTag(String tag) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String content) {
|
||||
Log.d(TAG, content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String content, Throwable t) {
|
||||
Log.d(TAG, content, t);
|
||||
}
|
||||
};
|
||||
Logger.setLogger(this, newLogger);
|
||||
}
|
||||
|
||||
//友盟推送
|
||||
final PushAgent pushAgent = PushAgent.getInstance(this);
|
||||
pushAgent.setAppkeyAndSecret(Config.UMENG_APPKEY, Config.UMENG_MESSAGE_SECRET);
|
||||
if (BuildConfig.DEBUG) {
|
||||
// 设置debug的key,覆盖掉原值,非debug build这个if分支会被去掉
|
||||
pushAgent.setAppkeyAndSecret(Config.DEBUG_UMENG_APPKEY, Config.DEBUG_UMENG_MESSAGE_SECRET);
|
||||
}
|
||||
|
||||
//注册推送服务,每次调用register方法都会回调该接口
|
||||
pushAgent.register(new IUmengRegisterCallback() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(String deviceToken) {
|
||||
//注册成功会返回device token
|
||||
Utils.log("deviceToken::" + deviceToken);
|
||||
|
||||
//设置别名
|
||||
pushAgent.addExclusiveAlias(TokenUtils.getDeviceId(getApplicationContext()),
|
||||
"GHDID", new UTrack.ICallBack() {
|
||||
@Override
|
||||
public void onMessage(boolean b, String s) {
|
||||
Utils.log(StringUtils.buildString("ExclusiveAlias::", String.valueOf(b), "==", s));
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(String s, String s1) {
|
||||
Utils.log("deviceToken::" + "注册失败");
|
||||
}
|
||||
});
|
||||
|
||||
// 友盟推送数据处理
|
||||
pushAgent.setNotificationClickHandler(new GHUmengNotificationClickHandler());
|
||||
|
||||
// // 监听屏幕状态广播
|
||||
// if (shouldInit()) {
|
||||
// UnlockScreenReceiver unlockScreenReceiver = new UnlockScreenReceiver();
|
||||
// IntentFilter intentFilter = new IntentFilter();
|
||||
// intentFilter.addAction(Intent.ACTION_SCREEN_ON);
|
||||
// intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||
// registerReceiver(unlockScreenReceiver, intentFilter);
|
||||
//
|
||||
// // 用户App运行数据统计服务
|
||||
// Intent intent = new Intent(getApplicationContext(), AppStaticService.class);
|
||||
// startService(intent);
|
||||
//
|
||||
// AppRunTimeDao dao = new AppRunTimeDao(getApplicationContext());
|
||||
// for (AppRunTimeInfo appRunTimeInfo : dao.getAll()) {
|
||||
// Utils.log(appRunTimeInfo.getPackageName() + "====1111=====" + appRunTimeInfo.getRunTime());
|
||||
// }
|
||||
// }
|
||||
|
||||
// 启用EventBus3.0加速功能
|
||||
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
|
||||
}
|
||||
|
||||
private boolean shouldInit() {
|
||||
ActivityManager am = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE));
|
||||
List<RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();
|
||||
String mainProcessName = getPackageName();
|
||||
Log.d(TAG, mainProcessName);
|
||||
int myPid = Process.myPid();
|
||||
for (RunningAppProcessInfo info : processInfos) {
|
||||
if (info.pid == myPid && mainProcessName.equals(info.processName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
//package com.gh.base;
|
||||
//
|
||||
//
|
||||
//import android.annotation.TargetApi;
|
||||
//import android.app.Application;
|
||||
//import android.content.Context;
|
||||
//import android.content.Intent;
|
||||
//import android.os.Build;
|
||||
//import android.support.multidex.MultiDex;
|
||||
//
|
||||
//import com.tencent.tinker.lib.listener.DefaultPatchListener;
|
||||
//import com.tencent.tinker.lib.listener.PatchListener;
|
||||
//import com.tencent.tinker.lib.patch.AbstractPatch;
|
||||
//import com.tencent.tinker.lib.patch.UpgradePatch;
|
||||
//import com.tencent.tinker.lib.reporter.DefaultLoadReporter;
|
||||
//import com.tencent.tinker.lib.reporter.DefaultPatchReporter;
|
||||
//import com.tencent.tinker.lib.reporter.LoadReporter;
|
||||
//import com.tencent.tinker.lib.reporter.PatchReporter;
|
||||
//import com.tencent.tinker.lib.tinker.TinkerInstaller;
|
||||
//import com.tencent.tinker.loader.app.DefaultApplicationLike;
|
||||
//
|
||||
//public class AppControllerLike extends DefaultApplicationLike {
|
||||
//
|
||||
// public AppControllerLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag,
|
||||
// long applicationStartElapsedTime, long applicationStartMillisTime,
|
||||
// Intent tinkerResultIntent) {
|
||||
// super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime,
|
||||
// applicationStartMillisTime, tinkerResultIntent);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onBaseContextAttached(Context base) {
|
||||
// super.onBaseContextAttached(base);
|
||||
// MultiDex.install(base);
|
||||
//
|
||||
// LoadReporter loadReporter = new DefaultLoadReporter(getApplication());
|
||||
// PatchReporter patchReporter = new DefaultPatchReporter(getApplication());
|
||||
// PatchListener patchListener = new DefaultPatchListener(getApplication());
|
||||
// AbstractPatch upgradePatchProcessor = new UpgradePatch();
|
||||
//
|
||||
// TinkerInstaller.install(this, loadReporter, patchReporter, patchListener,
|
||||
// AppTinkerResultService.class, upgradePatchProcessor);
|
||||
//// TinkerInstaller.install(this);
|
||||
// }
|
||||
//
|
||||
// @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
// public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {
|
||||
// getApplication().registerActivityLifecycleCallbacks(callback);
|
||||
// }
|
||||
//
|
||||
//}
|
||||
@ -1,29 +0,0 @@
|
||||
//package com.gh.base;
|
||||
//
|
||||
//import com.gh.common.util.Utils;
|
||||
//import com.tencent.tinker.lib.service.DefaultTinkerResultService;
|
||||
//import com.tencent.tinker.lib.service.PatchResult;
|
||||
//import com.tencent.tinker.lib.util.TinkerServiceInternals;
|
||||
//
|
||||
//import java.io.File;
|
||||
//
|
||||
//
|
||||
//public class AppTinkerResultService extends DefaultTinkerResultService {
|
||||
//
|
||||
// @Override
|
||||
// public void onPatchResult(PatchResult result) {
|
||||
// if (result == null) {
|
||||
// return;
|
||||
// }
|
||||
// Utils.log(result);
|
||||
//
|
||||
// //first, we want to kill the recover process
|
||||
// TinkerServiceInternals.killTinkerPatchServiceProcess(getApplicationContext());
|
||||
//
|
||||
// if (result.isSuccess) {
|
||||
// Utils.log("Tinkder Success");
|
||||
// deleteRawPatchFile(new File(result.rawPatchFilePath));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
@ -6,15 +6,17 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Looper;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.util.AppManager;
|
||||
import com.gh.common.util.DataCollectionUtils;
|
||||
import com.gh.common.util.FileUtils;
|
||||
import com.gh.common.util.DataUtils;
|
||||
import com.gh.gamecenter.SplashScreenActivity;
|
||||
import com.tencent.stat.StatService;
|
||||
import com.lightgame.config.CommonDebug;
|
||||
import com.lightgame.download.FileUtils;
|
||||
import com.lightgame.utils.AppManager;
|
||||
import com.lightgame.utils.Utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
@ -26,84 +28,58 @@ import java.util.Locale;
|
||||
|
||||
public class AppUncaughtHandler implements UncaughtExceptionHandler {
|
||||
|
||||
private UncaughtExceptionHandler mDefaultHandler;
|
||||
private AppController mAppController;
|
||||
private Context mContext;
|
||||
|
||||
public AppUncaughtHandler(AppController appController) {
|
||||
public AppUncaughtHandler(Context context) {
|
||||
// 获取系统默认的UncaughtException处理器
|
||||
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
mAppController = appController;
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable ex) {
|
||||
if (!handleException(ex) && mDefaultHandler != null) {
|
||||
// 如果用户没有处理则让系统默认的异常处理器来处理
|
||||
mDefaultHandler.uncaughtException(thread, ex);
|
||||
} else {
|
||||
AppController.MAIN_EXECUTOR.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Looper.prepare();
|
||||
Toast.makeText(mAppController.getApplicationContext(),
|
||||
"\"光环助手\"发生错误", Toast.LENGTH_SHORT).show();
|
||||
Looper.loop();
|
||||
}
|
||||
});
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Looper.prepare();
|
||||
Utils.toast(mContext.getApplicationContext(), "\"光环助手\"发生错误");
|
||||
Looper.loop();
|
||||
}
|
||||
|
||||
// 防止重复奔溃,导致助手一直重启,20秒内不做处理
|
||||
SharedPreferences sp = mAppController.getApplicationContext().getSharedPreferences(
|
||||
Config.PREFERENCE, Context.MODE_PRIVATE);
|
||||
long time = sp.getLong("last_restart_time", 0);
|
||||
if (System.currentTimeMillis() - time > 20 * 1000) {
|
||||
sp.edit().putLong("last_restart_time", System.currentTimeMillis()).apply();
|
||||
Intent intent = new Intent(mAppController.getApplicationContext(), SplashScreenActivity.class);
|
||||
intent.setAction(Intent.ACTION_MAIN);
|
||||
intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
PendingIntent restartIntent = PendingIntent.getActivity(
|
||||
mAppController.getApplicationContext(), 0, intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
// 退出程序并重启
|
||||
AlarmManager mgr = (AlarmManager) mAppController.getSystemService(Context.ALARM_SERVICE);
|
||||
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, restartIntent); // 1秒钟后重启应用
|
||||
}
|
||||
AppManager.getInstance().finishAllActivity();
|
||||
}
|
||||
});
|
||||
saveLocalLog(mContext, ex);
|
||||
restart(mContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
|
||||
*
|
||||
* @param ex
|
||||
* @return true:如果处理了该异常信息;否则返回false.
|
||||
*/
|
||||
private boolean handleException(Throwable ex) {
|
||||
if (ex == null) {
|
||||
return false;
|
||||
public static void restart(final Context context) {
|
||||
|
||||
// 防止重复奔溃,导致助手一直重启,20秒内不做处理
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
long curTime = System.currentTimeMillis();
|
||||
long time = sp.getLong("last_restart_time", 0);
|
||||
if (curTime - time > 20 * 1000) {
|
||||
sp.edit().putLong("last_restart_time", curTime).apply();
|
||||
Intent intent = new Intent(context, SplashScreenActivity.class);
|
||||
intent.setAction(Intent.ACTION_MAIN);
|
||||
intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
PendingIntent restartIntent = PendingIntent.getActivity(context, 0, intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
// 退出程序并重启
|
||||
AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
mgr.set(AlarmManager.RTC, curTime + 3000, restartIntent); // 1秒钟后重启应用
|
||||
}
|
||||
saveLog(ex);
|
||||
return true;
|
||||
|
||||
//error restart
|
||||
// System.exit(2);
|
||||
AppManager.getInstance().finishAllActivity();
|
||||
}
|
||||
|
||||
// 保存log到本地
|
||||
private void saveLog(Throwable ex) {
|
||||
public static void saveLocalLog(Context context, Throwable ex) {
|
||||
String errorMsg = Log.getStackTraceString(ex);
|
||||
|
||||
// MTA主动上传错误
|
||||
// StatService.reportError(mAppController.getApplicationContext(), errorMsg);
|
||||
StatService.reportException(mAppController, ex);
|
||||
|
||||
// 上传错误数据
|
||||
DataCollectionUtils.uploadError(mAppController, errorMsg);
|
||||
Config.setExceptionMsg(context, errorMsg);
|
||||
|
||||
// 保存到本地
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault());
|
||||
File file = new File(FileUtils.getLogPath(mAppController.getApplicationContext(),
|
||||
File file = new File(FileUtils.getLogPath(context.getApplicationContext(),
|
||||
format.format(new Date()) + "_gh_assist" + ".log"));
|
||||
FileWriter writer = null;
|
||||
try {
|
||||
@ -124,4 +100,25 @@ public class AppUncaughtHandler implements UncaughtExceptionHandler {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 下次应用启动再上报
|
||||
*
|
||||
* @param context
|
||||
* @param throwable
|
||||
*/
|
||||
public static void reportException(Context context, Throwable throwable) {
|
||||
|
||||
CommonDebug.logMethodWithParams(context, "ERRMSG", throwable);
|
||||
|
||||
// 上传错误数据
|
||||
try {
|
||||
DataCollectionUtils.uploadError(context, Log.getStackTraceString(throwable));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
DataUtils.onError(context, throwable);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,186 +1,211 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.app.Activity;
|
||||
import android.arch.lifecycle.Lifecycle;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.Window;
|
||||
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.util.AppManager;
|
||||
import com.gh.common.util.DataUtils;
|
||||
import com.gh.common.util.DialogUtils;
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.common.util.FileUtils;
|
||||
import com.gh.common.util.PackageUtils;
|
||||
import com.gh.common.util.RunningUtils;
|
||||
import com.gh.common.util.ShareUtils;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.gh.common.util.StringUtils;
|
||||
import com.gh.gamecenter.LoginActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.SuggestionActivity;
|
||||
import com.gh.gamecenter.eventbus.EBShowDialog;
|
||||
import com.gh.gamecenter.listener.OnCallBackListener;
|
||||
import com.readystatesoftware.systembartint.SystemBarTintManager.SystemBarConfig;
|
||||
import com.gh.gamecenter.suggest.SuggestType;
|
||||
import com.lightgame.download.FileUtils;
|
||||
import com.lightgame.utils.Utils;
|
||||
import com.tencent.tauth.Tencent;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import pub.devrel.easypermissions.EasyPermissions;
|
||||
|
||||
import static com.gh.common.util.EntranceUtils.KEY_DATA;
|
||||
import static com.gh.common.util.EntranceUtils.KEY_ENTRANCE;
|
||||
|
||||
public abstract class BaseActivity extends BaseAppCompatToolBarActivity implements OnCallBackListener {
|
||||
public abstract class BaseActivity extends BaseToolBarActivity implements EasyPermissions.PermissionCallbacks {
|
||||
|
||||
protected String mEntrance;
|
||||
|
||||
private boolean mIsPause;
|
||||
private boolean mIsExistLogoutDialog;
|
||||
|
||||
protected final Handler mBaseHandler = new BaseHandler(this);
|
||||
|
||||
protected static class BaseHandler extends Handler {
|
||||
private final WeakReference<BaseActivity> mActivityWeakReference;
|
||||
|
||||
BaseHandler(BaseActivity activity) {
|
||||
mActivityWeakReference = new WeakReference<>(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
super.handleMessage(msg);
|
||||
BaseActivity activity = mActivityWeakReference.get();
|
||||
if (activity != null) activity.handleMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleMessage(Message msg) {
|
||||
|
||||
}
|
||||
|
||||
//接收QQ或者QQ空间分享回调
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == com.tencent.connect.common.Constants.REQUEST_QQ_SHARE
|
||||
|| requestCode == com.tencent.connect.common.Constants.REQUEST_QZONE_SHARE) {
|
||||
Tencent.onActivityResultData(requestCode, resultCode, data, ShareUtils.getInstance(this).QqShareListener);
|
||||
}
|
||||
}
|
||||
|
||||
// 小米沉浸式黑色字体
|
||||
public void setStatusBarDarkMode(boolean darkmode, Activity activity) {
|
||||
Class<? extends Window> clazz = activity.getWindow().getClass();
|
||||
try {
|
||||
int darkModeFlag = 0;
|
||||
Class<?> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
|
||||
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
|
||||
darkModeFlag = field.getInt(layoutParams);
|
||||
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
|
||||
extraFlagField.invoke(activity.getWindow(), darkmode ? darkModeFlag : 0, darkModeFlag);
|
||||
} catch (Exception ignore) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
init(mContentView);
|
||||
AppManager.getInstance().addActivity(this);
|
||||
setStatusBarDarkMode(true, this);
|
||||
EventBus.getDefault().register(this);
|
||||
ButterKnife.bind(this);
|
||||
mEntrance = getIntent().getStringExtra(KEY_ENTRANCE);
|
||||
if (getIntent().getBundleExtra(KEY_DATA) != null) {
|
||||
mEntrance = getIntent().getBundleExtra(KEY_DATA).getString(KEY_ENTRANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
EventBus.getDefault().unregister(this);
|
||||
mBaseHandler.removeCallbacksAndMessages(null);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onNavigationIconClicked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void init(View contentView) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
SystemBarConfig config = getTintManager().getConfig();
|
||||
contentView.setPadding(0, config.getPixelInsetTop(false), 0, config.getPixelInsetBottom());
|
||||
}
|
||||
setContentView(contentView);
|
||||
|
||||
ButterKnife.bind(this);
|
||||
|
||||
View reuse_actionbar = findViewById(R.id.reuse_actionbar);
|
||||
if (reuse_actionbar != null) {
|
||||
int actionbar_height = getSharedPreferences(Config.PREFERENCE, Context.MODE_PRIVATE)
|
||||
.getInt("actionbar_height", DisplayUtils.dip2px(getApplicationContext(), 55));
|
||||
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, actionbar_height);
|
||||
reuse_actionbar.setLayoutParams(lparams);
|
||||
|
||||
findViewById(R.id.actionbar_rl_back).setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected void init(String title) {
|
||||
TextView actionbar_tv_title = (TextView) findViewById(R.id.actionbar_tv_title);
|
||||
actionbar_tv_title.setText(title);
|
||||
// setNavigationTitle(title);
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void toast(String msg) {
|
||||
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
|
||||
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED))
|
||||
Utils.toast(this, msg);
|
||||
}
|
||||
|
||||
public void toast(int msg) {
|
||||
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
|
||||
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED))
|
||||
toast(getString(msg));
|
||||
}
|
||||
|
||||
//如果是游戏分享,newsTitle默认为空
|
||||
public void showShare(String url, String gameName, String icon, String newsTitle, ArrayList<String> tag, boolean isToolsBox) {
|
||||
public void showShare(String url, String icon, String shareTitle, String shareSummary, ShareUtils.ShareType shareType) {
|
||||
|
||||
//判断是否是官方版
|
||||
boolean isPlugin = false;
|
||||
if (tag != null) {
|
||||
for (String s : tag) {
|
||||
if (!"官方版".equals(s)) {
|
||||
isPlugin = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
ShareUtils.getInstance(this).showShareWindows(this, getWindow().getDecorView(), url, icon, shareTitle, shareSummary, shareType);
|
||||
|
||||
ShareUtils.getInstance(this).showShareWindows(getWindow().getDecorView(), url, gameName, icon, newsTitle, isPlugin, true, isToolsBox);
|
||||
|
||||
if (newsTitle == null) {
|
||||
DataUtils.onEvent(this, "内容分享", gameName);
|
||||
if (shareType == ShareUtils.ShareType.game || shareType == ShareUtils.ShareType.plugin) {
|
||||
DataUtils.onEvent(this, "内容分享", shareTitle + shareSummary);
|
||||
} else {
|
||||
DataUtils.onEvent(this, "内容分享", newsTitle);
|
||||
DataUtils.onEvent(this, "内容分享", shareTitle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(final EBShowDialog showDialog) {
|
||||
if (!mIsPause && this.getClass().getName().equals(RunningUtils.getTopActivity(this))) {
|
||||
if ("hijack".equals(showDialog.getType())) {
|
||||
DialogUtils.showQqSessionDialog(this, null);// 建议用户联系客服
|
||||
DialogUtils.showQqSessionDialog(this, "2586716223");// 建议用户联系客服
|
||||
} else if ("plugin".equals(showDialog.getType())) {
|
||||
DialogUtils.showPluginDialog(this, new DialogUtils.ConfirmListener() {
|
||||
@Override
|
||||
public void onConfirm() {
|
||||
if (FileUtils.isEmptyFile(showDialog.getPath())) {
|
||||
Toast.makeText(BaseActivity.this, "解析包出错(可能被误删了),请重新下载", Toast.LENGTH_SHORT).show();
|
||||
toast(R.string.install_failure_hint);
|
||||
} else {
|
||||
startActivity(PackageUtils.getUninstallIntent(BaseActivity.this, showDialog.getPath()));
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if ("loginException".equals(showDialog.getType())) {
|
||||
if (mIsExistLogoutDialog) return;
|
||||
mIsExistLogoutDialog = true;
|
||||
try {
|
||||
JSONObject object = new JSONObject(showDialog.getPath());
|
||||
JSONObject device = object.getJSONObject("device");
|
||||
String manufacturer = device.getString("manufacturer");
|
||||
String model = device.getString("model");
|
||||
DialogUtils.showAlertDialog(this, "你的账号已在另外一台设备登录"
|
||||
, StringUtils.buildString("(", manufacturer, " - ", model, ")")
|
||||
, "知道了", "重新登录"
|
||||
, null
|
||||
, () -> startActivity(LoginActivity.getIntent(BaseActivity.this))
|
||||
);
|
||||
mBaseHandler.postDelayed(() -> mIsExistLogoutDialog = false, 5000);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else if ("notfound".equals(showDialog.getType())) {
|
||||
DialogUtils.showAlertDialog(this, "下载失败", "下载链接已失效,建议提交反馈"
|
||||
, "立即反馈", "取消"
|
||||
, () -> {
|
||||
SuggestionActivity.startSuggestionActivity(this, SuggestType.gameQuestion,
|
||||
null, showDialog.getPath() + ",问题反馈:下载链接失效");
|
||||
}, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
EventBus.getDefault().unregister(this);
|
||||
AppManager.getInstance().finishActivity(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
DataUtils.onPause(this);
|
||||
mIsPause = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
DataUtils.onResume(this);
|
||||
mIsPause = false;
|
||||
DownloadManager.getInstance(this).initGameMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadDone() {
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionsDenied(int requestCode, List<String> perms) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadDone(Object obj) {
|
||||
public void onPermissionsGranted(int requestCode, List<String> perms) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadError() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadEmpty() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,325 +0,0 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.view.View;
|
||||
|
||||
import com.gh.common.util.AppDebugConfig;
|
||||
import com.gh.common.util.AppManager;
|
||||
import com.gh.common.util.Util_System_Keyboard;
|
||||
import com.gh.gamecenter.R;
|
||||
|
||||
|
||||
/**
|
||||
* @author CsHeng
|
||||
*/
|
||||
public abstract class BaseAppCompatActivity extends BaseAppCompatActivityLog implements FragmentNavigationDelegate,
|
||||
OnBackPressedListener {
|
||||
|
||||
protected static final String ARGS_FRAGMENT_NAME = "frgName";
|
||||
protected static final String ARGS_FRAGMENT_BUNDLE = "frgBundle";
|
||||
protected View mContentView;
|
||||
|
||||
protected static Intent clearTop(Context context, Class<? extends Activity> cls) {
|
||||
final Intent intent = getReorderToFrontIntent(context, cls);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
return intent;
|
||||
}
|
||||
|
||||
protected static Intent getReorderToFrontIntent(Context context, Class<? extends Activity> cls) {
|
||||
final Intent intent = new Intent(context, cls);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
return intent;
|
||||
}
|
||||
|
||||
protected static void showActivity(Context context, Class<? extends Activity> cls) {
|
||||
final Intent intent = getReorderToFrontIntent(context, cls);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
protected static void startFragmentForResult(Context context, Class<? extends Activity> activity,
|
||||
Class<? extends Fragment> fragment, Bundle bundle, int requestCode) {
|
||||
if (context instanceof Activity) {
|
||||
((Activity) context).startActivityForResult(getFragmentIntent(context, activity, fragment, bundle),
|
||||
requestCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据传进来的fragment class和bundle extra来决定跳转到哪一个fragment
|
||||
*
|
||||
* @param context
|
||||
* @param fragment fragment.getCanonicalName()
|
||||
* @param bundle fragment的构造参数
|
||||
* @return
|
||||
*/
|
||||
protected static Intent getFragmentIntent(Context context, Class<? extends Activity> activity,
|
||||
Class<? extends Fragment> fragment, Bundle bundle) {
|
||||
final Intent intent = getReorderToFrontIntent(context, activity);
|
||||
intent.putExtra(ARGS_FRAGMENT_NAME, fragment.getCanonicalName());
|
||||
intent.putExtra(ARGS_FRAGMENT_BUNDLE, bundle);
|
||||
return intent;
|
||||
}
|
||||
|
||||
protected static void startFragment(Context context, Class<? extends Activity> activity,
|
||||
Class<? extends Fragment> fragment) {
|
||||
startFragment(context, activity, fragment, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动Fragment
|
||||
*
|
||||
* @param context
|
||||
* @param fragment
|
||||
* @param bundle
|
||||
*/
|
||||
protected static void startFragment(Context context, Class<? extends Activity> activity,
|
||||
Class<? extends Fragment> fragment, Bundle bundle) {
|
||||
context.startActivity(getFragmentIntent(context, activity, fragment, bundle));
|
||||
}
|
||||
|
||||
private void handleRedirectFromUri(Uri uri) {
|
||||
try {
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
AppDebugConfig.logMethodWithParams(this, uri, uri.getHost(), uri.getPath(), uri.getEncodedQuery());
|
||||
}
|
||||
// switch (uri.getPath()) {
|
||||
// case SchemeConstants.PATH_OPEN_GAME: {
|
||||
// /**
|
||||
// * 对于联运游戏:传入的game_id为0,game_url为对应的游戏链接,aid传联运游戏的aid
|
||||
// * 对于非联运游戏:传入非联运游戏的game_id,game_url不传,aid传0
|
||||
// */
|
||||
// GameModel game = new GameModel();
|
||||
// int gameId = Integer.parseInt(uri.getQueryParameter(SchemeConstants.PARAMS_GAMEID));
|
||||
// game.setId(gameId);
|
||||
//// if (AppDebugConfig.IS_DEBUG) {
|
||||
//// int debugId = 1049;
|
||||
//// game.setId(debugId);
|
||||
//// }
|
||||
// if (gameId == 0) {
|
||||
// // cooperate
|
||||
// int aid = Integer.parseInt(uri.getQueryParameter(SchemeConstants.PARAMS_AID));
|
||||
// game.setAid(aid);
|
||||
// String gameUrl = uri.getQueryParameter(SchemeConstants.PARAMS_GAMEURL);
|
||||
// game.setUrl(gameUrl);
|
||||
// }
|
||||
// ApiRequester.requestGameDetail(this, game);
|
||||
//
|
||||
// break;
|
||||
// }
|
||||
// case SchemeConstants.PATH_OPEN_ARTICLE: {
|
||||
//
|
||||
// String url = uri.getQueryParameter(SchemeConstants.PARAMS_ARTICLE_URL);
|
||||
// String title = uri.getQueryParameter(SchemeConstants.PARAMS_ARTICLE_TITLE);
|
||||
//
|
||||
// if (!TextUtils.isEmpty(url) && !TextUtils.isEmpty(title)) {
|
||||
// Bundle args = new Bundle();
|
||||
// args.putString(Fragment_Browser_Web.ARGS_URL, url);
|
||||
// args.putString(Fragment_Browser_Web.ARGS_TITLE, getString(R.string.title_article_detail));
|
||||
// CommonActivity.startFragment(this, Fragment_Browser_Web.class, args);
|
||||
// }
|
||||
//
|
||||
// break;
|
||||
// }
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
// Debug_SDK.e(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHandleBackPressed() {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean handleBackPressed() {
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
AppDebugConfig.logMethodWithParams(this);
|
||||
}
|
||||
final Fragment curFragment = getTopFragment();
|
||||
if (curFragment instanceof OnBackPressedListener &&
|
||||
((OnBackPressedListener) curFragment).onHandleBackPressed()) {
|
||||
return true;
|
||||
}
|
||||
if (popFragment()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Fragment getTopFragment() {
|
||||
return getSupportFragmentManager().findFragmentById(R.id.layout_activity_content);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
final int layoutId = getLayoutId();
|
||||
if (layoutId != 0) {
|
||||
mContentView = getLayoutInflater().inflate(layoutId, null);
|
||||
setContentView(mContentView);
|
||||
}
|
||||
if (savedInstanceState == null) {
|
||||
handleRedirectIntent(getIntent());
|
||||
}
|
||||
AppManager.getInstance().addActivity(this);
|
||||
}
|
||||
|
||||
protected abstract int getLayoutId();
|
||||
|
||||
/**
|
||||
* 1、判断当前是否存在某Fragment,例如下载管理器已经在栈中,则不重新启动,而将其带到最上层 (FindFragmentWithTag)
|
||||
* 2、支持URI调用?
|
||||
* 是否需要重新导向到其他Activity
|
||||
*/
|
||||
protected void handleRedirectIntent(Intent intent) {
|
||||
try {
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
AppDebugConfig.logMethodWithParams(this, intent.toUri(Intent.URI_INTENT_SCHEME));
|
||||
}
|
||||
|
||||
// 1、根据从网页抓取到的Intent跳转进来,处理custom uri
|
||||
// final Uri uri = intent.getData();
|
||||
// if (uri != null && SchemeConstants.SCHEME_FFSS.equals(uri.getScheme())) {
|
||||
// handleRedirectFromUri(uri);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 2、根据序列化的数据来重新构建Fragment实例,若没有,则是旧的形式来决定传参
|
||||
// final String fragmentName = intent.getStringExtra(ARGS_FRAGMENT_NAME);
|
||||
// if (AppDebugConfig.IS_DEBUG) {
|
||||
// AppDebugConfig.logMethodWithParams(this, fragmentName);
|
||||
// }
|
||||
// if (!TextUtils.isEmpty(fragmentName)) {
|
||||
// final Bundle args = intent.getBundleExtra(ARGS_FRAGMENT_BUNDLE);
|
||||
// final Fragment fragment = Fragment.instantiate(this, fragmentName, args);
|
||||
// if (fragment instanceof DialogFragment) {
|
||||
// ((DialogFragment) fragment).show(getSupportFragmentManager(), fragmentName);
|
||||
// } else {
|
||||
// getSupportFragmentManager().beginTransaction().replace(R.id.layout_activity_content, fragment)
|
||||
// .commitAllowingStateLoss();
|
||||
// }
|
||||
// }
|
||||
} catch (Throwable e) {
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
AppDebugConfig.logMethodWithParams(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
AppManager.getInstance().finishActivity(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFragment(Fragment fragment) {
|
||||
getSupportFragmentManager().beginTransaction().add(R.id.layout_activity_content, fragment)
|
||||
.addToBackStack(fragment.toString()).commitAllowingStateLoss();
|
||||
Util_System_Keyboard.hideSoftKeyboard(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceFragment(Fragment toReplace) {
|
||||
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
||||
ft.replace(R.id.layout_activity_content, toReplace).addToBackStack(toReplace.toString());
|
||||
ft.commitAllowingStateLoss();
|
||||
Util_System_Keyboard.hideSoftKeyboard(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFragment(Fragment toRemove) {
|
||||
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
||||
ft.replace(R.id.layout_activity_content, toRemove).addToBackStack(toRemove.toString());
|
||||
ft.commitAllowingStateLoss();
|
||||
Util_System_Keyboard.hideSoftKeyboard(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showFragment(Fragment toShow) {
|
||||
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
||||
ft.show(toShow).addToBackStack(toShow.toString());
|
||||
ft.commitAllowingStateLoss();
|
||||
Util_System_Keyboard.hideSoftKeyboard(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideFragment(Fragment toHide) {
|
||||
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
||||
ft.show(toHide).addToBackStack(toHide.toString());
|
||||
ft.commitAllowingStateLoss();
|
||||
Util_System_Keyboard.hideSoftKeyboard(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean popFragment() {
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
AppDebugConfig.logMethodWithParams(this, getSupportFragmentManager().getBackStackEntryCount());
|
||||
}
|
||||
final int backStackCount = getSupportFragmentManager().getBackStackEntryCount();
|
||||
if (backStackCount > 0) {
|
||||
Util_System_Keyboard.hideSoftKeyboard(this);
|
||||
getSupportFragmentManager().popBackStack();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void popFragmentToBase() {
|
||||
getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* forward ActivityResultListener to fragments this activity hosts
|
||||
*
|
||||
* @param requestCode
|
||||
* @param resultCode
|
||||
* @param data
|
||||
*/
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
AppDebugConfig.logMethodWithParams(this, requestCode, resultCode, data);
|
||||
}
|
||||
final Fragment fragment = getTopFragment();
|
||||
if (fragment != null) {
|
||||
fragment.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
AppDebugConfig.logMethodWithParams(this);
|
||||
}
|
||||
if (handleBackPressed()) {
|
||||
return;
|
||||
}
|
||||
// default finish activity
|
||||
Util_System_Keyboard.hideSoftKeyboard(this);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
setIntent(intent);
|
||||
handleRedirectIntent(intent);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,94 +0,0 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import com.gh.common.util.AppDebugConfig;
|
||||
|
||||
|
||||
/**
|
||||
* @author: CsHeng (csheng1204[at]gmail[dot]com)
|
||||
* Date: 13-7-25
|
||||
* Time: 下午8:42
|
||||
*/
|
||||
public class BaseAppCompatActivityLog extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
AppDebugConfig.logMethodName(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
AppDebugConfig.logMethodName(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
AppDebugConfig.logMethodName(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
AppDebugConfig.logMethodName(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLowMemory() {
|
||||
super.onLowMemory();
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
AppDebugConfig.logMethodName(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
try {
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
AppDebugConfig.logMethodName(this);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
try {
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
AppDebugConfig.logMethodName(this);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
AppDebugConfig.logMethodName(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
if (AppDebugConfig.IS_DEBUG) {
|
||||
AppDebugConfig.logMethodName(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,138 +0,0 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.MenuItem;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.gh.gamecenter.R;
|
||||
import com.readystatesoftware.systembartint.SystemBarTintManager;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Created by csheng on 15-10-12.
|
||||
*/
|
||||
public abstract class BaseAppCompatToolBarActivity extends BaseAppCompatActivity implements ToolbarController {
|
||||
|
||||
private Toolbar mToolbar;
|
||||
private SystemBarTintManager mTintManager;
|
||||
|
||||
// TODO 获取沉浸栏管理,要进行版本判断或者判断是否为空
|
||||
protected SystemBarTintManager getTintManager() {
|
||||
return mTintManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
initToolbar();
|
||||
// Util_Window.initStatusBarColor(getWindow(), ContextCompat.getColor(this, R.color.theme));
|
||||
initStatusBar();
|
||||
}
|
||||
|
||||
private void initToolbar() {
|
||||
mToolbar = (Toolbar) findViewById(R.id.toolbar_navigation);
|
||||
if (mToolbar != null) {
|
||||
mToolbar.setTitle("");
|
||||
setSupportActionBar(mToolbar);
|
||||
}
|
||||
}
|
||||
|
||||
private void initStatusBar() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
setTranslucentStatus(true);
|
||||
mTintManager = new SystemBarTintManager(this);
|
||||
mTintManager.setStatusBarTintEnabled(true);
|
||||
// mTintManager.setNavigationBarTintEnabled(true);
|
||||
if (Build.MANUFACTURER.equals("Meizu") || Build.MANUFACTURER.equals("Xiaomi")) {
|
||||
mTintManager.setStatusBarTintColor(Color.WHITE);
|
||||
} else {
|
||||
mTintManager.setStatusBarTintColor(Color.BLACK);
|
||||
}
|
||||
|
||||
switch (Build.MANUFACTURER) {
|
||||
case "Meizu":
|
||||
try {
|
||||
Window window = getWindow();
|
||||
if (window != null) {
|
||||
WindowManager.LayoutParams lp = window.getAttributes();
|
||||
Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
|
||||
Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags");
|
||||
darkFlag.setAccessible(true);
|
||||
meizuFlags.setAccessible(true);
|
||||
int bit = darkFlag.getInt(null);
|
||||
int value = meizuFlags.getInt(lp);
|
||||
value |= bit;
|
||||
meizuFlags.setInt(lp, value);
|
||||
window.setAttributes(lp);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
case "Xiaomi":
|
||||
try {
|
||||
Window window = getWindow();
|
||||
if (window != null) {
|
||||
Class<?> clazz = window.getClass();
|
||||
Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
|
||||
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
|
||||
int darkModeFlag = field.getInt(layoutParams);
|
||||
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
|
||||
extraFlagField.invoke(window, darkModeFlag, darkModeFlag);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setTranslucentStatus(boolean status) {
|
||||
Window window = getWindow();
|
||||
WindowManager.LayoutParams winParams = window.getAttributes();
|
||||
final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
|
||||
if (status) {
|
||||
winParams.flags |= bits;
|
||||
} else {
|
||||
winParams.flags &= ~bits;
|
||||
}
|
||||
window.setAttributes(winParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
return onNavigationIconClicked();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
protected abstract boolean onNavigationIconClicked();
|
||||
|
||||
@Override
|
||||
public void setNavigationTitle(int res) {
|
||||
mToolbar.setTitle(res);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigationTitle(CharSequence res) {
|
||||
mToolbar.setTitle(res);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Toolbar getToolBar() {
|
||||
return mToolbar;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,394 +0,0 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.util.ApkActiveUtils;
|
||||
import com.gh.common.util.DataUtils;
|
||||
import com.gh.common.util.DialogUtils;
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.common.util.FileUtils;
|
||||
import com.gh.common.util.GameUtils;
|
||||
import com.gh.common.util.NetworkUtils;
|
||||
import com.gh.common.util.PackageUtils;
|
||||
import com.gh.common.util.ShareUtils;
|
||||
import com.gh.common.view.DownloadDialog;
|
||||
import com.gh.download.DataWatcher;
|
||||
import com.gh.download.DownloadEntity;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.gh.gamecenter.DownloadManagerActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.entity.ApkEntity;
|
||||
import com.gh.gamecenter.entity.GameEntity;
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus;
|
||||
import com.gh.gamecenter.eventbus.EBPackage;
|
||||
import com.gh.gamecenter.manager.PackageManager;
|
||||
import com.tencent.tauth.Tencent;
|
||||
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
/**
|
||||
* Created by Administrator on 2016/9/19.
|
||||
* 游戏详情、新闻详情基类(控制底部下载栏)
|
||||
*/
|
||||
public abstract class BaseDetailActivity extends BaseActivity implements View.OnClickListener {
|
||||
|
||||
protected TextView actionbar_tv_title;
|
||||
protected RecyclerView detail_rv_show;
|
||||
protected LinearLayout detail_ll_bottom;
|
||||
protected TextView detail_tv_download;
|
||||
protected ProgressBar detail_pb_progressbar;
|
||||
protected TextView detail_tv_per;
|
||||
protected LinearLayout reuse_ll_loading;
|
||||
protected LinearLayout reuse_no_connection;
|
||||
protected LinearLayout reuse_none_data;
|
||||
protected TextView reuse_tv_none_data;
|
||||
protected ImageView iv_share;
|
||||
|
||||
protected GameEntity gameEntity;
|
||||
protected DownloadEntity mDownloadEntity;
|
||||
|
||||
protected String name;
|
||||
protected String title;
|
||||
protected String downloadAddWord;
|
||||
protected String downloadOffText;
|
||||
|
||||
private DataWatcher dataWatcher = new DataWatcher() {
|
||||
@Override
|
||||
public void onDataChanged(DownloadEntity downloadEntity) {
|
||||
if (gameEntity != null && gameEntity.getApk().size() == 1) {
|
||||
String url = gameEntity.getApk().get(0).getUrl();
|
||||
if (url.equals(downloadEntity.getUrl())) {
|
||||
if (!"pause".equals(DownloadManager.getInstance(BaseDetailActivity.this).
|
||||
getStatus(downloadEntity.getUrl()))) {
|
||||
mDownloadEntity = downloadEntity;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.activity_detail;
|
||||
}
|
||||
|
||||
//接收QQ或者QQ空间分享回调
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (requestCode == com.tencent.connect.common.Constants.REQUEST_QQ_SHARE
|
||||
|| requestCode == com.tencent.connect.common.Constants.REQUEST_QZONE_SHARE) {
|
||||
Tencent.onActivityResultData(requestCode, resultCode, data, ShareUtils.getInstance(this).QqShareListener);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// View contentView = View.inflate(this, R.layout.activity_detail, null);
|
||||
|
||||
// 添加分享图标
|
||||
iv_share = new ImageView(this);
|
||||
iv_share.setImageResource(R.drawable.ic_share);
|
||||
iv_share.setOnClickListener(this);
|
||||
iv_share.setVisibility(View.GONE);
|
||||
iv_share.setPadding(DisplayUtils.dip2px(this, 13), DisplayUtils.dip2px(this, 11)
|
||||
, DisplayUtils.dip2px(this, 11), DisplayUtils.dip2px(this, 13));
|
||||
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
|
||||
DisplayUtils.dip2px(this, 48), DisplayUtils.dip2px(this, 48));
|
||||
params.addRule(RelativeLayout.CENTER_VERTICAL);
|
||||
params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
|
||||
RelativeLayout reuse_actionbar = (RelativeLayout) mContentView.findViewById(
|
||||
R.id.reuse_actionbar);
|
||||
reuse_actionbar.addView(iv_share, params);
|
||||
|
||||
// init(contentView);
|
||||
|
||||
actionbar_tv_title = (TextView) findViewById(R.id.actionbar_tv_title);
|
||||
detail_rv_show = (RecyclerView) findViewById(R.id.detail_rv_show);
|
||||
detail_ll_bottom = (LinearLayout) findViewById(R.id.detail_ll_bottom);
|
||||
detail_tv_download = (TextView) findViewById(R.id.detail_tv_download);
|
||||
detail_pb_progressbar = (ProgressBar) findViewById(R.id.detail_pb_progressbar);
|
||||
detail_tv_per = (TextView) findViewById(R.id.detail_tv_per);
|
||||
reuse_ll_loading = (LinearLayout) findViewById(R.id.reuse_ll_loading);
|
||||
reuse_no_connection = (LinearLayout) findViewById(R.id.reuse_no_connection);
|
||||
reuse_none_data = (LinearLayout) findViewById(R.id.reuse_none_data);
|
||||
reuse_tv_none_data = (TextView) findViewById(R.id.reuse_tv_none_data);
|
||||
|
||||
detail_ll_bottom.setOnClickListener(this);
|
||||
detail_tv_download.setOnClickListener(this);
|
||||
detail_pb_progressbar.setOnClickListener(this);
|
||||
detail_tv_per.setOnClickListener(this);
|
||||
reuse_no_connection.setOnClickListener(this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
DownloadManager.getInstance(this).removeObserver(dataWatcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (gameEntity != null
|
||||
&& gameEntity.getApk() != null
|
||||
&& gameEntity.getApk().size() == 1) {
|
||||
initDownload(true);
|
||||
}
|
||||
DownloadManager.getInstance(this).addObserver(dataWatcher);
|
||||
}
|
||||
|
||||
protected void initDownload(boolean isCheck) {
|
||||
if (Config.isShow(this)) {
|
||||
detail_ll_bottom.setVisibility(View.VISIBLE);
|
||||
detail_rv_show.setPadding(0, 0, 0,
|
||||
DisplayUtils.dip2px(getApplicationContext(), 60));
|
||||
} else {
|
||||
detail_ll_bottom.setVisibility(View.GONE);
|
||||
detail_rv_show.setPadding(0, 0, 0, 0);
|
||||
}
|
||||
if (gameEntity != null && "光环助手".equals(gameEntity.getName())) {
|
||||
detail_ll_bottom.setVisibility(View.GONE);
|
||||
detail_rv_show.setPadding(0, 0, 0, 0);
|
||||
} else if (gameEntity == null || gameEntity.getApk().isEmpty()) {
|
||||
detail_tv_download.setVisibility(View.VISIBLE);
|
||||
detail_pb_progressbar.setVisibility(View.GONE);
|
||||
detail_tv_per.setVisibility(View.GONE);
|
||||
if (TextUtils.isEmpty(downloadOffText)) {
|
||||
detail_tv_download.setText("暂无下载");
|
||||
} else {
|
||||
detail_tv_download.setText(downloadOffText);
|
||||
}
|
||||
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_pause_style);
|
||||
detail_tv_download.setTextColor(0xFF999999);
|
||||
detail_tv_download.setClickable(false);
|
||||
} else {
|
||||
detail_tv_download.setVisibility(View.VISIBLE);
|
||||
detail_pb_progressbar.setVisibility(View.GONE);
|
||||
detail_tv_per.setVisibility(View.GONE);
|
||||
boolean isInstalled = false;
|
||||
if (gameEntity.getApk() != null && gameEntity.getApk().size() == 1
|
||||
&& PackageManager.isInstalled(gameEntity.getApk().get(0).getPackageName())) {
|
||||
isInstalled = true;
|
||||
}
|
||||
if (isInstalled) {
|
||||
if (PackageManager.isCanUpdate(gameEntity.getId(), gameEntity.getApk().get(0).getPackageName())) {
|
||||
if (TextUtils.isEmpty(downloadAddWord)) {
|
||||
detail_tv_download.setBackgroundResource(
|
||||
R.drawable.game_item_btn_download_style);
|
||||
detail_tv_download.setText(String.format("更新《%s》",
|
||||
gameEntity.getName()));
|
||||
} else {
|
||||
detail_tv_download.setBackgroundResource(
|
||||
R.drawable.game_item_btn_download_style);
|
||||
detail_tv_download.setText(String.format("更新《%s》%s",
|
||||
gameEntity.getName(), downloadAddWord));
|
||||
}
|
||||
} else {
|
||||
if (gameEntity.getTag() != null && gameEntity.getTag().size() != 0
|
||||
&& !TextUtils.isEmpty(gameEntity.getApk().get(0).getGhVersion())
|
||||
&& !PackageUtils.isSignature(this, gameEntity.getApk().get(0).getPackageName())) {
|
||||
if (TextUtils.isEmpty(downloadAddWord)) {
|
||||
detail_tv_download.setBackgroundResource(
|
||||
R.drawable.game_item_btn_plugin_style);
|
||||
detail_tv_download.setText(String.format("插件化《%s》",
|
||||
gameEntity.getName()));
|
||||
} else {
|
||||
detail_tv_download.setBackgroundResource(
|
||||
R.drawable.game_item_btn_plugin_style);
|
||||
detail_tv_download.setText(String.format("插件化《%s》%s",
|
||||
gameEntity.getName(), downloadAddWord));
|
||||
}
|
||||
} else {
|
||||
if (TextUtils.isEmpty(downloadAddWord)) {
|
||||
detail_tv_download.setBackgroundResource(
|
||||
R.drawable.game_item_btn_launch_style);
|
||||
detail_tv_download.setText(String.format("启动《%s》",
|
||||
gameEntity.getName()));
|
||||
} else {
|
||||
detail_tv_download.setBackgroundResource(
|
||||
R.drawable.game_item_btn_launch_style);
|
||||
detail_tv_download.setText(String.format("启动《%s》%s",
|
||||
gameEntity.getName(), downloadAddWord));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String status = GameUtils.getDownloadBtnText(this, gameEntity);
|
||||
if ("插件化".equals(status)) {
|
||||
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
|
||||
} else if ("打开".equals(status)) {
|
||||
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_launch_style);
|
||||
} else {
|
||||
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_download_style);
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(downloadAddWord)) {
|
||||
detail_tv_download.setText(String.format(status + "《%s》",
|
||||
gameEntity.getName()));
|
||||
} else {
|
||||
detail_tv_download.setText(String.format(status + "《%s》%s",
|
||||
gameEntity.getName(), downloadAddWord));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isCheck && gameEntity != null
|
||||
&& gameEntity.getApk() != null
|
||||
&& gameEntity.getApk().size() == 1) {
|
||||
String url = gameEntity.getApk().get(0).getUrl();
|
||||
DownloadEntity downloadEntity = DownloadManager.getInstance(getApplicationContext()).get(url);
|
||||
if (downloadEntity != null) {
|
||||
mDownloadEntity = downloadEntity;
|
||||
detail_tv_download.setVisibility(View.GONE);
|
||||
detail_pb_progressbar.setVisibility(View.VISIBLE);
|
||||
detail_tv_per.setVisibility(View.VISIBLE);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidate() {
|
||||
detail_pb_progressbar.setProgress((int) (mDownloadEntity.getPercent() * 10));
|
||||
detail_tv_per.setTextColor(0xFFFFFFFF);
|
||||
switch (mDownloadEntity.getStatus()) {
|
||||
case downloading:
|
||||
case pause:
|
||||
case timeout:
|
||||
case neterror:
|
||||
case waiting:
|
||||
detail_tv_per.setText("下载中");
|
||||
break;
|
||||
case done:
|
||||
detail_tv_per.setText("安装");
|
||||
if (mDownloadEntity.isPluggable()
|
||||
&& PackageManager.isInstalled(mDownloadEntity.getPackageName())) {
|
||||
detail_pb_progressbar.setProgressDrawable(getResources().getDrawable(R.drawable.progressbar_plugin_radius_style));
|
||||
} else {
|
||||
detail_pb_progressbar.setProgressDrawable(getResources().getDrawable(R.drawable.progressbar_normal_radius_style));
|
||||
}
|
||||
break;
|
||||
case cancel:
|
||||
case hijack:
|
||||
case notfound:
|
||||
initDownload(false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 接收下载被删除消息
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEvent(EBDownloadStatus status) {
|
||||
if ("delete".equals(status.getStatus())
|
||||
&& gameEntity != null
|
||||
&& gameEntity.getApk() != null
|
||||
&& gameEntity.getApk().size() == 1) {
|
||||
String url = gameEntity.getApk().get(0).getUrl();
|
||||
if (url.equals(status.getUrl())) {
|
||||
initDownload(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 接受安装、卸载消息
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(EBPackage busFour) {
|
||||
if (gameEntity != null
|
||||
&& gameEntity.getApk() != null
|
||||
&& gameEntity.getApk().size() > 0) {
|
||||
for (ApkEntity apkEntity : gameEntity.getApk()) {
|
||||
String packageName = apkEntity.getPackageName();
|
||||
if (packageName.equals(busFour.getPackageName())) {
|
||||
ApkActiveUtils.filterHideApk(gameEntity);
|
||||
initDownload(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v == detail_tv_download) {
|
||||
if (gameEntity != null && !gameEntity.getApk().isEmpty()) {
|
||||
if (gameEntity.getApk().size() == 1) {
|
||||
if (NetworkUtils.isWifiConnected(this)) {
|
||||
download();
|
||||
} else {
|
||||
DialogUtils.showDownloadDialog(this, new DialogUtils.ConfirmListener() {
|
||||
@Override
|
||||
public void onConfirm() {
|
||||
download();
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
DownloadDialog.getInstance(this).showPopupWindow(v, gameEntity, mEntrance, name + ":" + title);
|
||||
}
|
||||
} else {
|
||||
toast("稍等片刻~!游戏正在上传中...");
|
||||
}
|
||||
} else if (v == detail_pb_progressbar || v == detail_tv_per) {
|
||||
String str = detail_tv_per.getText().toString();
|
||||
if ("下载中".equals(str)) {
|
||||
DownloadManagerActivity.startDownloadManagerActivity(this, gameEntity.getApk().get(0).getUrl()
|
||||
, mEntrance + "+(" + name + "[" + title + "])");
|
||||
} else if ("安装".equals(str)) {
|
||||
PackageUtils.launchSetup(this, mDownloadEntity.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void download() {
|
||||
String str = detail_tv_download.getText().toString();
|
||||
if (str.contains("启动")) {
|
||||
DataUtils.onGameLaunchEvent(this, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), name);
|
||||
|
||||
PackageUtils.launchApplicationByPackageName(this, gameEntity.getApk().get(0).getPackageName());
|
||||
} else {
|
||||
String method;
|
||||
if (str.contains("更新")) {
|
||||
method = "更新";
|
||||
} else if (str.contains("插件化")) {
|
||||
method = "插件化";
|
||||
} else {
|
||||
method = "下载";
|
||||
}
|
||||
ApkEntity apkEntity = gameEntity.getApk().get(0);
|
||||
String msg = FileUtils.isCanDownload(this, apkEntity.getSize());
|
||||
if (TextUtils.isEmpty(msg)) {
|
||||
DataUtils.onGameDownloadEvent(this, gameEntity.getName(), apkEntity.getPlatform(), mEntrance, "下载开始");
|
||||
|
||||
DownloadManager.createDownload(this, apkEntity, gameEntity, method, mEntrance, name + ":" + title);
|
||||
|
||||
detail_tv_download.setVisibility(View.GONE);
|
||||
detail_pb_progressbar.setVisibility(View.VISIBLE);
|
||||
detail_tv_per.setVisibility(View.VISIBLE);
|
||||
detail_pb_progressbar.setProgress(0);
|
||||
detail_tv_per.setText("0.0%");
|
||||
|
||||
DownloadManager.getInstance(BaseDetailActivity.this).putStatus(apkEntity.getUrl(), "downloading");
|
||||
} else {
|
||||
toast(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,100 +0,0 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.gh.common.util.EntranceUtils;
|
||||
import com.gh.gamecenter.listener.OnCallBackListener;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
/**
|
||||
* Created by LGT on 2016/9/4.
|
||||
* Fragment 基类
|
||||
*/
|
||||
public class BaseFragment extends Fragment implements OnCallBackListener {
|
||||
|
||||
protected View view;
|
||||
|
||||
protected boolean isEverpause;
|
||||
|
||||
protected String mEntrance;
|
||||
|
||||
protected void init(int layout) {
|
||||
view = View.inflate(getActivity(), layout, null);
|
||||
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mEntrance = getActivity().getIntent().getStringExtra(EntranceUtils.KEY_ENTRANCE);
|
||||
isEverpause = false;
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
if (container != null) {
|
||||
container.removeView(view);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
isEverpause = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
isEverpause = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
public void toast(String msg) {
|
||||
Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
public boolean isEverpause() {
|
||||
return isEverpause;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadDone() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadDone(Object obj) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadError() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadEmpty() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,206 +0,0 @@
|
||||
//package com.gh.base;
|
||||
//
|
||||
//import android.annotation.TargetApi;
|
||||
//import android.content.Context;
|
||||
//import android.graphics.Color;
|
||||
//import android.os.Build;
|
||||
//import android.os.Bundle;
|
||||
//import android.support.v4.app.FragmentActivity;
|
||||
//import android.view.*;
|
||||
//import android.view.View.OnClickListener;
|
||||
//import android.view.ViewGroup.LayoutParams;
|
||||
//import android.widget.*;
|
||||
//import butterknife.ButterKnife;
|
||||
//import com.gh.common.constant.Config;
|
||||
//import com.gh.common.util.*;
|
||||
//import com.gh.download.DownloadManager;
|
||||
//import com.gh.gamecenter.R;
|
||||
//import com.gh.gamecenter.eventbus.EBShowDialog;
|
||||
//import com.readystatesoftware.systembartint.SystemBarTintManager;
|
||||
//import com.readystatesoftware.systembartint.SystemBarTintManager.SystemBarConfig;
|
||||
//import de.greenrobot.event.EventBus;
|
||||
//
|
||||
//import java.lang.reflect.Field;
|
||||
//import java.lang.reflect.Method;
|
||||
//import java.util.ArrayList;
|
||||
//
|
||||
//public class BaseFragmentActivity extends FragmentActivity {
|
||||
//
|
||||
// protected String mEntrance;
|
||||
//
|
||||
// private boolean isPause;
|
||||
//
|
||||
// @Override
|
||||
// protected void onCreate(Bundle savedInstanceState) {
|
||||
// super.onCreate(savedInstanceState);
|
||||
// AppManager.getInstance().addActivity(this);
|
||||
// EventBus.getDefault().register(this);
|
||||
// mEntrance = getIntent().getStringExtra("mEntrance");
|
||||
// if (getIntent().getBundleExtra("data") != null) {
|
||||
// mEntrance = getIntent().getBundleExtra("data").getString("mEntrance");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public void init(View contentView, String title) {
|
||||
// init(contentView);
|
||||
// TextView actionbar_tv_title = (TextView) findViewById(R.id.actionbar_tv_title);
|
||||
// actionbar_tv_title.setText(title);
|
||||
// }
|
||||
//
|
||||
// public void init(View contentView) {
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
// setTheme(R.style.AppTheme);
|
||||
// setTranslucentStatus(true);
|
||||
// SystemBarTintManager tintManager = new SystemBarTintManager(this);
|
||||
// tintManager.setStatusBarTintEnabled(true);
|
||||
// if (Build.MANUFACTURER.equals("Meizu") || Build.MANUFACTURER.equals("Xiaomi")) {
|
||||
// tintManager.setStatusBarTintColor(Color.WHITE);
|
||||
// } else {
|
||||
// tintManager.setStatusBarTintColor(Color.BLACK);
|
||||
// }
|
||||
// SystemBarConfig config = tintManager.getConfig();
|
||||
// contentView.setPadding(0, config.getPixelInsetTop(false), 0,
|
||||
// config.getPixelInsetBottom());
|
||||
//
|
||||
// if (Build.MANUFACTURER.equals("Meizu")) {
|
||||
// try {
|
||||
// Window window = getWindow();
|
||||
// if (window != null) {
|
||||
// WindowManager.LayoutParams lp = window.getAttributes();
|
||||
// Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
|
||||
// Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags");
|
||||
// darkFlag.setAccessible(true);
|
||||
// meizuFlags.setAccessible(true);
|
||||
// int bit = darkFlag.getInt(null);
|
||||
// int value = meizuFlags.getInt(lp);
|
||||
// value |= bit;
|
||||
// meizuFlags.setInt(lp, value);
|
||||
// window.setAttributes(lp);
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// } else if (Build.MANUFACTURER.equals("Xiaomi")) {
|
||||
// try {
|
||||
// Window window = getWindow();
|
||||
// if (window != null) {
|
||||
// Class<?> clazz = window.getClass();
|
||||
// Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
|
||||
// Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
|
||||
// int darkModeFlag = field.getInt(layoutParams);
|
||||
// Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
|
||||
// extraFlagField.invoke(window, darkModeFlag, darkModeFlag);
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// setContentView(contentView);
|
||||
//
|
||||
// ButterKnife.bind(this);
|
||||
//
|
||||
// int actionbar_height = getSharedPreferences(Config.PREFERENCE,
|
||||
// Context.MODE_PRIVATE).getInt("actionbar_height",
|
||||
// DisplayUtils.dip2px(getApplicationContext(), 48));
|
||||
//
|
||||
// RelativeLayout reuse_actionbar = (RelativeLayout) findViewById(R.id.reuse_actionbar);
|
||||
// LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
// LayoutParams.MATCH_PARENT, actionbar_height);
|
||||
// reuse_actionbar.setLayoutParams(params);
|
||||
//
|
||||
// findViewById(R.id.actionbar_rl_back).setOnClickListener(
|
||||
// new OnClickListener() {
|
||||
// @Override
|
||||
// public void onClick(View v) {
|
||||
// finish();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// public void toast(String msg) {
|
||||
// Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
|
||||
// }
|
||||
//
|
||||
// public void toast(int msg) {
|
||||
// Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
|
||||
// }
|
||||
//
|
||||
// @TargetApi(19)
|
||||
// protected void setTranslucentStatus(boolean status) {
|
||||
// Window window = getWindow();
|
||||
// WindowManager.LayoutParams winParams = window.getAttributes();
|
||||
// final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
|
||||
// if (status) {
|
||||
// winParams.flags |= bits;
|
||||
// } else {
|
||||
// winParams.flags &= ~bits;
|
||||
// }
|
||||
// window.setAttributes(winParams);
|
||||
// }
|
||||
//
|
||||
// //如果是游戏分享,newsTitle默认为空
|
||||
// public void showShare(String url, String gameName, String icon, String newsTitle, ArrayList<String> tag) {
|
||||
//
|
||||
// //判断是否是官方版
|
||||
// boolean isPlugin = false;
|
||||
// if (tag != null) {
|
||||
// for (String s : tag) {
|
||||
// if (!"官方版".equals(s)) {
|
||||
// isPlugin = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// ShareUtils.getInstance(this).showShareWindows(new View(this), url, gameName, icon, newsTitle, isPlugin, true);
|
||||
//
|
||||
// if (newsTitle == null) {
|
||||
// DataUtils.onEvent(this, "内容分享", gameName);
|
||||
// } else {
|
||||
// DataUtils.onEvent(this, "内容分享", newsTitle);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public void onEventMainThread(final EBShowDialog showDialog) {
|
||||
// if (!isPause && this.getClass().getName().equals(RunningUtils.getTopActivity(this))) {
|
||||
// if ("hijack".equals(showDialog.getType())) {
|
||||
// DialogUtils.showQqSessionDialog(this, null);// 建议用户联系客服
|
||||
// } else if ("plugin".equals(showDialog.getType())) {
|
||||
// DialogUtils.showPluginDialog(this, new DialogUtils.ConfirmListener() {
|
||||
// @Override
|
||||
// public void onConfirm() {
|
||||
// if (FileUtils.isEmptyFile(showDialog.getPath())) {
|
||||
// Toast.makeText(BaseFragmentActivity.this, "解析包出错(可能被误删了),请重新下载", Toast.LENGTH_SHORT).show();
|
||||
// } else {
|
||||
// startActivity(PackageUtils.getUninstallIntent(BaseFragmentActivity.this, showDialog.getPath()));
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected void onDestroy() {
|
||||
// super.onDestroy();
|
||||
// EventBus.getDefault().unregister(this);
|
||||
// AppManager.getInstance().finishActivity(this);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected void onPause() {
|
||||
// super.onPause();
|
||||
// DataUtils.onPause(this);
|
||||
// isPause = true;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected void onResume() {
|
||||
// super.onResume();
|
||||
// DataUtils.onResume(this);
|
||||
// isPause = false;
|
||||
// DownloadManager.getInstance(this).initGameMap();
|
||||
// }
|
||||
//
|
||||
//}
|
||||
57
app/src/main/java/com/gh/base/BaseRecyclerViewHolder.java
Normal file
57
app/src/main/java/com/gh/base/BaseRecyclerViewHolder.java
Normal file
@ -0,0 +1,57 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
/**
|
||||
* 目前仅提供butterknife bind方法
|
||||
*
|
||||
* @author CsHeng
|
||||
* @Date 16/06/2017
|
||||
* @Time 9:55 AM
|
||||
*/
|
||||
|
||||
public abstract class BaseRecyclerViewHolder<T> extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||
|
||||
private T mData;
|
||||
private OnListClickListener mListClickListener;
|
||||
|
||||
public BaseRecyclerViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
ButterKnife.bind(this, itemView);
|
||||
}
|
||||
|
||||
/**
|
||||
* 具体的设置监听在childViewHolder 设置
|
||||
*
|
||||
* @param itemView
|
||||
* @param data 一般情况下只传列表数据
|
||||
* @param listClickListener 列表事件接口
|
||||
*/
|
||||
public BaseRecyclerViewHolder(View itemView, T data, OnListClickListener listClickListener) {
|
||||
this(itemView);
|
||||
this.mData = data;
|
||||
this.mListClickListener = listClickListener;
|
||||
}
|
||||
|
||||
public BaseRecyclerViewHolder(View itemView, OnListClickListener listClickListener) {
|
||||
this(itemView);
|
||||
this.mListClickListener = listClickListener;
|
||||
}
|
||||
|
||||
public void setClickData(T clickData) {
|
||||
this.mData = clickData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
try {
|
||||
mListClickListener.onListClick(view, getAdapterPosition(), mData);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
124
app/src/main/java/com/gh/base/BaseToolBarActivity.java
Normal file
124
app/src/main/java/com/gh/base/BaseToolBarActivity.java
Normal file
@ -0,0 +1,124 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.normal.ToolbarController;
|
||||
import com.lightgame.BaseAppCompatActivity;
|
||||
import com.lightgame.OnTitleClickListener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by csheng on 15-10-12.
|
||||
*/
|
||||
|
||||
public abstract class BaseToolBarActivity extends BaseAppCompatActivity implements ToolbarController, Toolbar.OnMenuItemClickListener {
|
||||
|
||||
private Toolbar mToolbar;
|
||||
private TextView mTitleTv;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
initToolbar();
|
||||
}
|
||||
|
||||
private void initToolbar() {
|
||||
mToolbar = findViewById(R.id.normal_toolbar);
|
||||
mTitleTv = findViewById(R.id.normal_title);
|
||||
if (mToolbar != null) {
|
||||
// setSupportActionBar(mToolbar); // 替换actionBar后 toolBar无法控制
|
||||
mToolbar.setNavigationIcon(provideNavigationIcon());
|
||||
mToolbar.setNavigationOnClickListener(view -> onBackPressed());
|
||||
mTitleTv.setOnClickListener(view -> {
|
||||
final List<Fragment> fragmentList = getSupportFragmentManager().getFragments();
|
||||
for (Fragment fragment : fragmentList) {
|
||||
if (fragment instanceof OnTitleClickListener) {
|
||||
((OnTitleClickListener) fragment).onTitleClick();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
public int provideNavigationIcon() {
|
||||
return R.drawable.ic_bar_back; // default navigation icon
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigationTitle(String title) {
|
||||
if (mTitleTv != null) mTitleTv.setText(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigationTitle(@StringRes int res) {
|
||||
setNavigationTitle(getString(res));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setToolbarMenu(int res) {
|
||||
if (mToolbar == null) return;
|
||||
mToolbar.inflateMenu(res);
|
||||
mToolbar.setOnMenuItemClickListener(this);
|
||||
|
||||
Menu menu = mToolbar.getMenu();
|
||||
for (int i = 0; i < menu.size(); i++) {
|
||||
MenuItem menuItem = menu.getItem(i);
|
||||
// menu设置actionLayout后,无法捕捉点击事件,以icon为tag,如果icon is null 手动设置menuItem点击事件
|
||||
if (menuItem != null && menuItem.getIcon() == null) {
|
||||
if (menuItem.getActionView() != null) {
|
||||
menuItem.getActionView().setOnClickListener((v) -> this.onMenuItemClick(menuItem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 限制标题实际宽度 防止标题挡住toolbar menu按钮
|
||||
if (menu.size() > 2 && mTitleTv != null) {
|
||||
ViewGroup.LayoutParams layoutParams = mTitleTv.getLayoutParams();
|
||||
if (layoutParams instanceof RelativeLayout.LayoutParams) {
|
||||
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) layoutParams;
|
||||
params.setMargins(DisplayUtils.dip2px(90), 0, DisplayUtils.dip2px(90), 0);
|
||||
mTitleTv.setLayoutParams(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem getMenuItem(int res) {
|
||||
if (mToolbar == null) return null; //后续页面做好判断
|
||||
return mToolbar.getMenu().findItem(res);
|
||||
}
|
||||
|
||||
public Menu getMenu() {
|
||||
return mToolbar.getMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
return onNavigationIconClicked();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
protected abstract boolean onNavigationIconClicked();
|
||||
|
||||
}
|
||||
19
app/src/main/java/com/gh/base/CurrentActivityHolder.kt
Normal file
19
app/src/main/java/com/gh/base/CurrentActivityHolder.kt
Normal file
@ -0,0 +1,19 @@
|
||||
package com.gh.base
|
||||
|
||||
import android.app.Activity
|
||||
|
||||
object CurrentActivityHolder {
|
||||
|
||||
@JvmStatic
|
||||
val activitySet = HashSet<Activity>()
|
||||
|
||||
@JvmStatic
|
||||
fun getCurrentActivity(): Activity? {
|
||||
return if (activitySet.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
activitySet.iterator().next()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.support.v4.app.Fragment;
|
||||
|
||||
/**
|
||||
* @author CsHeng
|
||||
* @Date 15-9-29
|
||||
* @Time 上午10:24
|
||||
*/
|
||||
public interface FragmentNavigationDelegate {
|
||||
|
||||
void addFragment(Fragment toAdd);
|
||||
|
||||
void replaceFragment(Fragment toReplace);
|
||||
|
||||
void removeFragment(Fragment toRemove);
|
||||
|
||||
void showFragment(Fragment toShow);
|
||||
|
||||
void hideFragment(Fragment toHide);
|
||||
|
||||
boolean popFragment();
|
||||
|
||||
void popFragmentToBase();
|
||||
|
||||
}
|
||||
@ -4,6 +4,12 @@ import android.app.Activity;
|
||||
import android.app.Application.ActivityLifecycleCallbacks;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.gh.common.im.ImManager;
|
||||
import com.gh.common.notifier.Notifier;
|
||||
import com.gh.common.util.DataUtils;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.lightgame.utils.AppManager;
|
||||
|
||||
/**
|
||||
* 1、写点针对生命周期的统计代码
|
||||
* 2、写点通用的逻辑
|
||||
@ -15,10 +21,10 @@ import android.os.Bundle;
|
||||
*/
|
||||
public class GHActivityLifecycleCallbacksImpl implements ActivityLifecycleCallbacks {
|
||||
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
|
||||
AppManager.getInstance().addActivity(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -29,16 +35,27 @@ public class GHActivityLifecycleCallbacksImpl implements ActivityLifecycleCallba
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
|
||||
DataUtils.onResume(activity);
|
||||
CurrentActivityHolder.getActivitySet().add(activity);
|
||||
ImManager.updateFloatingWindow();
|
||||
//FIXME 这里应该只是部分Activity需要
|
||||
try {
|
||||
// 初始化gameMap
|
||||
DownloadManager.getInstance(activity).initGameMap();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
|
||||
DataUtils.onPause(activity);
|
||||
CurrentActivityHolder.getActivitySet().remove(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
|
||||
Notifier.hide();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -48,7 +65,7 @@ public class GHActivityLifecycleCallbacksImpl implements ActivityLifecycleCallba
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
|
||||
AppManager.getInstance().finishActivity(activity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,312 +0,0 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.util.ArrayMap;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.gh.common.util.AppDebugConfig;
|
||||
import com.gh.common.util.EntranceUtils;
|
||||
import com.gh.common.util.FileUtils;
|
||||
import com.gh.common.util.PackageUtils;
|
||||
import com.gh.common.util.TokenUtils;
|
||||
import com.gh.common.util.Utils;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.xiaomi.mipush.sdk.ErrorCode;
|
||||
import com.xiaomi.mipush.sdk.MiPushClient;
|
||||
import com.xiaomi.mipush.sdk.MiPushCommandMessage;
|
||||
import com.xiaomi.mipush.sdk.MiPushMessage;
|
||||
import com.xiaomi.mipush.sdk.PushMessageReceiver;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.gh.common.util.EntranceUtils.ENTRANCE_MIPUSH;
|
||||
import static com.gh.common.util.EntranceUtils.HOST_ARTICLE;
|
||||
import static com.gh.common.util.EntranceUtils.HOST_GAME;
|
||||
import static com.gh.common.util.EntranceUtils.HOST_WEB;
|
||||
import static com.gh.common.util.EntranceUtils.HOSt_COLUMN;
|
||||
import static com.gh.common.util.EntranceUtils.KEY_ENTRANCE;
|
||||
import static com.gh.common.util.EntranceUtils.KEY_GAMEID;
|
||||
import static com.gh.common.util.EntranceUtils.KEY_ID;
|
||||
import static com.gh.common.util.EntranceUtils.KEY_NEWSID;
|
||||
import static com.gh.common.util.EntranceUtils.KEY_TARGET;
|
||||
import static com.gh.common.util.EntranceUtils.KEY_TO;
|
||||
import static com.gh.common.util.EntranceUtils.KEY_TYPE;
|
||||
import static com.gh.common.util.EntranceUtils.KEY_URL;
|
||||
|
||||
/**
|
||||
* 1、PushMessageReceiver是个抽象类,该类继承了BroadcastReceiver。
|
||||
* 2、需要将自定义的DemoMessageReceiver注册在AndroidManifest.xml文件中 <receiver
|
||||
* android:exported="true"
|
||||
* android:name="com.xiaomi.mipushdemo.DemoMessageReceiver"> <intent-filter>
|
||||
* <action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE" /> </intent-filter>
|
||||
* <intent-filter> <action android:name="com.xiaomi.mipush.ERROR" />
|
||||
* </intent-filter> <intent-filter> <action
|
||||
* android:name="com.xiaomi.mipush.MESSAGE_ARRIVED" /></intent-filter>
|
||||
* </receiver>
|
||||
* 3、DemoMessageReceiver的onReceivePassThroughMessage方法用来接收服务器向客户端发送的透传消息
|
||||
* 4、DemoMessageReceiver的onNotificationMessageClicked方法用来接收服务器向客户端发送的通知消息,
|
||||
* 这个回调方法会在用户手动点击通知后触发
|
||||
* 5、DemoMessageReceiver的onNotificationMessageArrived方法用来接收服务器向客户端发送的通知消息,
|
||||
* 这个回调方法是在通知消息到达客户端时触发。另外应用在前台时不弹出通知的通知消息到达客户端也会触发这个回调函数
|
||||
* 6、DemoMessageReceiver的onCommandResult方法用来接收客户端向服务器发送命令后的响应结果
|
||||
* 7、DemoMessageReceiver的onReceiveRegisterResult方法用来接收客户端向服务器发送注册命令后的响应结果
|
||||
* 8、以上这些方法运行在非UI线程中
|
||||
*
|
||||
* @author mayixiang
|
||||
* //TODO 请勿更改此类路径,若需更改时请注意更改./libraries/MiPush/proguard-library.txt混淆对应配置
|
||||
*/
|
||||
public class GHPushMessageReceiver extends PushMessageReceiver {
|
||||
|
||||
private String mAlias;
|
||||
|
||||
@Override
|
||||
public void onReceivePassThroughMessage(Context context, MiPushMessage message) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
AppDebugConfig.logMethodWithParams(this, message);
|
||||
}
|
||||
// 1判断notifyid是否为4
|
||||
try {
|
||||
//TODO what is magic number 4?
|
||||
if (message.getNotifyId() == 4) {
|
||||
JSONObject jsonObject = new JSONObject(message.getContent());
|
||||
Utils.log(jsonObject.toString());
|
||||
String channel = jsonObject.getString("channel");
|
||||
Utils.log("channel = " + channel);
|
||||
|
||||
// 1判断渠道号是否一致或是否为ALL
|
||||
String packageChannel = AppController.getInstance().getChannel();
|
||||
if ("ALL".equals(channel) || channel.equalsIgnoreCase(packageChannel)) {
|
||||
String type = jsonObject.getString(KEY_TYPE);
|
||||
Utils.log("type = " + type);
|
||||
if ("NEWS".equals(type)) {
|
||||
// 新闻推送
|
||||
JSONArray jsonArray = jsonObject.getJSONArray("package");
|
||||
ArrayMap<String, Boolean> map = getInstalledMapFromLocal(context);
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
Boolean b = map.get(jsonArray.getString(i));
|
||||
if (b != null) {
|
||||
// 显示推送的消息
|
||||
showNotification(context, jsonObject, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if ("PLUGIN_UPDATE".equals(type)) {
|
||||
// 插件更新推送
|
||||
JSONArray jsonArray = jsonObject.getJSONArray("apk");
|
||||
ArrayMap<String, Boolean> map = getInstalledMapFromLocal(context);
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
JSONObject apk = jsonArray.getJSONObject(i);
|
||||
String packageName = apk.getString("package");
|
||||
Boolean b = map.get(packageName);
|
||||
if (b != null) {
|
||||
// 判断是否gh_version是否相同
|
||||
String gh_version = (String) PackageUtils
|
||||
.getMetaData(context, packageName, "gh_version");
|
||||
if (gh_version != null) {
|
||||
gh_version = gh_version.substring(2);
|
||||
// 判断gh_version是否相同
|
||||
if (gh_version.equals(apk.getString("gh_version"))) {
|
||||
// 判断version是否相同
|
||||
String version = PackageUtils.getVersionByPackage(context, packageName);
|
||||
if (apk.getString("version").equals(version)) {
|
||||
// 版本相同,无需显示插件更新,继续查看是否有可更新的游戏包
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 显示推送的消息
|
||||
showNotification(context, jsonObject, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if ("NEW_GAME".equals(type)) {
|
||||
// 新游推送
|
||||
showNotification(context, jsonObject, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayMap<String, Boolean> getInstalledMapFromLocal(Context context) {
|
||||
ArrayMap<String, Boolean> map = new ArrayMap<>();
|
||||
ArrayList<String> list = getAllPackageName(context);
|
||||
for (String str : list) {
|
||||
map.put(str, true);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private void showNotification(Context context, JSONObject jsonObject, int id) throws JSONException {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction("com.gh.gamecenter.NOTIFICATION");
|
||||
intent.putExtra("notifyId", id);
|
||||
intent.putExtra("notifyData", jsonObject.toString());
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_ONE_SHOT);
|
||||
|
||||
NotificationManager nManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
Notification notification = new NotificationCompat.Builder(context)
|
||||
.setSmallIcon(R.drawable.logo)
|
||||
.setTicker(jsonObject.getString("pushTitle"))
|
||||
.setContentTitle(jsonObject.getString("pushTitle"))
|
||||
.setContentText(jsonObject.getString("pushDesc"))
|
||||
.setContentIntent(pendingIntent).build();
|
||||
|
||||
RemoteViews remoteViews = null;
|
||||
|
||||
if (Build.MANUFACTURER.equals("Meizu") &&
|
||||
(Build.MODEL.startsWith("m") || Build.MODEL.startsWith("MX"))) {
|
||||
remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_meizu);
|
||||
SimpleDateFormat format = new SimpleDateFormat("HH:mm", Locale.getDefault());
|
||||
remoteViews.setTextViewText(R.id.time, format.format(new Date()));
|
||||
} else if (Build.MANUFACTURER.equals("Xiaomi") &&
|
||||
(Build.MODEL.startsWith("MI") || Build.MODEL.startsWith("HM") || Build.MODEL.startsWith("Redmi"))) {
|
||||
// 小米系统
|
||||
remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_xiaomi);
|
||||
SimpleDateFormat format = new SimpleDateFormat("ah:mm", Locale.getDefault());
|
||||
remoteViews.setTextViewText(R.id.time, format.format(new Date()));
|
||||
} else if (Build.MANUFACTURER.equals("HUAWEI")) {
|
||||
// 华为系统
|
||||
remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_huawei);
|
||||
}
|
||||
|
||||
String url = jsonObject.getString("icon");
|
||||
String path = context.getCacheDir() + File.separator + url.substring(url.lastIndexOf("/") + 1);
|
||||
int result = FileUtils.downloadFile(url, path);
|
||||
if (result != 200) {
|
||||
// 下载出错,使用光环logo
|
||||
path = null;
|
||||
}
|
||||
|
||||
if (remoteViews != null) {
|
||||
if (path == null) {
|
||||
remoteViews.setImageViewBitmap(R.id.icon, BitmapFactory.decodeResource(context.getResources(), R.drawable.logo));
|
||||
} else {
|
||||
remoteViews.setImageViewBitmap(R.id.icon, BitmapFactory.decodeFile(path));
|
||||
}
|
||||
remoteViews.setTextViewText(R.id.title, jsonObject.getString("pushTitle"));
|
||||
remoteViews.setTextViewText(R.id.intro, jsonObject.getString("pushDesc"));
|
||||
notification.contentView = remoteViews;
|
||||
} else {
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
|
||||
.setSmallIcon(R.drawable.logo_black)
|
||||
.setTicker(jsonObject.getString("pushTitle"))
|
||||
.setContentTitle(jsonObject.getString("pushTitle"))
|
||||
.setContentText(jsonObject.getString("pushDesc"))
|
||||
.setContentIntent(pendingIntent);
|
||||
if (path == null) {
|
||||
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.logo));
|
||||
} else {
|
||||
builder.setLargeIcon(BitmapFactory.decodeFile(path));
|
||||
}
|
||||
notification = builder.build();
|
||||
}
|
||||
|
||||
notification.defaults = Notification.DEFAULT_SOUND;// 添加系统默认声音
|
||||
notification.flags |= Notification.FLAG_AUTO_CANCEL; // FLAG_AUTO_CANCEL表明当通知被用户点击时,通知将被清除。
|
||||
nManager.notify(((int) System.currentTimeMillis() / 1000), notification);// 通过通知管理器来发起通知。如果id不同,则每click,在status哪里增加一个提示
|
||||
}
|
||||
|
||||
private ArrayList<String> getAllPackageName(Context context) {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
List<PackageInfo> packageInfos = context.getPackageManager().getInstalledPackages(0);
|
||||
for (PackageInfo packageInfo : packageInfos) {
|
||||
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
|
||||
list.add(packageInfo.packageName);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationMessageClicked(Context context, MiPushMessage message) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
AppDebugConfig.logMethodWithParams(this, message);
|
||||
}
|
||||
try {
|
||||
String content = message.getContent();
|
||||
JSONObject response = new JSONObject(content);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(KEY_ENTRANCE, ENTRANCE_MIPUSH);
|
||||
String type = response.getString(KEY_TYPE);
|
||||
String target = response.getString(KEY_TARGET);
|
||||
if (HOST_ARTICLE.equals(type)) {
|
||||
bundle.putString(KEY_TO, "NewsDetailActivity");
|
||||
bundle.putString(KEY_NEWSID, target);
|
||||
} else if (HOST_GAME.equals(type)) {
|
||||
bundle.putString(KEY_TO, "GameDetailActivity");
|
||||
bundle.putString(KEY_GAMEID, target);
|
||||
} else if (HOSt_COLUMN.equals(type)) {
|
||||
bundle.putString(KEY_TO, "SubjectActivity");
|
||||
bundle.putString(KEY_ID, target);
|
||||
} else if (HOST_WEB.equals(type)) {
|
||||
bundle.putString(KEY_TO, "WebActivity");
|
||||
bundle.putString(KEY_URL, target);
|
||||
}
|
||||
EntranceUtils.jumpActivity(context, bundle);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationMessageArrived(Context context,
|
||||
MiPushMessage message) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
AppDebugConfig.logMethodWithParams(this, message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveRegisterResult(Context context, MiPushCommandMessage message) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
AppDebugConfig.logMethodWithParams(this, message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommandResult(Context context, MiPushCommandMessage message) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
AppDebugConfig.logMethodWithParams(this, message);
|
||||
}
|
||||
|
||||
String command = message.getCommand();
|
||||
List<String> arguments = message.getCommandArguments();
|
||||
|
||||
if (MiPushClient.COMMAND_SET_ALIAS.equals(command)) {
|
||||
if (message.getResultCode() == ErrorCode.SUCCESS) {
|
||||
mAlias = arguments.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(mAlias)) {
|
||||
//添加别名
|
||||
MiPushClient.setAlias(context, TokenUtils.getDeviceId(context), null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -4,6 +4,10 @@ import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.gh.common.util.EntranceUtils;
|
||||
import com.gh.gamecenter.GameDetailActivity;
|
||||
import com.gh.gamecenter.NewsDetailActivity;
|
||||
import com.gh.gamecenter.WebActivity;
|
||||
import com.gh.gamecenter.subject.refactor.SubjectActivity;
|
||||
import com.umeng.message.UmengNotificationClickHandler;
|
||||
import com.umeng.message.entity.UMessage;
|
||||
|
||||
@ -24,18 +28,23 @@ public class GHUmengNotificationClickHandler extends UmengNotificationClickHandl
|
||||
bundle.putString(EntranceUtils.KEY_ENTRANCE, EntranceUtils.ENTRANCE_UMENG);
|
||||
String type = response.getString(EntranceUtils.KEY_TYPE);
|
||||
String target = response.getString(EntranceUtils.KEY_TARGET);
|
||||
if (EntranceUtils.HOST_ARTICLE.equals(type)) {
|
||||
bundle.putString(EntranceUtils.KEY_TO, "NewsDetailActivity");
|
||||
bundle.putString(EntranceUtils.KEY_NEWSID, target);
|
||||
} else if (EntranceUtils.HOST_GAME.equals(type)) {
|
||||
bundle.putString(EntranceUtils.KEY_TO, "GameDetailActivity");
|
||||
bundle.putString(EntranceUtils.KEY_GAMEID, target);
|
||||
} else if (EntranceUtils.HOSt_COLUMN.equals(type)) {
|
||||
bundle.putString(EntranceUtils.KEY_TO, "SubjectActivity");
|
||||
bundle.putString(EntranceUtils.KEY_ID, target);
|
||||
} else if (EntranceUtils.HOST_WEB.equals(type)) {
|
||||
bundle.putString(EntranceUtils.KEY_TO, "WebActivity");
|
||||
bundle.putString(EntranceUtils.KEY_URL, target);
|
||||
switch (type) {
|
||||
case EntranceUtils.HOST_ARTICLE:
|
||||
bundle.putString(EntranceUtils.KEY_TO, NewsDetailActivity.class.getSimpleName());
|
||||
bundle.putString(EntranceUtils.KEY_NEWSID, target);
|
||||
break;
|
||||
case EntranceUtils.HOST_GAME:
|
||||
bundle.putString(EntranceUtils.KEY_TO, GameDetailActivity.class.getSimpleName());
|
||||
bundle.putString(EntranceUtils.KEY_GAMEID, target);
|
||||
break;
|
||||
case EntranceUtils.HOST_COLUMN:
|
||||
bundle.putString(EntranceUtils.KEY_TO, SubjectActivity.class.getName());
|
||||
bundle.putString(EntranceUtils.KEY_ID, target);
|
||||
break;
|
||||
case EntranceUtils.HOST_WEB:
|
||||
bundle.putString(EntranceUtils.KEY_TO, WebActivity.class.getSimpleName());
|
||||
bundle.putString(EntranceUtils.KEY_URL, target);
|
||||
break;
|
||||
}
|
||||
EntranceUtils.jumpActivity(context, bundle);
|
||||
} catch (JSONException e) {
|
||||
|
||||
232
app/src/main/java/com/gh/base/GHUmengNotificationService.kt
Normal file
232
app/src/main/java/com/gh/base/GHUmengNotificationService.kt
Normal file
@ -0,0 +1,232 @@
|
||||
package com.gh.base
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.v4.app.NotificationCompat
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import com.gh.common.notifier.Notifier
|
||||
import com.gh.common.util.DataUtils
|
||||
import com.gh.common.util.EntranceUtils
|
||||
import com.gh.common.util.PackageUtils
|
||||
import com.gh.common.util.StringUtils
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.PushEntity
|
||||
import com.gh.gamecenter.entity.PushMessageEntity
|
||||
import com.gh.gamecenter.entity.PushMessageUnreadEntity
|
||||
import com.gh.gamecenter.entity.PushNotificationEntity
|
||||
import com.gh.gamecenter.message.MessageUnreadRepository
|
||||
import com.gh.gamecenter.qa.answer.detail.AnswerDetailActivity
|
||||
import com.gh.gamecenter.receiver.UmengMessageReceiver
|
||||
import com.gh.gamecenter.receiver.UmengMessageReceiver.Companion.TYPE_CLICK
|
||||
import com.gh.gamecenter.receiver.UmengMessageReceiver.Companion.TYPE_REMOVE
|
||||
import com.google.gson.Gson
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.umeng.message.UmengMessageService
|
||||
import org.android.agoo.common.AgooConstants
|
||||
import java.util.*
|
||||
|
||||
class GHUmengNotificationService : UmengMessageService() {
|
||||
|
||||
companion object {
|
||||
const val ACTION_UMENG = "com.gh.gamecenter.UMENG"
|
||||
|
||||
const val MESSAGE_FROM_SYSTEM = "message_from_system"
|
||||
|
||||
const val HALO_MESSAGE_DIALOG = "HALO_MESSAGE_DIALOG"
|
||||
|
||||
const val HALO_MESSAGE_CENTER = "HALO_MESSAGE_CENTER"
|
||||
|
||||
const val ANSWER = "answer"
|
||||
|
||||
const val FOLLOW_QUESTION = "follow_question"
|
||||
|
||||
const val NOTIFICATION_ID = 2015
|
||||
|
||||
const val DISPLAY_TYPE_NOTIFICATION = "notification"
|
||||
|
||||
const val DISPLAY_TYPE_CUSTOM = "custom"
|
||||
}
|
||||
|
||||
val notificationTags = arrayOf("GH_UMENG_TAG_1", "GH_UMENG_TAG_2", "GH_UMENG_TAG_3")
|
||||
val gson = Gson()
|
||||
|
||||
override fun onMessage(context: Context, intent: Intent) {
|
||||
val message = intent.getStringExtra(AgooConstants.MESSAGE_BODY)
|
||||
val isMessageFromSystem = intent.getBooleanExtra(MESSAGE_FROM_SYSTEM, false)
|
||||
|
||||
try {
|
||||
val pushData = gson.fromJson(message, PushEntity::class.java)
|
||||
pushData?.let { handlePushData(context, it, message, isMessageFromSystem) }
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePushData(context: Context, pushData: PushEntity, message: String, isMessageFromSystem: Boolean) {
|
||||
val notificationManager = context.applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
if (pushData.displayType == DISPLAY_TYPE_NOTIFICATION) {
|
||||
// 其它类型的透传信息
|
||||
// 显示到通知栏
|
||||
val msg = gson.fromJson(message, PushNotificationEntity::class.java)
|
||||
val data = msg?.extra?.data
|
||||
|
||||
// 系统推送,直接处理跳转
|
||||
if (isMessageFromSystem) {
|
||||
val intent = Intent()
|
||||
intent.setClass(context, UmengMessageReceiver::class.java)
|
||||
intent.putExtra(EntranceUtils.KEY_DATA, data?.link)
|
||||
intent.putExtra(EntranceUtils.KEY_TYPE, UmengMessageReceiver.DIRECT_ONLY)
|
||||
intent.putExtra(EntranceUtils.KEY_MESSAGE, message)
|
||||
context.sendBroadcast(intent)
|
||||
return
|
||||
}
|
||||
|
||||
// 判断是否过滤该消息
|
||||
if (validatePush(data?.condition)) {
|
||||
val clickIntent = Intent()
|
||||
val removeIntent = Intent()
|
||||
|
||||
clickIntent.setClass(context, UmengMessageReceiver::class.java)
|
||||
clickIntent.putExtra(EntranceUtils.KEY_DATA, data?.link)
|
||||
clickIntent.putExtra(EntranceUtils.KEY_MESSAGE, message)
|
||||
clickIntent.putExtra(EntranceUtils.KEY_TYPE, TYPE_CLICK)
|
||||
|
||||
removeIntent.setClass(context, UmengMessageReceiver::class.java)
|
||||
removeIntent.putExtra(EntranceUtils.KEY_TYPE, TYPE_REMOVE)
|
||||
removeIntent.putExtra(EntranceUtils.KEY_MESSAGE, message)
|
||||
|
||||
val clickPendingIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt(),
|
||||
clickIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
|
||||
val deletePendingIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt() + 1,
|
||||
removeIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel("Halo_Push", "Halo_Push", NotificationManager.IMPORTANCE_DEFAULT)
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
val notification = NotificationCompat.Builder(context, "Halo_Push")
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setTicker(pushData.body?.ticker)
|
||||
.setContentTitle(pushData.body?.title)
|
||||
.setContentText(pushData.body?.text)
|
||||
.setContentIntent(clickPendingIntent)
|
||||
.setDeleteIntent(deletePendingIntent)
|
||||
.build()
|
||||
notification.flags = notification.flags or Notification.FLAG_AUTO_CANCEL
|
||||
|
||||
notificationManager.notify(getNotificationTag(context), NOTIFICATION_ID, notification)
|
||||
}
|
||||
} else {
|
||||
if (HALO_MESSAGE_DIALOG == pushData.body?.custom) {
|
||||
// 回答了问题或者关注了问题的消息
|
||||
val msg = gson.fromJson(message, PushMessageEntity::class.java)
|
||||
val data = msg?.extra?.data
|
||||
|
||||
val type = if (ANSWER == data?.type) {
|
||||
"回答了你的问题"
|
||||
} else {
|
||||
"回答了你关注的问题"
|
||||
}
|
||||
|
||||
val userName = StringUtils.shrinkStringWithDot(data?.userEntity?.name, 8)
|
||||
val displayText = userName + type
|
||||
|
||||
if (Notifier.isActivityValid(CurrentActivityHolder.getCurrentActivity()) &&
|
||||
Notifier.shouldShowNotifier(data?.answer?.id + displayText)) {
|
||||
Notifier.create(CurrentActivityHolder.getCurrentActivity())
|
||||
.setText(displayText)
|
||||
.setDuration(5000)
|
||||
.setIcon(data?.userEntity?.icon)
|
||||
.setOnClickListener(View.OnClickListener { _ ->
|
||||
val bundle = Bundle()
|
||||
bundle.putString(EntranceUtils.KEY_ANSWER_ID, data?.answer?.id)
|
||||
bundle.putString(EntranceUtils.KEY_ENTRANCE, EntranceUtils.ENTRANCE_UMENG)
|
||||
bundle.putString(EntranceUtils.KEY_TO, AnswerDetailActivity::class.java.name)
|
||||
EntranceUtils.jumpActivity(context, bundle)
|
||||
|
||||
DataUtils.onMtaEvent(context, "消息弹窗",
|
||||
type, "")
|
||||
|
||||
Notifier.hide()
|
||||
})
|
||||
.show(false)
|
||||
Notifier.tagNotifierAsShowed(data?.answer?.id + displayText)
|
||||
}
|
||||
} else if (HALO_MESSAGE_CENTER == pushData.body?.custom) {
|
||||
// 消息中心逻辑
|
||||
val msg = gson.fromJson(message, PushMessageUnreadEntity::class.java)
|
||||
val data = msg?.extra?.data
|
||||
data?.let { MessageUnreadRepository.loadMessageUnreadData() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun validatePush(condition: PushNotificationEntity.Data.Condition?): Boolean {
|
||||
if (condition == null) return true
|
||||
|
||||
// 校验渠道是否匹配
|
||||
condition.ghzs?.channel?.let {
|
||||
if (it.isNotEmpty() && it != HaloApp.getInstance().channel) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 校验光环版本版本是否匹配
|
||||
condition.ghzs?.version?.let {
|
||||
if (it.isNotEmpty() && !BuildConfig.VERSION_NAME.contains(it)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 校验已安装的应用里是否存在条件的包名
|
||||
if (condition.packageName.isNotEmpty()) {
|
||||
val installedPackageList = PackageUtils.getAllPackageName(HaloApp.getInstance().application)
|
||||
for (packageName in installedPackageList) {
|
||||
if (condition.packageName == packageName) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 规则:最多三条消息,以旧换新
|
||||
*
|
||||
* @return NotificationTag
|
||||
*/
|
||||
private fun getNotificationTag(context: Context): String {
|
||||
val sp = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val edit = sp.edit()
|
||||
|
||||
val timeTagMap = HashMap<Long, String>()
|
||||
for (tag in notificationTags) {
|
||||
val time = sp.getLong(tag, 0)
|
||||
if (time == 0L) {
|
||||
edit.putLong(tag, System.currentTimeMillis()).apply()
|
||||
return tag
|
||||
} else {
|
||||
timeTagMap[time] = tag
|
||||
}
|
||||
}
|
||||
|
||||
val minTime = Collections.min(timeTagMap.keys)
|
||||
val tag = timeTagMap[minTime]
|
||||
edit.putLong(tag, System.currentTimeMillis()).apply()
|
||||
return if (TextUtils.isEmpty(tag)) notificationTags[0] else tag!!
|
||||
}
|
||||
}
|
||||
@ -1,275 +0,0 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.ScaleAnimation;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.util.DataCollectionUtils;
|
||||
import com.gh.common.util.DataUtils;
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.common.util.EntranceUtils;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.gh.gamecenter.ConcernActivity;
|
||||
import com.gh.gamecenter.DownloadManagerActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.SearchActivity;
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus;
|
||||
import com.gh.gamecenter.eventbus.EBReuse;
|
||||
import com.gh.gamecenter.manager.PackageManager;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
/**
|
||||
* Created by LGT on 2016/9/9.
|
||||
* 工具栏 搜索控制
|
||||
*/
|
||||
public class HomeFragment extends Fragment implements View.OnClickListener, SearchBarHint {
|
||||
|
||||
protected View view;
|
||||
|
||||
private TextView downloadHint;
|
||||
private TextView searchHint;
|
||||
|
||||
private AlphaAnimation mAlphaAnimation;
|
||||
|
||||
private ArrayList<String> hintList;
|
||||
private int hintIndex;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
hintList = savedInstanceState.getStringArrayList("hint");
|
||||
}
|
||||
|
||||
hintIndex = 0;
|
||||
|
||||
view = View.inflate(getActivity(), R.layout.fragment_home, null);
|
||||
|
||||
// SharedPreferences sp = getActivity().getSharedPreferences(
|
||||
// Config.PREFERENCE, Context.MODE_PRIVATE);
|
||||
LinearLayout home_actionbar = (LinearLayout) view.findViewById(R.id.home_actionbar);
|
||||
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT, DisplayUtils.dip2px(getActivity(), 55));
|
||||
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
// int top = DisplayUtils.getStatusBarHeight(getResources());
|
||||
// home_actionbar.setPadding(0, top, 0, 0);
|
||||
// lparams.height += top;
|
||||
// }
|
||||
home_actionbar.setLayoutParams(lparams);
|
||||
|
||||
initActionBar();
|
||||
|
||||
final ScaleAnimation scaleAnimation = new ScaleAnimation(0.4f, 1.0f, 0.4f, 1.0f
|
||||
, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
|
||||
scaleAnimation.setDuration(500);
|
||||
|
||||
mAlphaAnimation = new AlphaAnimation(1f, 0.2f);
|
||||
mAlphaAnimation.setDuration(300);
|
||||
mAlphaAnimation.setStartOffset(5000);
|
||||
|
||||
scaleAnimation.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
if (mAlphaAnimation != null) {
|
||||
searchHint.setAnimation(mAlphaAnimation);
|
||||
mAlphaAnimation.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
mAlphaAnimation.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
// 切换数据
|
||||
if (hintIndex > hintList.size() - 1) {
|
||||
hintIndex = 0;
|
||||
}
|
||||
searchHint.setHint(hintList.get(hintIndex));
|
||||
hintIndex++;
|
||||
|
||||
if (scaleAnimation != null) {
|
||||
searchHint.setAnimation(scaleAnimation);
|
||||
scaleAnimation.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
if (hintList != null && hintList.size() > 0) {
|
||||
if (hintList.size() > 1) {
|
||||
searchHint.setAnimation(mAlphaAnimation);
|
||||
} else {
|
||||
String hint = hintList.get(0);
|
||||
searchHint.setHint(hint);
|
||||
}
|
||||
}
|
||||
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
if (container != null) {
|
||||
container.removeView(view);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
if (hintList != null && !hintList.isEmpty()) {
|
||||
outState.putStringArrayList("hint", hintList);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
private void initActionBar() {
|
||||
view.findViewById(R.id.actionbar_rl_download).setOnClickListener(this);
|
||||
view.findViewById(R.id.actionbar_iv_search).setOnClickListener(this);
|
||||
view.findViewById(R.id.actionbar_notification).setOnClickListener(this);
|
||||
view.findViewById(R.id.actionbar_search_rl).setOnClickListener(this);
|
||||
|
||||
if (Config.isShow(getActivity())) {
|
||||
view.findViewById(R.id.actionbar_rl_download).setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
view.findViewById(R.id.actionbar_rl_download).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
downloadHint = (TextView) view.findViewById(R.id.action_tip);
|
||||
int updateSize = PackageManager.getUpdateListSize();
|
||||
int downloadSize = DownloadManager.getInstance(getActivity()).getAll().size();
|
||||
if (downloadSize != 0) {
|
||||
downloadHint.setVisibility(View.VISIBLE);
|
||||
downloadHint.setText(String.valueOf(downloadSize));
|
||||
} else if (updateSize != 0) {
|
||||
downloadHint.setVisibility(View.VISIBLE);
|
||||
downloadHint.setText(String.valueOf(updateSize));
|
||||
} else {
|
||||
downloadHint.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
searchHint = (TextView) view.findViewById(R.id.actionbar_search_input);
|
||||
searchHint.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final int id = v.getId();
|
||||
if (id == R.id.actionbar_rl_download) {
|
||||
DataUtils.onEvent(getActivity(), "主页", "下载图标");
|
||||
DataCollectionUtils.uploadClick(getActivity(), "下载图标", "主页");
|
||||
|
||||
DownloadManagerActivity.startDownloadManagerActivity(getContext(), null, "(工具栏)");
|
||||
} else if (id == R.id.actionbar_iv_search) {
|
||||
DataUtils.onEvent(getActivity(), "主页", "搜索图标");
|
||||
DataCollectionUtils.uploadClick(getActivity(), "搜索图标", "主页");
|
||||
|
||||
Intent intent = new Intent(getActivity(), SearchActivity.class);
|
||||
intent.putExtra("clicked", true);
|
||||
intent.putExtra("hint", searchHint.getHint().toString());
|
||||
intent.putExtra(EntranceUtils.KEY_ENTRANCE, "(工具栏)");
|
||||
startActivity(intent);
|
||||
} else if (id == R.id.actionbar_search_input || id == R.id.actionbar_search_rl) {
|
||||
DataUtils.onEvent(getActivity(), "主页", "搜索框");
|
||||
DataCollectionUtils.uploadClick(getActivity(), "搜索框", "主页");
|
||||
|
||||
Intent intent = new Intent(getActivity(), SearchActivity.class);
|
||||
intent.putExtra("clicked", false);
|
||||
intent.putExtra("hint", searchHint.getHint().toString());
|
||||
intent.putExtra(EntranceUtils.KEY_ENTRANCE, "(工具栏)");
|
||||
startActivity(intent);
|
||||
} else if (id == R.id.actionbar_notification) {
|
||||
DataUtils.onEvent(getActivity(), "主页", "关注图标");
|
||||
DataCollectionUtils.uploadClick(getActivity(), "关注图标", "主页");
|
||||
|
||||
Intent intent = new Intent(getActivity(), ConcernActivity.class);
|
||||
intent.putExtra(EntranceUtils.KEY_ENTRANCE, "(工具栏)");
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
// 打开下载按钮事件
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(EBReuse reuse) {
|
||||
if ("Refresh".equals(reuse.getType())) {
|
||||
if (Config.isShow(getActivity())) {
|
||||
view.findViewById(R.id.actionbar_rl_download).setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
view.findViewById(R.id.actionbar_rl_download).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(EBDownloadStatus status) {
|
||||
int updateSize = PackageManager.getUpdateListSize();
|
||||
int downloadSize = DownloadManager.getInstance(getActivity()).getAll().size();
|
||||
if (downloadSize != 0) {
|
||||
downloadHint.setVisibility(View.VISIBLE);
|
||||
downloadHint.setText(String.valueOf(downloadSize));
|
||||
} else if (updateSize != 0) {
|
||||
downloadHint.setVisibility(View.VISIBLE);
|
||||
downloadHint.setText(String.valueOf(updateSize));
|
||||
} else {
|
||||
downloadHint.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHint(ArrayList<String> hint) {
|
||||
if (hint != null && hint.size() > 0) {
|
||||
hintList = hint;
|
||||
if (hint.size() == 1 && searchHint != null) {
|
||||
searchHint.setHint(hintList.get(0));
|
||||
} else if (mAlphaAnimation != null && searchHint != null) {
|
||||
searchHint.setAnimation(mAlphaAnimation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
package com.gh.base;
|
||||
|
||||
/**
|
||||
* Forward activity onBackPressed() events to fragment
|
||||
* (If nested fragments need this, just forward again)
|
||||
*/
|
||||
public interface OnBackPressedListener {
|
||||
public boolean onHandleBackPressed();
|
||||
}
|
||||
19
app/src/main/java/com/gh/base/OnListClickListener.java
Normal file
19
app/src/main/java/com/gh/base/OnListClickListener.java
Normal file
@ -0,0 +1,19 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Created by khy on 26/09/17.
|
||||
*/
|
||||
|
||||
public interface OnListClickListener {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param view
|
||||
* @param position list position
|
||||
* @param data list data (直接强转 如果列表传入不同数据类型 请做好判断)
|
||||
* @param <T>
|
||||
*/
|
||||
<T> void onListClick(View view, int position, T data);
|
||||
}
|
||||
@ -1,13 +1,13 @@
|
||||
package com.gh.gamecenter.listener;
|
||||
package com.gh.base;
|
||||
|
||||
/**
|
||||
* Created by Administrator on 2016/9/8.
|
||||
*/
|
||||
public interface OnCallBackListener {
|
||||
public interface OnRequestCallBackListener<T> {
|
||||
|
||||
void loadDone();
|
||||
|
||||
void loadDone(Object obj);
|
||||
void loadDone(T obj);
|
||||
|
||||
void loadError();
|
||||
|
||||
7
app/src/main/java/com/gh/base/OnViewClickListener.java
Normal file
7
app/src/main/java/com/gh/base/OnViewClickListener.java
Normal file
@ -0,0 +1,7 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
public interface OnViewClickListener {
|
||||
void onClick(View v, Object data);
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
package com.gh.base;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public enum SuggestionType implements Serializable {
|
||||
|
||||
FEEDBACK("普通反馈", 1),
|
||||
SUGGESTION("功能建议", 2),
|
||||
CRASH("发生闪退", 3),
|
||||
GAME("游戏问题", 4),
|
||||
COLLECT("游戏收录", 5),
|
||||
POST("文章投稿", 6);
|
||||
|
||||
private String mName;
|
||||
private int mIndex;
|
||||
|
||||
private SuggestionType(String name, int index) {
|
||||
mName = name;
|
||||
mIndex = index;
|
||||
}
|
||||
|
||||
public static String getName(int index) {
|
||||
for (SuggestionType c : SuggestionType.values()) {
|
||||
if (c.mIndex == index) {
|
||||
return c.mName;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static int getIndex(String name) {
|
||||
for (SuggestionType c : SuggestionType.values()) {
|
||||
if (c.mName == name) {
|
||||
return c.mIndex;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return mIndex;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
|
||||
/**
|
||||
* Created by csheng on 15-10-12.
|
||||
*/
|
||||
public interface ToolbarController {
|
||||
|
||||
void setNavigationTitle(@StringRes int res);
|
||||
|
||||
void setNavigationTitle(CharSequence res);
|
||||
|
||||
Toolbar getToolBar();
|
||||
|
||||
}
|
||||
49
app/src/main/java/com/gh/base/adapter/FragmentAdapter.java
Normal file
49
app/src/main/java/com/gh/base/adapter/FragmentAdapter.java
Normal file
@ -0,0 +1,49 @@
|
||||
package com.gh.base.adapter;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentPagerAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by LGT on 2016/11/17.
|
||||
* ViewPager FragmentAdapter
|
||||
*/
|
||||
public class FragmentAdapter extends FragmentPagerAdapter {
|
||||
|
||||
private List<Fragment> mFragmentList;
|
||||
|
||||
private List<String> mTitleList;
|
||||
|
||||
public FragmentAdapter(FragmentManager fm, List<Fragment> fragmentList) {
|
||||
super(fm);
|
||||
this.mFragmentList = fragmentList;
|
||||
}
|
||||
|
||||
public FragmentAdapter(FragmentManager fm, List<Fragment> fragmentList, List<String> titleList) {
|
||||
super(fm);
|
||||
this.mFragmentList = fragmentList;
|
||||
this.mTitleList = titleList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
return mFragmentList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mFragmentList.size();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
if (mTitleList != null && mTitleList.size() > position) {
|
||||
return mTitleList.get(position);
|
||||
}
|
||||
return super.getPageTitle(position);
|
||||
}
|
||||
}
|
||||
@ -1,20 +1,19 @@
|
||||
package com.gh.gamecenter.adapter;
|
||||
package com.gh.base.adapter;
|
||||
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentPagerAdapter;
|
||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by LGT on 2016/11/17.
|
||||
* ViewPager FragmentAdapter
|
||||
* Created by khy on 7/12/28.
|
||||
*/
|
||||
public class FragmentAdapter extends FragmentPagerAdapter {
|
||||
public class FragmentStateAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
private List<Fragment> mFragmentList;
|
||||
|
||||
public FragmentAdapter(FragmentManager fm, List<Fragment> fragmentList) {
|
||||
public FragmentStateAdapter(FragmentManager fm, List<Fragment> fragmentList) {
|
||||
super(fm);
|
||||
this.mFragmentList = fragmentList;
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package com.gh.base.fragment;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.arch.lifecycle.Lifecycle;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import com.gh.common.util.ClickUtils;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.lightgame.utils.RuntimeUtils;
|
||||
import com.lightgame.utils.Utils;
|
||||
|
||||
/**
|
||||
* @author CsHeng
|
||||
* @Date 17/05/2017
|
||||
* @Time 4:30 PM
|
||||
*/
|
||||
|
||||
public class BaseDialogFragment extends DialogFragment {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final Dialog dialog = new Dialog(getActivity(), R.style.DialogWindowTransparent);
|
||||
dialog.setCanceledOnTouchOutside(false);
|
||||
dialog.setOnKeyListener((dialog1, keyCode, event) -> {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && ClickUtils.isFastDoubleClick()) { //会多次响应??
|
||||
return onBack();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
dialog.setCancelable(false);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public void toast(@StringRes int res) {
|
||||
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED))
|
||||
toast(getString(res));
|
||||
}
|
||||
|
||||
public void toast(String msg) {
|
||||
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED))
|
||||
Utils.toast(getContext(), msg);
|
||||
}
|
||||
|
||||
public void toastLong(@StringRes int msg) {
|
||||
toastLong(getString(msg));
|
||||
}
|
||||
|
||||
public void toastLong(String msg) {
|
||||
RuntimeUtils.getInstance().toastLong(getContext(), msg);
|
||||
}
|
||||
|
||||
public boolean onBack() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,65 @@
|
||||
package com.gh.base.fragment;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.gh.gamecenter.R;
|
||||
|
||||
/**
|
||||
* Wrap another fragment with dialog fragment.
|
||||
*/
|
||||
public class BaseDialogWrapperFragment extends BaseDialogFragment {
|
||||
|
||||
private Fragment mFragmentToWrap;
|
||||
|
||||
public static BaseDialogWrapperFragment getInstance(Fragment fragmentToWrap) {
|
||||
BaseDialogWrapperFragment fragment = new BaseDialogWrapperFragment();
|
||||
fragment.mFragmentToWrap = fragmentToWrap;
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public static BaseDialogWrapperFragment getInstance(Fragment fragmentToWrap, boolean isCancelable) {
|
||||
BaseDialogWrapperFragment fragment = new BaseDialogWrapperFragment();
|
||||
fragment.mFragmentToWrap = fragmentToWrap;
|
||||
fragment.setCancelable(isCancelable);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_dialog_wrapper, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (mFragmentToWrap != null) {
|
||||
getChildFragmentManager().beginTransaction().replace(R.id.fragment_placeholder, mFragmentToWrap).commitNowAllowingStateLoss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
getDialog().getWindow().setGravity(Gravity.BOTTOM);
|
||||
getDialog().getWindow().setLayout(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
Dialog dialog = super.onCreateDialog(savedInstanceState);
|
||||
dialog.setCanceledOnTouchOutside(true);
|
||||
return dialog;
|
||||
}
|
||||
}
|
||||
237
app/src/main/java/com/gh/base/fragment/BaseFragment.java
Normal file
237
app/src/main/java/com/gh/base/fragment/BaseFragment.java
Normal file
@ -0,0 +1,237 @@
|
||||
package com.gh.base.fragment;
|
||||
|
||||
import android.arch.lifecycle.Lifecycle;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.gh.base.OnListClickListener;
|
||||
import com.gh.base.OnRequestCallBackListener;
|
||||
import com.gh.gamecenter.eventbus.EBMiPush;
|
||||
import com.lightgame.OnTitleClickListener;
|
||||
import com.lightgame.utils.RuntimeUtils;
|
||||
import com.lightgame.utils.Utils;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
import static com.gh.common.util.EntranceUtils.KEY_ENTRANCE;
|
||||
|
||||
/**
|
||||
* Created by LGT on 2016/9/4.
|
||||
* Fragment 基类
|
||||
*/
|
||||
public abstract class BaseFragment<T> extends Fragment implements OnRequestCallBackListener<T>,
|
||||
View.OnClickListener, OnListClickListener, OnTitleClickListener {
|
||||
|
||||
protected View mCachedView;
|
||||
|
||||
protected boolean isEverPause;
|
||||
|
||||
protected String mEntrance;
|
||||
|
||||
protected final Handler mBaseHandler = new BaseFragment.BaseHandler(this);
|
||||
|
||||
protected static class BaseHandler extends Handler {
|
||||
private final WeakReference<BaseFragment> mFragmentWeakReference;
|
||||
|
||||
BaseHandler(BaseFragment fragment) {
|
||||
mFragmentWeakReference = new WeakReference<>(fragment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
super.handleMessage(msg);
|
||||
BaseFragment fragment = mFragmentWeakReference.get();
|
||||
if (fragment != null) fragment.handleMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleMessage(Message msg) {
|
||||
|
||||
}
|
||||
|
||||
@LayoutRes
|
||||
protected abstract int getLayoutId();
|
||||
|
||||
/**
|
||||
* 提供 Inflated 的 view ,可用于 data binding.
|
||||
*/
|
||||
protected View getInflatedLayout() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 责任链,谁处理了就返回true,否则返回super.handleOnClick(View view)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean handleOnClick(View view) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleOnClick(v);
|
||||
}
|
||||
|
||||
|
||||
protected void initView(View view) {
|
||||
}
|
||||
|
||||
protected void postRunnable(Runnable runnable) {
|
||||
RuntimeUtils.getInstance().runOnUiThread(runnable);
|
||||
}
|
||||
|
||||
// 定时任务全部改用这个方法, 在onDestroy做统一取消定时
|
||||
protected void postDelayedRunnable(Runnable runnable, long delayMillis) {
|
||||
RuntimeUtils.getInstance().runOnUiThread(runnable, delayMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
final Intent intent = getActivity().getIntent();
|
||||
mEntrance = intent.getStringExtra(KEY_ENTRANCE);
|
||||
if (TextUtils.isEmpty(mEntrance) && getArguments() != null) {
|
||||
mEntrance = getArguments().getString(KEY_ENTRANCE);
|
||||
}
|
||||
|
||||
isEverPause = false;
|
||||
EventBus.getDefault().register(this);
|
||||
|
||||
// For data binding.
|
||||
if (getInflatedLayout() != null) {
|
||||
mCachedView = getInflatedLayout();
|
||||
} else {
|
||||
mCachedView = View.inflate(getContext(), getLayoutId(), null);
|
||||
}
|
||||
ButterKnife.bind(this, mCachedView);
|
||||
|
||||
initView(mCachedView);
|
||||
}
|
||||
|
||||
//TODO 尴尬,必须的有subscribe才能register
|
||||
@Subscribe(threadMode = ThreadMode.BACKGROUND)
|
||||
public void onDummyEvent(EBMiPush push) {
|
||||
//
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
if (container != null) {
|
||||
container.removeView(mCachedView);
|
||||
}
|
||||
return mCachedView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
isEverPause = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
isEverPause = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
mBaseHandler.removeCallbacksAndMessages(null);
|
||||
RuntimeUtils.getInstance().removeRunnable();
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
public void toast(@StringRes int res) {
|
||||
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED))
|
||||
toast(getString(res));
|
||||
}
|
||||
|
||||
public void toast(String msg) {
|
||||
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED))
|
||||
Utils.toast(getContext(), msg);
|
||||
}
|
||||
|
||||
public void toastLong(@StringRes int msg) {
|
||||
toastLong(getString(msg));
|
||||
}
|
||||
|
||||
public void toastLong(String msg) {
|
||||
RuntimeUtils.getInstance().toastLong(getContext(), msg);
|
||||
}
|
||||
|
||||
public boolean isEverPause() {
|
||||
return isEverPause;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadDone() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadDone(T obj) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadError() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadEmpty() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <LIST> void onListClick(View view, int position, LIST data) {
|
||||
|
||||
}
|
||||
|
||||
protected <K> Observable<K> asyncCall(Observable<K> observable) {
|
||||
return observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
// 将所有的Fragment都置为隐藏状态。
|
||||
protected void hideFragments(FragmentTransaction transaction) {
|
||||
List<Fragment> list = getChildFragmentManager().getFragments();
|
||||
for (Fragment fragment : list) {
|
||||
transaction.hide(fragment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTitleClick() {
|
||||
List<Fragment> list = getChildFragmentManager().getFragments();
|
||||
for (Fragment fragment : list) {
|
||||
if (fragment instanceof OnTitleClickListener) {
|
||||
((OnTitleClickListener) fragment).onTitleClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,118 @@
|
||||
package com.gh.base.fragment;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.TabLayout;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.view.View;
|
||||
|
||||
import com.gh.base.adapter.FragmentAdapter;
|
||||
import com.gh.common.view.TabIndicatorView;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.normal.NormalFragment;
|
||||
import com.lightgame.view.NoScrollableViewPager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
|
||||
/**
|
||||
* Created by khy on 15/03/18.
|
||||
*/
|
||||
|
||||
public abstract class BaseFragment_TabLayout extends NormalFragment implements ViewPager.OnPageChangeListener {
|
||||
|
||||
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;
|
||||
|
||||
protected List<Fragment> mFragmentsList;
|
||||
|
||||
protected List<String> mTabTitleList;
|
||||
|
||||
protected int mCheckedIndex = 0;
|
||||
|
||||
protected abstract void initFragmentList(List<Fragment> fragments);
|
||||
|
||||
protected abstract void initTabTitleList(List<String> tabTitleList);
|
||||
|
||||
protected int provideIndicatorWidth() {
|
||||
return 65;
|
||||
}
|
||||
|
||||
protected View provideTabView(int position, String tabTitle) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_taglyout_viewpager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
List<Fragment> fragments = getChildFragmentManager().getFragments();
|
||||
if (fragments != null) {
|
||||
for (Fragment fragment : fragments) {
|
||||
fragment.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (getArguments() != null) mCheckedIndex = getArguments().getInt(PAGE_INDEX, 0);
|
||||
mFragmentsList = new ArrayList<>();
|
||||
initFragmentList(mFragmentsList);
|
||||
mTabTitleList = new ArrayList<>();
|
||||
initTabTitleList(mTabTitleList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mViewPager.setOffscreenPageLimit(mFragmentsList.size());
|
||||
mViewPager.addOnPageChangeListener(this);
|
||||
mViewPager.setAdapter(new FragmentAdapter(getChildFragmentManager(), mFragmentsList, mTabTitleList));
|
||||
mViewPager.setCurrentItem(mCheckedIndex);
|
||||
mTabLayout.setupWithViewPager(mViewPager);
|
||||
mTabIndicatorView.setupWithTabLayout(mTabLayout);
|
||||
mTabIndicatorView.setupWithViewPager(mViewPager);
|
||||
mTabIndicatorView.setIndicatorWidth(provideIndicatorWidth());
|
||||
|
||||
for (int i = 0; i < mTabLayout.getTabCount(); i++) {
|
||||
TabLayout.Tab tab = mTabLayout.getTabAt(i);
|
||||
if (tab == null) continue;
|
||||
View tabView = provideTabView(i, tab.getText() != null ? tab.getText().toString() : "");
|
||||
if (tabView == null) continue;
|
||||
tab.setCustomView(tabView);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
|
||||
}
|
||||
}
|
||||
126
app/src/main/java/com/gh/base/fragment/BaseFragment_ViewPager.java
Executable file
126
app/src/main/java/com/gh/base/fragment/BaseFragment_ViewPager.java
Executable file
@ -0,0 +1,126 @@
|
||||
/**
|
||||
* project: OPlay
|
||||
* <p/>
|
||||
* <p/>
|
||||
* ========================================================================
|
||||
* amend date amend user amend reason
|
||||
* 2013-3-6 CsHeng
|
||||
*/
|
||||
|
||||
package com.gh.base.fragment;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.view.PagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.view.View;
|
||||
|
||||
import com.gh.gamecenter.normal.NormalFragment;
|
||||
import com.lightgame.adapter.BaseFragmentPagerAdapter;
|
||||
import com.lightgame.config.CommonDebug;
|
||||
import com.lightgame.view.DoubleTapTextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ViewPager 配合RadioGroup实现双切换<br/>
|
||||
* 记得自己控制onCreateView返回和radioGroup里面radiobutton个数,Viewpager的布局<br/>
|
||||
*
|
||||
* @author CsHeng
|
||||
* @date 2013-3-6
|
||||
*/
|
||||
public abstract class BaseFragment_ViewPager extends NormalFragment implements DoubleTapTextView.OnDoubleTapListener {
|
||||
|
||||
public static final String ARGS_INDEX = "index";
|
||||
protected int mCheckedIndex = 0;
|
||||
protected PagerAdapter mAdapter;
|
||||
protected List<Fragment> mFragmentsList;
|
||||
protected ViewPager mViewPager;
|
||||
|
||||
@LayoutRes
|
||||
protected abstract int getLayoutId();
|
||||
|
||||
@IdRes
|
||||
protected abstract int getViewPagerId();
|
||||
|
||||
protected abstract void initFragmentList(List<Fragment> fragments);
|
||||
|
||||
@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.setOffscreenPageLimit(mFragmentsList.size());
|
||||
mViewPager.setAdapter(mAdapter);
|
||||
if (mCheckedIndex < mFragmentsList.size()) {
|
||||
mViewPager.setCurrentItem(mCheckedIndex, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
if (getArguments() != null) {
|
||||
getArguments().putInt(ARGS_INDEX, mCheckedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
if (mViewPager != null) {
|
||||
mViewPager.setAdapter(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mFragmentsList != null) {
|
||||
mFragmentsList.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTap() {
|
||||
final Fragment fragment = mFragmentsList.get(mViewPager.getCurrentItem());
|
||||
return fragment instanceof DoubleTapTextView.OnDoubleTapListener && ((DoubleTapTextView.OnDoubleTapListener)
|
||||
fragment).onDoubleTap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (CommonDebug.IS_DEBUG) {
|
||||
CommonDebug.logMethodWithParams(this, requestCode, resultCode, data);
|
||||
}
|
||||
List<Fragment> fragments = getChildFragmentManager().getFragments();
|
||||
if (fragments != null) {
|
||||
for (Fragment fragment : fragments) {
|
||||
fragment.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int getCurrentItem() {
|
||||
return mViewPager != null ? mViewPager.getCurrentItem() : 0;
|
||||
}
|
||||
|
||||
}
|
||||
109
app/src/main/java/com/gh/base/fragment/BaseFragment_ViewPager_Checkable.java
Executable file
109
app/src/main/java/com/gh/base/fragment/BaseFragment_ViewPager_Checkable.java
Executable file
@ -0,0 +1,109 @@
|
||||
/**
|
||||
* project: OPlay
|
||||
* <p/>
|
||||
* <p/>
|
||||
* ========================================================================
|
||||
* amend date amend user amend reason
|
||||
* 2013-3-6 CsHeng
|
||||
*/
|
||||
|
||||
package com.gh.base.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Checkable;
|
||||
|
||||
|
||||
/**
|
||||
* ViewPager 配合ViewGroup Checkable实现双切换<br/>
|
||||
* 记得自己控制onCreateView返回和ViewGroup里面Checkable个数,ViewPager的布局<br/>
|
||||
*
|
||||
* @author CsHeng
|
||||
* @date 2013-3-6
|
||||
* @update 2014-09-29
|
||||
*/
|
||||
public abstract class BaseFragment_ViewPager_Checkable extends BaseFragment_ViewPager implements
|
||||
ViewPager.OnPageChangeListener {
|
||||
|
||||
protected ViewGroup mCheckableGroup;
|
||||
|
||||
@IdRes
|
||||
protected abstract int getCheckableGroupId();
|
||||
|
||||
protected boolean getSmoothScroll() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mCheckableGroup = (ViewGroup) view.findViewById(getCheckableGroupId());
|
||||
for (int i = 0, size = mCheckableGroup.getChildCount(); i < size; i++) {
|
||||
mCheckableGroup.getChildAt(i).setOnClickListener(this);
|
||||
}
|
||||
mViewPager.addOnPageChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
mViewPager.removeOnPageChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
checkIndex(mCheckedIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int index) {
|
||||
onPageChanged(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean handleOnClick(View view) {
|
||||
final int toCheck = mCheckableGroup.indexOfChild(view);
|
||||
if (toCheck != -1) {
|
||||
mViewPager.setCurrentItem(toCheck, getSmoothScroll());
|
||||
return true;
|
||||
}
|
||||
return super.handleOnClick(view);
|
||||
}
|
||||
|
||||
protected void checkIndex(int index) {
|
||||
final int childCount = mCheckableGroup.getChildCount();
|
||||
if (index < childCount && mCheckedIndex < childCount) {
|
||||
final View toChecked = mCheckableGroup.getChildAt(index);
|
||||
if (toChecked instanceof Checkable) {
|
||||
((Checkable) toChecked).setChecked(true);
|
||||
}
|
||||
if (index != mCheckedIndex) {
|
||||
final View checkedChild = mCheckableGroup.getChildAt(mCheckedIndex);
|
||||
if (checkedChild instanceof Checkable) {
|
||||
((Checkable) checkedChild).setChecked(false);
|
||||
}
|
||||
}
|
||||
mCheckedIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
protected void onPageChanged(int index) {
|
||||
checkIndex(index);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
package com.gh.base.fragment;
|
||||
|
||||
public interface OnDialogBackListener {
|
||||
void onBack();
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
package com.gh.base.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.gh.gamecenter.R;
|
||||
|
||||
/**
|
||||
* @author CsHeng
|
||||
* @Date 17/05/2017
|
||||
* @Time 4:27 PM
|
||||
*/
|
||||
public class WaitingDialogFragment extends BaseDialogFragment {
|
||||
|
||||
public static final String KEY_MSG = "msg";
|
||||
|
||||
private OnDialogBackListener mBackListener;
|
||||
|
||||
private TextView message;
|
||||
|
||||
public static WaitingDialogFragment newInstance(String message) {
|
||||
Bundle args = new Bundle();
|
||||
args.putString(KEY_MSG, message);
|
||||
WaitingDialogFragment fragment = new WaitingDialogFragment();
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public static WaitingDialogFragment newInstance(String message, boolean isCancelable) {
|
||||
Bundle args = new Bundle();
|
||||
args.putString(KEY_MSG, message);
|
||||
WaitingDialogFragment fragment = new WaitingDialogFragment();
|
||||
fragment.setArguments(args);
|
||||
fragment.setCancelable(isCancelable);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
final View view = inflater.inflate(R.layout.set_wait_dialog, null);
|
||||
message = (TextView) view.findViewById(R.id.set_wait_message);
|
||||
message.setText(getArguments().getString(KEY_MSG));
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(FragmentManager manager, String tag) {
|
||||
try {
|
||||
super.show(manager, tag);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void show(FragmentManager manager, String tag, OnDialogBackListener backListener) {
|
||||
show(manager, tag);
|
||||
this.mBackListener = backListener;
|
||||
}
|
||||
|
||||
public void uploadWaitingHint(String hint) {
|
||||
if (message != null) message.setText(hint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBack() {
|
||||
if (mBackListener != null) {
|
||||
mBackListener.onBack();
|
||||
return true;
|
||||
}
|
||||
return super.onBack();
|
||||
}
|
||||
|
||||
public static class WaitingDialogData {
|
||||
private String msg;
|
||||
|
||||
private boolean isShow;
|
||||
|
||||
public WaitingDialogData(String msg, boolean isShow) {
|
||||
this.msg = msg;
|
||||
this.isShow = isShow;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public boolean isShow() {
|
||||
return isShow;
|
||||
}
|
||||
|
||||
public void setShow(boolean show) {
|
||||
isShow = show;
|
||||
}
|
||||
}
|
||||
}
|
||||
33
app/src/main/java/com/gh/base/onDoubleTapListener.java
Normal file
33
app/src/main/java/com/gh/base/onDoubleTapListener.java
Normal file
@ -0,0 +1,33 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Created by khy on 25/04/18.
|
||||
*/
|
||||
|
||||
public abstract class onDoubleTapListener implements View.OnTouchListener {
|
||||
private GestureDetector mGestureDetector;
|
||||
|
||||
public onDoubleTapListener(Context context) {
|
||||
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent e) {
|
||||
onDoubleTapListener.this.onDoubleTap();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
mGestureDetector.onTouchEvent(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
public abstract void onDoubleTap();
|
||||
}
|
||||
21
app/src/main/java/com/gh/common/AppExecutor.kt
Normal file
21
app/src/main/java/com/gh/common/AppExecutor.kt
Normal file
@ -0,0 +1,21 @@
|
||||
package com.gh.common
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import java.util.concurrent.Executor
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
object AppExecutor {
|
||||
@JvmStatic
|
||||
var ioExecutor = Executors.newSingleThreadExecutor()
|
||||
@JvmStatic
|
||||
var uiExecutor = MainThreadExecutor()
|
||||
|
||||
class MainThreadExecutor : Executor {
|
||||
private val mainThreadHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
override fun execute(command: Runnable) {
|
||||
mainThreadHandler.post(command)
|
||||
}
|
||||
}
|
||||
}
|
||||
39
app/src/main/java/com/gh/common/LocalBroadcastReceiver.kt
Normal file
39
app/src/main/java/com/gh/common/LocalBroadcastReceiver.kt
Normal file
@ -0,0 +1,39 @@
|
||||
package com.gh.common
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.gh.common.im.ImManager
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.m7.imkfsdk.chat.ChatActivity
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
||||
/**
|
||||
* 可使用 [LocalBroadcastManager] 来进行简单的模块间消息通知
|
||||
*/
|
||||
|
||||
class LocalBroadcastReceiver : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
intent?.let {
|
||||
when (intent.action) {
|
||||
ChatActivity.ACTION_DISMISS_FLOATING_WINDOW -> {
|
||||
ImManager.dismissFloatingWindow()
|
||||
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application).api.postImEnding(UserManager.getInstance().userId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe()
|
||||
}
|
||||
|
||||
ChatActivity.ACTION_HIDE_UNREAD_DOT -> {
|
||||
ImManager.updateShouldShowFloatingWindowDot(false)
|
||||
}
|
||||
|
||||
else -> return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
137
app/src/main/java/com/gh/common/PushManager.kt
Normal file
137
app/src/main/java/com/gh/common/PushManager.kt
Normal file
@ -0,0 +1,137 @@
|
||||
package com.gh.common
|
||||
|
||||
import android.preference.PreferenceManager
|
||||
import com.gh.base.GHUmengNotificationService
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.common.exposure.meta.MetaUtil
|
||||
import com.gh.common.util.edit
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.entity.AliasEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.google.gson.Gson
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import com.umeng.commonsdk.UMConfigure
|
||||
import com.umeng.message.IUmengRegisterCallback
|
||||
import com.umeng.message.PushAgent
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.RequestBody
|
||||
import org.android.agoo.huawei.HuaWeiRegister
|
||||
import org.android.agoo.mezu.MeizuRegister
|
||||
import org.android.agoo.xiaomi.MiPushRegistar
|
||||
import org.json.JSONObject
|
||||
|
||||
object PushManager {
|
||||
|
||||
var gson = Gson()
|
||||
var deviceToken: String? = ""
|
||||
var previousAlias: AliasEntity? = null
|
||||
var application = HaloApp.getInstance().application
|
||||
|
||||
const val SP_PUSH_ALIAS = "push_alias"
|
||||
|
||||
@JvmStatic
|
||||
fun init(channel: String) {
|
||||
//初始化友盟推送
|
||||
UMConfigure.init(application,
|
||||
Config.UMENG_APPKEY, channel,
|
||||
UMConfigure.DEVICE_TYPE_PHONE,
|
||||
Config.UMENG_MESSAGE_SECRET)
|
||||
|
||||
// 注册小米、华为和魅族通道
|
||||
MiPushRegistar.register(application, Config.MIPUSH_APPID, Config.MIPUSH_APPKEY)
|
||||
HuaWeiRegister.register(application)
|
||||
MeizuRegister.register(application, BuildConfig.MEIZUPUSH_APPID, BuildConfig.MEIZUPUSH_APPKEY)
|
||||
|
||||
//友盟推送
|
||||
val pushAgent = PushAgent.getInstance(application)
|
||||
pushAgent.onAppStart() // 开启App统计
|
||||
|
||||
//注册推送服务,每次调用register方法都会回调该接口
|
||||
registerDevice()
|
||||
|
||||
val aliasInSp = PreferenceManager.getDefaultSharedPreferences(application).getString(SP_PUSH_ALIAS, "")
|
||||
previousAlias = gson.fromJson(aliasInSp, AliasEntity::class.java)
|
||||
|
||||
if (previousAlias == null) {
|
||||
getAndSetAlias()
|
||||
}
|
||||
|
||||
// 完全自定义处理(透传)
|
||||
pushAgent.setPushIntentServiceClass(GHUmengNotificationService::class.java)
|
||||
}
|
||||
|
||||
private fun registerDevice() {
|
||||
PushAgent.getInstance(application).register(object : IUmengRegisterCallback {
|
||||
override fun onSuccess(dToken: String) {
|
||||
//注册成功会返回device token
|
||||
deviceToken = dToken
|
||||
getAndSetAlias()
|
||||
Utils.log("deviceToken::$dToken")
|
||||
}
|
||||
|
||||
override fun onFailure(s: String, s1: String) {
|
||||
Utils.log("deviceToken::" + "注册失败")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getAndSetAlias() {
|
||||
if (deviceToken.isNullOrEmpty()) {
|
||||
registerDevice()
|
||||
return
|
||||
}
|
||||
|
||||
val meta = MetaUtil.getMeta()
|
||||
|
||||
val jsonObject = JSONObject()
|
||||
jsonObject.put("device_token", deviceToken)
|
||||
jsonObject.put("imei", meta.imei)
|
||||
jsonObject.put("android_id", meta.android_id)
|
||||
jsonObject.put("model", meta.model)
|
||||
jsonObject.put("manufacturer", meta.manufacturer)
|
||||
jsonObject.put("os", meta.os)
|
||||
jsonObject.put("os_version", meta.android_version)
|
||||
jsonObject.put("mac", meta.mac)
|
||||
|
||||
val body = RequestBody.create(MediaType.parse("application/json"), jsonObject.toString())
|
||||
|
||||
RetrofitManager.getInstance(application).api.getAlias(body)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(
|
||||
{ setAlias(it) },
|
||||
{ it.printStackTrace() }
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun setAlias(alias: AliasEntity) {
|
||||
val pushAgent = PushAgent.getInstance(application)
|
||||
|
||||
previousAlias = alias
|
||||
PreferenceManager.getDefaultSharedPreferences(application).edit {
|
||||
putString(SP_PUSH_ALIAS, gson.toJson(previousAlias))
|
||||
}
|
||||
|
||||
pushAgent.setAlias(alias.alias, alias.aliasType) { b, s ->
|
||||
Utils.log("注册别名 $b + $s")
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deleteAlias() {
|
||||
val pushAgent = PushAgent.getInstance(application)
|
||||
|
||||
previousAlias?.let {
|
||||
pushAgent.deleteAlias(it.alias, it.aliasType) { b, s ->
|
||||
Utils.log("删除别名 $b + $s")
|
||||
}
|
||||
}
|
||||
PreferenceManager.getDefaultSharedPreferences(application).edit {
|
||||
putString(SP_PUSH_ALIAS, "")
|
||||
}
|
||||
previousAlias = null
|
||||
}
|
||||
}
|
||||
61
app/src/main/java/com/gh/common/TimeElapsedHelper.kt
Normal file
61
app/src/main/java/com/gh/common/TimeElapsedHelper.kt
Normal file
@ -0,0 +1,61 @@
|
||||
package com.gh.common
|
||||
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v4.app.FragmentManager
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
/**
|
||||
* 统计用户在当前 Fragment 的停留时间,在 onViewDestroy 或 onDestroy 里获取 elapsedTime 即可,单位为秒
|
||||
*/
|
||||
class TimeElapsedHelper(var fragment: Fragment) {
|
||||
|
||||
private var isWorking = false
|
||||
|
||||
var elapsedTime: Int = 0
|
||||
|
||||
init {
|
||||
fragment.fragmentManager?.registerFragmentLifecycleCallbacks(
|
||||
object : FragmentManager.FragmentLifecycleCallbacks() {
|
||||
override fun onFragmentResumed(fm: FragmentManager?, f: Fragment?) {
|
||||
if (f === fragment) {
|
||||
resumeCounting()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFragmentPaused(fm: FragmentManager?, f: Fragment?) {
|
||||
if (f === fragment) {
|
||||
pauseCounting()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFragmentViewDestroyed(fm: FragmentManager?, f: Fragment?) {
|
||||
if (f === fragment) {
|
||||
fragment.fragmentManager?.unregisterFragmentLifecycleCallbacks(this)
|
||||
}
|
||||
}
|
||||
}, false)
|
||||
}
|
||||
|
||||
private fun resumeCounting() {
|
||||
isWorking = true
|
||||
TimeElapsedThreadHolder.threadService.execute {
|
||||
while (isWorking) {
|
||||
try {
|
||||
elapsedTime++
|
||||
Thread.sleep(1000)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun pauseCounting() {
|
||||
isWorking = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object TimeElapsedThreadHolder {
|
||||
val threadService = Executors.newSingleThreadExecutor()
|
||||
}
|
||||
@ -3,39 +3,195 @@ package com.gh.common.constant;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.gh.common.util.GsonUtils;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
import com.gh.gamecenter.entity.NewsEntity;
|
||||
import com.gh.gamecenter.entity.SettingsEntity;
|
||||
import com.halo.assistant.HaloApp;
|
||||
import com.lightgame.utils.Utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Config {
|
||||
|
||||
public static final String HOST = "http://api2.ghzhushou.com/v2d6/";
|
||||
public static final String USER_HOST = "http://user2.ghzhushou.com/v1d2/";
|
||||
public static final String COMMENT_HOST = "http://comment2.ghzhushou.com/v1d2/";
|
||||
public static final String DATA_HOST = "http://data.ghzhushou.com/";
|
||||
public static final String LIBAO_HOST = "http://libao2.ghzhushou.com/v1d2/";
|
||||
public static final String MESSAGE_HOST = "http://message.ghzhushou.com/v1d0/"; // TODO TEST v1d1
|
||||
public static final String PREFERENCE = "ghzhushou";
|
||||
public static final String API_HOST = BuildConfig.API_HOST;
|
||||
public static final String USER_HOST = BuildConfig.USER_HOST;
|
||||
public static final String COMMENT_HOST = BuildConfig.COMMENT_HOST;
|
||||
public static final String DATA_HOST = BuildConfig.DATA_HOST;
|
||||
public static final String LIBAO_HOST = BuildConfig.LIBAO_HOST;
|
||||
public static final String MESSAGE_HOST = BuildConfig.MESSAGE_HOST;
|
||||
|
||||
/**
|
||||
* 需要配置的请使用{@link PreferenceManager#getDefaultSharedPreferences(Context)}
|
||||
*/
|
||||
// @Deprecated
|
||||
// public static final String PREFERENCE = "ghzhushou";
|
||||
|
||||
// Third-Party confs
|
||||
public static final String WECHAT_APPID = BuildConfig.WECHAT_APPID;
|
||||
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 TD_APPID = BuildConfig.TD_APPID;// TalkingData
|
||||
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 PATCH_VERSION_NAME = BuildConfig.PATCH_VERSION_NAME; // 补丁包版本 对应关于->版本号
|
||||
// http://www.ghzs666.com/article/${articleId}.html
|
||||
public static final String URL_ARTICLE = "http://www.ghzs666.com/article/"; // TODO ghzs/ghzs666 统一
|
||||
public static final String PATCHES = "patches";
|
||||
|
||||
public static final String DEBUG_MIPUSH_APPID = BuildConfig.DEBUG_MIPUSH_APPID;
|
||||
public static final String DEBUG_MIPUSH_APPKEY = BuildConfig.DEBUG_MIPUSH_APPKEY;
|
||||
public static final String DEBUG_UMENG_APPKEY = BuildConfig.DEBUG_UMENG_APPKEY;
|
||||
public static final String DEBUG_UMENG_MESSAGE_SECRET = BuildConfig.DEBUG_UMENG_MESSAGE_SECRET;
|
||||
private static String SETTINGS_KEY = "settingsKey";
|
||||
|
||||
private static SettingsEntity mSettingsEntity;
|
||||
|
||||
public static boolean isShow() {
|
||||
if (PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().getApplication())
|
||||
.getBoolean("isFixDownload", false)) return true;
|
||||
|
||||
if (!isExistDownloadFilter()) return false;
|
||||
|
||||
for (SettingsEntity.Download entity : getSettings().getDownload()) {
|
||||
if ("all".equals(entity.getGame())) {
|
||||
if (entity.isPluginfy() && "normal".equals(entity.getPolicy()) && filterTime(entity.getTime())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String getExceptionMsg(Context context) {
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
return sp.getString("errMsg", null);
|
||||
}
|
||||
|
||||
public static void setExceptionMsg(Context context, String errMsg) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit().putString("errMsg", errMsg).apply();
|
||||
}
|
||||
|
||||
public static boolean isShowDownload(String gameId) {
|
||||
|
||||
if (PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().getApplication())
|
||||
.getBoolean("isFixDownload", false)) return true;
|
||||
|
||||
if (TextUtils.isEmpty(gameId) || !isExistDownloadFilter())
|
||||
return false;
|
||||
|
||||
for (SettingsEntity.Download entity : getSettings().getDownload()) {
|
||||
if (gameId.equals(entity.getGame())) {
|
||||
if ("normal".equals(entity.getPolicy()) && filterTime(entity.getTime())) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if ("all".equals(entity.getGame())) {
|
||||
if ("normal".equals(entity.getPolicy()) && filterTime(entity.getTime())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isShow(Context context) {
|
||||
SharedPreferences sp = context.getSharedPreferences(Config.PREFERENCE, Context.MODE_PRIVATE);
|
||||
return sp.getBoolean("isShow", true);
|
||||
public static boolean isShowPlugin(String gameId) {
|
||||
if (TextUtils.isEmpty(gameId) || !isExistDownloadFilter())
|
||||
return false;
|
||||
|
||||
for (SettingsEntity.Download entity : getSettings().getDownload()) {
|
||||
if (gameId.equals(entity.getGame())) {
|
||||
if (entity.isPluginfy() && filterTime(entity.getTime())) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if ("all".equals(entity.getGame())) {
|
||||
if (entity.isPluginfy() && filterTime(entity.getTime())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isShowPlugin() {
|
||||
if (!isExistDownloadFilter())
|
||||
return false;
|
||||
|
||||
for (SettingsEntity.Download entity : getSettings().getDownload()) {
|
||||
if ("all".equals(entity.getGame())) {
|
||||
if (entity.isPluginfy() && filterTime(entity.getTime())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean filterTime(SettingsEntity.Download.TimeEntity timeEntity) {
|
||||
long end = timeEntity.getEnd();
|
||||
long start = timeEntity.getStart();
|
||||
long curTime = Utils.getTime(HaloApp.getInstance().getApplication());
|
||||
|
||||
if ((start == 0 || curTime >= start) && (end == 0 || curTime <= end)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void setSettings(SettingsEntity settingsEntity) {
|
||||
SharedPreferences.Editor edit = PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().getApplication()).edit();
|
||||
edit.putString(SETTINGS_KEY, GsonUtils.getInstance().toJson(settingsEntity)).apply();
|
||||
|
||||
mSettingsEntity = settingsEntity;
|
||||
}
|
||||
|
||||
public static SettingsEntity getSettings() {
|
||||
if (mSettingsEntity == null) {
|
||||
try {
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().getApplication());
|
||||
String json = sp.getString(SETTINGS_KEY, null);
|
||||
if (!TextUtils.isEmpty(json)) {
|
||||
mSettingsEntity = GsonUtils.getInstance().fromJsonBean(json, SettingsEntity.class);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return mSettingsEntity;
|
||||
}
|
||||
|
||||
private static boolean isExistDownloadFilter() {
|
||||
if (getSettings() == null || getSettings().getDownload() == null || getSettings().getDownload().size() == 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void filterPluginArticle(List<NewsEntity> list) {
|
||||
if (isShowPlugin() || list == null) return;
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
NewsEntity newsEntity = list.get(i);
|
||||
String title = newsEntity.getTitle();
|
||||
if (!TextUtils.isEmpty(title) && title.contains("插件")) {
|
||||
list.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,18 +2,22 @@ package com.gh.common.constant;
|
||||
|
||||
public class Constants {
|
||||
|
||||
public static final int CONTINUE_DOWNLOAD_TASK = 0x123;
|
||||
public static final int PAUSE_DOWNLOAD_TASK = 0x124;
|
||||
public static final int DOWNLOAD_ROLL = 0x125;
|
||||
public static final int SEND_NEWS_FEEDBACK = 0x126;
|
||||
public static final int SEND_COMMENT_FEEDBACK = 0x127;
|
||||
|
||||
public static final String KEY_DOWNLOAD_ENTRY = "key_download_entry";
|
||||
public static final String KEY_DOWNLOAD_ACTION = "key_download_action";
|
||||
public final static int LIST_FOOTER_ITEM = 1;
|
||||
public final static int LIST_HEAD_ITEM = 1;
|
||||
|
||||
public static final int MAX_DOWNLOAD_THREAD_SIZE = 3;
|
||||
public static final int MAX_DOWNLOADING_SIZE = 3;
|
||||
public static final long SPEED_CHECK_INTERVAL = 1000;//速度监测频率
|
||||
public final static int NOT_NETWORK_CODE = 504; // 没有网络的状态码(应该是这个吧!)
|
||||
|
||||
public static final String LOGIN_TOKEN_ID = "userToken_id"; // 用户ID 与服务器无关
|
||||
|
||||
public static final String USER_TOKEN_KEY = "userTokenKey";
|
||||
public static final String USER_INFO_KEY = "userInfoKey";
|
||||
|
||||
// 最近显示的弹窗信息
|
||||
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 REGEX_MOBILE = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$";
|
||||
|
||||
@ -8,6 +8,7 @@ public class ItemViewType {
|
||||
public static final int COLUMN_HEADER = 0; // 专题头部布局
|
||||
public static final int GAME_SLIDE = 1; // 滚动图布局
|
||||
public static final int GAME_NORMAL = 2; // 正常游戏布局
|
||||
public static final int GAME_SUBJECT = 19;
|
||||
public static final int GAME_TEST = 3; // 测试游戏布局
|
||||
public static final int GAME_IMAGE = 4; // 游戏大图布局
|
||||
public static final int NEWS_HEADER = 5; // 新闻头部布局
|
||||
@ -21,6 +22,17 @@ public class ItemViewType {
|
||||
public static final int LOADING = 14; // 加载布局
|
||||
public static final int LIBAO_NORMAL = 15; // 礼包正常布局
|
||||
public static final int LIBAO_SKIP_CONCERN = 16; // 跳转关注管理页面布局
|
||||
public static final int KC_HINT = 16;
|
||||
public static final int KC_HINT = 17;
|
||||
public static final int GAME_PLUGIN = 18; // 游戏插件模块
|
||||
public static final int ASK_REFRESH = 19; // 问答精选 刷新
|
||||
public static final int ITEM_EMPTY = 20;
|
||||
|
||||
/**
|
||||
* 普通列表
|
||||
*/
|
||||
public static final int ITEM_BODY = 100;
|
||||
public static final int ITEM_FOOTER = 101;
|
||||
public static final int ITEM_TOP = 102;
|
||||
|
||||
|
||||
}
|
||||
|
||||
488
app/src/main/java/com/gh/common/databind/BindingAdapters.java
Normal file
488
app/src/main/java/com/gh/common/databind/BindingAdapters.java
Normal file
@ -0,0 +1,488 @@
|
||||
package com.gh.common.databind;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.databinding.BindingAdapter;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.text.TextUtils;
|
||||
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 com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.gh.base.OnViewClickListener;
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.exposure.ExposureEvent;
|
||||
import com.gh.common.exposure.ExposureUtils;
|
||||
import com.gh.common.util.DataUtils;
|
||||
import com.gh.common.util.DialogUtils;
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.common.util.GameUtils;
|
||||
import com.gh.common.util.GameViewUtils;
|
||||
import com.gh.common.util.ImageUtils;
|
||||
import com.gh.common.util.KaiFuUtils;
|
||||
import com.gh.common.util.NetworkUtils;
|
||||
import com.gh.common.util.NewsUtils;
|
||||
import com.gh.common.util.NumberUtils;
|
||||
import com.gh.common.util.PackageUtils;
|
||||
import com.gh.common.util.StringUtils;
|
||||
import com.gh.common.view.DownloadDialog;
|
||||
import com.gh.common.view.DownloadProgressBar;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.gh.gamecenter.DownloadManagerActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
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;
|
||||
import com.gh.gamecenter.entity.KaiFuCalendarEntity;
|
||||
import com.gh.gamecenter.eventbus.EBReuse;
|
||||
import com.gh.gamecenter.manager.PackageManager;
|
||||
import com.lightgame.download.DownloadEntity;
|
||||
import com.lightgame.download.FileUtils;
|
||||
import com.lightgame.utils.Utils;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Created by khy on 12/02/18.
|
||||
*/
|
||||
|
||||
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({"addDetailKaiFuView", "addDetailKaiFuViewListener", "isReadyPatch"})
|
||||
public static void addDetailKaiFuView(LinearLayout view, List<KaiFuCalendarEntity> list
|
||||
, OnViewClickListener listener, Boolean isReadyPatch) {
|
||||
if (list == null) return;
|
||||
view.removeAllViews();
|
||||
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 {
|
||||
KaiFuCalendarEntity serverEntity = list.get(i - 1);
|
||||
binding.setEntity(serverEntity);
|
||||
binding.getRoot().setOnClickListener(v -> {
|
||||
listener.onClick(v, isReadyPatch != null && isReadyPatch ? serverEntity : null);
|
||||
});
|
||||
|
||||
// 滑动冲突处理
|
||||
binding.getRoot().setOnTouchListener((v, event) -> {
|
||||
if (list.size() > 5) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
EventBus.getDefault().post(new EBReuse("CalenderDown"));
|
||||
} else if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) {
|
||||
EventBus.getDefault().post(new EBReuse("CalenderCancel"));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
view.addView(inflate);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果超过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<KaiFuCalendarEntity> 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("visibleGone")
|
||||
public static void showHide(View view, Boolean show) {
|
||||
if (show != null && show) {
|
||||
view.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
view.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("messageUnread")
|
||||
public static void setMessageUnread(TextView view, int unreadCount) {
|
||||
if (unreadCount < 100) {
|
||||
view.setText(String.valueOf(unreadCount));
|
||||
} else {
|
||||
view.setText("99+");
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("serverTypePadding")
|
||||
public static void setServerTypePadding(TextView view, String serverType) {
|
||||
int paddRight = 0;
|
||||
if (TextUtils.isEmpty(serverType)) {
|
||||
} else {
|
||||
if ("删档内测".equals(serverType) || "不删档内测".equals(serverType)) {
|
||||
if ("删档内测".equals(serverType)) {
|
||||
paddRight = DisplayUtils.dip2px(view.getContext(), 50);
|
||||
} else {
|
||||
paddRight = DisplayUtils.dip2px(view.getContext(), 60);
|
||||
}
|
||||
} else {
|
||||
paddRight = DisplayUtils.dip2px(view.getContext(), 30);
|
||||
}
|
||||
}
|
||||
view.setPadding(0, 0, paddRight, 0);
|
||||
}
|
||||
|
||||
@BindingAdapter("serverType")
|
||||
public static void setServerType(TextView view, String serverType) {
|
||||
view.setText(serverType);
|
||||
if ("删档内测".equals(serverType) || "不删档内测".equals(serverType)) {
|
||||
view.setBackgroundResource(R.drawable.textview_server_tag);
|
||||
} else {
|
||||
view.setBackgroundResource(R.drawable.textview_orange_up);
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("articleType")
|
||||
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);
|
||||
view.setTextColor(0xFF999999);
|
||||
view.setClickable(false);
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("liBaoBtn")
|
||||
public static void setLiBaoBtn(TextView view, String status) {
|
||||
if (TextUtils.isEmpty(status)) return;
|
||||
switch (status) {
|
||||
case "coming":
|
||||
view.setText(R.string.libao_coming);
|
||||
view.setBackgroundResource(R.drawable.textview_blue_style);
|
||||
break;
|
||||
case "ling":
|
||||
view.setText(R.string.libao_ling);
|
||||
view.setBackgroundResource(R.drawable.textview_green_style);
|
||||
break;
|
||||
case "tao":
|
||||
view.setText(R.string.libao_tao);
|
||||
view.setBackgroundResource(R.drawable.textview_orange_style);
|
||||
break;
|
||||
case "used_up":
|
||||
view.setText(R.string.libao_used_up);
|
||||
view.setBackgroundResource(R.drawable.textview_cancel_up);
|
||||
break;
|
||||
case "finish":
|
||||
view.setText(R.string.libao_finish);
|
||||
view.setBackgroundResource(R.drawable.textview_cancel_up);
|
||||
break;
|
||||
case "linged":
|
||||
view.setText(R.string.libao_linged);
|
||||
view.setBackgroundResource(R.drawable.libao_linged_style);
|
||||
view.setTextColor(ContextCompat.getColorStateList(view.getContext(), R.color.libao_linged_selector));
|
||||
break;
|
||||
case "taoed":
|
||||
view.setText(R.string.libao_taoed);
|
||||
view.setBackgroundResource(R.drawable.libao_taoed_style);
|
||||
view.setTextColor(ContextCompat.getColorStateList(view.getContext(), R.color.libao_taoed_selector));
|
||||
break;
|
||||
case "copy":
|
||||
view.setText(R.string.libao_copy);
|
||||
view.setBackgroundResource(R.drawable.textview_blue_style);
|
||||
break;
|
||||
case "repeatLing":
|
||||
view.setText(R.string.libao_repeat_ling);
|
||||
view.setBackgroundResource(R.drawable.textview_cancel_up);
|
||||
break;
|
||||
case "repeatLinged":
|
||||
view.setText(R.string.libao_repeat_ling);
|
||||
view.setBackgroundResource(R.drawable.textview_green_style);
|
||||
break;
|
||||
case "repeatTao":
|
||||
view.setText(R.string.libao_repeat_tao);
|
||||
view.setBackgroundResource(R.drawable.textview_cancel_up);
|
||||
break;
|
||||
case "repeatTaoed":
|
||||
view.setText(R.string.libao_repeat_tao);
|
||||
view.setBackgroundResource(R.drawable.textview_orange_style);
|
||||
break;
|
||||
case "unshelve":
|
||||
view.setBackgroundResource(R.drawable.textview_cancel_style);
|
||||
view.setText(R.string.libao_unshelve);
|
||||
break;
|
||||
default:
|
||||
view.setBackgroundResource(R.drawable.textview_cancel_style);
|
||||
view.setText("异常");
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter({"downloadButton", "traceEvent"})
|
||||
public static void setDownloadButton(DownloadProgressBar progressBar, GameEntity gameEntity, ExposureEvent traceEvent) {
|
||||
// 判断是否显示按钮
|
||||
if (gameEntity != null
|
||||
&& Config.isShowDownload(gameEntity.getId())
|
||||
&& !"光环助手".equals(gameEntity.getName())) {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示下载按钮状态
|
||||
if (gameEntity.getApk().isEmpty()) {
|
||||
progressBar.setText("暂无下载");
|
||||
progressBar.setDownloadType(DownloadProgressBar.DownloadType.NONE);
|
||||
} else {
|
||||
String status = GameUtils.getDownloadBtnText(progressBar.getContext(), gameEntity);
|
||||
switch (status) {
|
||||
case "插件化":
|
||||
progressBar.setDownloadType(DownloadProgressBar.DownloadType.PLUGIN);
|
||||
break;
|
||||
case "打开":
|
||||
if (gameEntity.getApk().size() == 1) {
|
||||
status = "启动";
|
||||
}
|
||||
progressBar.setDownloadType(DownloadProgressBar.DownloadType.LAUNCH_OR_OPEN);
|
||||
break;
|
||||
default:
|
||||
progressBar.setDownloadType(DownloadProgressBar.DownloadType.NORMAL);
|
||||
break;
|
||||
}
|
||||
progressBar.setText(status);
|
||||
}
|
||||
|
||||
// 显示下载过程状态
|
||||
if (gameEntity.getApk().size() == 1) {
|
||||
DownloadEntity downloadEntity = DownloadManager.getInstance(progressBar.getContext()).getDownloadEntityByUrl(gameEntity.getApk().get(0).getUrl());
|
||||
if (downloadEntity != null) {
|
||||
progressBar.setProgress((int) (downloadEntity.getPercent() * 10));
|
||||
switch (downloadEntity.getStatus()) {
|
||||
case downloading:
|
||||
case pause:
|
||||
case timeout:
|
||||
case neterror:
|
||||
case waiting:
|
||||
progressBar.setText(R.string.downloading);
|
||||
if (downloadEntity.isPluggable() && PackageManager.INSTANCE.isInstalled(downloadEntity.getPackageName())) {
|
||||
progressBar.setDownloadType(DownloadProgressBar.DownloadType.DOWNLOADING_PLUGIN);
|
||||
} else {
|
||||
progressBar.setDownloadType(DownloadProgressBar.DownloadType.DOWNLOADING_NORMAL);
|
||||
}
|
||||
break;
|
||||
case done:
|
||||
progressBar.setText(R.string.install);
|
||||
if (downloadEntity.isPluggable()
|
||||
&& PackageManager.INSTANCE.isInstalled(downloadEntity.getPackageName())) {
|
||||
progressBar.setDownloadType(DownloadProgressBar.DownloadType.INSTALL_PLUGIN);
|
||||
} else {
|
||||
progressBar.setDownloadType(DownloadProgressBar.DownloadType.INSTALL_NORMAL);
|
||||
}
|
||||
break;
|
||||
case cancel:
|
||||
case hijack:
|
||||
case notfound:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 点击事件
|
||||
progressBar.setOnClickListener(v -> {
|
||||
switch (progressBar.getDownloadType()) {
|
||||
case DOWNLOADING_PLUGIN:
|
||||
case DOWNLOADING_NORMAL:
|
||||
Intent intent = DownloadManagerActivity.getDownloadMangerIntent(v.getContext(),
|
||||
gameEntity.getApk().get(0).getUrl(), "(我的光环:我的游戏)");
|
||||
v.getContext().startActivity(intent);
|
||||
break;
|
||||
case NONE:
|
||||
Utils.toast(v.getContext(), "该游戏已关闭下载");
|
||||
break;
|
||||
case NORMAL:
|
||||
case PLUGIN:
|
||||
if (gameEntity.getApk().size() == 1) {
|
||||
if (NetworkUtils.isWifiConnected(v.getContext())) {
|
||||
download(progressBar, gameEntity, traceEvent);
|
||||
} else {
|
||||
DialogUtils.showDownloadDialog(v.getContext(), () -> {
|
||||
download(progressBar, gameEntity, traceEvent);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
DownloadDialog.getInstance(v.getContext()).showPopupWindow(v, gameEntity,
|
||||
"(我的光环:我的游戏)", "我的光环-我的游戏:" + gameEntity.getName(), traceEvent);
|
||||
}
|
||||
break;
|
||||
case LAUNCH_OR_OPEN:
|
||||
if (gameEntity.getApk().size() == 1) {
|
||||
DataUtils.onGameLaunchEvent(v.getContext(), gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), "我的光环-我的游戏");
|
||||
PackageUtils.launchApplicationByPackageName(v.getContext(), gameEntity.getApk().get(0).getPackageName());
|
||||
} else {
|
||||
DownloadDialog.getInstance(v.getContext()).showPopupWindow(v, gameEntity,
|
||||
"(我的光环:我的游戏)", "我的光环-我的游戏:" + gameEntity.getName(), traceEvent);
|
||||
}
|
||||
break;
|
||||
case INSTALL_PLUGIN:
|
||||
case INSTALL_NORMAL:
|
||||
if (gameEntity.getApk().size() == 1) {
|
||||
DownloadEntity downloadEntity = DownloadManager.getInstance(progressBar.getContext()).getDownloadEntityByUrl(gameEntity.getApk().get(0).getUrl());
|
||||
if (downloadEntity != null) {
|
||||
PackageUtils.launchSetup(v.getContext(), downloadEntity.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 开始下载
|
||||
private static void download(DownloadProgressBar progressBar, GameEntity
|
||||
gameEntity, ExposureEvent traceEvent) {
|
||||
String str = progressBar.getText();
|
||||
String method;
|
||||
if (str.contains("更新")) {
|
||||
method = "更新";
|
||||
} else if (str.contains("插件化")) {
|
||||
method = "插件化";
|
||||
} else {
|
||||
method = progressBar.getContext().getString(R.string.download);
|
||||
}
|
||||
ApkEntity apkEntity = gameEntity.getApk().get(0);
|
||||
String msg = FileUtils.isCanDownload(progressBar.getContext(), apkEntity.getSize());
|
||||
if (TextUtils.isEmpty(msg)) {
|
||||
DataUtils.onGameDownloadEvent(progressBar.getContext(), gameEntity.getName(), apkEntity.getPlatform(), "(我的光环:我的游戏)", "下载开始", method);
|
||||
|
||||
ExposureEvent downloadExposureEvent = ExposureUtils.logADownloadExposureEvent(gameEntity, apkEntity.getPlatform(), traceEvent, ExposureUtils.DownloadType.DOWNLOAD);
|
||||
|
||||
DownloadManager.createDownload(progressBar.getContext(),
|
||||
apkEntity,
|
||||
gameEntity,
|
||||
method,
|
||||
StringUtils.buildString("(我的光环:我的游戏)"), "我的光环-我的游戏:" + gameEntity.getName(),
|
||||
downloadExposureEvent);
|
||||
|
||||
progressBar.setProgress(0);
|
||||
progressBar.setDownloadType("插件化".equals(method) ?
|
||||
DownloadProgressBar.DownloadType.DOWNLOADING_PLUGIN : DownloadProgressBar.DownloadType.DOWNLOADING_NORMAL);
|
||||
} else {
|
||||
Utils.toast(progressBar.getContext(), msg);
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter({"gameLabelList", "subjectTag"})
|
||||
public static void setGameLabelList(LinearLayout layout, GameEntity gameEntity, String subjectTag) {
|
||||
if (gameEntity == null) return;
|
||||
if (gameEntity.getTest() != null) {
|
||||
layout.removeAllViews();
|
||||
View testView = LayoutInflater.from(layout.getContext()).inflate(R.layout.game_test_label, null);
|
||||
TextView testType = testView.findViewById(R.id.test_type);
|
||||
TextView testTime = testView.findViewById(R.id.test_time);
|
||||
String type = gameEntity.getTest().getType();
|
||||
KaiFuUtils.setKaiFuType(testType, type);
|
||||
|
||||
if (gameEntity.getTest().getStart() == 0) {
|
||||
testTime.setVisibility(View.GONE);
|
||||
} else {
|
||||
testTime.setText(GameViewUtils.getGameTestDate(gameEntity.getTest().getStart()));
|
||||
}
|
||||
layout.addView(testView);
|
||||
} else {
|
||||
GameViewUtils.setLabelList(layout.getContext(), layout, gameEntity.getTag(), subjectTag, gameEntity.getTagStyle());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@BindingAdapter("isRefreshing")
|
||||
public static void isRefreshing(SwipeRefreshLayout layout, LoadStatus status) {
|
||||
if (status == LoadStatus.INIT_LOADING) {
|
||||
layout.setRefreshing(true);
|
||||
} else {
|
||||
layout.setRefreshing(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package com.gh.common.exposure
|
||||
|
||||
import android.arch.persistence.room.TypeConverter
|
||||
import com.gh.common.exposure.meta.Meta
|
||||
import com.google.gson.Gson
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class ExposureConverters {
|
||||
|
||||
@TypeConverter
|
||||
fun convertPayload2String(any: ExposureEntity): String {
|
||||
return Gson().toJson(any)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun convertString2Payload(string: String): ExposureEntity {
|
||||
return Gson().fromJson(string, ExposureEntity::class.java)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun convertSource2String(sourceList: List<ExposureSource>): String {
|
||||
return Gson().toJson(sourceList)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun convertString2Source(sourceList: String): List<ExposureSource> {
|
||||
return ArrayList(Arrays.asList(Gson().fromJson(sourceList, Array<ExposureSource>::class.java))) as List<ExposureSource>
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun convertETrace2String(sourceList: List<ExposureEvent>?): String {
|
||||
return Gson().toJson(sourceList)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun convertStringToETrace(sourceList: String): List<ExposureEvent> {
|
||||
return ArrayList(Arrays.asList(Gson().fromJson(sourceList, Array<ExposureEvent>::class.java))) as List<ExposureEvent>
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun convertExposeType2String(exposureType: ExposureType): String {
|
||||
return exposureType.toString()
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun convertStringToExposeType(exposureType: String): ExposureType {
|
||||
return ExposureType.valueOf(exposureType)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun convertMeta2String(any: Meta): String {
|
||||
return Gson().toJson(any)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun convertString2Meta(string: String): Meta {
|
||||
return Gson().fromJson(string, Meta::class.java)
|
||||
}
|
||||
|
||||
}
|
||||
23
app/src/main/java/com/gh/common/exposure/ExposureDatabase.kt
Normal file
23
app/src/main/java/com/gh/common/exposure/ExposureDatabase.kt
Normal file
@ -0,0 +1,23 @@
|
||||
package com.gh.common.exposure
|
||||
|
||||
import android.arch.persistence.room.Database
|
||||
import android.arch.persistence.room.Room
|
||||
import android.arch.persistence.room.RoomDatabase
|
||||
import android.arch.persistence.room.TypeConverters
|
||||
import android.content.Context
|
||||
|
||||
@TypeConverters(ExposureConverters::class)
|
||||
@Database(entities = [ExposureEvent::class], version = 1, exportSchema = false)
|
||||
abstract class ExposureDatabase : RoomDatabase() {
|
||||
companion object {
|
||||
private const val DATABASE = "exposure_database"
|
||||
|
||||
fun buildDatabase(context: Context): ExposureDatabase {
|
||||
return Room.databaseBuilder(context, ExposureDatabase::class.java, DATABASE)
|
||||
.fallbackToDestructiveMigration()
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun logHubEventDao(): ExposureEventDao
|
||||
}
|
||||
18
app/src/main/java/com/gh/common/exposure/ExposureEntity.kt
Normal file
18
app/src/main/java/com/gh/common/exposure/ExposureEntity.kt
Normal file
@ -0,0 +1,18 @@
|
||||
package com.gh.common.exposure
|
||||
|
||||
import android.os.Parcelable
|
||||
import android.support.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Keep
|
||||
@Parcelize
|
||||
data class ExposureEntity(
|
||||
@SerializedName("game_id")
|
||||
val gameId: String? = "",
|
||||
val gameName: String? = "",
|
||||
val sequence: Int? = 0,
|
||||
val platform: String? = "",
|
||||
val downloadType: String? = "",
|
||||
val downloadCompleteType: String? = ""
|
||||
) : Parcelable
|
||||
40
app/src/main/java/com/gh/common/exposure/ExposureEvent.kt
Normal file
40
app/src/main/java/com/gh/common/exposure/ExposureEvent.kt
Normal file
@ -0,0 +1,40 @@
|
||||
package com.gh.common.exposure
|
||||
|
||||
import android.arch.persistence.room.Entity
|
||||
import android.arch.persistence.room.PrimaryKey
|
||||
import android.os.Parcelable
|
||||
import android.support.annotation.Keep
|
||||
import com.gh.common.exposure.meta.Meta
|
||||
import com.gh.common.exposure.meta.MetaUtil
|
||||
import com.gh.common.exposure.time.TimeUtil
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import java.util.*
|
||||
|
||||
@Keep
|
||||
@Parcelize
|
||||
@Entity(tableName = "exposureEvent")
|
||||
data class ExposureEvent(
|
||||
val 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 {
|
||||
fun createEvent(gameEntity: GameEntity?, source: List<ExposureSource>, eTrace: List<ExposureEvent>?, event: ExposureType): ExposureEvent {
|
||||
return ExposureEvent(
|
||||
ExposureEntity(gameId = gameEntity?.id,
|
||||
gameName = gameEntity?.name,
|
||||
sequence = gameEntity?.sequence,
|
||||
platform = gameEntity?.platform,
|
||||
downloadType = gameEntity?.downloadType,
|
||||
downloadCompleteType = gameEntity?.downloadCompleteType),
|
||||
source = source,
|
||||
eTrace = eTrace,
|
||||
event = event)
|
||||
}
|
||||
}
|
||||
}
|
||||
19
app/src/main/java/com/gh/common/exposure/ExposureEventDao.kt
Normal file
19
app/src/main/java/com/gh/common/exposure/ExposureEventDao.kt
Normal file
@ -0,0 +1,19 @@
|
||||
package com.gh.common.exposure
|
||||
|
||||
import android.arch.persistence.room.*
|
||||
|
||||
@Dao
|
||||
interface ExposureEventDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertMany(eventList: List<ExposureEvent>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insert(event: ExposureEvent)
|
||||
|
||||
@Query("SELECT * FROM exposureEvent")
|
||||
fun getAll(): List<ExposureEvent>
|
||||
|
||||
@Delete
|
||||
fun deleteMany(eventList: List<ExposureEvent>)
|
||||
}
|
||||
70
app/src/main/java/com/gh/common/exposure/ExposureListener.kt
Normal file
70
app/src/main/java/com/gh/common/exposure/ExposureListener.kt
Normal file
@ -0,0 +1,70 @@
|
||||
package com.gh.common.exposure
|
||||
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v4.app.FragmentManager
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import io.reactivex.functions.Consumer
|
||||
|
||||
/**
|
||||
* Exposure Event Listener for RecyclerView
|
||||
*
|
||||
* TODO 1. Pull down refresh change in first page without scroll down action
|
||||
* TODO 2. Item change not triggered by user scroll action (vm data change etc.)
|
||||
*/
|
||||
class ExposureListener(var fragment: Fragment, var exposable: IExposable) : RecyclerView.OnScrollListener() {
|
||||
|
||||
var throttleBus: ExposureThrottleBus? = null
|
||||
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()
|
||||
}
|
||||
}, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitor items in display when scrolling, record those newly displayed as well as
|
||||
* those newly disappeared. And finally trigger commitExposure().
|
||||
*/
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
|
||||
if (layoutManager == null) layoutManager = recyclerView.layoutManager as LinearLayoutManager
|
||||
|
||||
layoutManager?.run {
|
||||
visibleState = ExposureThrottleBus.VisibleState(findFirstCompletelyVisibleItemPosition(), findLastCompletelyVisibleItemPosition())
|
||||
throttleBus?.postVisibleState(visibleState!!)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check disappearMap items together with according data in displayMap,
|
||||
* log any items displayed long enough to be called a EXPOSURE
|
||||
*/
|
||||
private fun commitExposure(visibleState: ExposureThrottleBus.VisibleState) {
|
||||
|
||||
val eventList = arrayListOf<ExposureEvent>()
|
||||
|
||||
for (pos in visibleState.firstCompletelyVisible..visibleState.lastCompletelyVisible) {
|
||||
try {
|
||||
exposable.getEventByPosition(pos)?.let { eventList.add(it) }
|
||||
exposable.getEventListByPosition(pos)?.let { eventList.addAll(it) }
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
ExposureManager.log(eventList)
|
||||
}
|
||||
|
||||
}
|
||||
200
app/src/main/java/com/gh/common/exposure/ExposureManager.kt
Normal file
200
app/src/main/java/com/gh/common/exposure/ExposureManager.kt
Normal file
@ -0,0 +1,200 @@
|
||||
package com.gh.common.exposure
|
||||
|
||||
import android.app.Application
|
||||
import com.aliyun.sls.android.sdk.LogException
|
||||
import com.aliyun.sls.android.sdk.model.LogGroup
|
||||
import com.gh.common.exposure.aliyun.LGLOG
|
||||
import com.gh.common.exposure.aliyun.LGLOGClient
|
||||
import com.gh.common.exposure.meta.MetaUtil
|
||||
import com.gh.common.exposure.time.TimeUtil
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.google.gson.Gson
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.concurrent.fixedRateTimer
|
||||
|
||||
/**
|
||||
* ExposureManager tool to commit logs to aliyun loghub
|
||||
* TODO handle logs that failed to be committed multiple times
|
||||
*/
|
||||
object ExposureManager {
|
||||
|
||||
private var TAG: String = ExposureManager::class.java.simpleName
|
||||
|
||||
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 const val STORE_SIZE = 100
|
||||
private const val STORE_FORCE_UPLOAD_PERIOD = 300 * 1000L
|
||||
|
||||
private var LOG_STORE = BuildConfig.EXPOSURE_REPO
|
||||
|
||||
private lateinit var client: LGLOGClient
|
||||
private lateinit var db: ExposureEventDao
|
||||
private val storeSet = hashSetOf<ExposureEvent>()
|
||||
private val storeOpThread = Executors.newSingleThreadExecutor()
|
||||
private val gson = Gson()
|
||||
|
||||
private val exposureCache = FixedSizeLinkedHashSet<String>(20)
|
||||
|
||||
/**
|
||||
* Must be called early to init object then real use (for example in Application)
|
||||
*/
|
||||
fun init(application: Application) {
|
||||
|
||||
client = LGLOGClient(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET, PROJECT)
|
||||
db = ExposureDatabase.buildDatabase(application).logHubEventDao()
|
||||
|
||||
storeOpThread.execute {
|
||||
val eventList = db.getAll()
|
||||
storeSet.addAll(eventList)
|
||||
}
|
||||
|
||||
fixedRateTimer(name = "ExposureManager-Store-Checker", initialDelay = 500, period = STORE_FORCE_UPLOAD_PERIOD) {
|
||||
checkAndUploadFromDatabase(true)
|
||||
}
|
||||
|
||||
MetaUtil.init(application)
|
||||
TimeUtil.init()
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an Event
|
||||
*/
|
||||
fun log(event: ExposureEvent, uploadImmediately: Boolean = false) {
|
||||
when (uploadImmediately) {
|
||||
false -> store(event)
|
||||
true -> upload(event)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log Many Events
|
||||
*/
|
||||
fun log(eventList: List<ExposureEvent>, uploadImmediately: Boolean = false) {
|
||||
when (uploadImmediately) {
|
||||
false -> store(eventList)
|
||||
true -> upload(eventList)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store an Event to storeSet, upload when storeSet size exceeds STORE_SIZE
|
||||
*/
|
||||
private fun store(event: ExposureEvent) {
|
||||
storeOpThread.execute {
|
||||
if (!exposureCache.contains(event.id)) {
|
||||
storeSet.add(event)
|
||||
db.insert(event)
|
||||
exposureCache.add(event.id)
|
||||
}
|
||||
checkAndUploadFromDatabase()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store Many Events to storeSet, upload when storeSet size exceeds STORE_SIZE
|
||||
*/
|
||||
private fun store(eventList: List<ExposureEvent>) {
|
||||
storeOpThread.execute {
|
||||
for (event in eventList) {
|
||||
if (!exposureCache.contains(event.id)) {
|
||||
storeSet.add(event)
|
||||
db.insert(event)
|
||||
exposureCache.add(event.id)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload an Event
|
||||
*/
|
||||
private fun upload(event: ExposureEvent) {
|
||||
storeOpThread.execute {
|
||||
client.PostLog(buildLogGroup(event), LOG_STORE)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload Many Events
|
||||
*/
|
||||
private fun upload(eventList: List<ExposureEvent>) {
|
||||
storeOpThread.execute {
|
||||
client.PostLog(buildLogGroup(eventList), LOG_STORE)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload Events From Store, and removed them
|
||||
*/
|
||||
private fun checkAndUploadFromDatabase(isForceUpload: Boolean = false) {
|
||||
storeOpThread.execute {
|
||||
if (storeSet.size < STORE_SIZE && !isForceUpload || storeSet.size == 0) return@execute
|
||||
val uploaded = storeSet.toList()
|
||||
try {
|
||||
client.PostLog(buildLogGroup(uploaded), LOG_STORE)
|
||||
} catch (exception: LogException) {
|
||||
// Return to insure no logs lost because of online commit failure
|
||||
return@execute
|
||||
}
|
||||
storeSet.removeAll(uploaded)
|
||||
db.deleteMany(uploaded)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildLog(event: ExposureEvent): LGLOG {
|
||||
val log = LGLOG()
|
||||
|
||||
log.PutContent("id", event.id)
|
||||
log.PutContent("payload", gson.toJson(event.payload))
|
||||
log.PutContent("event", event.event.toString())
|
||||
log.PutContent("source", eliminateMultipleBrackets(gson.toJson(event.source)))
|
||||
log.PutContent("meta", gson.toJson(event.meta))
|
||||
log.PutContent("e-traces", if (event.eTrace != null) eliminateMultipleBrackets(gson.toJson(event.eTrace)) else "")
|
||||
log.PutTime(event.time)
|
||||
|
||||
return log
|
||||
}
|
||||
|
||||
private fun eliminateMultipleBrackets(jsonWithMultipleBracket: String): String {
|
||||
return jsonWithMultipleBracket.replace("[[", "[").replace("]]", "]")
|
||||
}
|
||||
|
||||
private fun buildLogGroup(event: ExposureEvent): LogGroup {
|
||||
|
||||
val logGroup = LogGroup("sls android", "no ip")
|
||||
|
||||
logGroup.PutLog(buildLog(event))
|
||||
|
||||
return logGroup
|
||||
}
|
||||
|
||||
private fun buildLogGroup(eventList: List<ExposureEvent>): LogGroup {
|
||||
|
||||
val logGroup = LogGroup("sls android", "no ip")
|
||||
|
||||
eventList.forEach { event ->
|
||||
logGroup.PutLog(buildLog(event))
|
||||
}
|
||||
|
||||
return logGroup
|
||||
}
|
||||
|
||||
internal class FixedSizeLinkedHashSet<T>(var maxSize: Int) : LinkedHashSet<T>() {
|
||||
override fun add(element: T): Boolean {
|
||||
if (size == maxSize) {
|
||||
pop()
|
||||
}
|
||||
return super.add(element);
|
||||
}
|
||||
|
||||
private fun pop() {
|
||||
if (size > 0) {
|
||||
remove(iterator().next())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package com.gh.common.exposure
|
||||
|
||||
import android.os.Parcelable
|
||||
import android.support.annotation.Keep
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Keep
|
||||
@Parcelize
|
||||
data class ExposureSource(var k: String, var v: String): Parcelable
|
||||
@ -0,0 +1,59 @@
|
||||
package com.gh.common.exposure
|
||||
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.functions.Consumer
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class ExposureThrottleBus(var onSuccess: Consumer<VisibleState>, var onError: Consumer<Throwable>) {
|
||||
|
||||
companion object {
|
||||
private const val THRESHOLD_TIME = 300L
|
||||
}
|
||||
|
||||
private val mPublishSubject: PublishSubject<VisibleState> = PublishSubject.create()
|
||||
private val mCompositeDisposable: CompositeDisposable = CompositeDisposable()
|
||||
|
||||
init {
|
||||
|
||||
/**
|
||||
* Since onScroll() callback will be triggered multiple times for every swipe, we use
|
||||
* distinctUntilChanged() to prevent committing the same visibleState event and
|
||||
* throttleWithTimeout() to pass a visibleState event with a delay and drop current event if another event arrives before the timeout.
|
||||
*/
|
||||
val disposable = mPublishSubject
|
||||
.distinctUntilChanged()
|
||||
.throttleWithTimeout(THRESHOLD_TIME, TimeUnit.MILLISECONDS)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(onSuccess, onError)
|
||||
mCompositeDisposable.add(disposable)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
mCompositeDisposable.clear()
|
||||
}
|
||||
|
||||
fun postVisibleState(visibleState: VisibleState) {
|
||||
mPublishSubject.onNext(visibleState)
|
||||
}
|
||||
|
||||
class VisibleState(val firstCompletelyVisible: Int, val lastCompletelyVisible: Int) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || javaClass != other.javaClass) return false
|
||||
|
||||
val that = other as VisibleState
|
||||
|
||||
if (firstCompletelyVisible != that.firstCompletelyVisible) return false
|
||||
|
||||
return lastCompletelyVisible == that.lastCompletelyVisible
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = firstCompletelyVisible
|
||||
result = 31 * result + lastCompletelyVisible
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.gh.common.exposure
|
||||
|
||||
object ExposureTraceUtils {
|
||||
|
||||
fun appendTrace(event: ExposureEvent?): List<ExposureEvent> {
|
||||
val traceList = arrayListOf<ExposureEvent>()
|
||||
|
||||
event?.let {
|
||||
if (event.eTrace == null) {
|
||||
traceList.add(event)
|
||||
} else {
|
||||
traceList.addAll(event.eTrace!!)
|
||||
traceList.add(flattenTrace(event))
|
||||
}
|
||||
}
|
||||
|
||||
return traceList
|
||||
}
|
||||
|
||||
private fun flattenTrace(event: ExposureEvent): ExposureEvent {
|
||||
event.eTrace = arrayListOf()
|
||||
return event
|
||||
}
|
||||
|
||||
}
|
||||
11
app/src/main/java/com/gh/common/exposure/ExposureType.kt
Normal file
11
app/src/main/java/com/gh/common/exposure/ExposureType.kt
Normal file
@ -0,0 +1,11 @@
|
||||
package com.gh.common.exposure
|
||||
|
||||
enum class ExposureType {
|
||||
EXPOSURE,
|
||||
|
||||
CLICK,
|
||||
|
||||
DOWNLOAD,
|
||||
|
||||
DOWNLOAD_COMPLETE
|
||||
}
|
||||
46
app/src/main/java/com/gh/common/exposure/ExposureUtils.kt
Normal file
46
app/src/main/java/com/gh/common/exposure/ExposureUtils.kt
Normal file
@ -0,0 +1,46 @@
|
||||
package com.gh.common.exposure
|
||||
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.google.gson.Gson
|
||||
import java.util.*
|
||||
|
||||
object ExposureUtils {
|
||||
|
||||
val gson = Gson()
|
||||
|
||||
@JvmStatic
|
||||
fun logADownloadExposureEvent(entity: GameEntity, platform: String?, traceEvent: ExposureEvent?, downloadType: DownloadType): ExposureEvent {
|
||||
val gameEntity = entity.clone()
|
||||
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, false)
|
||||
return exposureEvent
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun logADownloadCompleteExposureEvent(entity: GameEntity, platform: String?, trace: String?, downloadType: DownloadType) {
|
||||
val gameEntity = entity.clone()
|
||||
gameEntity.platform = platform
|
||||
gameEntity.downloadCompleteType = downloadType.toString()
|
||||
val traceEvent = gson.fromJson(trace, ExposureEvent::class.java)
|
||||
val exposureEvent = ExposureEvent.createEvent(gameEntity = gameEntity,
|
||||
source = traceEvent?.source ?: ArrayList(),
|
||||
eTrace = ExposureTraceUtils.appendTrace(traceEvent),
|
||||
event = ExposureType.DOWNLOAD_COMPLETE)
|
||||
ExposureManager.log(exposureEvent, false)
|
||||
}
|
||||
|
||||
enum class DownloadType {
|
||||
DOWNLOAD,
|
||||
|
||||
UPDATE,
|
||||
|
||||
PLUGIN_UPDATE,
|
||||
|
||||
PLUGIN_DOWNLOAD
|
||||
}
|
||||
}
|
||||
7
app/src/main/java/com/gh/common/exposure/IExposable.kt
Normal file
7
app/src/main/java/com/gh/common/exposure/IExposable.kt
Normal file
@ -0,0 +1,7 @@
|
||||
package com.gh.common.exposure
|
||||
|
||||
interface IExposable {
|
||||
fun getEventByPosition(pos: Int): ExposureEvent?
|
||||
|
||||
fun getEventListByPosition(pos: Int): List<ExposureEvent>?
|
||||
}
|
||||
42
app/src/main/java/com/gh/common/exposure/aliyun/LGLOG.java
Normal file
42
app/src/main/java/com/gh/common/exposure/aliyun/LGLOG.java
Normal file
@ -0,0 +1,42 @@
|
||||
package com.gh.common.exposure.aliyun;
|
||||
|
||||
import com.aliyun.sls.android.sdk.model.Log;
|
||||
import com.gh.common.exposure.time.TimeUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Extend to change __time__ field in mContent to use the correct time from TimeUtil
|
||||
*/
|
||||
public class LGLOG extends Log {
|
||||
|
||||
private Map<String, Object> mContent = new HashMap<String, Object>();
|
||||
|
||||
public LGLOG() {
|
||||
mContent.put("__time__", TimeUtil.INSTANCE.currentTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void PutTime(int time) {
|
||||
mContent.put("__time__", time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void PutContent(String key, String value) {
|
||||
if (key == null || key.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (value == null) {
|
||||
mContent.put(key, "");
|
||||
} else {
|
||||
mContent.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> GetContent() {
|
||||
return mContent;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
package com.gh.common.exposure.aliyun;
|
||||
|
||||
import com.aliyun.sls.android.sdk.LOGClient;
|
||||
import com.aliyun.sls.android.sdk.LogException;
|
||||
import com.gh.common.exposure.time.TimeUtil;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Extend to override GetHttpHeadersFrom, so we can change "Date" header attribute using
|
||||
* correct time from TimeUtil.
|
||||
* And accordingly, the value of "Authorization" attribute should also be re-calculate
|
||||
* since the sign became different as before.
|
||||
*/
|
||||
public class LGLOGClient extends LOGClient {
|
||||
|
||||
private String mAccessKeyID;
|
||||
private String mAccessKeySecret;
|
||||
private String mAccessToken;
|
||||
|
||||
public LGLOGClient(String endPoint, String accessKeyID, String accessKeySecret, String projectName) {
|
||||
super(endPoint, accessKeyID, accessKeySecret, projectName);
|
||||
this.mAccessKeyID = accessKeyID;
|
||||
this.mAccessKeySecret = accessKeySecret;
|
||||
this.mAccessToken = "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> GetHttpHeadersFrom(String logStoreName, byte[] body, byte[] bodyZipped) throws LogException {
|
||||
|
||||
Map<String, String> headers = super.GetHttpHeadersFrom(logStoreName, body, bodyZipped);
|
||||
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
|
||||
sdf.setTimeZone(TimeZone.getTimeZone("GMT")); // 设置时区为GMT
|
||||
String str = sdf.format(new Date(TimeUtil.INSTANCE.currentTimeMillis()));
|
||||
headers.put("Date", str);
|
||||
|
||||
StringBuilder signStringBuf = new StringBuilder("POST" + "\n").
|
||||
append(headers.get("Content-MD5") + "\n").
|
||||
append(headers.get("Content-Type") + "\n").
|
||||
append(headers.get("Date") + "\n");
|
||||
String token = mAccessToken;
|
||||
if (token != null && token != "") {
|
||||
headers.put("x-acs-security-token", token);
|
||||
signStringBuf.append("x-acs-security-token:" + headers.get("x-acs-security-token") + "\n");
|
||||
}
|
||||
signStringBuf.append("x-log-apiversion:0.6.0\n").
|
||||
append("x-log-bodyrawsize:" + headers.get("x-log-bodyrawsize") + "\n").
|
||||
append("x-log-compresstype:deflate\n").
|
||||
append("x-log-signaturemethod:hmac-sha1\n").
|
||||
append("/logstores/" + logStoreName + "/shards/lb");
|
||||
String signString = signStringBuf.toString();
|
||||
try {
|
||||
String sign = hmac_sha1(signString, mAccessKeySecret);
|
||||
headers.put("Authorization", "LOG " + mAccessKeyID + ":" + sign);
|
||||
} catch (Exception e) {
|
||||
throw new LogException("LogClientError", "fail to get encode signature", e, "");
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
24
app/src/main/java/com/gh/common/exposure/meta/Meta.kt
Normal file
24
app/src/main/java/com/gh/common/exposure/meta/Meta.kt
Normal file
@ -0,0 +1,24 @@
|
||||
package com.gh.common.exposure.meta
|
||||
|
||||
import android.os.Parcelable
|
||||
import android.support.annotation.Keep
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Keep
|
||||
@Parcelize
|
||||
data class Meta(
|
||||
val mac: String? = "",
|
||||
val imei: String? = "",
|
||||
val model: String? = "",
|
||||
val manufacturer: String? = "",
|
||||
val android_id: String? = "",
|
||||
val android_sdk: Int? = -1,
|
||||
val android_version: String? = "",
|
||||
val network: String? = "",
|
||||
val ip: String? = "",
|
||||
val os: String? = "",
|
||||
val channel: String? = "",
|
||||
val appVersion: String? = "",
|
||||
val userId: String? = "",
|
||||
val exposureVersion: String? = ""
|
||||
) : Parcelable
|
||||
170
app/src/main/java/com/gh/common/exposure/meta/MetaUtil.kt
Normal file
170
app/src/main/java/com/gh/common/exposure/meta/MetaUtil.kt
Normal file
@ -0,0 +1,170 @@
|
||||
package com.gh.common.exposure.meta
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.wifi.WifiManager
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import android.telephony.TelephonyManager
|
||||
import android.text.TextUtils
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.leon.channel.helper.ChannelReaderUtil
|
||||
import java.io.File
|
||||
|
||||
object MetaUtil {
|
||||
|
||||
private lateinit var application: Application
|
||||
private var channel = ""
|
||||
|
||||
private var m: Meta? = null
|
||||
|
||||
fun init(application: Application) {
|
||||
MetaUtil.application = application
|
||||
}
|
||||
|
||||
fun refreshMeta() {
|
||||
m = Meta(getMac(), getIMEI(), getModel(), getManufacturer(), getAndroidId(), getAndroidSDK(),
|
||||
getAndroidVersion(), getNetwork(), getIP(), getOS(), getChannel(), BuildConfig.VERSION_NAME, UserManager.getInstance().userId, BuildConfig.EXPOSURE_VERSION)
|
||||
}
|
||||
|
||||
fun getMeta(): Meta {
|
||||
if (m == null) {
|
||||
refreshMeta()
|
||||
}
|
||||
return m!!
|
||||
}
|
||||
|
||||
private fun getChannel(): String? {
|
||||
if (TextUtils.isEmpty(channel)) {
|
||||
channel = if (ChannelReaderUtil.getChannel(application) != null) ChannelReaderUtil.getChannel(application) else ""
|
||||
}
|
||||
return channel
|
||||
}
|
||||
|
||||
/**
|
||||
* Get MAC address
|
||||
* TODO check > 6.0 results
|
||||
*/
|
||||
fun getMac(): String? {
|
||||
|
||||
var mac: String = ""
|
||||
|
||||
//Plan A
|
||||
try {
|
||||
mac = File("/sys/class/net/wlan0/address").inputStream().bufferedReader().use { it.readText() }
|
||||
if (!TextUtils.isEmpty(mac)) return mac.trim()
|
||||
} catch (e: Exception) {
|
||||
// e.printStackTrace()
|
||||
}
|
||||
|
||||
// Plan B
|
||||
try {
|
||||
mac = File("/sys/class/net/eth0/address").inputStream().bufferedReader().use { it.readText() }
|
||||
if (!TextUtils.isEmpty(mac)) return mac.trim()
|
||||
} catch (e: Exception) {
|
||||
// e.printStackTrace()
|
||||
}
|
||||
|
||||
// Plan C
|
||||
val wifiManager = application.getSystemService(Context.WIFI_SERVICE) as WifiManager
|
||||
try {
|
||||
mac = wifiManager.connectionInfo.macAddress
|
||||
} catch (e: Exception) {
|
||||
// e.printStackTrace()
|
||||
}
|
||||
|
||||
return mac.trim()
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get IMEI
|
||||
*/
|
||||
fun getIMEI(): String? {
|
||||
|
||||
if (application.checkCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED)
|
||||
return ""
|
||||
|
||||
val telephonyManager = application.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
return telephonyManager.imei
|
||||
}
|
||||
|
||||
return telephonyManager.getDeviceId()
|
||||
|
||||
}
|
||||
|
||||
fun getModel(): String? {
|
||||
return Build.MODEL
|
||||
}
|
||||
|
||||
fun getManufacturer(): String? {
|
||||
return Build.MANUFACTURER
|
||||
}
|
||||
|
||||
fun getAndroidId(): String? {
|
||||
var android_id: String = ""
|
||||
try {
|
||||
android_id = Settings.Secure.getString(application.contentResolver, Settings.Secure.ANDROID_ID)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return android_id
|
||||
}
|
||||
|
||||
fun getAndroidSDK(): Int? {
|
||||
return Build.VERSION.SDK_INT
|
||||
}
|
||||
|
||||
fun getAndroidVersion(): String? {
|
||||
return Build.VERSION.RELEASE
|
||||
}
|
||||
|
||||
fun getNetwork(): String? {
|
||||
|
||||
if (application.checkCallingOrSelfPermission(Manifest.permission.ACCESS_NETWORK_STATE) != PackageManager.PERMISSION_GRANTED)
|
||||
return "unknown"
|
||||
|
||||
val activeNetwork = (application.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).activeNetworkInfo
|
||||
?: return "unknown"
|
||||
|
||||
return when (activeNetwork.type) {
|
||||
ConnectivityManager.TYPE_WIFI -> "Wifi"
|
||||
ConnectivityManager.TYPE_WIMAX -> "WifiMax"
|
||||
ConnectivityManager.TYPE_MOBILE -> {
|
||||
val telephonyManager = application.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
|
||||
if (telephonyManager.simState != TelephonyManager.SIM_STATE_READY) return "unknown"
|
||||
when (telephonyManager.networkType) {
|
||||
// Unknown
|
||||
TelephonyManager.NETWORK_TYPE_UNKNOWN -> "Cellular - Unknown"
|
||||
// Cellular Data–2G
|
||||
TelephonyManager.NETWORK_TYPE_EDGE, TelephonyManager.NETWORK_TYPE_GPRS, TelephonyManager.NETWORK_TYPE_CDMA,
|
||||
TelephonyManager.NETWORK_TYPE_IDEN, TelephonyManager.NETWORK_TYPE_1xRTT -> "Cellular - 2G"
|
||||
// Cellular Data–3G
|
||||
TelephonyManager.NETWORK_TYPE_UMTS, TelephonyManager.NETWORK_TYPE_HSDPA, TelephonyManager.NETWORK_TYPE_HSPA,
|
||||
TelephonyManager.NETWORK_TYPE_HSPAP, TelephonyManager.NETWORK_TYPE_HSUPA, TelephonyManager.NETWORK_TYPE_EVDO_0,
|
||||
TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyManager.NETWORK_TYPE_EVDO_B -> "Cellular - 3G"
|
||||
// Cellular Data–4G
|
||||
TelephonyManager.NETWORK_TYPE_LTE -> "Cellular - 4G"
|
||||
else -> "Cellular - Unknown Generation"
|
||||
}
|
||||
|
||||
}
|
||||
else -> "unknown"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun getIP(): String {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
fun getOS(): String {
|
||||
return "android"
|
||||
}
|
||||
}
|
||||
27
app/src/main/java/com/gh/common/exposure/time/Corrector.kt
Normal file
27
app/src/main/java/com/gh/common/exposure/time/Corrector.kt
Normal file
@ -0,0 +1,27 @@
|
||||
package com.gh.common.exposure.time
|
||||
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlin.concurrent.fixedRateTimer
|
||||
|
||||
class Corrector {
|
||||
|
||||
companion object {
|
||||
const val TIME_CORRECTOR_ADJUST_PERIOD: Long = 600000
|
||||
}
|
||||
|
||||
var delta: Long = 0
|
||||
|
||||
init {
|
||||
fixedRateTimer("TimeUtil-Corrector-Checker", initialDelay = 0, period = TIME_CORRECTOR_ADJUST_PERIOD) {
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application).api.time
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe({
|
||||
val serverTime = java.lang.Long.parseLong(it.string())
|
||||
delta = serverTime * 1000 - System.currentTimeMillis()
|
||||
}, Throwable::printStackTrace)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
22
app/src/main/java/com/gh/common/exposure/time/TimeUtil.kt
Normal file
22
app/src/main/java/com/gh/common/exposure/time/TimeUtil.kt
Normal file
@ -0,0 +1,22 @@
|
||||
package com.gh.common.exposure.time
|
||||
|
||||
object TimeUtil {
|
||||
|
||||
private lateinit var corrector: Corrector
|
||||
|
||||
fun currentTimeMillis(): Long {
|
||||
return corrector.delta + System.currentTimeMillis()
|
||||
}
|
||||
|
||||
fun currentTime(): Int {
|
||||
return ( ( corrector.delta + System.currentTimeMillis() ) / 1000 ).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called early then real use (for example in Application)
|
||||
*/
|
||||
fun init() {
|
||||
corrector = Corrector()
|
||||
}
|
||||
|
||||
}
|
||||
67
app/src/main/java/com/gh/common/im/ImHintHelper.kt
Normal file
67
app/src/main/java/com/gh/common/im/ImHintHelper.kt
Normal file
@ -0,0 +1,67 @@
|
||||
package com.gh.common.im
|
||||
|
||||
import android.app.Activity
|
||||
import android.support.v4.view.ViewCompat
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
||||
object ImHintHelper {
|
||||
|
||||
@JvmStatic
|
||||
fun show(activity: Activity?) {
|
||||
activity?.let {
|
||||
var hintView = retrieveHintViewFromActivity(it)
|
||||
if (hintView == null) {
|
||||
hintView = ImHintView(it)
|
||||
hintView.showDot(ImManager.shouldShowFloatingWindowDot)
|
||||
|
||||
val decorView = it.window.decorView as ViewGroup
|
||||
it.runOnUiThread {
|
||||
decorView.addView(hintView)
|
||||
}
|
||||
} else {
|
||||
hintView.showDot(ImManager.shouldShowFloatingWindowDot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun dismiss(activity: Activity?) {
|
||||
activity?.let {
|
||||
clearCurrent(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun retrieveHintViewFromActivity(activity: Activity?) : ImHintView? {
|
||||
(activity?.window?.decorView as? ViewGroup)?.let {
|
||||
for (i in 0..it.childCount) {
|
||||
val childView = if (it.getChildAt(i) is ImHintView) it.getChildAt(i) as ImHintView else null
|
||||
if (childView != null && childView.windowToken != null) {
|
||||
return childView
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun clearCurrent(activity: Activity?) {
|
||||
(activity?.window?.decorView as? ViewGroup)?.let {
|
||||
for (i in 0..it.childCount) {
|
||||
val childView = if (it.getChildAt(i) is ImHintView) it.getChildAt(i) as ImHintView else null
|
||||
if (childView != null && childView.windowToken != null) {
|
||||
ViewCompat.animate(childView).alpha(0f).withEndAction(getRemoveViewRunnable(childView))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRemoveViewRunnable(childView: View?): Runnable {
|
||||
return Runnable {
|
||||
childView?.let {
|
||||
(childView.parent as? ViewGroup)?.removeView(childView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
84
app/src/main/java/com/gh/common/im/ImHintView.kt
Normal file
84
app/src/main/java/com/gh/common/im/ImHintView.kt
Normal file
@ -0,0 +1,84 @@
|
||||
package com.gh.common.im
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.support.v4.view.ViewCompat
|
||||
import android.util.AttributeSet
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.TypedValue
|
||||
import android.view.KeyCharacterMap
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.ViewConfiguration
|
||||
import android.widget.RelativeLayout
|
||||
import com.gh.gamecenter.R
|
||||
import kotlinx.android.synthetic.main.view_im_hint.view.*
|
||||
|
||||
class ImHintView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0)
|
||||
: RelativeLayout(context, attrs, defStyle) {
|
||||
|
||||
init {
|
||||
inflate(context, R.layout.view_im_hint, this)
|
||||
|
||||
ViewCompat.setTranslationZ(this, Integer.MAX_VALUE.toFloat() - 1)
|
||||
|
||||
ivContainer.setOnClickListener {
|
||||
if (context is Activity) {
|
||||
ImManager.startChatActivity(context)
|
||||
ImManager.removeNotification()
|
||||
}
|
||||
}
|
||||
|
||||
val lp = ivContainer.layoutParams as RelativeLayout.LayoutParams
|
||||
|
||||
lp.setMargins(0, 0, dp2px(30f), dp2px(106f) + retrieveNavigationHeight())
|
||||
}
|
||||
|
||||
fun showDot(show: Boolean) {
|
||||
if (show) {
|
||||
unreadDot.visibility = View.VISIBLE
|
||||
} else {
|
||||
unreadDot.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun dp2px(dp: Float): Int {
|
||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.applicationContext.resources.displayMetrics).toInt()
|
||||
}
|
||||
|
||||
private fun hasSoftKeys(): Boolean {
|
||||
if (context !is Activity) return false
|
||||
|
||||
val hasSoftwareKeys: Boolean
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
val d = (context as Activity).windowManager.defaultDisplay
|
||||
|
||||
val realDisplayMetrics = DisplayMetrics()
|
||||
d.getRealMetrics(realDisplayMetrics)
|
||||
|
||||
val realHeight = realDisplayMetrics.heightPixels
|
||||
val realWidth = realDisplayMetrics.widthPixels
|
||||
|
||||
val displayMetrics = DisplayMetrics()
|
||||
d.getMetrics(displayMetrics)
|
||||
|
||||
val displayHeight = displayMetrics.heightPixels
|
||||
val displayWidth = displayMetrics.widthPixels
|
||||
|
||||
hasSoftwareKeys = realWidth - displayWidth > 0 || realHeight - displayHeight > 0
|
||||
} else {
|
||||
val hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey()
|
||||
val hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK)
|
||||
hasSoftwareKeys = !hasMenuKey && !hasBackKey
|
||||
}
|
||||
return hasSoftwareKeys
|
||||
}
|
||||
|
||||
private fun retrieveNavigationHeight(): Int {
|
||||
val resources = context.resources
|
||||
val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
|
||||
return if (resourceId > 0 && hasSoftKeys()) resources.getDimensionPixelSize(resourceId) else 0
|
||||
}
|
||||
}
|
||||
146
app/src/main/java/com/gh/common/im/ImManager.kt
Normal file
146
app/src/main/java/com/gh/common/im/ImManager.kt
Normal file
@ -0,0 +1,146 @@
|
||||
package com.gh.common.im
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import com.gh.base.CurrentActivityHolder
|
||||
import com.gh.common.util.SPUtils
|
||||
import com.gh.gamecenter.MainActivity
|
||||
import com.gh.gamecenter.MessageActivity
|
||||
import com.gh.gamecenter.SuggestSelectActivity
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.m7.imkfsdk.KfStartHelper
|
||||
import com.moor.imkf.ChatListener
|
||||
import com.moor.imkf.IMChat
|
||||
import com.moor.imkf.IMChatManager
|
||||
import com.moor.imkf.IMMessage
|
||||
|
||||
object ImManager {
|
||||
|
||||
const val IM_KEY = "893be270-9c75-11e8-a344-212975ba32b9"
|
||||
const val SP_FLOATING_WINDOW_KEY = "IM_FLOATING_WINDOW"
|
||||
const val SP_FLOATING_WINDOW_DOT_KEY = "IM_FLOATING_WINDOW_DOT"
|
||||
|
||||
var shouldShowFloatingWindow = false
|
||||
var shouldShowFloatingWindowDot = false
|
||||
|
||||
@JvmStatic
|
||||
fun attachIm() {
|
||||
try {
|
||||
if (UserManager.getInstance().userInfoEntity != null) {
|
||||
IMChatManager.getInstance().init(
|
||||
HaloApp.getInstance().application,
|
||||
ImReceiver.UNIQUE_BROADCAST_ACTION,
|
||||
IM_KEY,
|
||||
UserManager.getInstance().userInfoEntity.name + "(" + UserManager.getInstance().userId + ")",
|
||||
UserManager.getInstance().userId)
|
||||
|
||||
shouldShowFloatingWindow = SPUtils.getBoolean(SP_FLOATING_WINDOW_KEY + UserManager.getInstance().userId)
|
||||
shouldShowFloatingWindowDot = SPUtils.getBoolean(SP_FLOATING_WINDOW_DOT_KEY + UserManager.getInstance().userId)
|
||||
updateFloatingWindow()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun detachIm() {
|
||||
try {
|
||||
IMChatManager.getInstance().quitSDk()
|
||||
shouldShowFloatingWindow = false
|
||||
updateFloatingWindow()
|
||||
removeNotification()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun startChatActivity(activity: Activity) {
|
||||
if (!UserManager.getInstance().userId.isNullOrEmpty()) {
|
||||
try {
|
||||
SPUtils.setBoolean(SP_FLOATING_WINDOW_DOT_KEY + UserManager.getInstance().userId, false)
|
||||
shouldShowFloatingWindowDot = false
|
||||
val chatHelper = KfStartHelper(activity, UserManager.getInstance().userInfoEntity.icon)
|
||||
chatHelper.initSdkChat(
|
||||
ImReceiver.UNIQUE_BROADCAST_ACTION,
|
||||
IM_KEY,
|
||||
UserManager.getInstance().userInfoEntity.name + "(" + UserManager.getInstance().userId + ")",
|
||||
UserManager.getInstance().userId)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun showFloatingWindow() {
|
||||
updateShouldShowFloatingWindow(true)
|
||||
updateShouldShowFloatingWindowDot(true)
|
||||
updateFloatingWindow()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun dismissFloatingWindow() {
|
||||
updateShouldShowFloatingWindow(false)
|
||||
updateShouldShowFloatingWindowDot(false)
|
||||
updateFloatingWindow()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun removeNotification() {
|
||||
val notificationManager = HaloApp.getInstance().application?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.cancel(ImReceiver.NOTIFICATION_ID)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun updateFloatingWindow() {
|
||||
try {
|
||||
CurrentActivityHolder.getCurrentActivity()?.let {
|
||||
if (isActivityValid(it)) {
|
||||
if (shouldShowFloatingWindow) {
|
||||
ImHintHelper.show(it)
|
||||
} else {
|
||||
ImHintHelper.dismiss(it)
|
||||
removeNotification()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun sendFeedbackMessage(message: String) {
|
||||
val fromToMessage = IMMessage.createTxtMessage(message)
|
||||
HaloApp.getInstance().mainExecutor.execute {
|
||||
IMChat.getInstance().sendMessage(fromToMessage, object : ChatListener {
|
||||
override fun onProgress(p0: Int) {}
|
||||
override fun onSuccess() {}
|
||||
override fun onFailed() {}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun updateShouldShowFloatingWindow(show: Boolean) {
|
||||
SPUtils.setBoolean(SP_FLOATING_WINDOW_KEY + UserManager.getInstance().userId, show)
|
||||
shouldShowFloatingWindow = show
|
||||
}
|
||||
|
||||
fun updateShouldShowFloatingWindowDot(show: Boolean) {
|
||||
SPUtils.setBoolean(SP_FLOATING_WINDOW_DOT_KEY + UserManager.getInstance().userId, show)
|
||||
shouldShowFloatingWindowDot = show
|
||||
}
|
||||
|
||||
private fun isActivityValid(activity: Activity): Boolean {
|
||||
return when (activity) {
|
||||
is MainActivity -> true
|
||||
is SuggestSelectActivity -> true
|
||||
is MessageActivity -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
76
app/src/main/java/com/gh/common/im/ImReceiver.kt
Normal file
76
app/src/main/java/com/gh/common/im/ImReceiver.kt
Normal file
@ -0,0 +1,76 @@
|
||||
package com.gh.common.im
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.support.v4.app.NotificationCompat
|
||||
import com.gh.base.CurrentActivityHolder
|
||||
import com.gh.gamecenter.R
|
||||
import com.m7.imkfsdk.chat.ChatActivity
|
||||
import com.moor.imkf.IMChatManager
|
||||
import com.moor.imkf.utils.Utils
|
||||
|
||||
class ImReceiver : BroadcastReceiver() {
|
||||
|
||||
companion object {
|
||||
const val UNIQUE_BROADCAST_ACTION = "com.gh.im"
|
||||
const val NOTIFICATION_ID: Int = 987321
|
||||
}
|
||||
|
||||
var notificationManager: NotificationManager? = null
|
||||
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
intent?.let {
|
||||
if (intent.action == IMChatManager.NEW_MSG_ACTION) {
|
||||
notificationManager = context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
// 判断 ImActivity 是否在最顶端
|
||||
if (CurrentActivityHolder.getCurrentActivity() is ChatActivity) {
|
||||
ImManager.showFloatingWindow()
|
||||
ImManager.updateShouldShowFloatingWindowDot(false)
|
||||
} else {
|
||||
val contentIntent = Intent(Utils.getApp(), ChatActivity::class.java)
|
||||
|
||||
contentIntent.putExtra("PeerId", "")
|
||||
contentIntent.putExtra("type", "peedId")
|
||||
|
||||
contentIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
val resultPendingIntent = PendingIntent.getActivity(
|
||||
Utils.getApp(),
|
||||
0,
|
||||
contentIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
// 新的通知
|
||||
val builder = NotificationCompat.Builder(Utils.getApp(), "Halo_IM")
|
||||
val notification = builder.setTicker("您有新的消息")
|
||||
.setDefaults(Notification.DEFAULT_ALL)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setContentIntent(resultPendingIntent)
|
||||
.setContentTitle("光环助手客服回复")
|
||||
.setContentText("您有新的消息")
|
||||
.setAutoCancel(true)
|
||||
.build()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel("Halo_IM", "Halo_IM", NotificationManager.IMPORTANCE_DEFAULT)
|
||||
notificationManager?.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
if (notification != null) {
|
||||
notificationManager?.notify(NOTIFICATION_ID, notification)
|
||||
ImManager.showFloatingWindow()
|
||||
}
|
||||
}
|
||||
} else if (intent.action == IMChatManager.FINISH_ACTION) {
|
||||
ImManager.dismissFloatingWindow()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
170
app/src/main/java/com/gh/common/notifier/Notifier.kt
Normal file
170
app/src/main/java/com/gh/common/notifier/Notifier.kt
Normal file
@ -0,0 +1,170 @@
|
||||
package com.gh.common.notifier
|
||||
|
||||
import android.app.Activity
|
||||
import android.support.v4.view.ViewCompat
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.gh.common.util.SPUtils
|
||||
import com.gh.gamecenter.*
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
class Notifier private constructor() {
|
||||
|
||||
companion object {
|
||||
|
||||
private var activityWeakReference: WeakReference<Activity>? = null
|
||||
|
||||
private const val SP_VIEWED_NOTIFIER = "viewed_notifier"
|
||||
|
||||
/**
|
||||
* 根据内容决定是否显示 Notifier
|
||||
*/
|
||||
@JvmStatic
|
||||
fun shouldShowNotifier(content: String): Boolean {
|
||||
val viewedNotifierCollection = SPUtils.getString(SP_VIEWED_NOTIFIER)
|
||||
return !viewedNotifierCollection.contains(content)
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记相应内容的 Notifier 已经显示过了
|
||||
*/
|
||||
@JvmStatic
|
||||
fun tagNotifierAsShowed(content: String) {
|
||||
val viewedNotifierCollection = SPUtils.getString(SP_VIEWED_NOTIFIER)
|
||||
if (viewedNotifierCollection.length > 1000) {
|
||||
SPUtils.setString(SP_VIEWED_NOTIFIER, content)
|
||||
} else {
|
||||
SPUtils.setString(SP_VIEWED_NOTIFIER, viewedNotifierCollection + content)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun create(activity: Activity?): Notifier {
|
||||
if (activity == null) {
|
||||
throw IllegalArgumentException("Activity cannot be null!")
|
||||
}
|
||||
|
||||
val notifier = Notifier()
|
||||
|
||||
// Hide current NotifierView, if one is active
|
||||
clearCurrent(activity)
|
||||
|
||||
notifier.setActivity(activity)
|
||||
notifier.notifierView = NotifierView(activity)
|
||||
|
||||
return notifier
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isActivityValid(activity: Activity?): Boolean {
|
||||
if (activity == null) return false
|
||||
|
||||
return when (activity) {
|
||||
is MessageActivity -> false
|
||||
is DownloadManagerActivity -> false
|
||||
is CleanApkActivity -> false
|
||||
is SplashScreenActivity -> false
|
||||
else -> isNotExistInActivity(activity)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isNotExistInActivity(activity: Activity?): Boolean {
|
||||
if (activity == null) return false
|
||||
|
||||
(activity.window?.decorView as? ViewGroup)?.let {
|
||||
//Find all NotifierView Views in Parent layout
|
||||
for (i in 0..it.childCount) {
|
||||
val childView = if (it.getChildAt(i) is NotifierView) it.getChildAt(i) as NotifierView else null
|
||||
if (childView != null && childView.windowToken != null) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the currently showing notifierView view, if one is present
|
||||
*
|
||||
* @param activity The current Activity
|
||||
*/
|
||||
@JvmStatic
|
||||
fun clearCurrent(activity: Activity?) {
|
||||
(activity?.window?.decorView as? ViewGroup)?.let {
|
||||
//Find all NotifierView Views in Parent layout
|
||||
for (i in 0..it.childCount) {
|
||||
val childView = if (it.getChildAt(i) is NotifierView) it.getChildAt(i) as NotifierView else null
|
||||
if (childView != null && childView.windowToken != null) {
|
||||
ViewCompat.animate(childView).alpha(0f).withEndAction(getRemoveViewRunnable(childView))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun hide() {
|
||||
activityWeakReference?.get()?.let { clearCurrent(it) }
|
||||
}
|
||||
|
||||
private fun getRemoveViewRunnable(childView: NotifierView?): Runnable {
|
||||
return Runnable {
|
||||
childView?.let {
|
||||
(childView.parent as? ViewGroup)?.removeView(childView)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var notifierView: NotifierView? = null
|
||||
|
||||
private val activityDecorView: ViewGroup?
|
||||
get() {
|
||||
var decorView: ViewGroup? = null
|
||||
|
||||
activityWeakReference?.get()?.let {
|
||||
decorView = it.window.decorView as ViewGroup
|
||||
}
|
||||
|
||||
return decorView
|
||||
}
|
||||
|
||||
fun show(showVerticalTranslateAnimation: Boolean, delay: Long? = 0): NotifierView? {
|
||||
activityWeakReference?.get()?.let {
|
||||
it.runOnUiThread {
|
||||
activityDecorView?.postDelayed({
|
||||
notifierView?.showVerticalTranslateAnimation = showVerticalTranslateAnimation
|
||||
activityDecorView?.addView(notifierView)
|
||||
}, delay!!)
|
||||
}
|
||||
}
|
||||
|
||||
return notifierView
|
||||
}
|
||||
|
||||
fun setIcon(url: String?): Notifier {
|
||||
url?.let { notifierView?.setIcon(it) }
|
||||
return this
|
||||
}
|
||||
|
||||
fun setText(text: String?): Notifier {
|
||||
notifierView?.setText(text)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
fun setDuration(time: Long): Notifier {
|
||||
notifierView?.duration = time
|
||||
return this
|
||||
}
|
||||
|
||||
fun setOnClickListener(onClickListener: View.OnClickListener): Notifier {
|
||||
notifierView?.findViewById<View>(R.id.cardView)?.setOnClickListener(onClickListener)
|
||||
return this
|
||||
}
|
||||
|
||||
private fun setActivity(activity: Activity) {
|
||||
activityWeakReference = WeakReference(activity)
|
||||
}
|
||||
}
|
||||
332
app/src/main/java/com/gh/common/notifier/NotifierView.kt
Normal file
332
app/src/main/java/com/gh/common/notifier/NotifierView.kt
Normal file
@ -0,0 +1,332 @@
|
||||
package com.gh.common.notifier
|
||||
|
||||
import android.animation.*
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.graphics.Path
|
||||
import android.os.Build
|
||||
import android.support.v4.view.ViewCompat
|
||||
import android.text.TextUtils
|
||||
import android.util.AttributeSet
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.*
|
||||
import android.widget.FrameLayout
|
||||
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) {
|
||||
|
||||
companion object {
|
||||
const val SCALE_MINI = 0.2F
|
||||
const val SCALE_DEFAULT = 1F
|
||||
|
||||
const val DEFAULT_DURATION = 500L
|
||||
}
|
||||
|
||||
var onShowListener: OnShowNotificationListener? = null
|
||||
var onHideListener: OnHideNotificationListener? = null
|
||||
|
||||
lateinit var expandAnimator: ValueAnimator
|
||||
lateinit var shrinkAnimator: ValueAnimator
|
||||
|
||||
lateinit var translateUpAnimator: ObjectAnimator
|
||||
lateinit var translateDownAnimator: ObjectAnimator
|
||||
|
||||
lateinit var translateToLeftAnimator: ObjectAnimator
|
||||
lateinit var translateToRightAnimator: ObjectAnimator
|
||||
|
||||
lateinit var zoomInAnimator: ObjectAnimator
|
||||
lateinit var zoomOutAnimator: ObjectAnimator
|
||||
|
||||
var showAnimatorSet: AnimatorSet
|
||||
var hideAnimatorSet: AnimatorSet
|
||||
|
||||
var rightToLeftPath: Path
|
||||
var leftToRightPath: Path
|
||||
|
||||
var veryRight: Float = 0F
|
||||
var veryBottom: Float = 0F
|
||||
var centerX: Float = 0F
|
||||
|
||||
var navigationHeight = 0
|
||||
|
||||
var textWidth: Int = 0
|
||||
var cardViewWidth: Int = 0
|
||||
var verticalAnimationOffset: Int = 0
|
||||
|
||||
var duration = DEFAULT_DURATION
|
||||
|
||||
var showVerticalTranslateAnimation: Boolean = true
|
||||
|
||||
init {
|
||||
inflate(context, R.layout.view_notifier, this)
|
||||
|
||||
ViewCompat.setTranslationZ(this, Integer.MAX_VALUE.toFloat())
|
||||
|
||||
cardView.scaleX = SCALE_MINI
|
||||
cardView.scaleY = SCALE_MINI
|
||||
|
||||
verticalAnimationOffset = dp2px(100F)
|
||||
|
||||
navigationHeight = retrieveNavigationHeight()
|
||||
|
||||
rightToLeftPath = Path()
|
||||
leftToRightPath = Path()
|
||||
|
||||
showAnimatorSet = AnimatorSet()
|
||||
hideAnimatorSet = AnimatorSet()
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
|
||||
centerX = (left + right - cardViewWidth) / 2F
|
||||
|
||||
// TODO Provide method to change these absolute offset.
|
||||
val r = right - dp2px(72F).toFloat()
|
||||
val b = bottom - dp2px(145F).toFloat() - navigationHeight
|
||||
|
||||
// Only set
|
||||
if (veryRight != r || veryBottom != b) {
|
||||
veryRight = r
|
||||
veryBottom = b
|
||||
|
||||
rightToLeftPath.moveTo(r, b)
|
||||
rightToLeftPath.lineTo(centerX, b)
|
||||
|
||||
leftToRightPath.moveTo(centerX, b)
|
||||
leftToRightPath.lineTo(r, b)
|
||||
|
||||
initAnimator()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initAnimator() {
|
||||
expandAnimator = ValueAnimator.ofFloat(0F, 1F)
|
||||
expandAnimator.duration = DEFAULT_DURATION
|
||||
expandAnimator.addUpdateListener { a ->
|
||||
val progress = a?.animatedValue as Float
|
||||
tvText.width = (textWidth * progress).toInt()
|
||||
}
|
||||
expandAnimator.doOnEnd {
|
||||
enableSwipeToDismiss()
|
||||
shrinkAfterDelay()
|
||||
}
|
||||
|
||||
shrinkAnimator = ValueAnimator.ofFloat(1F, 0F)
|
||||
shrinkAnimator.duration = DEFAULT_DURATION
|
||||
shrinkAnimator.addUpdateListener { a ->
|
||||
val progress = a?.animatedValue as Float
|
||||
tvText.width = (textWidth * progress).toInt()
|
||||
}
|
||||
shrinkAnimator.doOnEnd {
|
||||
val lp = FrameLayout.LayoutParams(cardView.layoutParams)
|
||||
lp.gravity = Gravity.NO_GRAVITY
|
||||
cardView.layoutParams = lp
|
||||
|
||||
disableSwipeToDismiss()
|
||||
}
|
||||
|
||||
translateToLeftAnimator = ObjectAnimator.ofFloat(cardView, "translationX", veryRight, centerX)
|
||||
translateToLeftAnimator.duration = DEFAULT_DURATION
|
||||
translateToLeftAnimator.doOnEnd {
|
||||
onShowListener?.onShow()
|
||||
|
||||
val lp = FrameLayout.LayoutParams(cardView.layoutParams)
|
||||
lp.gravity = Gravity.CENTER_HORIZONTAL
|
||||
cardView.layoutParams = lp
|
||||
cardView.translationX = 0f
|
||||
|
||||
expandAnimator.start()
|
||||
}
|
||||
|
||||
translateToRightAnimator = ObjectAnimator.ofFloat(cardView, "translationX", centerX, veryRight)
|
||||
translateToRightAnimator.duration = DEFAULT_DURATION
|
||||
|
||||
translateUpAnimator = ObjectAnimator.ofFloat(cardView, "translationY", veryBottom + verticalAnimationOffset, veryBottom)
|
||||
translateUpAnimator.duration = DEFAULT_DURATION
|
||||
translateUpAnimator.doOnStart { cardView.translationX = veryRight }
|
||||
|
||||
translateDownAnimator = ObjectAnimator.ofFloat(cardView, "translationY", veryBottom, veryBottom + verticalAnimationOffset)
|
||||
translateDownAnimator.duration = DEFAULT_DURATION
|
||||
|
||||
zoomInAnimator = ObjectAnimator.ofPropertyValuesHolder(cardView, PropertyValuesHolder.ofFloat("scaleX", SCALE_DEFAULT),
|
||||
PropertyValuesHolder.ofFloat("scaleY", SCALE_DEFAULT))
|
||||
zoomInAnimator.duration = DEFAULT_DURATION
|
||||
zoomInAnimator.doOnStart { cardView.translationX = veryRight }
|
||||
zoomInAnimator.doOnStart { cardView.translationY = veryBottom }
|
||||
|
||||
zoomOutAnimator = ObjectAnimator.ofPropertyValuesHolder(cardView, PropertyValuesHolder.ofFloat("scaleX", SCALE_MINI),
|
||||
PropertyValuesHolder.ofFloat("scaleY", SCALE_MINI))
|
||||
zoomOutAnimator.duration = DEFAULT_DURATION
|
||||
zoomOutAnimator.doOnEnd { removeFromParent() }
|
||||
|
||||
if (showVerticalTranslateAnimation) {
|
||||
showAnimatorSet.play(translateUpAnimator).with(zoomInAnimator).before(translateToLeftAnimator)
|
||||
} else {
|
||||
showAnimatorSet.play(zoomInAnimator).before(translateToLeftAnimator)
|
||||
}
|
||||
showAnimatorSet.start()
|
||||
}
|
||||
|
||||
private fun enableSwipeToDismiss() {
|
||||
cardView?.setOnTouchListener(SwipeDismissTouchListener(cardView, object : SwipeDismissTouchListener.DismissCallbacks {
|
||||
override fun canDismiss(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDismiss(view: View) {
|
||||
removeFromParent()
|
||||
}
|
||||
|
||||
override fun onTouch(view: View, touch: Boolean) {
|
||||
// Ignore.
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
private fun disableSwipeToDismiss() {
|
||||
cardView?.setOnTouchListener(null)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
|
||||
showAnimatorSet.cancel()
|
||||
hideAnimatorSet.cancel()
|
||||
|
||||
removeAllListeners(expandAnimator,
|
||||
shrinkAnimator,
|
||||
translateUpAnimator,
|
||||
translateDownAnimator,
|
||||
translateToLeftAnimator,
|
||||
translateToRightAnimator)
|
||||
}
|
||||
|
||||
private fun removeAllListeners(vararg ts: Animator) {
|
||||
for (a in ts) {
|
||||
a.removeAllListeners()
|
||||
if (a is ValueAnimator) {
|
||||
a.removeAllUpdateListeners()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
performClick()
|
||||
return super.onTouchEvent(event)
|
||||
}
|
||||
|
||||
private fun shrinkAfterDelay() {
|
||||
postDelayed({ shrink() }, duration)
|
||||
}
|
||||
|
||||
fun shrink() {
|
||||
shrinkAnimator.doOnEnd { hide() }
|
||||
shrinkAnimator.start()
|
||||
}
|
||||
|
||||
private fun removeFromParent() {
|
||||
clearAnimation()
|
||||
visibility = View.GONE
|
||||
|
||||
postDelayed(object : Runnable {
|
||||
override fun run() {
|
||||
try {
|
||||
if (parent == null) {
|
||||
Log.e(javaClass.simpleName, "getParent() returning Null")
|
||||
} else {
|
||||
try {
|
||||
(parent as ViewGroup).removeView(this@NotifierView)
|
||||
|
||||
onHideListener?.onHide()
|
||||
} catch (ex: Exception) {
|
||||
Log.e(javaClass.simpleName, "Cannot remove from parent layout")
|
||||
}
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
Log.e(javaClass.simpleName, Log.getStackTraceString(ex))
|
||||
}
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
fun setText(text: String?) {
|
||||
if (!TextUtils.isEmpty(text)) {
|
||||
tvText.text = text
|
||||
tvText.measure(0, 0)
|
||||
textWidth = tvText.measuredWidth
|
||||
tvText.width = 0
|
||||
|
||||
cardView.measure(0, 0)
|
||||
cardViewWidth = cardView.measuredWidth
|
||||
}
|
||||
}
|
||||
|
||||
fun hide() {
|
||||
if (showVerticalTranslateAnimation) {
|
||||
hideAnimatorSet.play(translateDownAnimator).with(zoomOutAnimator).after(translateToRightAnimator)
|
||||
} else {
|
||||
hideAnimatorSet.play(zoomOutAnimator).after(translateToRightAnimator)
|
||||
}
|
||||
hideAnimatorSet.start()
|
||||
}
|
||||
|
||||
fun setIcon(url: String) {
|
||||
ImageUtils.display(ivIcon, url)
|
||||
}
|
||||
|
||||
private fun dp2px(dp: Float): Int {
|
||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.applicationContext.resources.displayMetrics).toInt()
|
||||
}
|
||||
|
||||
private fun hasSoftKeys(): Boolean {
|
||||
if (context !is Activity) return false
|
||||
|
||||
val hasSoftwareKeys: Boolean
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
val d = (context as Activity).windowManager.defaultDisplay
|
||||
|
||||
val realDisplayMetrics = DisplayMetrics()
|
||||
d.getRealMetrics(realDisplayMetrics)
|
||||
|
||||
val realHeight = realDisplayMetrics.heightPixels
|
||||
val realWidth = realDisplayMetrics.widthPixels
|
||||
|
||||
val displayMetrics = DisplayMetrics()
|
||||
d.getMetrics(displayMetrics)
|
||||
|
||||
val displayHeight = displayMetrics.heightPixels
|
||||
val displayWidth = displayMetrics.widthPixels
|
||||
|
||||
hasSoftwareKeys = realWidth - displayWidth > 0 || realHeight - displayHeight > 0
|
||||
} else {
|
||||
val hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey()
|
||||
val hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK)
|
||||
hasSoftwareKeys = !hasMenuKey && !hasBackKey
|
||||
}
|
||||
return hasSoftwareKeys
|
||||
}
|
||||
|
||||
private fun retrieveNavigationHeight(): Int {
|
||||
val resources = context.resources
|
||||
val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
|
||||
return if (resourceId > 0 && hasSoftKeys()) resources.getDimensionPixelSize(resourceId) else 0
|
||||
}
|
||||
|
||||
interface OnShowNotificationListener {
|
||||
fun onShow()
|
||||
}
|
||||
|
||||
interface OnHideNotificationListener {
|
||||
fun onHide()
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,238 @@
|
||||
package com.gh.common.notifier
|
||||
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Modifications Copyright (C) 2017 David Kwon
|
||||
*/
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.ValueAnimator
|
||||
import android.os.Build
|
||||
import android.support.annotation.RequiresApi
|
||||
import android.view.MotionEvent
|
||||
import android.view.VelocityTracker
|
||||
import android.view.View
|
||||
import android.view.ViewConfiguration
|
||||
|
||||
/**
|
||||
* A [View.OnTouchListener] that makes any [View] dismissable when the
|
||||
* user swipes (drags her finger) horizontally across the view.
|
||||
*
|
||||
* @param view The view to make dismissable.
|
||||
* @param callbacks The callback to trigger when the user has indicated that she would like to
|
||||
* dismiss this view.
|
||||
*/
|
||||
class SwipeDismissTouchListener(
|
||||
private val mView: View,
|
||||
private val mCallbacks: DismissCallbacks) : View.OnTouchListener {
|
||||
|
||||
// Cached ViewConfiguration and system-wide constant values
|
||||
private val mSlop: Int
|
||||
private val mMinFlingVelocity: Int
|
||||
private val mAnimationTime: Long
|
||||
private var mViewWidth = 1 // 1 and not 0 to prevent dividing by zero
|
||||
|
||||
// Transient properties
|
||||
private var mDownX: Float = 0.toFloat()
|
||||
private var mDownY: Float = 0.toFloat()
|
||||
private var mSwiping: Boolean = false
|
||||
private var mSwipingSlop: Int = 0
|
||||
private var mVelocityTracker: VelocityTracker? = null
|
||||
private var mTranslationX: Float = 0.toFloat()
|
||||
|
||||
init {
|
||||
val vc = ViewConfiguration.get(mView.context)
|
||||
mSlop = vc.scaledTouchSlop
|
||||
mMinFlingVelocity = vc.scaledMinimumFlingVelocity * 16
|
||||
mAnimationTime = mView.context.resources.getInteger(
|
||||
android.R.integer.config_shortAnimTime).toLong()
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB_MR1)
|
||||
override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
|
||||
// offset because the view is translated during swipe
|
||||
motionEvent.offsetLocation(mTranslationX, 0f)
|
||||
|
||||
if (mViewWidth < 2) {
|
||||
mViewWidth = mView.width
|
||||
}
|
||||
|
||||
when (motionEvent.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
mDownX = motionEvent.rawX
|
||||
mDownY = motionEvent.rawY
|
||||
if (mCallbacks.canDismiss()) {
|
||||
mVelocityTracker = VelocityTracker.obtain()
|
||||
mVelocityTracker!!.addMovement(motionEvent)
|
||||
}
|
||||
mCallbacks.onTouch(view, true)
|
||||
return false
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
mVelocityTracker?.run {
|
||||
val deltaX = motionEvent.rawX - mDownX
|
||||
this.addMovement(motionEvent)
|
||||
this.computeCurrentVelocity(1000)
|
||||
val velocityX = this.xVelocity
|
||||
val absVelocityX = Math.abs(velocityX)
|
||||
val absVelocityY = Math.abs(this.yVelocity)
|
||||
var dismiss = false
|
||||
var dismissRight = false
|
||||
if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) {
|
||||
dismiss = true
|
||||
dismissRight = deltaX > 0
|
||||
} else if (mMinFlingVelocity <= absVelocityX && absVelocityY < absVelocityX && mSwiping) {
|
||||
// dismiss only if flinging in the same direction as dragging
|
||||
dismiss = velocityX < 0 == deltaX < 0
|
||||
dismissRight = this.xVelocity > 0
|
||||
}
|
||||
if (dismiss) {
|
||||
// dismiss
|
||||
mView.animate()
|
||||
.translationX((if (dismissRight) mViewWidth else -mViewWidth).toFloat())
|
||||
.alpha(0f)
|
||||
.setDuration(mAnimationTime)
|
||||
.setListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
performDismiss()
|
||||
}
|
||||
})
|
||||
} else if (mSwiping) {
|
||||
// cancel
|
||||
mView.animate()
|
||||
.translationX(0f)
|
||||
.alpha(1f)
|
||||
.setDuration(mAnimationTime)
|
||||
.setListener(null)
|
||||
mCallbacks.onTouch(view, false)
|
||||
}
|
||||
this.recycle()
|
||||
mVelocityTracker = null
|
||||
mTranslationX = 0f
|
||||
mDownX = 0f
|
||||
mDownY = 0f
|
||||
mSwiping = false
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_CANCEL -> {
|
||||
mVelocityTracker?.run {
|
||||
mView.animate()
|
||||
.translationX(0f)
|
||||
.alpha(1f)
|
||||
.setDuration(mAnimationTime)
|
||||
.setListener(null)
|
||||
this.recycle()
|
||||
mVelocityTracker = null
|
||||
mTranslationX = 0f
|
||||
mDownX = 0f
|
||||
mDownY = 0f
|
||||
mSwiping = false
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
mVelocityTracker?.run {
|
||||
this.addMovement(motionEvent)
|
||||
val deltaX = motionEvent.rawX - mDownX
|
||||
val deltaY = motionEvent.rawY - mDownY
|
||||
if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) {
|
||||
mSwiping = true
|
||||
mSwipingSlop = if (deltaX > 0) mSlop else -mSlop
|
||||
mView.parent.requestDisallowInterceptTouchEvent(true)
|
||||
|
||||
// Cancel listview's touch
|
||||
val cancelEvent = MotionEvent.obtain(motionEvent)
|
||||
cancelEvent.action = MotionEvent.ACTION_CANCEL or (motionEvent.actionIndex shl MotionEvent.ACTION_POINTER_INDEX_SHIFT)
|
||||
mView.onTouchEvent(cancelEvent)
|
||||
cancelEvent.recycle()
|
||||
}
|
||||
|
||||
if (mSwiping) {
|
||||
mTranslationX = deltaX
|
||||
mView.translationX = deltaX - mSwipingSlop
|
||||
// TODO: use an ease-out interpolator or such
|
||||
mView.alpha = Math.max(0f, Math.min(1f,
|
||||
1f - 2f * Math.abs(deltaX) / mViewWidth))
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
view.performClick()
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
|
||||
private fun performDismiss() {
|
||||
// Animate the dismissed view to zero-height and then fire the dismiss callback.
|
||||
// This triggers layout on each animation frame; in the future we may want to do something
|
||||
// smarter and more performant.
|
||||
|
||||
val lp = mView.layoutParams
|
||||
val originalHeight = mView.height
|
||||
|
||||
val animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime)
|
||||
|
||||
animator.addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
mCallbacks.onDismiss(mView)
|
||||
// Reset view presentation
|
||||
mView.alpha = 1f
|
||||
mView.translationX = 0f
|
||||
lp.height = originalHeight
|
||||
mView.layoutParams = lp
|
||||
}
|
||||
})
|
||||
|
||||
animator.addUpdateListener { valueAnimator ->
|
||||
lp.height = valueAnimator.animatedValue as Int
|
||||
mView.layoutParams = lp
|
||||
}
|
||||
|
||||
animator.start()
|
||||
}
|
||||
|
||||
/**
|
||||
* The callback interface used by [SwipeDismissTouchListener] to inform its client
|
||||
* about a successful dismissal of the view for which it was created.
|
||||
*/
|
||||
interface DismissCallbacks {
|
||||
/**
|
||||
* Called to determine whether the view can be dismissed.
|
||||
*
|
||||
* @return boolean The view can dismiss.
|
||||
*/
|
||||
fun canDismiss(): Boolean
|
||||
|
||||
/**
|
||||
* Called when the user has indicated they she would like to dismiss the view.
|
||||
*
|
||||
* @param view The originating [View]
|
||||
*/
|
||||
fun onDismiss(view: View)
|
||||
|
||||
/**
|
||||
* Called when the user touches the view or release the view.
|
||||
*
|
||||
* @param view The originating [View]
|
||||
* @param touch The view is being touched.
|
||||
*/
|
||||
fun onTouch(view: View, touch: Boolean)
|
||||
}
|
||||
}
|
||||
128
app/src/main/java/com/gh/common/util/Animator.kt
Normal file
128
app/src/main/java/com/gh/common/util/Animator.kt
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.gh.common.util
|
||||
|
||||
import android.animation.Animator
|
||||
import android.support.annotation.RequiresApi
|
||||
|
||||
/**
|
||||
* Since [Android KTX] has not release a stable build yet,
|
||||
* we copy a single class to achieve the same goal.
|
||||
*
|
||||
* You might check the original extension class in the link below.
|
||||
*
|
||||
* https://github.com/android/android-ktx/blob/master/src/main/java/androidx/core/animation/Animator.kt.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add an action which will be invoked when the animation has ended.
|
||||
*
|
||||
* @return the [Animator.AnimatorListener] added to the Animator
|
||||
* @see Animator.end
|
||||
*/
|
||||
fun Animator.doOnEnd(action: (animator: Animator) -> Unit) = addListener(onEnd = action)
|
||||
|
||||
/**
|
||||
* Add an action which will be invoked when the animation has started.
|
||||
*
|
||||
* @return the [Animator.AnimatorListener] added to the Animator
|
||||
* @see Animator.start
|
||||
*/
|
||||
fun Animator.doOnStart(action: (animator: Animator) -> Unit) = addListener(onStart = action)
|
||||
|
||||
/**
|
||||
* Add an action which will be invoked when the animation has been cancelled.
|
||||
*
|
||||
* @return the [Animator.AnimatorListener] added to the Animator
|
||||
* @see Animator.cancel
|
||||
*/
|
||||
fun Animator.doOnCancel(action: (animator: Animator) -> Unit) = addListener(onCancel = action)
|
||||
|
||||
/**
|
||||
* Add an action which will be invoked when the animation has repeated.
|
||||
* @return the [Animator.AnimatorListener] added to the Animator
|
||||
*/
|
||||
fun Animator.doOnRepeat(action: (animator: Animator) -> Unit) = addListener(onRepeat = action)
|
||||
|
||||
/**
|
||||
* Add an action which will be invoked when the animation has resumed after a pause.
|
||||
*
|
||||
* @return the [Animator.AnimatorPauseListener] added to the Animator
|
||||
* @see Animator.resume
|
||||
*/
|
||||
@RequiresApi(19)
|
||||
fun Animator.doOnResume(action: (animator: Animator) -> Unit) = addPauseListener(onResume = action)
|
||||
|
||||
/**
|
||||
* Add an action which will be invoked when the animation has been paused.
|
||||
*
|
||||
* @return the [Animator.AnimatorPauseListener] added to the Animator
|
||||
* @see Animator.pause
|
||||
*/
|
||||
@RequiresApi(19)
|
||||
fun Animator.doOnPause(action: (animator: Animator) -> Unit) = addPauseListener(onPause = action)
|
||||
|
||||
/**
|
||||
* Add a listener to this Animator using the provided actions.
|
||||
*/
|
||||
fun Animator.addListener(
|
||||
onEnd: ((animator: Animator) -> Unit)? = null,
|
||||
onStart: ((animator: Animator) -> Unit)? = null,
|
||||
onCancel: ((animator: Animator) -> Unit)? = null,
|
||||
onRepeat: ((animator: Animator) -> Unit)? = null
|
||||
): Animator.AnimatorListener {
|
||||
val listener = object : Animator.AnimatorListener {
|
||||
override fun onAnimationRepeat(animator: Animator) {
|
||||
onRepeat?.invoke(animator)
|
||||
}
|
||||
|
||||
override fun onAnimationEnd(animator: Animator) {
|
||||
onEnd?.invoke(animator)
|
||||
}
|
||||
|
||||
override fun onAnimationCancel(animator: Animator) {
|
||||
onCancel?.invoke(animator)
|
||||
}
|
||||
|
||||
override fun onAnimationStart(animator: Animator) {
|
||||
onStart?.invoke(animator)
|
||||
}
|
||||
}
|
||||
addListener(listener)
|
||||
return listener
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a pause and resume listener to this Animator using the provided actions.
|
||||
*/
|
||||
@RequiresApi(19)
|
||||
fun Animator.addPauseListener(
|
||||
onResume: ((animator: Animator) -> Unit)? = null,
|
||||
onPause: ((animator: Animator) -> Unit)? = null
|
||||
): Animator.AnimatorPauseListener {
|
||||
val listener = object : Animator.AnimatorPauseListener {
|
||||
override fun onAnimationPause(animator: Animator) {
|
||||
onPause?.invoke(animator)
|
||||
}
|
||||
|
||||
override fun onAnimationResume(animator: Animator) {
|
||||
onResume?.invoke(animator)
|
||||
}
|
||||
}
|
||||
addPauseListener(listener)
|
||||
return listener
|
||||
}
|
||||
@ -22,7 +22,7 @@ public class ApkActiveUtils {
|
||||
ApkEntity apkEntity = apkList.get(i);
|
||||
String packageName = apkEntity.getPackageName();
|
||||
String id = gameEntity.getId();
|
||||
if (!apkEntity.isActive() && !PackageManager.isCanPluggable(id, packageName)) {
|
||||
if (!apkEntity.isActive() && !PackageManager.INSTANCE.isCanPluggable(id, packageName)) {
|
||||
apkList.remove(i);
|
||||
i--;
|
||||
}
|
||||
|
||||
@ -1,143 +0,0 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* 应用程序Activity管理类:用于Activity和Intent相关的管理
|
||||
*
|
||||
* @author CsHeng
|
||||
* @version 1.0
|
||||
*/
|
||||
public class AppManager {
|
||||
|
||||
private static AppManager mInstance;
|
||||
private Stack<Activity> mActivityStack;
|
||||
|
||||
private AppManager() {
|
||||
mActivityStack = new Stack<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 单一实例
|
||||
*/
|
||||
public static AppManager getInstance() {
|
||||
if (mInstance == null) {
|
||||
mInstance = new AppManager();
|
||||
}
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return mActivityStack.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加Activity到堆栈
|
||||
*/
|
||||
public void addActivity(Activity activity) {
|
||||
mActivityStack.add(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前Activity(堆栈中最后一个压入的)
|
||||
*/
|
||||
public Activity currentActivity() {
|
||||
return mActivityStack.lastElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束当前Activity(堆栈中最后一个压入的)
|
||||
*/
|
||||
public void finishActivity() {
|
||||
final Activity activity = mActivityStack.lastElement();
|
||||
finishActivity(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束指定的Activity
|
||||
*/
|
||||
public void finishActivity(Activity activity) {
|
||||
if (activity != null) {
|
||||
mActivityStack.remove(activity);
|
||||
activity.finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束指定类名的Activity
|
||||
*/
|
||||
public void finishActivity(Class<?> cls) {
|
||||
for (Activity activity : mActivityStack) {
|
||||
if (activity.getClass().equals(cls)) {
|
||||
finishActivity(activity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isOnStack(Activity activity) {
|
||||
return mActivityStack.contains(activity);
|
||||
}
|
||||
|
||||
public Intent[] getStartIntents(Intent... intent) {
|
||||
List<Intent> intentList = getCurrentIntents();
|
||||
if (intentList != null) {
|
||||
Collections.addAll(intentList, intent);
|
||||
return intentList.toArray(new Intent[intentList.size()]);
|
||||
}
|
||||
return intent;
|
||||
}
|
||||
|
||||
public List<Intent> getCurrentIntents() {
|
||||
List<Intent> intentList = new ArrayList<>();
|
||||
for (Activity activity : mActivityStack) {
|
||||
intentList.add(activity.getIntent());
|
||||
}
|
||||
return intentList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出应用程序
|
||||
*/
|
||||
public void appExit(Context context) {
|
||||
try {
|
||||
finishAllActivity();
|
||||
/* Need Permission */
|
||||
// ActivityManager activityMgr= (ActivityManager)
|
||||
// context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
// activityMgr.restartPackage(context.getPackageName());
|
||||
// activityMgr.killBackgroundProcesses(context.getPackageName());
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束所有Activity
|
||||
*/
|
||||
public void finishAllActivity() {
|
||||
for (Activity activity : mActivityStack) {
|
||||
activity.finish();
|
||||
}
|
||||
mActivityStack.clear();
|
||||
try {
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (mActivityStack != null) {
|
||||
mActivityStack.clear();
|
||||
}
|
||||
mInstance = null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.lightgame.utils.Utils;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import retrofit2.HttpException;
|
||||
|
||||
/**
|
||||
* Created by khy on 28/12/17.
|
||||
*/
|
||||
|
||||
public class AskErrorResponseUtils {
|
||||
|
||||
public static void errorResponseControl(Context context, HttpException e) {
|
||||
if (e == null) return;
|
||||
int code = e.code();
|
||||
try {
|
||||
if (code == 403) {
|
||||
JSONObject object = new JSONObject(e.response().errorBody().string());
|
||||
int errorCode = object.getInt("code");
|
||||
switch (errorCode) {
|
||||
case 403001:
|
||||
Utils.toast(context, "标签名称太长了");
|
||||
break;
|
||||
case 403002:
|
||||
Utils.toast(context, "已经被邀请了");
|
||||
break;
|
||||
case 403003:
|
||||
Utils.toast(context, "每天最多只能邀请10次");
|
||||
break;
|
||||
case 403004:
|
||||
Utils.toast(context, "客户端提供的ID无效(空/无效ID)");
|
||||
break;
|
||||
case 403005:
|
||||
Utils.toast(context, "已经回答过了(限制频率)");
|
||||
break;
|
||||
case 403006:
|
||||
Utils.toast(context, "图片数量达到限制点");
|
||||
break;
|
||||
case 403007:
|
||||
Utils.toast(context, "不合法的用户");
|
||||
break;
|
||||
case 403008:
|
||||
Utils.toast(context, "已投票");
|
||||
break;
|
||||
case 403009:
|
||||
Utils.toast(context, "已经收藏过了");
|
||||
break;
|
||||
case 403010:
|
||||
Utils.toast(context, "无效的标签栏");
|
||||
break;
|
||||
case 403011:
|
||||
Utils.toast(context, "标题内容过长");
|
||||
break;
|
||||
case 403012:
|
||||
Utils.toast(context, "描述内容过长");
|
||||
break;
|
||||
case 403013:
|
||||
Utils.toast(context, "无效的标签");
|
||||
break;
|
||||
case 403014:
|
||||
Utils.toast(context, "标签数量太多了");
|
||||
break;
|
||||
case 403015:
|
||||
Utils.toast(context, "已经关注过了");
|
||||
break;
|
||||
default:
|
||||
Utils.toast(context, "网络错误");
|
||||
break;
|
||||
}
|
||||
} else if (code == 401) {
|
||||
JSONObject object = new JSONObject(e.response().errorBody().string());
|
||||
int errorCode = object.getInt("code");
|
||||
if (errorCode == 404001) {
|
||||
Utils.toast(context, "请求的资源不存在");
|
||||
}
|
||||
} else {
|
||||
Utils.toast(context, "网络错误");
|
||||
}
|
||||
|
||||
} catch (Exception e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
49
app/src/main/java/com/gh/common/util/AskUtils.java
Normal file
49
app/src/main/java/com/gh/common/util/AskUtils.java
Normal file
@ -0,0 +1,49 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Created by khy on 27/12/17.
|
||||
*/
|
||||
|
||||
public class AskUtils {
|
||||
|
||||
// public static String voteCountFormat(int voteCount) {
|
||||
// String vote;
|
||||
// if (voteCount >= 10000) {
|
||||
// DecimalFormat df = new DecimalFormat("#.0万");
|
||||
// vote = df.format(voteCount / 10000f);
|
||||
// } else {
|
||||
// vote = String.valueOf(voteCount);
|
||||
// }
|
||||
// return vote;
|
||||
// }
|
||||
|
||||
public static String stripHtml(String htmlStr) {
|
||||
|
||||
|
||||
if (TextUtils.isEmpty(htmlStr)) return "";
|
||||
|
||||
String regEx_script = "<script[^>]*?>[\\s\\S]*?<\\/script>"; //定义script的正则表达式
|
||||
String regEx_style = "<style[^>]*?>[\\s\\S]*?<\\/style>"; //定义style的正则表达式
|
||||
String regEx_html = "<[^>]+>"; //定义HTML标签的正则表达式
|
||||
|
||||
Pattern p_script = Pattern.compile(regEx_script, Pattern.CASE_INSENSITIVE);
|
||||
Matcher m_script = p_script.matcher(htmlStr);
|
||||
htmlStr = m_script.replaceAll(""); //过滤script标签
|
||||
|
||||
Pattern p_style = Pattern.compile(regEx_style, Pattern.CASE_INSENSITIVE);
|
||||
Matcher m_style = p_style.matcher(htmlStr);
|
||||
htmlStr = m_style.replaceAll(""); //过滤style标签
|
||||
|
||||
Pattern p_html = Pattern.compile(regEx_html, Pattern.CASE_INSENSITIVE);
|
||||
Matcher m_html = p_html.matcher(htmlStr);
|
||||
htmlStr = m_html.replaceAll(""); //过滤html标签
|
||||
|
||||
return htmlStr.trim(); //返回文本字符串
|
||||
}
|
||||
|
||||
}
|
||||
@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable;
|
||||
import android.media.ExifInterface;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@ -27,13 +28,16 @@ public class BitmapUtils {
|
||||
*/
|
||||
public static boolean savePicture(String newPath, String filePath) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
// options.inSampleSize = 2;
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(filePath, options);
|
||||
options.inSampleSize = 2;
|
||||
options.inJustDecodeBounds = false;
|
||||
|
||||
File file = new File(newPath);
|
||||
int quality = 80;
|
||||
do {
|
||||
try {
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
|
||||
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, bos);
|
||||
bos.flush();
|
||||
@ -51,6 +55,62 @@ public class BitmapUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存图片
|
||||
*
|
||||
* @param newPath
|
||||
* @param filePath
|
||||
* @return
|
||||
*/
|
||||
public static boolean savePicture(String newPath, String filePath, int compressSize) {
|
||||
if (compressSize > new File(filePath).length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(filePath, options);
|
||||
options.inSampleSize = 2;
|
||||
options.inJustDecodeBounds = false;
|
||||
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 85, bos);
|
||||
float zoom = (float) Math.sqrt(compressSize / (float) bos.toByteArray().length);
|
||||
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setScale(zoom, zoom);
|
||||
Bitmap result = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
bos.reset();
|
||||
bitmap.recycle();
|
||||
|
||||
result.compress(Bitmap.CompressFormat.JPEG, 85, bos);
|
||||
while (bos.toByteArray().length > compressSize) {
|
||||
matrix.setScale(0.9f, 0.9f);
|
||||
|
||||
result = Bitmap.createBitmap(result, 0, 0, result.getWidth(), result.getHeight(), matrix, true);
|
||||
bos.reset();
|
||||
result.compress(Bitmap.CompressFormat.JPEG, 85, bos);
|
||||
}
|
||||
|
||||
File file = new File(newPath);
|
||||
try {
|
||||
BufferedOutputStream fbos = new BufferedOutputStream(new FileOutputStream(file));
|
||||
result.compress(Bitmap.CompressFormat.JPEG, 85, fbos);
|
||||
fbos.flush();
|
||||
fbos.close();
|
||||
|
||||
result.recycle();
|
||||
} catch (Exception e) {
|
||||
file.delete();
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件路径返回bitmap
|
||||
*
|
||||
|
||||
36
app/src/main/java/com/gh/common/util/CheckLoginUtils.java
Normal file
36
app/src/main/java/com/gh/common/util/CheckLoginUtils.java
Normal file
@ -0,0 +1,36 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.gh.gamecenter.LoginActivity;
|
||||
import com.gh.gamecenter.manager.UserManager;
|
||||
import com.lightgame.utils.Utils;
|
||||
|
||||
/**
|
||||
* Created by khy on 28/06/17.
|
||||
*/
|
||||
|
||||
public class CheckLoginUtils {
|
||||
|
||||
public static void checkLogin(final Context context, OnLoginListener listener) {
|
||||
if (TextUtils.isEmpty(UserManager.getInstance().getToken())) {
|
||||
Utils.toast(context, "需要登录");
|
||||
LogUtils.login(context, "dialog", null);
|
||||
LogUtils.login(context, "activity", null);
|
||||
Intent intent = LoginActivity.getIntent(context);
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
listener.onLogin();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isLogin() {
|
||||
return !TextUtils.isEmpty(UserManager.getInstance().getToken());
|
||||
}
|
||||
|
||||
public interface OnLoginListener {
|
||||
void onLogin();
|
||||
}
|
||||
}
|
||||
@ -10,7 +10,11 @@ public class ClassUtils {
|
||||
name = "GameDetailActivity";
|
||||
}
|
||||
try {
|
||||
return Class.forName("com.gh.gamecenter." + name);
|
||||
if (!name.contains("com.gh")) {
|
||||
return Class.forName("com.gh.gamecenter." + name);
|
||||
} else {
|
||||
return Class.forName(name);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user