Repository: bayescom/EasyAds-Android Branch: main Commit: 7c10daf9c407 Files: 225 Total size: 490.2 KB Directory structure: gitextract_wasj5s9r/ ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── docs/ │ ├── banner.md │ ├── draw.md │ ├── easyAds.md │ ├── easyBDManger.md │ ├── easyCsjManger.md │ ├── easyKSManger.md │ ├── faq.md │ ├── full.md │ ├── home.md │ ├── interstital.md │ ├── nativeExpress.md │ ├── reward.md │ ├── splash.md │ └── strategyJsonFormat.md ├── easyads/ │ ├── .gitignore │ ├── build.gradle │ ├── libs/ │ │ ├── Baidu_MobAds_SDK_9352.aar │ │ └── kssdk-ad-3363.aar │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── com/ │ └── easyads/ │ ├── EasyAds.java │ ├── EasyAdsConstant.java │ ├── EasyAdsManger.java │ ├── core/ │ │ ├── EABaseSupplierAdapter.java │ │ ├── EasyAdBaseAdspot.java │ │ ├── banner/ │ │ │ ├── EABannerListener.java │ │ │ ├── EABannerSetting.java │ │ │ └── EasyAdBanner.java │ │ ├── draw/ │ │ │ ├── EADrawListener.java │ │ │ ├── EADrawSetting.java │ │ │ └── EasyAdDraw.java │ │ ├── full/ │ │ │ ├── EAFullScreenVideoListener.java │ │ │ ├── EAFullScreenVideoSetting.java │ │ │ └── EasyAdFullScreenVideo.java │ │ ├── inter/ │ │ │ ├── EAInterstitialListener.java │ │ │ ├── EAInterstitialSetting.java │ │ │ └── EasyAdInterstitial.java │ │ ├── nati/ │ │ │ ├── EANativeExpressListener.java │ │ │ ├── EANativeExpressSetting.java │ │ │ └── EasyAdNativeExpress.java │ │ ├── reward/ │ │ │ ├── EARewardServerCallBackInf.java │ │ │ ├── EARewardVideoListener.java │ │ │ ├── EARewardVideoSetting.java │ │ │ └── EasyAdRewardVideo.java │ │ └── splash/ │ │ ├── EASplashLifeCallback.java │ │ ├── EASplashListener.java │ │ ├── EASplashSetting.java │ │ └── EasyAdSplash.java │ ├── custom/ │ │ ├── EABannerCustomAdapter.java │ │ ├── EADrawCustomAdapter.java │ │ ├── EAFullScreenCustomAdapter.java │ │ ├── EAInterstitialCustomAdapter.java │ │ ├── EANativeExpressCustomAdapter.java │ │ ├── EARewardCustomAdapter.java │ │ └── EASplashCustomAdapter.java │ ├── itf/ │ │ ├── AdCoreAction.java │ │ ├── BaseAdapterEvent.java │ │ ├── BaseEnsureListener.java │ │ └── EABaseADListener.java │ ├── model/ │ │ ├── EALogLevel.java │ │ ├── EasyAdError.java │ │ ├── EasyAdType.java │ │ ├── SdkRule.java │ │ ├── SdkSupplier.java │ │ └── StrategyModel.java │ └── utils/ │ ├── BigDecimalUtil.java │ ├── EAAdapterLoader.java │ ├── EALog.java │ ├── EASplashPlusManager.java │ ├── EASplashZoomOutLayout.java │ ├── EAUtil.java │ └── ScreenUtil.java ├── easybd/ │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── libs/ │ │ └── Baidu_MobAds_SDK_9352.aar │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── easyads/ │ │ └── supplier/ │ │ └── baidu/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── easyads/ │ │ │ └── supplier/ │ │ │ └── baidu/ │ │ │ ├── BDBannerAdapter.java │ │ │ ├── BDFullScreenVideoAdapter.java │ │ │ ├── BDInterstitialAdapter.java │ │ │ ├── BDNativeExpressAdapter.java │ │ │ ├── BDRewardVideoAdapter.java │ │ │ ├── BDSplashAdapter.java │ │ │ ├── BDUtil.java │ │ │ └── EasyBDManager.java │ │ └── res/ │ │ └── values/ │ │ └── strings.xml │ └── test/ │ └── java/ │ └── com/ │ └── easyads/ │ └── supplier/ │ └── baidu/ │ └── ExampleUnitTest.java ├── easycsj/ │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── easyads/ │ │ └── supplier/ │ │ └── csj/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── easyads/ │ │ │ └── supplier/ │ │ │ └── csj/ │ │ │ ├── CSJSplashClickEyeManager.java │ │ │ ├── CsjBannerAdapter.java │ │ │ ├── CsjDrawAdapter.java │ │ │ ├── CsjFullScreenVideoAdapter.java │ │ │ ├── CsjInterstitialAdapter.java │ │ │ ├── CsjNativeExpressAdapter.java │ │ │ ├── CsjRewardVideoAdapter.java │ │ │ ├── CsjSplashAdapter.java │ │ │ ├── CsjUtil.java │ │ │ └── EasyCsjManger.java │ │ └── res/ │ │ ├── values/ │ │ │ └── strings.xml │ │ └── xml/ │ │ └── ea_csj_file_paths.xml │ └── test/ │ └── java/ │ └── com/ │ └── easyads/ │ └── supplier/ │ └── csj/ │ └── ExampleUnitTest.java ├── easyks/ │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── libs/ │ │ └── kssdk-ad-3363.aar │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── easyads/ │ │ └── supplier/ │ │ └── ks/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── easyads/ │ │ │ └── supplier/ │ │ │ └── ks/ │ │ │ ├── EasyKSManager.java │ │ │ ├── KSDrawAdapter.java │ │ │ ├── KSFullScreenVideoAdapter.java │ │ │ ├── KSInterstitialAdapter.java │ │ │ ├── KSNativeExpressAdapter.java │ │ │ ├── KSRewardVideoAdapter.java │ │ │ ├── KSSplashAdapter.java │ │ │ └── KSUtil.java │ │ └── res/ │ │ └── values/ │ │ └── strings.xml │ └── test/ │ └── java/ │ └── com/ │ └── easyads/ │ └── supplier/ │ └── ks/ │ └── ExampleUnitTest.java ├── easyylh/ │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── easyads/ │ │ └── supplier/ │ │ └── ylh/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── easyads/ │ │ │ └── supplier/ │ │ │ └── ylh/ │ │ │ ├── SplashZoomOutManager.java │ │ │ ├── YlhBannerAdapter.java │ │ │ ├── YlhFullScreenVideoAdapter.java │ │ │ ├── YlhInterstitialAdapter.java │ │ │ ├── YlhNativeExpressAdapter.java │ │ │ ├── YlhRewardVideoAdapter.java │ │ │ ├── YlhSplashAdapter.java │ │ │ └── YlhUtil.java │ │ └── res/ │ │ ├── values/ │ │ │ └── strings.xml │ │ └── xml/ │ │ └── ea_ylh_file_path.xml │ └── test/ │ └── java/ │ └── com/ │ └── easyads/ │ └── supplier/ │ └── ylh/ │ └── ExampleUnitTest.java ├── example/ │ ├── .gitignore │ ├── build.gradle │ ├── libs/ │ │ ├── Baidu_MobAds_SDK_9352.aar │ │ ├── circleimageview-2.2.0.aar │ │ ├── kssdk-ad-3363.aar │ │ └── mimo-ad-sdk.aar │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── easyads/ │ │ └── demo/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── assets/ │ │ │ ├── banner_config.json │ │ │ ├── draw_config.json │ │ │ ├── full_config.json │ │ │ ├── interstitial_config.json │ │ │ ├── native_config.json │ │ │ ├── reward_config.json │ │ │ ├── splash_config.json │ │ │ └── splash_cus_config.json │ │ ├── java/ │ │ │ └── com/ │ │ │ └── easyads/ │ │ │ └── demo/ │ │ │ ├── BannerActivity.java │ │ │ ├── BaseActivity.java │ │ │ ├── DrawActivity.java │ │ │ ├── EasyADController.java │ │ │ ├── FullScreenVideoActivity.java │ │ │ ├── InterstitialActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── MyApplication.java │ │ │ ├── NativeExpressActivity.java │ │ │ ├── NativeExpressRecyclerViewActivity.java │ │ │ ├── RewardVideoActivity.java │ │ │ ├── SplashActivity.java │ │ │ ├── SplashDialog.java │ │ │ ├── SplashToMainActivity.java │ │ │ ├── custom/ │ │ │ │ ├── CustomActivity.java │ │ │ │ ├── HuaWeiSplashAdapter.java │ │ │ │ └── XiaoMiSplashAdapter.java │ │ │ ├── draw/ │ │ │ │ ├── FullScreenVideoView.java │ │ │ │ ├── OnViewPagerListener.java │ │ │ │ └── ViewPagerLayoutManager.java │ │ │ └── utils/ │ │ │ ├── BaseCallBack.java │ │ │ ├── Constant.java │ │ │ ├── DialogLogcat.java │ │ │ ├── NormalSetting.java │ │ │ ├── UIUtils.java │ │ │ └── UserPrivacyDialog.java │ │ └── res/ │ │ ├── drawable/ │ │ │ ├── circle_big_red.xml │ │ │ ├── dialog_white_radius_bg.xml │ │ │ ├── ic_launcher_background.xml │ │ │ └── play_arrow.xml │ │ ├── drawable-v24/ │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout/ │ │ │ ├── activity_banner.xml │ │ │ ├── activity_custom.xml │ │ │ ├── activity_draw.xml │ │ │ ├── activity_full_screen_video.xml │ │ │ ├── activity_interstitial.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_native_express.xml │ │ │ ├── activity_native_express_recycler_view.xml │ │ │ ├── activity_reward_video.xml │ │ │ ├── activity_splash_custom_logo.xml │ │ │ ├── activity_splash_to_main.xml │ │ │ ├── dialog_logcat.xml │ │ │ ├── dialog_user_privacy.xml │ │ │ ├── item_data.xml │ │ │ ├── item_express_ad.xml │ │ │ └── item_view_pager.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── values/ │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── ic_launcher_background.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── xml/ │ │ ├── netwc.xml │ │ └── xm_file_paths.xml │ └── test/ │ └── java/ │ └── com/ │ └── easyads/ │ └── demo/ │ └── ExampleUnitTest.java ├── gradle/ │ └── wrapper/ │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Built application 文件 *.apk *.ap_ # ART/Dalvik VM 文件 *.dex # Java class 文件 *.class # Generated 文件夹 bin/ gen/ out/ # Gradle files .gradle/ build/ # 本地配置文件 (sdk path, etc) local.properties # Eclipse生成的Proguard文件夹 proguard/ # 日志文件 *.log # Android Studio导航编辑器临时文件 .navigation/ # Android Studio captures folder captures/ # IntelliJ (IDE配置文件) *.iml .idea/workspace.xml .idea/tasks.xml .idea/gradle.xml .idea/assetWizardSettings.xml .idea/dictionaries .idea/libraries .idea/caches # 密钥储存库文件 # 如果您不想检查密钥库文件,请取消注释以下行 #*.jks # 在Android Studio 2.2及更高版本中生成的外部原生构建文件夹 .externalNativeBuild # Google服务 (e.g. APIs or Firebase) google-services.json # Freeline (Android 平台上的秒级编译方案) freeline.py freeline/ freeline_project_description.json # fastlane (自动化打包框架) fastlane/report.xml fastlane/Preview.html fastlane/screenshots fastlane/test_output fastlane/readme.md # .gitignore_global #################################### ######## OS generated files ######## #################################### .DS_Store .DS_Store? *.swp ._* .Spotlight-V100 .Trashes Icon? ehthumbs.db Thumbs.db #################################### ############# Packages ############# #################################### *.7z *.dmg *.gz *.iso *.jar *.rar *.tar *.zip ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ================================================ FILE: README.md ================================================ ![levelup](levelup.jpg) # 🚀🚀EasyAds项目全面升级,Pro版本全面开源,搭建媒体属于自己的聚合SDK管理平台。 # 了解更多请点击: [EasyAds Pro 主页](http://easyads-pro.bayescom.cn/),开启您的安全、高效、全面的商业化之路。 -------------------------------------------- **❗️❗原EasyAds项目将停止维护❗️❗** # EasyAds-Android 快速指引 [![](https://jitpack.io/v/bayescom/EasyAds-Android.svg)](https://jitpack.io/#bayescom/EasyAds-Android) ## 1. 支持的SDK平台及广告位 | SDK平台 | 开屏 | 激励视频 | 横幅 | 插屏(弹窗) | 模板信息流 | 全屏视频 | draw信息流 | |-------|---|---|---|---|---|---|---| | 穿山甲 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | 优量汇 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | 百青藤 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | 快手 | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ## 2. 快速接入 下面介绍EasyAds的快速接入方法,开发中也可以参考[Example](https://github.com/bayescom/EasyAds-Android/tree/main/example)下的示例工程,快速了解。 ### 2.1 引入SDK **方式一(推荐)**: 使用Gradle 添加依赖,在项目根目录的build.gradle 文件中添加 `JitPack` 仓库 ``` allprojects { repositories { ... maven { url 'https://jitpack.io' } } } ``` 在项目module目录下的build.gradle 文件中添加SDK内容依赖 ``` dependencies { //EasyAds sdk(必须) implementation "com.github.bayescom:EasyAds-Android:1.2.1" //穿山甲广告SDK(建议) implementation "com.pangle.cn:mediation-sdk:6.1.2.5" //优量汇广告SDK(建议) implementation "com.qq.e.union:union:4.575.1445" //百度广告SDK(可选) implementation(name: "Baidu_MobAds_SDK_9352", ext: "aar") // //快手广告SDK(可选) implementation(name: "kssdk-ad-3363", ext: "aar") } ``` **方式二**: 将`EasyAds`项目以`Library`的形式导入APP项目工程。 ### 2.2 SDK分发策略配置 配置SDK的分发策略,可方便的实现流量的切分操作,实现多SDK的混合执行策略。 在EasyAds中,我们通过JSON文件的方式配置SDK的分发策略,开发者可根据自身流量分发的需求,按照EasyAds中提供的JSON配置格式及方法设置流量分发策略。 以开屏广告对接穿山甲和优量汇两个SDK为例,配置选择80%流量穿山甲->优量汇的顺序请求,20%流量优量汇->穿山甲的顺序请求,配置如下所示。 其中,suppliers字段下配置媒体在穿山甲和优量汇平台申请的广告代码位信息,rules字段下配置流量分发策略及比例; 策略JSON的配置说明详细见:[SDK策略配置JSON说明](https://github.com/bayescom/EasyAds-Android/wiki/2.-SDK%E5%88%86%E5%8F%91%E7%AD%96%E7%95%A5%E9%85%8D%E7%BD%AEJSON) 不同广告位的JSON策略配置示例见:[不同广告位JSON配置示例](https://github.com/bayescom/EasyAds-Android/tree/main/example/src/main/assets) ```json { "rules": [ { "tag": "A", "sort": [ 1, 2 ], "percent": 80 }, { "tag": "B", "sort": [ 2, 1 ], "percent": 20 } ], "suppliers": [ { "tag": "csj", "adspotId": "穿山甲广告位ID", "appId": "穿山甲应用ID", "index": 1 }, { "tag": "ylh", "adspotId": "优量汇广告位ID", "appId": "优量汇应用ID", "index": 2 } ] } ``` **注:** 为了方便开发者配置流量分发策略,我们也提供了在线可视化的便捷工具[EasyTools](http://easyads.bayescom.cn/#/tools),方便生成广告位的策略配置JSON。 ### 2.3 获取广告 以下步骤,为获取广告的必要步骤,**适用于所有广告位**。 不同广告位置的不同实现,可参考[Example示例](https://github.com/bayescom/EasyAds-Android/tree/main/example/src/main/java/com/easyads/demo); #### 2.3.1 初始化广告对象 初始化广告对象`EasyAdXXX`,以开屏为例 ``` EasyAdSplash easySplash = new EasyAdSplash(mActivity, adContainer, listener); ``` #### 2.3.2 设置执行策略 调用共有方法`setData(String strategyJson)`,给当前位置广告**赋予执行策略**,即2.2步骤中生成的SDK分发策略配置。 ``` easySplash.setData(FileUtil.getJson(mActivity, "splash_config.json")); ``` #### 2.3.3 调用获取广告 方式一:**请求并展示广告**。 ``` easySplash.loadAndShow(); ``` 方式二:先发起请求广告: ``` easySplash.loadOnly(); ``` 待广告成功拉取后,开发者可根据业务需求在合适的时机,决定调用展示广告方法。(**注意:广告会存在有效期,过久未调用展示,会导致广告失效**) ``` easySplash.show(); ``` #### 2.3.4 各广告位集成实现 * [开屏广告:EasyAdSplash](https://github.com/bayescom/EasyAds-Android/wiki/3.1-%E5%BC%80%E5%B1%8F%E5%B9%BF%E5%91%8A) * [插屏广告:EasyAdInterstitial](https://github.com/bayescom/EasyAds-Android/wiki/3.2-%E6%8F%92%E5%B1%8F%28%E5%BC%B9%E7%AA%97%29%E5%B9%BF%E5%91%8A) * [横幅广告:EasyAdBanner](https://github.com/bayescom/EasyAds-Android/wiki/3.3-%E6%A8%AA%E5%B9%85%E5%B9%BF%E5%91%8A) * [原生模板、信息流广告:EasyAdNativeExpress](https://github.com/bayescom/EasyAds-Android/wiki/3.4-%E5%8E%9F%E7%94%9F%E6%A8%A1%E6%9D%BF%E3%80%81%E4%BF%A1%E6%81%AF%E6%B5%81%E5%B9%BF%E5%91%8A) * [激励视频广告:EasyAdRewardVideo](https://github.com/bayescom/EasyAds-Android/wiki/3.5-%E6%BF%80%E5%8A%B1%E8%A7%86%E9%A2%91%E5%B9%BF%E5%91%8A) * [全屏视频广告:EasyAdFullScreenVideo](https://github.com/bayescom/EasyAds-Android/wiki/3.6-%E5%85%A8%E5%B1%8F%E8%A7%86%E9%A2%91%E5%B9%BF%E5%91%8A) * [DRAW视频信息流广告:EasyAdDraw](https://github.com/bayescom/EasyAds-Android/wiki/3.7-DRAW%E8%A7%86%E9%A2%91%E4%BF%A1%E6%81%AF%E6%B5%81%E5%B9%BF%E5%91%8A) ## 3. 进阶设置 ### 3.1 SDK全局配置 App多广告位情况下,想要优化SDK的广告位配置,可参考[SDK全局配置](https://github.com/bayescom/EasyAds-Android/wiki/4.-SDK%E5%85%A8%E5%B1%80%E9%85%8D%E7%BD%AE)优化管理你的众多广告位。 ### 3.2 自定义广告SDK渠道 想要使用其他的广告SDK,[自定义广告SDK渠道](https://github.com/bayescom/EasyAds-Android/wiki/5.-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%B9%BF%E5%91%8ASDK%E6%B8%A0%E9%81%93)可以帮你轻松加入任何广告SDK。 ## 4. SDK错误码 & 常见问题 调试遇到问题?欢迎查询SDK错误码及常见问题FAQ,帮助你快速解决对接中遇到的问题。 [SDK错误码](https://github.com/bayescom/EasyAds-Android/wiki/6.1-SDK%E9%94%99%E8%AF%AF%E7%A0%81) [常见问题](https://github.com/bayescom/EasyAds-Android/wiki/6.2-%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98) ## 技术支持 QQ群: EasyAds开源社区群 QQ群二维码: ![image](http://www.bayescom.com/uploads/20211220/43af3f34fc5a7bb50d84f94e374b3e98.png) 邮件技术支持: ================================================ FILE: build.gradle ================================================ // Top-level build file where you can add configuration options common to all sub-projects/modules. def version = '1.2.1' ext { //统一版本号,同时使用在渠道版本号信息中 EasyAds_version = version //是否进行SDK版本发布,true会通过embed 绑定广告aar到渠道处理包中 releaseSDK = false } buildscript { repositories { maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' } google() maven { url "https://jitpack.io" } //GroMore SDK依赖 maven { url "https://artifact.bytedance.com/repository/pangle" } // 配置HUAWEI Ads SDK的Maven仓地址 maven { url 'https://developer.huawei.com/repo/' } maven { url "https://plugins.gradle.org/m2/" } } dependencies { classpath 'com.android.tools.build:gradle:3.6.4' classpath 'com.github.kezong:fat-aar:1.3.8' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' } google() maven { url "https://jitpack.io" } //GroMore SDK依赖 maven { url "https://artifact.bytedance.com/repository/pangle" } // 配置HUAWEI Ads SDK的Maven仓地址 maven { url 'https://developer.huawei.com/repo/' } flatDir { dirs 'libs' } } } task clean(type: Delete) { delete rootProject.buildDir } ================================================ FILE: docs/banner.md ================================================ # 横幅广告 ### 集成最佳实践: - 适用场景:一般会在 APP 页面的底部或中部呈现。 - 常见尺寸:640*100 - 穿山甲横幅**不支持在列表中展示**,具体表现为,当在列表上下滑动时,原本正常展示的banner广告,会变成灰色纯色色块且无法响应点击。如果希望在列表展示广告,推荐使用模板信息流方式接入,或者SDK渠道中不配置穿山甲渠道。 - 如果banner放在有触摸交互的view中时,请确保非交互情况下`onInterceptTouchEvent`事件返回为`false`,否则会出现广告无法点击问题。 - 若开启了自动刷新,广告所在页面移除时,务必调用广告`destroy()`方法,移除广告资源 ### 请求广告 请求广告核心方法,详细请参考demo中代码: ```java public class BannerActivity extends BaseActivity { RelativeLayout rl; EasyADController ad; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_banner); //banner父布局 rl = findViewById(R.id.banner_layout); //初始化广告处理封装类 ad = new EasyADController(this); //加载banner ad.loadBanner("banner_config.json", rl); } } ``` EasyADController中相关处理代码: ```java /** * 加载并展示banner广告 * * @param adContainer banner广告的承载布局 */ public void loadBanner(String jsonFileName, ViewGroup adContainer) { //必须:核心事件监听回调 EABannerListener listener = new EABannerListener() { @Override public void onAdClose() { logAndToast(mActivity, "广告关闭"); } @Override public void onAdExposure() { logAndToast(mActivity, "广告展现"); } @Override public void onAdFailed(EasyAdError error) { logAndToast(mActivity, "广告加载失败 code=" + error.code + " msg=" + error.msg); } @Override public void onAdClicked() { logAndToast(mActivity, "广告点击"); } @Override public void onAdSucceed() { logAndToast(mActivity, "广告加载成功"); } }; EasyAdBanner easyAdBanner = new EasyAdBanner(mActivity, adContainer, listener); baseAD = easyAdBanner; //如果集成穿山甲,这里必须配置,建议尺寸要和穿山甲后台中的"代码位尺寸"宽高比例一致,值单位为dp,这里示例使用的广告位宽高比为640:100。 int adWidth = ScreenUtil.px2dip(mActivity, ScreenUtil.getScreenWidth(mActivity)); int adHeight = (int) (((double) adWidth / (double) 640) * 100); //如果高度传入0代表自适应高度 easyAdBanner.setCsjExpressSize(adWidth, adHeight); //必须:设置策略信息 easyAdBanner.setData(getJson(mActivity, jsonFileName)); //必须:请求并展示广告 easyAdBanner.loadAndShow(); logAndToast(mActivity, "广告请求中"); } ``` ### 主要API * EasyAdBanner |方法名 | 方法介绍 |:------------- |:---------------| |EasyAdBanner(Activity activity, ViewGroup adContainer, EABannerListener listener) | 构造方法,需要传入广告载体布局adContainer |setRefreshInterval(int refreshInterval) | 设置定时刷新时间,单位为秒,仅优量汇、穿山甲生效,不设置默认不进行定时刷新 |setCsjExpressSize(int width, int height) |设置穿山甲模板尺寸,建议尺寸要和穿山甲后台中的"代码位尺寸"宽高比例一致,值单位为dp |addCustomSupplier(String sdkTag, EABaseSupplierAdapter adapter) | 通用方法,添加自定义SDK的适配器 |getSupplierInf() | 通用方法,获取当前正在执行的渠道信息,可以在Listener的任意事件回调中获取,方便统计执行情况。 |getReqId() |通用方法,获取当前策略执行的唯一id,方便统计 |setData(String strategyJson)|通用方法,设置策略执行数据,为固定格式的json字符串 |loadOnly() |通用方法,仅拉取广告 |show() | 通用方法,展示广告,和loadOnly()方法搭配使用 |loadAndShow() | 通用方法,拉取并展示广告 |destroy()|通用方法, 销毁广告 * EABannerListener |方法名 | 方法介绍 |:------------- |:---------------| |onAdSucceed() |通用回调方法,成功加载到广告 |onAdExposure() |通用回调方法,广告曝光 |onAdClicked() |通用回调方法,广告点击 |onAdClose() |通用回调方法,广告关闭 |onAdFailed(EasyAdError easyAdError) |通用回调方法,广告失败,easyAdError包含了具体的失败原因,easyAdError可能为null ================================================ FILE: docs/draw.md ================================================ # Draw视频信息流广告 集成最佳实践: - 适用场景:短视频类APP,上划切换时展示广告 - 支持提前加载广告(注意控制时机,不可过早提前),但要注意,一次展示需对应一个广告实例,切忌复用广告实例。 加载广告核心方法,详细请参考demo中代码: ```java //初始化列表item TestItem item = new TestItem(TYPE_AD_ITEM); item.ad = new EasyADController(DrawActivity.this); mDrawList.add(item); mRecyclerAdapter = new DrawRecyclerAdapter(this, mDrawList); //RecycleView得adapter中进行广告展示逻辑 @Override public void onBindViewHolder(@NonNull DrawRecyclerAdapter.ViewHolder holder, int position) { View view = new View(mContext); TestItem item = null; if (mDataList != null) { item = mDataList.get(position); if (item.type == TYPE_COMMON_ITEM) {//普通draw信息 ……………… } else if (item.type == TYPE_AD_ITEM && item.ad != null) {//广告 //请求并展示draw信息流广告 item.ad.loadDraw("draw_config.json",holder.videoLayout); } } if (item != null) { changeUIVisibility(holder, item.type); } } ``` EasyADController中相关处理代码: ```java public EasyAdDraw easyAdDraw; public void loadDraw(String jsonFileName, ViewGroup adContainer) { EADrawListener listener = new EADrawListener() { @Override public void onAdSucceed() { if (easyAdDraw != null) { easyAdDraw.show(); } logAndToast(mActivity, "广告加载成功"); } @Override public void onAdExposure() { logAndToast(mActivity, "广告展示"); } @Override public void onAdClicked() { logAndToast(mActivity, "广告点击"); } @Override public void onAdClose() { //此位置不存在广告关闭动作,当前回调不会触发 logAndToast(mActivity, "广告关闭"); } @Override public void onAdFailed(EasyAdError error) { logAndToast(mActivity, "广告加载失败 code=" + error.code + " msg=" + error.msg); } }; easyAdDraw = new EasyAdDraw(mActivity, listener); baseAD = easyAdDraw; easyAdDraw.setAdContainer(adContainer); //必须:设置策略信息 easyAdDraw.setData(getJson(mActivity, jsonFileName)); //必须:请求并展示广告 easyAdDraw.loadAndShow(); logAndToast(mActivity, "广告请求中"); } ``` ### 主要API * EasyAdDraw |方法名 | 方法介绍 |:------------- |:---------------| |EasyAdDraw(Activity activity, EADrawListener listener) |构造方法 |setAdContainer(final ViewGroup adContainer) | 设置广告承载父布局 |addCustomSupplier(String sdkTag, EABaseSupplierAdapter adapter) | 通用方法,添加自定义SDK的适配器 |getSupplierInf() | 通用方法,获取当前正在执行的渠道信息,可以在Listener的任意事件回调中获取,方便统计执行情况。 |getReqId() |通用方法,获取当前策略执行的唯一id,方便统计 |setData(String strategyJson)|通用方法,设置策略执行数据,为固定格式的json字符串 |loadOnly() |通用方法,仅拉取广告 |show() | 通用方法,展示广告,和loadOnly()方法搭配使用 |loadAndShow() | 通用方法,拉取并展示广告 |destroy()|通用方法, 销毁广告 * EADrawListener |方法名 | 方法介绍 |:------------- |:---------------| |onAdSucceed() |通用回调方法,成功加载到广告 |onAdExposure() |通用回调方法,广告曝光 |onAdClicked() |通用回调方法,广告点击 |onAdClose() |通用回调方法,广告关闭 |onAdFailed(EasyAdError easyAdError) |通用回调方法,广告失败,easyAdError包含了具体的失败原因,easyAdError可能为null ================================================ FILE: docs/easyAds.md ================================================ # EasyAds | 方法名| 方法介绍| |:------------- |:---------------| |getVersion() | 获取EasyAds SDK的版本号信息 |setDebug(boolean isDebug, EALogLevel logLevel) | 设置debug模式,用来调试是查看日志信息,日志tag标记为:`EasyAds-log` 。EALogLevel代表了日志等级,常用的Level如下:
**EALogLevel.DEFAULT**:默认等级,等于SIMPLE等级,打印SDK核心方法日志信息
**EALogLevel.SIMPLE**:基础等级,打印SDK核心方法日志信息
**EALogLevel.HIGH**:高级模式,可打印一些辅助判断的执行信息,方便排查问题
**EALogLevel.MAX**: 最大等级,可打印全部信息,比较详细的看到SDK执行信息,用来定位错误信息 |setSplashPlusAutoClose(int time)| 设置开屏v+ 小窗口自动关闭时间,单位毫秒,不设置使用默认各个渠道默认展示逻辑 ================================================ FILE: docs/easyBDManger.md ================================================ # EasyBDManager 百度个性化配置信息API方法介绍 | 参数名| 参数类型| 基本介绍| |:------------- |:---------------|:---------------| |splashParameters |RequestParameters |开屏位置的个性化请求配置信息,[点此了解配置详情](https://union.baidu.com/miniappblog/2020/12/01/newAndroidSDK/#RequestParameters-Builder) |nativeExpressParameters |RequestParameters |信息流个性化请求配置信息,[点此了解配置详情](https://union.baidu.com/miniappblog/2020/12/01/newAndroidSDK/#BaiduNativeManager)| |nativeExpressSmartStyle | StyleParams | 信息流个性化布局配置信息,[点此了解配置详情](https://union.baidu.com/miniappblog/2020/12/01/newAndroidSDK/#StyleParams) | interstitialType | AdSize | 枚举类型,插屏广告的类型,当设置为视频贴片等广告类型时(AdSize.InterstitialForVideoBeforePlay和AdSize.InterstitialForVideoPausePlay),需配合下方的`interstitialVideoLayout`一起使用。 |interstitialVideoLayout | RelativeLayout | 视频贴片类型的插屏布局父布局,需特定interstitialType下生效 | fullScreenUseSurfaceView |boolean | 全屏视频是否使用SurfaceView来渲染,默认false |rewardUseSurfaceView |boolean | 激励视频是否使用SurfaceView来渲染,默认false | rewardDownloadAppConfirmPolicy | int | 激励视频下载确认弹框设置,默认永不弹框 ================================================ FILE: docs/easyCsjManger.md ================================================ # EasyCsjManager 穿山甲个性化配置API方法介绍 | 参数名| 参数类型| 基本介绍| |:------------- |:---------------|:---------------| |csj_splashButtonType | int | 用于控制开屏广告的点击区域,[具体设置值及含义可参考此处](https://www.pangle.cn/support/doc/611f0f0c1b039f004611e4da) |csj_downloadType | int | 用于控制下载APP前是否弹出二次确认弹窗(适用所有广告类型)。[具体设置值及含义可参考此处](https://www.pangle.cn/support/doc/611f0f0c1b039f004611e4da) | csj_supportMultiProcess | boolean | 是否支持多进程,默认true支持 | csj_askPermission | boolean | 是否允许穿山甲SDK,在必要时进行权限询问,默认false不允许 |csj_directDownloadNetworkType | int[] | 下载类广告允许直接下载的网络状态,如果未设置下载状态集合,默认4g和wifi下可以下载。 ================================================ FILE: docs/easyKSManger.md ================================================ # EasyKSManager 快手广告SDK个性化配置API方法介绍 | 参数名| 参数类型| 基本介绍| |:------------- |:---------------|:---------------| |rewardVideoConfig | KsVideoPlayConfig | 激励视频播放配置,可以进行声音、跳过、横竖屏等内容配置。 | fullScreenVideoConfig |KsVideoPlayConfig | 全屏视频播放配置,作用同上 |interstitialVideoConfig | KsVideoPlayConfig | 全屏视频播放配置,作用同上 ================================================ FILE: docs/faq.md ================================================ # 常见问题处理 ## 提要 聚合SDK主要负责加载策略的分发逻辑和各广告SDK调用逻辑,不负责具体的广告展示,广告的展示逻辑主要是由各个广告SDK来负责。 --- Q: 项目构建时出现 “unexpected element < queries> found in < manifest>”异常? A: 穿山甲新版本包引入了queries内容导致的,queries是Android11新引入的manifest基础成分,低版本的gradle无法识别,[点此查看详细说明](https://stackoverflow.com/questions/62969917/how-do-i-fix-unexpected-element-queries-found-in-manifest)。Google发布了以下插件包更新来支持此特性,建议升级Android Gradle Plugin 到以下版本: - 3.3.3 - 3.4.3 - 3.5.4 - 3.6.4 - 4.0.1 --- Q: 请求不到广告怎么办? A: 首先检查广告的策略配置是否正确,如果配置正确,根据SDK返回的异常code和msg,翻阅**SDK错误码**部分来分析错误原因。
此外,APP应适配好无广告返回时的页面展示和跳转流程,因为不可能所有用户100%都有填充广告。 如果配置一切正常,可以检查是否为未适配http请求导致,Android 9.0开始应用默认不支持http的请求,可能导致广告请求失败,资源和应用下载出现如下报错,请媒体自行适配http请求 `java.io.IOException: Cleartext HTTP traffic to *** notpermitted` --- Q: 广告拉取成功了,但是为什么没有展示回调? A: 首先检查是否正常调用了展示方法。一切都正常的话,有可能下发了视频类广告,但未开启硬件加速,导致视频无法正常播放,可以检查日志中是否包含类似 `A TextureView or a subclass can only be used with hardware acceleration enabled.` 这样的日志信息,说明APP没有开启硬件加速。建议在应用`AndroidManifest`中的` application` 层级配置`android:hardwareAccelerated="true"` 以后重试即可。也可配置在指定activity上 --- Q: Native、Banner广告展示大小有问题 A: Native、Banner广告展示内容太大或太小、广告内容被截掉了一部分等,这些一般是代码中的宽高设置有问题导致的。建议首先**阅读广告位的最佳实践说明**,有明确解释广告位大小设置相关建议。 --- Q: 如何确定广告正常展示? A: 首先检查广告回调事件,如果有收到广告展示回调,代表展示正常。其次观测广告素材是否完全展示出来,素材角落里是否有"广告"字样,如果缺少或者未完整展示,请检查布局尺寸设置,尽量露出所有布局,以免影响广告效果。 ================================================ FILE: docs/full.md ================================================ # 全屏视频广告 集成最佳实践: - 使用场景基本类似激励视频,广告展示样式也和激励视频基本一致,区别是对于用户来说强制性更低,用户可以提前“跳过”广告。 - 支持提前加载广告(注意控制时机,不可过早提前),但要注意,一次展示需对应一个广告实例,切忌复用广告实例。 ### 请求广告 加载广告核心方法,详细请参考demo中代码: ```java public class FullScreenVideoActivity extends BaseActivity { EasyAdFullScreenVideo fullScreenVideo; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_full_screen_video); } public void loadFull(View view) { fullScreenVideo = new EasyADController(this).initFullVideo("full_config.json"); fullScreenVideo.loadOnly(); } public void showFull(View view) { if (fullScreenVideo != null) { fullScreenVideo.show(); } else { EasyADController.logAndToast(this, "需要先调用loadOnly()"); } } public void loadAndShowFull(View view) { new EasyADController(this).initFullVideo("full_config.json").loadAndShow(); } } ``` EasyADController中相关处理代码: ```java /** * 初始化获取展示全屏视频的广告对象。 * 也可以选择先提前加载,然后在合适的时机再调用展示方法 */ public EasyAdFullScreenVideo initFullVideo(String jsonFileName) { //推荐:核心事件监听回调 EAFullScreenVideoListener listener = new EAFullScreenVideoListener() { @Override public void onAdSucceed() { logAndToast(mActivity, "广告加载成功"); } @Override public void onAdClose() { logAndToast(mActivity, "广告关闭"); } @Override public void onVideoComplete() { logAndToast(mActivity, "视频播放结束"); } @Override public void onVideoSkipped() { logAndToast(mActivity, "跳过视频"); } @Override public void onVideoCached() { //广告缓存成功,可以在此记录状态,但要注意:不一定所有的广告会返回该回调 logAndToast(mActivity, "广告缓存成功"); } @Override public void onAdExposure() { logAndToast(mActivity, "广告展示"); } @Override public void onAdFailed(EasyAdError error) { logAndToast(mActivity, "广告加载失败 code=" + error.code + " msg=" + error.msg); } @Override public void onAdClicked() { logAndToast(mActivity, "广告点击"); } }; //初始化 EasyAdFullScreenVideo easyFullScreenVideo = new EasyAdFullScreenVideo(mActivity, listener); baseAD = easyFullScreenVideo; //必须:设置策略信息 easyFullScreenVideo.setData(getJson(mActivity, jsonFileName)); return easyFullScreenVideo; } ``` ### 主要API * EasyAdFullScreenVideo |方法名 | 方法介绍 |:------------- |:---------------| |EasyAdFullScreenVideo(Activity activity, EAFullScreenVideoListener listener) | 构造方法 |setYlhMediaListener(UnifiedInterstitialMediaListener mediaListener) |**优量汇特有设置**,监听广告资源的运行情况 |setYlhVideoOption(VideoOption videoOption) |**优量汇特有设置**,设置视频播放配置 |addCustomSupplier(String sdkTag, EABaseSupplierAdapter adapter) | 通用方法,添加自定义SDK的适配器 |getSupplierInf() | 通用方法,获取当前正在执行的渠道信息,可以在Listener的任意事件回调中获取,方便统计执行情况。 |getReqId() |通用方法,获取当前策略执行的唯一id,方便统计 |setData(String strategyJson)|通用方法,设置策略执行数据,为固定格式的json字符串 |loadOnly() |通用方法,仅拉取广告 |show() | 通用方法,展示广告,和loadOnly()方法搭配使用 |loadAndShow() | 通用方法,拉取并展示广告 |destroy()|通用方法, 销毁广告 * EAFullScreenVideoListener |方法名 | 方法介绍 |:------------- |:---------------| |onVideoComplete()|视频播放结束 |onVideoSkipped()|用户点击了跳过,未看完提前关闭了视频 |onVideoCached()|视频缓存完成 |onAdSucceed() |通用回调方法,成功加载到广告 |onAdExposure() |通用回调方法,广告曝光 |onAdClicked() |通用回调方法,广告点击 |onAdClose() |通用回调方法,广告关闭 |onAdFailed(EasyAdError easyAdError) |通用回调方法,广告失败,easyAdError包含了具体的失败原因,easyAdError可能为null ================================================ FILE: docs/home.md ================================================ # EasyAds APP开发者一般不会在广告集成上进行过多得探索,各种第三方服务又玩法五花八门,如何能使得APP即自由又放心得管理广告SDK? EasyAds可以让APP开发者在利用广告变现时,做到高效、透明、简洁、安全,真正属于你自己的聚合广告SDK。 ### 概述 EasyAds 目前支持以下广告类型,您可以根据开发需要选择合适的广告。 | 广告SDK平台| 开屏 | 激励视频 | 横幅| 插屏(弹窗) | 模板信息流 | 全屏视频 | draw信息流 |:-------------|:---------------| :---------------| :---------------| :---------------| :---------------| :---------------| :---------------| | 穿山甲|✅ |✅ |✅ |✅|✅|✅ |✅ | 优量汇(广点通) |✅ |✅ |✅ |✅ |✅ |✅ |❌ | 百度 |✅ |✅ |✅ | ✅ | ✅ | ✅ |❌ | 快手 |✅ |✅ | ❌ | ✅ | ✅ | ✅|✅ 补充说明:不在支持列表的广告SDK平台,您也可以通过使用**自定义SDK渠道**,来统一管理。 ## 开始使用 ### 1.引入SDK 建议使用Gradle 添加依赖,在项目根目录的build.gradle 文件中添加 `JitPack` 仓库 ``` allprojects { repositories { ... maven { url 'https://jitpack.io' } } } ``` 在项目module目录下的build.gradle 文件中添加SDK内容依赖 ``` ``` ### 2.获取广告 #### 2.1 获取广告的必要步骤: 以下步骤,**适用于所有广告位**,具体广告位置实现,请参考example目录中示例代码。 * 步骤1.初始化广告对象`EasyAdXXX`,以开屏为例 ``` //adContainer为开屏广告展示的布局容器 EasyAdSplash easySplash = new EasyAdSplash(mActivity, adContainer, listener); ``` * 步骤2.调用共有方法`setData(String strategyJson)`,给当前位置广告**赋予执行策略**,详细配置方法参考下方`2.2策略配置说明`。 ``` easySplash.setData(FileUtil.getJson(mActivity, "splash_config.json")); ``` * 步骤3.调用广告获取方法: 方式一:**请求并展示广告**。 ``` easySplash.loadAndShow(); ``` 方式二:先发起请求广告: ``` easySplash.loadOnly(); ``` 待广告成功拉取后,或者在合适的时机,由APP自己决定调用展示广告方法:(**注意:广告会存在有效期,过久未调用展示,会导致广告失效**) ``` easySplash.show(); ``` #### 2.2 策略配置说明 步骤2`setData(String strategyJson) `中的 `strategyJson`参数为SDK的策略配置信息,可通过修改配置内容来实现流量切分、顺序执行、分组等多种需求,对应的字段结构说明,[点击此处了解策略Json配置说明](docs/strategyJsonFormat.md)。 **为防止json配置异常导致无法执行广告加载,建议您通过以下几种方式来处理**: * 方式一:通过[点击此处,使用在线生成配置json工具](http://easyads.bayescom.cn),一键获取到配置json信息,复制后直接使用。 * 方式二:修改`assets`下对应的`XXX_config.json`配置文件,将其中的广告信息和策略替换为自己的内容。 #### 2.3 各广告位详细说明 点击下方信息,了解各个位置包含的的详细配置参数,和实现建议。 * [开屏广告:EasyAdSplash](docs/splash.md) * [插屏广告:EasyAdInterstitial](docs/interstital.md) * [横幅广告:EasyAdBanner](docs/banner.md) * [原生模板、信息流广告:EasyAdNativeExpress](docs/nativeExpress.md) * [激励视频广告:EasyAdRewardVideo](docs/reward.md) * [全屏视频广告:EasyAdFullScreenVideo](docs/full.md) * [DRAW视频信息流广告:EasyAdDraw](docs/draw.md) #### 2.4 SDK渠道配置详细说明 个性化配置参数,满足不同需求场景。 * [全局核心配置类:EasyAds](docs/easyAds.md) * [百度渠道配置类:EasyBDManager](docs/easyBDManger.md) * [穿山甲渠道配置类:EasyCsjManger](docs/easyCsjManger.md) * [快手渠道配置类:EasyKSManger](docs/easyKSManger.md) #### 2.5 自定义支持更多广告SDK渠道 **我能不能把小米广告SDK也集成进来,聚合统一管理?可以。** 如果穿山甲、优量汇、百度、快手这些SDK渠道无法满足您的需求,您可以通过自定义SDK渠道,来拓展需要支持的任意广告SDK。建议参考demo示例工程`custom`文件夹的`HuaWeiSplashAdapter`和`XiaoMiSplashAdapter`代码。 自定义步骤主要分为三步: 第一步:新建自定义支持的渠道类文件,假设命名为`ABCAdapter`,根据其广告类型,如果是开屏(已支持的广告类型),那么需要让`ABCAdapter`继承`EASplashCustomAdapter`开屏自定义基类。此时需要实现父类的下列抽象方法: ``` /** * 抽象基础方法,子类仅关注对应的广告处理逻辑 */ //抽象方法,此方法内应执行广告加载方法 protected abstract void doLoadAD(); //抽象方法,此方法内应执行广告展示方法(如有) protected abstract void doShowAD(); //抽象方法,此方法内应执行广告销毁操作(如有) protected abstract void doDestroy(); ``` 为保证广告正常执行,有以下注意事项: * 1.在`doLoadAD()`方法内执行广告请求方法时,一定要保证广告SDK已经执行过初始化方法了,如需获取渠道的appId,可以通过基类的`getAppID()`方法获取到。 * 2.一定要在广告成功、曝光、点击、失败时,调用基类对应`handleXXX`方法 第二步:将`ABCAdapter`通过`EasyAdXXX`广告的`addCustomSupplier(String sdkTag, EABaseSupplierAdapter adapter) `方法添加进去。注意`sdkTag`需要和json策略中的`supplier`内`tag`一致。 第三步:修改策略json,添加该自定义的渠道的`supplier`和 `rules`信息 ### 3.问题排查 如果广告执行异常,可以通过`EasyAds.setDebug(boolean isDebug, EALogLevel logLevel)`方法,开启debug日志,筛选:`EasyAds-log`,查看错误码和错误信息,通过log信息来判断SDK执行情况。如果没有观测到有帮助的log信息,可以选择启用更高级别的日志配置:`EALogLevel.MAX`,打印更多SDK执行log信息来进一步排查。 #### 3.1 错误码 * com.easyads.model.EasyAdError 异常信息类,包含code、msg;各个code含义如下表所示: |错误码 | 错误详情 | 排查方向 |:------------- |:---------------| :---------------| |9901| 数据为空 |广告返回的数据为空,一般为渠道广告无填充。 |9902 | 加载某个SDK渠道时发生异常| 查看logcat下日志输出,寻找具体原因 |9903 |展示某个SDK渠道时发生异常| 查看logcat下日志输出,寻找具体原因 |9904|渲染某个SDK渠道时发生异常| 查看logcat下日志输出,寻找具体原因 |9905|当前策略中无有效的SDK渠道,或渠道信息为空 | 检查聚合策略配置信息,有可能是配错渠道或者没有配置 |9906| 策略调度时发生异常 | 查看logcat下日志输出,及时反馈我们帮助排查 |9907|未获取到策略 | 检查是否遗漏了`setData(String strategyJson)`策略配置,或者配置的json格式有问题导致未成功解析。 |9908|某个SDK渠道启动异常| 查看logcat下日志输出,及时反馈我们帮助排查 |9909|穿山甲渠道SDK超时且不再加载优先级更低的渠道 | 加载超时,检查网络是否正常。仅开屏中会出现,代表开屏广告位中配置了setCsjTimeOutQuit(true),低频可忽略 |9910 | 穿山甲渠道SDK加载超时 | 加载超时,检查网络是否正常 |9911 | 百度SDK加载失败 | 查看logcat下日志输出,寻找具体原因 |9912 | 快手SDK加载失败,广告位id类型转换异常 | 一般是广告位转换出错,检查配置的快手渠道下广告位id是否为纯数字,非纯数字会引起该异常。 |9913 | 当前activity已被销毁,导致广告无可用载体 | 检查广告展示方法调用时机,有可能广告载体页面已经关闭了,但是又调用了展示方法 |9914 |快手SDK初始化失败 | 查看logcat下日志输出,寻找具体原因,检查广告位id信息配置是否正确 |9915 |广告渲染失败 |可能是网络缓慢导致素材未成功加载,或者页面实现问题,有布局遮挡了广告布局展示 |99_XXX | 某个SDK渠道返回的错误,其中XXX为该SDK渠道返回的具体错误码 | 翻阅各个平台下的错误码说明查找具体原因:
穿山甲: [查看官方线上文档,点击跳转](https://www.pangle.cn/union/media/union/download/detail?id=4&docId=5de8d9b925b16b00113af0ed&osType=android)
优量汇(广点通): [查看官方线上文档,点击跳转](https://developers.adnet.qq.com/doc/android/union/union_debug#sdk%20%E9%94%99%E8%AF%AF%E7%A0%81)
[查看官方线上文档,点击跳转;错误码详见19.2](https://union.baidu.com/miniappblog/2020/06/16/AndroidSDK/)
快手: [查看官方线上文档,查看详细错误信息](https://static.yximgs.com/udata/pkg/KS-Android-KSAdSDk/doc/4701b963d40a77bc0f45fd71d30b57da.pdf) #### 3.2 常见问题FAQ [点击查看常见问题FAQ](docs/faq.md) #### 3.3 联系我们 ### 4.版本更新记录 |版本|日期|更新内容 |:------|:------|:-----| |v1.0 | |SDK发布,主要支持内容:
1.统一聚合穿山甲、优量汇、百度、快手广告SDK
2.支持本地导入策略配置,极简调用
3.支持自定义支持更多广告SDK ================================================ FILE: docs/interstital.md ================================================ # 插屏(弹窗)广告 集成最佳实践: - 适用场景:一般暂停时(多见于视频类 APP 节目暂停);页面间跳转、切换时(比如游戏类 APP 过关、工具类 APP 页面转换);APP 退出时;返回上一级时等场景,均可适当植入插屏广告。 - 常见尺寸:插屏广告尺寸通常等于或大于手机屏幕的一半,多设计 600x600、600x500 等规格。 - 一些建议:不要突然展示广告,最好在用户完成应用内操作后自然而然的植入,比如游戏通关后、视频播放结束后; 无论是插屏广告植入场景,还是展示频次,媒体均需要反复测试,以达到最佳变现效果; - 插屏支持提前加载广告(注意控制时机,不可过早提前),但要注意,一次展示需对应一个广告实例,切忌复用广告实例。 ### 请求广告 请求广告核心方法,详细请参考demo中代码: ```java public class InterstitialActivity extends BaseActivity { EasyAdInterstitial interstitialAD; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_interstitial); } public void loadAd(View view) { interstitialAD = new EasyADController(this).initInterstitial("interstitial_config.json"); interstitialAD.loadOnly(); } public void showAd(View view) { if (interstitialAD != null) { interstitialAD.show(); } else { EasyADController.logAndToast(this, "需要先调用loadOnly()"); } } public void loadAndShowAd(View view) { new EasyADController(this).initInterstitial("interstitial_config.json").loadAndShow(); } } ``` EasyADController中相关处理代码: ```java /** * 初始话插屏广告。 * 可以选择性先提前加载,然后在合适的时机再调用展示方法 * 或者直接调用加载并展示广告 *

* 注意!!!:穿山甲默认为"新插屏广告" */ public EasyAdInterstitial initInterstitial(String jsonFileName) { //必须:核心事件监听回调 EAInterstitialListener listener = new EAInterstitialListener() { @Override public void onAdSucceed() { logAndToast(mActivity, "广告就绪"); } @Override public void onAdClose() { logAndToast(mActivity, "广告关闭"); } @Override public void onAdExposure() { logAndToast(mActivity, "广告展示"); } @Override public void onAdFailed(EasyAdError error) { logAndToast(mActivity, "广告加载失败 code=" + error.code + " msg=" + error.msg); } @Override public void onAdClicked() { logAndToast(mActivity, "广告点击"); } }; //初始化 EasyAdInterstitial easyInterstitial = new EasyAdInterstitial(mActivity, listener); baseAD = easyInterstitial; //注意:穿山甲默认为"新插屏广告",如果要使用旧版请打开这条设置 // easyInterstitial.setCsjNew(false); //必须:设置策略信息 easyInterstitial.setData(getJson(mActivity, jsonFileName)); return easyInterstitial; } ``` ### 主要API * EasyAdInterstitial |方法名 | 方法介绍 |:------------- |:---------------| |EasyAdInterstitial(Activity activity, EAInterstitialListener listener)|构造方法 |setYlhMediaListener(UnifiedInterstitialMediaListener mediaListener) |**优量汇特有设置**,监听广告资源的运行情况 |setCsjNew(boolean csjNew)|**穿山甲特有设置**,是否为'新插屏广告',不设置默认true |setCsjExpressViewAcceptedSize(float expressViewWidth, float expressViewHeight)| **穿山甲特有设置**,期望模板广告view的size,单位dp;注意:参数请按照平台勾选的比例去进行请求。现有1:1,3:2 ,2:3 三种比例可供选择。 |addCustomSupplier(String sdkTag, EABaseSupplierAdapter adapter) | 通用方法,添加自定义SDK的适配器 |getSupplierInf() | 通用方法,获取当前正在执行的渠道信息,可以在Listener的任意事件回调中获取,方便统计执行情况。 |getReqId() |通用方法,获取当前策略执行的唯一id,方便统计 |setData(String strategyJson)|通用方法,设置策略执行数据,为固定格式的json字符串 |loadOnly() |通用方法,仅拉取广告 |show() | 通用方法,展示广告,和loadOnly()方法搭配使用 |loadAndShow() | 通用方法,拉取并展示广告 |destroy()|通用方法, 销毁广告 * EAInterstitialListener |方法名 | 方法介绍 |:------------- |:---------------| |onAdSucceed() |通用回调方法,成功加载到广告 |onAdExposure() |通用回调方法,广告曝光 |onAdClicked() |通用回调方法,广告点击 |onAdClose() |通用回调方法,广告关闭 |onAdFailed(EasyAdError easyAdError) |通用回调方法,广告失败,easyAdError包含了具体的失败原因,easyAdError可能为null ================================================ FILE: docs/nativeExpress.md ================================================ # 原生模板广告 ### 集成最佳实践: - 适用场景:适用于有内容输出的 APP,比如新闻资讯类、视频类、音频类、社区类等,通常在内容流中或内容详情页中植入。在内容流中植入广告,需对广告展现位置、展现频次等反复测试,避免过度干扰的同时,确保高可见性和效果性。 - 建议广告承载布局高度自适应,如要设置固定**高度须知**:穿山甲模板有最低高度限制,已知为最低125dp,Mercury最低80dp,优量汇(广点通)最低约100dp。 - **瀑布流式内容信息流需单独设置广告宽度**,否则广点通可能展示异常,需增加如下设置代码: ```java //需要特别注意广告父布局宽度要和自定义的宽度dp值一致,否则优量汇可能出现展示问题。 advanceNativeExpress.setExpressViewAcceptedSize(自定义宽度dp值, 0); ``` - 支持提前加载广告(注意**控制时机**,不可过早提前),但要注意,一次展示需对应一个广告实例,切忌复用广告实例。 - RecyclerView中使用的话请参考示例工程中的**NativeExpressRecyclerViewActivity**,建议在列表中每5-10个条目展示一条广告,且**每次只请求一条广告**,如果一定要请求多条请注意穿山甲支持的条数上限为3条。 - 如果在**LstView中**使用,需要同一位置广告不可重复请求,当在**getView**方法中执行广告加载方法时,需标记同一位置广告**是否已经请求过**,如果已请求过不再进行请求步骤,否则会出现重复无效请求导致页面闪烁。 - 列表数据刷新后,也需要**同步刷新广告**。 ### 请求广告 请求广告核心方法,详细请参考demo中代码: ```java public class NativeExpressActivity extends BaseActivity { private FrameLayout container; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_native_express); container = findViewById(R.id.native_express_container); new EasyADController(this).loadNativeExpress("native_config.json",container); } public void loadNEAD(View view) { new EasyADController(this).loadNativeExpress("native_config.json",container); } } ``` EasyADController中相关处理代码: ```java public boolean hasNativeShow = false; boolean isNativeLoading = false; /** * 加载并展示原生模板信息流广告 * * @param adContainer 广告的承载布局 */ public void loadNativeExpress(String jsonFileName, ViewGroup adContainer) { if (hasNativeShow) {//同一位置广告,已展示过不再重复发起请求 EALog.d("loadNativeExpress hasNativeShow"); return; } if (isNativeLoading) {//同一位置广告,正在请求中,不再重复请求 EALog.d("loadNativeExpress isNativeLoading"); return; } isNativeLoading = true; if (adContainer.getChildCount() > 0) { adContainer.removeAllViews(); } //推荐:核心事件监听回调 EANativeExpressListener listener = new EANativeExpressListener() { @Override public void onAdSucceed() { logAndToast(mActivity, "广告加载成功"); } @Override public void onAdRenderSuccess() { logAndToast(mActivity, "广告渲染成功"); } @Override public void onAdClose() { logAndToast(mActivity, "广告关闭"); } @Override public void onAdExposure() { hasNativeShow = true; isNativeLoading = false; logAndToast(mActivity, "广告展示"); } @Override public void onAdFailed(EasyAdError error) { isNativeLoading = false; logAndToast(mActivity, "广告加载失败 code=" + error.code + " msg=" + error.msg); } @Override public void onAdRenderFailed() { isNativeLoading = false; logAndToast(mActivity, "广告渲染失败"); } @Override public void onAdClicked() { logAndToast(mActivity, "广告点击"); } }; //初始化 final EasyAdNativeExpress easyNativeExpress = new EasyAdNativeExpress(mActivity, listener); baseAD = easyNativeExpress; easyNativeExpress.setAdContainer(adContainer); //必须:设置策略信息 easyNativeExpress.setData(getJson(mActivity, jsonFileName)); //必须:请求并展示广告 easyNativeExpress.loadAndShow(); logAndToast(mActivity, "广告请求中"); } ``` ### 主要API * EasyAdNativeExpress |方法名 | 方法介绍 |:------------- |:---------------| |EasyAdNativeExpress(Activity activity, EANativeExpressListener listener)|构造方法 |setAdContainer(ViewGroup container)|设置广告展示承载布局 |setVideoMute(boolean mute)|设置视频静音,默认true,仅优量汇生效,穿山甲在后台中配置 |setYlhMaxVideoDuration(int ylhMaxVideoDuration)|**优量汇特有设置**,设置视频最大时长,单位秒 |View getExpressADView()| 获取广告的实际展示view信息,数据来源于广告SDK回调 |addCustomSupplier(String sdkTag, EABaseSupplierAdapter adapter) | 通用方法,添加自定义SDK的适配器 |getSupplierInf() | 通用方法,获取当前正在执行的渠道信息,可以在Listener的任意事件回调中获取,方便统计执行情况。 |getReqId() |通用方法,获取当前策略执行的唯一id,方便统计 |setData(String strategyJson)|通用方法,设置策略执行数据,为固定格式的json字符串 |loadOnly() |通用方法,仅拉取广告 |show() | 通用方法,展示广告,和loadOnly()方法搭配使用 |loadAndShow() | 通用方法,拉取并展示广告 |destroy()|通用方法, 销毁广告 * EANativeExpressListener |方法名 | 方法介绍 |:------------- |:---------------| |onAdRenderFailed | 广告渲染失败 |onAdRenderSuccess| 广告渲染成功 |onAdSucceed() |通用回调方法,成功加载到广告 |onAdExposure() |通用回调方法,广告曝光 |onAdClicked() |通用回调方法,广告点击 |onAdClose() |通用回调方法,广告关闭 |onAdFailed(EasyAdError easyAdError) |通用回调方法,广告失败,easyAdError包含了具体的失败原因,easyAdError可能为null ================================================ FILE: docs/reward.md ================================================ # 激励视频广告 集成最佳实践: - 适用场景:激励视频广告在游戏 APP 使用较多,比如通关失败后通过观看激励视频广告,用户可获取复活机会。此外,一些非游戏类 APP,也可通过植入 5-15 秒(甚至更长)的激励视频广告,让用户通过主动观看来享受到一些福利奖励、付费功能/权限等——比如网盘类 APP 通过观看激励视频广告来获取空间;视频、音频、音乐类 APP 通过观看激励视频广告来获取视频/音频/音乐免费观看或收听特权;图片类 APP 通过观看激励视频广告来获取图片下载特权;阅读类 APP 通过观看激励视频广告来获取书籍/漫画阅读特权等。 - 常见尺寸:激励视频广告常见为竖屏,比例 9:16,尺寸 720x1280;也有些 APP 采用横版样式,比例 16:9,尺寸 1280x720。 - 一些建议:广告植入密度不要过于频繁,以免拉低媒体调性和用户好感度; 将广告植入在既能刺激用户主动观看,同时又兼顾不对用户正常使用体验产生影响的位置。 - 激励达成事件请在广告激励回调`onAdReward `中进行,如需服务器回调,需要翻阅个平台中文档说明来了解,本SDK不参与服务端回调功能。 - 支持提前加载广告(注意控制时机,不可过早提前),但要注意,一次展示需对应一个广告实例,切忌复用广告实例。 ### 请求广告 加载广告核心方法,详细请参考demo中代码: ```java public class RewardVideoActivity extends BaseActivity { EasyAdRewardVideo rewardVideo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_reward_video); } public void onLoad(View view) { rewardVideo = new EasyADController(this).initReward("reward_config.json"); rewardVideo.loadOnly(); } public void onShow(View view) { if (rewardVideo != null) { rewardVideo.show(); } else { EasyADController.logAndToast(this, "需要先调用loadOnly()"); } } public void loadAndShow(View view) { new EasyADController(this).initReward("reward_config.json").loadAndShow(); } } ``` EasyADController中相关处理代码: ```java /** * 加载并展示激励视频广告。 * 也可以选择性先提前加载,然后在合适的时机再调用展示方法 */ public EasyAdRewardVideo initReward(String jsonFileName) { //必须:核心事件监听回调 EARewardVideoListener listener = new EARewardVideoListener() { @Override public void onAdSucceed() { logAndToast(mActivity, "广告加载成功"); } @Override public void onAdExposure() { logAndToast(mActivity, "广告展示"); } @Override public void onAdFailed(EasyAdError error) { logAndToast(mActivity, "广告加载失败 code=" + error.code + " msg=" + error.msg); } @Override public void onAdClicked() { logAndToast(mActivity, "广告点击"); } @Override public void onVideoCached() { logAndToast(mActivity, "广告缓存成功"); } @Override public void onVideoComplete() { logAndToast(mActivity, "视频播放完毕"); } @Override public void onVideoSkip() { } @Override public void onAdClose() { logAndToast(mActivity, "广告关闭"); } @Override public void onAdReward() { logAndToast(mActivity, "激励发放"); } @Override public void onRewardServerInf(EARewardServerCallBackInf inf) { //优量汇和穿山甲支持回调服务端激励验证信息,详见RewardServerCallBackInf中字段信息 logAndToast(mActivity, "onRewardServerInf" + inf); } }; //初始化,注意需要时再初始化,不要复用。 EasyAdRewardVideo easyRewardVideo = new EasyAdRewardVideo(mActivity, listener); baseAD = easyRewardVideo; //必须:设置策略信息 easyRewardVideo.setData(getJson(mActivity, jsonFileName)); return easyRewardVideo; } ``` ### 主要API * EasyAdRewardVideo |方法名 | 方法介绍 |:------------- |:---------------| |EasyAdRewardVideo(Activity activity, EARewardVideoListener listener)|构造方法 |setYlhVolumeOn(boolean vo)|**优量汇特有设置**,是否开启声音,默认false不开启 |setYlhSSVO(ServerSideVerificationOptions options)|**优量汇特有设置**,设置服务器检验选项,具体参考优量汇官方说明文档 |setCsjOrientation(int csjOrientation)|**穿山甲特有设置**,设置广告展示的屏幕方向,默认竖屏 |setCsjMediaExtra(String mediaExtra)|**穿山甲特有设置**,媒体补充信息,用于和服务端检验使用 |setCsjUserId(String userId)|**穿山甲特有设置**,用户id |setCsjRewardName(String rewardName)| **穿山甲特有设置**,穿山甲奖励名称 |setCsjRewardAmount(int rewardAmount) |**穿山甲特有设置**,穿山甲奖励数量 |addCustomSupplier(String sdkTag, EABaseSupplierAdapter adapter) | 通用方法,添加自定义SDK的适配器 |getSupplierInf() | 通用方法,获取当前正在执行的渠道信息,可以在Listener的任意事件回调中获取,方便统计执行情况。 |getReqId() |通用方法,获取当前策略执行的唯一id,方便统计 |setData(String strategyJson)|通用方法,设置策略执行数据,为固定格式的json字符串 |loadOnly() |通用方法,仅拉取广告 |show() | 通用方法,展示广告,和loadOnly()方法搭配使用 |loadAndShow() | 通用方法,拉取并展示广告 |destroy()|通用方法, 销毁广告 * EARewardVideoListener |方法名 | 方法介绍 |:------------- |:---------------| |onVideoCached()|视频缓存成功 |onVideoComplete()|视频播放完毕 |onVideoSkip()|视频跳过,部分SDK会回调此方法 |onAdReward()|广告激励达成 |onRewardServerInf(EARewardServerCallBackInf inf)|激励视频返回的服务器回调信息,穿山甲一直支持,优量汇自v4.330.1200 开始支持,百度9.13开始支持 |onAdSucceed() |通用回调方法,成功加载到广告 |onAdExposure() |通用回调方法,广告曝光 |onAdClicked() |通用回调方法,广告点击 |onAdClose() |通用回调方法,广告关闭 |onAdFailed(EasyAdError easyAdError) |通用回调方法,广告失败,easyAdError包含了具体的失败原因,easyAdError可能为null ================================================ FILE: docs/splash.md ================================================ # 开屏广告 ### 集成最佳实践建议: - 理论上开屏广告应该在**用户阅读并同意隐私政策授权之后**展示。 - 展示区域最好**大于整体画面 75%且高度不得低于400dp**,全屏效果更佳; - 开屏请求、展示时机:**使用单独的页面展示开屏**,并且加载广告时尽量不要进行其他的网络请求或耗时操作,这类操作可以在广告曝光或加载失败回调事件以后再进行。 - 开屏**冷启动和热启动**区分设置策略json。 ### 代码示例 开屏页面调用 ``` //初始化广告处理封装类 EasyADController ad = new EasyADController(this); ad.loadSplash(jsonName, adContainer, logo, true, new EasyADController.SplashCallBack() { @Override public void jumpMain() { goToMainActivity(); } }); ``` EasyADController中相关处理代码: ``` /** * 加载开屏广告 * * @param adContainer 广告承载布局,不可为空 * @param logoContainer 底部logo布局,可以为空 * @param singleActivity 是否为单独activity中展示开屏广告 * @param callBack 跳转回调,在回调中进行跳转主页或其他操作 */ public void loadSplash(String jsonFileName, final ViewGroup adContainer, final ViewGroup logoContainer, boolean singleActivity, final SplashCallBack callBack) { //必须:设置开屏核心回调事件的监听器。 EASplashListener listener = new EASplashListener() { @Override public void onAdClose() { if (callBack != null) callBack.jumpMain(); logAndToast(mActivity, "广告关闭"); } @Override public void onAdSucceed() { logAndToast(mActivity, "广告加载成功"); if (EasyAdsConstant.SDK_TAG_CSJ.equals(baseAD.getSupplierInf().tag)) { if (logoContainer != null) logoContainer.setVisibility(View.VISIBLE); } } @Override public void onAdExposure() { //设置开屏父布局背景色为白色 if (adContainer != null) adContainer.setBackgroundColor(Color.WHITE); //logo展示建议:广告展示的时候再展示logo,其他时刻都是展示的全屏的background图片 if (logoContainer != null) logoContainer.setVisibility(View.VISIBLE); logAndToast(mActivity, "广告展示成功"); } @Override public void onAdFailed(EasyAdError error) { logAndToast(mActivity, "广告加载失败 code=" + error.code + " msg=" + error.msg); } @Override public void onAdClicked() { logAndToast(mActivity, "广告点击"); } }; EasyAdSplash easySplash = new EasyAdSplash(mActivity, adContainer, listener); baseAD = easySplash; //注意:如果开屏页是fragment或者dialog实现,这里需要置为false。默认为true,代表开屏和首页为两个不同的activity easySplash.setShowInSingleActivity(singleActivity); //必须:设置策略信息 easySplash.setData(FileUtil.getJson(mActivity, jsonFileName)); //必须:请求并展示广告 easySplash.loadAndShow(); logAndToast(mActivity, "广告请求中"); } /** * 开屏跳转回调 */ public interface SplashCallBack { void jumpMain(); } ``` ### 主要API * EasyAdSplash |方法名 | 方法介绍 |:------------- |:---------------| |EasyAdSplash(Activity activity, ViewGroup adContainer, EASplashListener listener) | 构造方法,需要传入广告载体布局adContainer| |setCsjShowAsExpress(boolean isExpress) |设置穿山甲为模板广告类型请求广告(需在穿山甲后台申请),默认为非模板类型 |setShowInSingleActivity(boolean single) |开屏是否在单独的activity中,默认true。 |isADSkip | 当回调了onAdClose事件时,可以获取此值来区分是否为用户点击了跳过 |addCustomSupplier(String sdkTag, EABaseSupplierAdapter adapter) | 通用方法,添加自定义SDK的适配器 |getSupplierInf() | 通用方法,获取当前正在执行的渠道信息,可以在Listener的任意事件回调中获取,方便统计执行情况。 |getReqId() |通用方法,获取当前策略执行的唯一id,方便统计 |setData(String strategyJson)|通用方法,设置策略执行数据,为固定格式的json字符串 |loadOnly() |通用方法,仅拉取广告 |show() | 通用方法,展示广告,和loadOnly()方法搭配使用 |loadAndShow() | 通用方法,拉取并展示广告 |destroy()|通用方法, 销毁广告 * EASplashListener |方法名 | 方法介绍 |:------------- |:---------------| |onAdSucceed() |通用回调方法,成功加载到广告 |onAdExposure() |通用回调方法,广告曝光 |onAdClicked() |通用回调方法,广告点击 |onAdClose() |通用回调方法,广告关闭 |onAdFailed(EasyAdError easyAdError) |通用回调方法,广告失败,easyAdError包含了具体的失败原因,easyAdError可能为null ================================================ FILE: docs/strategyJsonFormat.md ================================================ # 策略Json配置说明 每个聚合广告位均可通过`setData(String strategyJson) `方法设置策略信息,其中 `strategyJson`参数为SDK的策略配置信息,其json结构示例如下: ``` { "rules": [ { "tag": "A", "sort": [ 1, 3 ], "percent": 30 }, { "tag": "B", "sort": [ 2, 4 ], "percent": 70 } ], "suppliers": [ { "tag": "csj", "adspotId": "887477661", "appId": "5051624", "index": 1 }, { "tag": "ylh", "adspotId": "2001447730515391", "appId": "1101152570", "index": 2 }, { "tag": "ks", "adspotId": "4000000042", "appId": "90009", "index": 3 }, { "tag": "bd", "adspotId": "2058622", "appId": "e866cfb0", "index": 4 } ] } ``` * strategyJson包含内容 |字段名 | 字段类型 |含义 |:--- |:--- |:--- | |suppliers | List<Supplier> | **SDK渠道详细信息**,相当于“广告池”,Supplier对象含义见下表 |rules | List<Rule> | **策略规则配置数组**,将suppliers“广告池”中的广告以约定的规则进行排序和分发。可分为多组执行广告策略,组内变量主要是SDK顺序排序方式和切分比例。Rule对象含义见下表 * Rule 对象包含内容 |字段名 | 字段类型 |含义 |:--- |:--- |:--- | |tag |String |**策略组唯一标记**,用于区分标记不同组的执行情况 | sort | List<Integer> | **广告SDK执行顺序表**,依照组内顺序,优先级从高到低,组内成员对应suppliers字段中的index变量 |percent | int | **流量占比值**,SDK内部会根据多组内配置的值,自动计算比例,执行流量百分比的分发模式,**建议使用百分值**。
比如上述json示例中配置的含义为:在发起请求后,有**30%**的概率执行策略组A中配置,按照1->3的顺序依次执行广告加载;**70%**的概率执行策略组A中配置,按照2->4的顺序依次执行广告加载。
如果A、B两组中percent配置值分别为**201、799**,代表**20.1%**的概率执行A,**79.9%**的概率执行B。
如果A、B两组中percent配置值分别为**2、3**,代表**40%**的概率执行A,**60%**的概率执行B。
如果**仅有一组A**,不论percent按照多少设置,都默认**100%**的流量执行A * Supplier 对象包含内容 字段名 | 字段类型 |含义 |:--- |:--- |:--- | |index |int|**唯一坐标**,用来和rules信息内sort字段关联,确定广告执行顺序 |tag | String | **SDK类别标识**,
"csj"代表头条-穿山甲SDK
"ylh"代表腾讯-优量汇SDK(前广点通)
"ks"代表快手-快手联盟SDK
"bd"代表百度-百青藤SDK |adspotId | String | **广告位id**,在变现SDK后台申请到的具体广告位id |appId | String | **应用id**,在变现SDK后台申请到的应用id ================================================ FILE: easyads/.gitignore ================================================ /build ================================================ FILE: easyads/build.gradle ================================================ apply plugin: 'com.android.library' apply plugin: 'com.kezong.fat-aar' // JitPack Maven apply plugin: 'com.github.dcendents.android-maven' // Your Group group = 'com.github.bayescom' //maven依赖的项目名称 def pomName = "easyads-core" def extVersionName = "" def VersionName = "${rootProject.ext.EasyAds_version}${extVersionName}" def VersionCode = 1 def outputName = pomName + "_" + VersionName + ".aar" android { compileSdkVersion 29 defaultConfig { minSdkVersion 14 targetSdkVersion 29 versionCode VersionCode versionName VersionName // 混淆配置自动同步到接入方 consumerProguardFiles 'proguard-rules.pro' } buildTypes { debug { minifyEnabled false } release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } lintOptions { abortOnError false } println pomName + " VersionName = " + VersionName } tasks.withType(Javadoc) { options.addStringOption('Xdoclint:none', '-quiet') options.addStringOption('encoding', 'UTF-8') options.addStringOption('charSet', 'UTF-8') } repositories { flatDir { dirs 'libs' } } dependencies { compileOnly 'com.android.support:appcompat-v7:28.0.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' // the needed aars are here compileOnly(name: 'Baidu_MobAds_SDK_9352', ext: 'aar') compileOnly(name: 'kssdk-ad-3363', ext: 'aar') compileOnly "com.pangle.cn:mediation-sdk:6.1.2.5" compileOnly "com.qq.e.union:union:4.575.1445" } //android.libraryVariants.all { variant -> // variant.outputs.all { // outputFileName = outputName // } //} ================================================ FILE: easyads/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile -keep class com.bytedance.sdk.openadsdk.** { *; } -keep public interface com.bytedance.sdk.openadsdk.downloadnew.** {*;} -keep class com.ss.sys.ces.* {*;} -keep class com.ss.android.**{*;} -keep class com.pgl.sys.ces.* {*;} -keep class com.qq.e.** { public protected *; } -keep class android.support.v4.app.NotificationCompat**{ public *; } -keep class com.bun.miitmdid.core.** {*;} -keep class com.easyads.supplier.** {*;} #oaid 混淆部分 -keep class com.bun.miitmdid.** {*;} -dontwarn XI.** -keep class XI.** {*;} ================================================ FILE: easyads/src/main/AndroidManifest.xml ================================================ ================================================ FILE: easyads/src/main/java/com/easyads/EasyAds.java ================================================ package com.easyads; import com.easyads.core.BuildConfig; import com.easyads.model.EALogLevel; public class EasyAds { //获取聚合SDK版本号 public static String getVersion() { return BuildConfig.VERSION_NAME; } //设置debug状态,同时可设置log打印等级 public static void setDebug(boolean isDebug, EALogLevel logLevel) { EasyAdsManger.getInstance().debug = isDebug; EasyAdsManger.getInstance().logLevel = logLevel; } //设置开屏v+ 小窗口自动关闭时间,单位毫秒,不设置使用默认各个渠道默认展示逻辑 public static void setSplashPlusAutoClose(int time) { EasyAdsManger.getInstance().splashPlusAutoClose = time; } } ================================================ FILE: easyads/src/main/java/com/easyads/EasyAdsConstant.java ================================================ package com.easyads; public class EasyAdsConstant { public static final String NOT_SUPPORT_SUPPLIER_TIPS = "未支持的SDK渠道,跳过该渠道加载。请先检查是否引入了该渠道处理依赖,如已引入,检查下发渠道信息,如未在渠道已支持列表中,请请查看文档使用自定义渠道来完成广告加载"; /** * SDK的tag标记 */ //腾讯优量汇(原优量汇) public static final String SDK_TAG_YLH = "ylh"; //头条穿山甲 public static final String SDK_TAG_CSJ = "csj"; //百度百青藤 public static final String SDK_TAG_BAIDU = "bd"; //快手 public static final String SDK_TAG_KS = "ks"; /** * 策略分发占比默认值,默认100 代表100%执行 */ public static final double DEFAULT_PERCENT = 0d; /** * 广告状态记录,主要是用户调用广告展示时,分析广告状态,来执行不同操作 */ //默认状态 public static final int AD_STATUS_DEFAULT = -1; //广告请求中 public static final int AD_STATUS_LOADING = 0; //广告请求成功了 public static final int AD_STATUS_LOAD_SUCCESS = 1; //广告失败了 public static final int AD_STATUS_LOAD_FAILED = 2; //广告需要被展示 public static final int AD_STATUS_NEED_SHOW = 3; } ================================================ FILE: easyads/src/main/java/com/easyads/EasyAdsManger.java ================================================ package com.easyads; import com.easyads.model.EALogLevel; /** * 单例处理一些全局配置信息 */ public class EasyAdsManger { private static EasyAdsManger instance; private EasyAdsManger() { } public static synchronized EasyAdsManger getInstance() { if (instance == null) { instance = new EasyAdsManger(); } return instance; } //调试状态 public boolean debug = false; //dev状态代表开发者模式,会有更多日志输出 public boolean isDev = false; //标记各平台是否初始化过 public boolean hasYLHInit = false; public boolean hasCSJInit = false; public boolean hasKSInit = false; public boolean hasBDInit = false; //各平台最近一次初始化的APP_ID public String lastYLHAID = ""; public String lastCSJAID = ""; public String lastKSAID = ""; public String lastBDAID = ""; public String currentSupTag = "";//当前执行的SDK渠道id public boolean isSplashSupportZoomOut = false; //是否需要执行v+形式的缩放操作 public int splashPlusAutoClose = -1;//开屏v+自动关闭时间,单位毫秒,默认-1不执行强制关闭 public EALogLevel logLevel = EALogLevel.DEFAULT; public int getSplashPlusAutoClose() { try { if (splashPlusAutoClose <= 0) { return -1; } //最短3秒 if (splashPlusAutoClose < 3000) { return 3000; } //最长30秒 if (splashPlusAutoClose > 30000) { return 30000; } } catch (Throwable e) { e.printStackTrace(); } return splashPlusAutoClose; } } ================================================ FILE: easyads/src/main/java/com/easyads/core/EABaseSupplierAdapter.java ================================================ package com.easyads.core; import android.app.Activity; import com.easyads.EasyAdsConstant; import com.easyads.itf.AdCoreAction; import com.easyads.itf.BaseAdapterEvent; import com.easyads.itf.BaseEnsureListener; import com.easyads.model.EasyAdError; import com.easyads.model.SdkSupplier; import com.easyads.utils.EAUtil; import com.easyads.utils.EALog; import java.lang.ref.SoftReference; import static com.easyads.model.EasyAdError.ERROR_EXCEPTION_LOAD; import static com.easyads.model.EasyAdError.ERROR_EXCEPTION_SHOW; public abstract class EABaseSupplierAdapter implements AdCoreAction { public String TAG = "[" + this.getClass().getSimpleName() + "] "; private BaseAdapterEvent baseListener; // 广告基础回调事件 private boolean hasFailed = false; //广告失败标记 private int lastFailedPri = -1;//上次广告失败对应优先级,主要是处理同时配置了两个一样SDK渠道时,防止失败回调事件异常 private int adNum = 0;//记录广告数量,每load一次+1.每失败一次-1.如果此值为负,那么可能为回调了多次的失败回调,需抛弃处理此次回调。 private boolean isLoadOnly = false; //标记是否为仅load广告 protected SoftReference softReferenceActivity; //软引用上下文,避免强引用导致内存泄漏 protected SdkSupplier sdkSupplier; //当前adapter正在执行的渠道配置信息 protected boolean isDestroy = false;//销毁标记 protected boolean refreshing = false;//banner位置的特殊标记,标记广告正在进行自动刷新 /** * 通用初始化方法 * * @param softReferenceActivity 上下文 * @param baseListener 基类adapter回调 */ public EABaseSupplierAdapter(SoftReference softReferenceActivity, final BaseAdapterEvent baseListener) { this.softReferenceActivity = softReferenceActivity; this.baseListener = baseListener; } /** * 设置当前渠道处理需要的渠道数据 * * @param sdkSupplier 渠道信息model */ public void setSDKSupplier(SdkSupplier sdkSupplier) { try { this.sdkSupplier = sdkSupplier; } catch (Throwable e) { e.printStackTrace(); } } /** * 统一获取activity方法 * * @return 页面上下文 */ public Activity getActivity() { if (softReferenceActivity != null) { return softReferenceActivity.get(); } return null; } /** * 基础的核心方法,加载、展示、销毁 */ @Override public void loadOnly() { isLoadOnly = true; startAdapterADLoad(); } @Override public void show() { try { //当使用分离的加载和展示调用时,只要广告是未成功状态,均不执行show操作 if (isLoadOnly && sdkSupplier.adStatus != EasyAdsConstant.AD_STATUS_LOAD_SUCCESS) { sdkSupplier.adStatus = EasyAdsConstant.AD_STATUS_NEED_SHOW; EALog.simple(TAG + "广告尚未获取到,暂无法执行展示,请耐心等待,SDK会在收到广告后,立即执行广告展示。若广告一直未展示,请检查广告失败回调和对应log信息,了解展示失败原因"); return; } //保证在主线程调用show EAUtil.switchMainThread(new BaseEnsureListener() { @Override public void ensure() { doShowAD(); } }); } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.parseErr(ERROR_EXCEPTION_SHOW, "BaseSupplierAdapter show Throwable")); } } @Override public void loadAndShow() { isLoadOnly = false; startAdapterADLoad(); } @Override public void destroy() { try { isDestroy = true; doDestroy(); } catch (Throwable e) { e.printStackTrace(); } } /** * 抽象基础方法,使子类仅关注对应的广告处理逻辑 */ //抽象方法,此方法内应执行广告加载方法 protected abstract void doLoadAD(); //抽象方法,此方法内应执行广告展示方法(如有) protected abstract void doShowAD(); //抽象方法,此方法内应执行广告销毁操作(如有) protected abstract void doDestroy(); //开始调用加载广告方法 public void startAdapterADLoad() { try { if (sdkSupplier != null) { String msg = TAG + "开始请求广告 "; EALog.simple(msg); } ++adNum; //保证在主线程调用load EAUtil.switchMainThread(new BaseEnsureListener() { @Override public void ensure() { doLoadAD(); } }); sdkSupplier.adStatus = EasyAdsConstant.AD_STATUS_LOADING; } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.parseErr(ERROR_EXCEPTION_LOAD, "BaseSupplierAdapter load Throwable")); } } /** * banner 特有的失败处理,主要是自动刷新部分的处理比较特殊 * * @param easyAdError */ public void doBannerFailed(EasyAdError easyAdError) { try { if (isBannerFailed()) { handleFailed(easyAdError); //广告失败并进行销毁 doDestroy(); } } catch (Throwable e) { e.printStackTrace(); } } /** * banner 广告是否算失败,因为有刷新的逻辑,如果刷新中失败,是可以按照不失败继续执行刷新的 */ private boolean isBannerFailed() { String tag = "【isBannerFailed-check】"; //如果未在展示中,失败了需要进行销毁,否则会在后台自动进行请求 boolean isRunning = true; if (sdkSupplier != null) { int pri = sdkSupplier.priority; if (baseListener != null && baseListener.getSupplierInf() != null) { int curPri = baseListener.getSupplierInf().priority; EALog.high(tag + "curPri = " + curPri + " pri = " + pri); isRunning = curPri == pri; } } EALog.high(tag + "refreshing = " + refreshing + " isRunning = " + isRunning); if (refreshing && isRunning) { EALog.high(tag + "等待刷新中,即使失败也不进行销毁操作"); return false; } EALog.simple(tag + " 广告失败,进行销毁操作"); return true; } /** * 是否优化初始化流程,默认为true代表自动优化初始化方法,多次相同appID的初始化不重复执行 * * @return 优化 */ public boolean canOptInit() { return true; } /** * 获取策略中配置的媒体id。 */ public String getAppID() { String appID = ""; if (sdkSupplier != null) { appID = sdkSupplier.appId; } return appID; } /** * 获取策略中配置的广告位id。 */ public String getPosID() { String posID = ""; if (sdkSupplier != null) { posID = sdkSupplier.adspotId; } return posID; } /** * --------- 以下是公共处理核心回调事件方法 ---------- */ public void handleClick() { try { EALog.simple(TAG + "广告点击"); EAUtil.switchMainThread(new BaseEnsureListener() { @Override public void ensure() { if (baseListener == null) { EALog.e(TAG + "baseListener is null"); return; } //这里集中处理回调事件 baseListener.adapterDidClicked(sdkSupplier); } }); } catch (Throwable e) { e.printStackTrace(); } } public void handleExposure() { try { EALog.simple(TAG + "广告曝光成功"); EAUtil.switchMainThread(new BaseEnsureListener() { @Override public void ensure() { if (baseListener == null) { EALog.e(TAG + "baseListener is null"); return; } //这里集中处理回调事件 baseListener.adapterDidExposure(sdkSupplier); } }); } catch (Throwable e) { e.printStackTrace(); } } public void handleSucceed() { try { EALog.simple(TAG + "广告获取成功"); sdkSupplier.adStatus = EasyAdsConstant.AD_STATUS_LOAD_SUCCESS; EAUtil.switchMainThread(new BaseEnsureListener() { @Override public void ensure() { if (baseListener == null) { EALog.e(TAG + "baseListener is null"); return; } //这里集中处理回调事件 baseListener.adapterDidSucceed(sdkSupplier); if (!isLoadOnly) { show(); } else { //代表调用过show方法,此时就要自动去执行展示广告流程 if (sdkSupplier.adStatus == EasyAdsConstant.AD_STATUS_NEED_SHOW) { show(); } } } }); } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.parseErr(EasyAdError.ERROR_EXCEPTION_LOAD)); } } public void handleFailed(int errCode, String errMsg) { handleFailed(errCode + "", errMsg); } public void handleFailed(String errCode, String errMsg) { handleFailed(EasyAdError.parseErr(errCode, errMsg)); } //统一处理,告诉基础类失败了,需要走下一优先级的渠道 public void handleFailed(EasyAdError error) { try { if (error != null) { EALog.simple(TAG + "广告获取失败 , 失败信息:" + error.toString()); } //避免重复执行失败任务 checkFailed(); sdkSupplier.adStatus = EasyAdsConstant.AD_STATUS_LOAD_FAILED; if (baseListener == null) { EALog.e(TAG + "baseListener is null"); return; } baseListener.adapterDidFailed(error, sdkSupplier); } catch (Throwable e) { e.printStackTrace(); } } /** * 检查失败唯一性,避免重复回调失败 * * @throws Exception */ private void checkFailed() throws Exception { String exInf = " -- ad failed check -- , already failed , skip callback onFailed"; --adNum; EALog.max(TAG + "[checkFailed] adNum = " + adNum); if (sdkSupplier != null) { //避免重复执行失败任务 if (hasFailed && lastFailedPri == sdkSupplier.priority && adNum < 0) { EALog.high(TAG + exInf); throw new Exception(exInf); } hasFailed = true; lastFailedPri = sdkSupplier.priority; } } } ================================================ FILE: easyads/src/main/java/com/easyads/core/EasyAdBaseAdspot.java ================================================ package com.easyads.core; import android.app.Activity; import android.app.Application; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; import com.easyads.EasyAdsConstant; import com.easyads.EasyAdsManger; import com.easyads.core.splash.EASplashListener; import com.easyads.core.splash.EASplashLifeCallback; import com.easyads.itf.AdCoreAction; import com.easyads.itf.EABaseADListener; import com.easyads.itf.BaseAdapterEvent; import com.easyads.itf.BaseEnsureListener; import com.easyads.model.EasyAdType; import com.easyads.model.EasyAdError; import com.easyads.model.SdkRule; import com.easyads.model.SdkSupplier; import com.easyads.model.StrategyModel; import com.easyads.utils.EAAdapterLoader; import com.easyads.utils.EAUtil; import com.easyads.utils.BigDecimalUtil; import com.easyads.utils.EALog; import java.lang.ref.SoftReference; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; /** * 广告处理基类,统一处理SDK渠道adapter加载、选择,adapter中基础回调事件统一处理。 */ public abstract class EasyAdBaseAdspot implements AdCoreAction, BaseAdapterEvent { private String BTAG = "[" + this.getClass().getSimpleName() + "] "; private EABaseADListener baseADListener; private boolean isLoadOnly = false; //是否为仅加载广告模 private boolean fromActivityDestroy = false; private String reqId = ""; //请求唯一id private StrategyModel mStrategyModel; //策略执行对象 private Application.ActivityLifecycleCallbacks alcb; protected SoftReference mSoftActivity;//软引用上下文 //需要执行的广告SDK列表 protected ArrayList suppliers; //当前选中的广告SDK protected SdkSupplier currentSdkSupplier; //当前执行的广告SDK渠道 protected SdkSupplier callBackRunningSupplier; //异常记录 protected EasyAdError easyAdError; //各SDK渠道适配adapter集合,初始化时传入,后续渠道选择时从map中选取, protected HashMap supplierAdapters = new HashMap<>(); protected String currentSdkTag = "";//当前正在执行SDK的tag protected EASplashLifeCallback splashLifeCallback;//适用于开屏的生命周期回调 protected EasyAdType adType;//广告类型 public EasyAdBaseAdspot(Activity activity, EABaseADListener listener) { try { this.mSoftActivity = new SoftReference<>(activity); this.baseADListener = listener; //初始化渠道列表 initSupplierAdapterList(); if (alcb != null) { //如果已存在,先注销 activity.getApplication().unregisterActivityLifecycleCallbacks(alcb); } //注册Lifecycle生命周期监控,用于在合适的生命周期处理广告事件 alcb = new Application.ActivityLifecycleCallbacks() { @Override public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) { } @Override public void onActivityStarted(@NonNull Activity activity) { } @Override public void onActivityResumed(@NonNull Activity mActivity) { if (splashLifeCallback != null && mActivity == getActivity()) { splashLifeCallback.onResume(); } } @Override public void onActivityPaused(@NonNull Activity mActivity) { if (splashLifeCallback != null && mActivity == getActivity()) { splashLifeCallback.onPause(); } } @Override public void onActivityStopped(@NonNull Activity activity) { } @Override public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) { } @Override public void onActivityDestroyed(@NonNull Activity mActivity) { if (getActivity() == mActivity) { fromActivityDestroy = true; destroy(); } } }; activity.getApplication().registerActivityLifecycleCallbacks(alcb); } catch (Throwable e) { e.printStackTrace(); } } /** * 初始化 */ void initSupplierAdapterList() { try { if (supplierAdapters == null) { supplierAdapters = new HashMap<>(); } else { supplierAdapters.clear(); } } catch (Throwable e) { e.printStackTrace(); } } /** * 必须!!设置策略信息,注意json格式一定要正确,否则有可能解析策略失败导致无广告。 * * @param strategyJson 策略信息json串,一定要传入指定要求的格式,具体请阅读文档对应内容了解。 */ public void setData(String strategyJson) { try { //解析json并转换为StrategyModel对象 mStrategyModel = StrategyModel.covert(strategyJson); ArrayList rules = mStrategyModel.rules; int ruleSize = rules.size(); if (ruleSize <= 0) {//代表无策略规则信息,不加载广告 } else if (ruleSize == 1) {//只有一组策略规则信息,全量加载 suppliers = rules.get(0).sortedSuppliers; EALog.simple(BTAG + "策略组唯一,全量执行: " + rules.get(0).tag); } else { //> 1,一组策略规则信息,按照设定的percent值进行比例切分,按比例随机选择规则组。 SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); // 以10000为基数,可以随机到0.01%精度 int maxNum = 10000; //获取0-999随机数 int randomPos = random.nextInt(maxNum); for (int i = 0; i < ruleSize; i++) { SdkRule rule = rules.get(i); //分母为各个setting内容的percent之和 double perResult = BigDecimalUtil.div(rule.percent, mStrategyModel.sumPercent, 4); double gapNum = BigDecimalUtil.mul(perResult, maxNum); if (i == 0) { rule.lowLimit = 0; } else { rule.lowLimit = rules.get(i - 1).highLimit + 1; } if (i == ruleSize - 1) { rule.highLimit = maxNum - 1; } else { rule.highLimit = rule.lowLimit + (int) gapNum - 1; } EALog.devDebug(BTAG + " randomPos = " + randomPos + ", gapNum = " + gapNum + " ;rule.lowLimit = " + rule.lowLimit + ", rule.highLimit = " + rule.highLimit); //命中当前rule,锁定对应排序后的渠道信息为当前执行的渠道信息 if (randomPos < rule.highLimit && randomPos > rule.lowLimit) { suppliers = rule.sortedSuppliers; EALog.simple(BTAG + "根据已配置流量切分比例,策略组随机选择,此次命中: " + rule.tag + ",流量占比:" + BigDecimalUtil.mul(perResult, 100) + "%"); for (SdkSupplier supplier : suppliers) { EALog.devDebug(BTAG + "SDK渠道信息: " + supplier.toString()); } break; } } } } catch (Throwable e) { e.printStackTrace(); } } @Override public void loadOnly() { isLoadOnly = true; startLoad(); } @Override public void loadAndShow() { isLoadOnly = false; startLoad(); } /** * 用来统一调用adapter中的show方法 */ @Override public void show() { try { if (TextUtils.isEmpty(currentSdkTag)) { EALog.e("未选中任何SDK"); return; } if (supplierAdapters == null || supplierAdapters.size() == 0) { EALog.e("无可用渠道"); return; } EABaseSupplierAdapter adapter = supplierAdapters.get(currentSdkTag); if (adapter == null) { EALog.e("未找到当前渠道下adapter,渠道id:" + currentSdkTag); return; } if (adapter.isDestroy) { EALog.e("广告已销毁,无法展示,请重新初始化"); return; } adapter.show(); } catch (Throwable e) { e.printStackTrace(); } } /** * 开始加载 */ protected void startLoad() { try { //添加各广告位初始化支持的adapter initSdkSupplier(); //选择执行adapter dispatchSuppliers(); //生成当前请求的唯一id reqId = EAUtil.getUUID(); } catch (Throwable t) { t.printStackTrace(); } } //获取此次广告的唯一请求id,值为本地生成的uuid public String getReqId() { return reqId; } /** * BaseAdapterEventListener中方法,在此统一进行回调处理 ----start * 不同广告位可能会有其他的回调listener事件,此处为共有的广告成功、展示、点击等处理 */ @Override public void adapterDidFailed(EasyAdError easyAdError, SdkSupplier supplier) { try { this.easyAdError = easyAdError; updateSupplier("adapterDidFailed", supplier); selectSdkSupplier(); } catch (Throwable e) { e.printStackTrace(); } } @Override public void adapterDidSucceed(SdkSupplier supplier) { updateSupplier("adapterDidSucceed", supplier); if (null != baseADListener) { baseADListener.onAdSucceed(); } } @Override public void adapterDidExposure(SdkSupplier supplier) { updateSupplier("adapterDidExposure", supplier); if (null != baseADListener) { baseADListener.onAdExposure(); } } @Override public void adapterDidClicked(SdkSupplier supplier) { updateSupplier("adapterDidClicked", supplier); if (null != baseADListener) { baseADListener.onAdClicked(); } } @Override public SdkSupplier getSupplierInf() { return callBackRunningSupplier; } /** * BaseAdapterEventListener中方法,在此统一进行回调处理 ----end */ protected void updateSupplier(String func, SdkSupplier sdkSupplier) { try { String extMsg = ""; if (sdkSupplier != null) { callBackRunningSupplier = sdkSupplier; extMsg = ", sdkSupplier = " + sdkSupplier.toString(); } EALog.high(BTAG + "_" + func + "_" + extMsg); } catch (Throwable e) { e.printStackTrace(); } } /** * 添加自定义的渠道adapter * * @param sdkTag 自定义的渠道tag * @param adapter 自定义的渠道adapter */ public void addCustomSupplier(String sdkTag, EABaseSupplierAdapter adapter) { try { if (supplierAdapters == null) { supplierAdapters = new HashMap<>(); } EABaseSupplierAdapter adapter1 = supplierAdapters.get(sdkTag); if (adapter1 == null) { supplierAdapters.put(sdkTag, adapter); } else { EALog.simple("该sdkTag:" + sdkTag + "下已存在渠道adapter,无法重复添加"); } } catch (Throwable e) { e.printStackTrace(); } } /** * 分发获取到的策略信息 */ private void dispatchSuppliers() { //未设置策略信息,直接抛错 if (mStrategyModel == null) { if (easyAdError == null) { easyAdError = EasyAdError.parseErr(EasyAdError.ERROR_NONE_STRATEGY); } onTotalFailed(); return; } //确保在主线程执行策略选择逻辑 EAUtil.switchMainThread(new BaseEnsureListener() { @Override public void ensure() { receivedSuppliers(); } }); } /** * 获取activity上下文实例,注意默认为软引用的Activity * * @return activity上下文 */ protected Activity getActivity() { try { if (mSoftActivity != null) { return mSoftActivity.get(); } } catch (Throwable e) { e.printStackTrace(); } return null; } private void receivedSuppliers() { try { //对当前渠道组进行排序 Collections.sort(suppliers); } catch (Throwable e) { e.printStackTrace(); } selectSdkSupplier(); } /** * 统一处理渠道选择流程 * 选择SDK渠道逻辑,如果渠道为空,执行策略失败方法,不为空则开始渠道选中后的广告执行逻辑 */ void selectSdkSupplier() { try { EALog.simple(BTAG + "策略调度执行中"); if (suppliers == null || suppliers.size() == 0) { EALog.e(BTAG + "渠道信息为空"); if (easyAdError == null) { EALog.simple("None SDK: sdk suppliers is empty, callback failed"); easyAdError = EasyAdError.parseErr(EasyAdError.ERROR_NONE_SDK); } onTotalFailed(); return; } currentSdkSupplier = suppliers.get(0); if (EAUtil.isActivityDestroyed(getActivity())) { try { EALog.e(BTAG + "当前activity已被销毁,不再请求广告"); //activity被销毁也算是异常,广告流程的结束,需要回调失败,并上报整体失败时间 easyAdError = EasyAdError.parseErr(EasyAdError.ERROR_NO_ACTIVITY); onTotalFailed(); } catch (Throwable e) { e.printStackTrace(); } return; } //串行加载广告 runOrderSuppliers(); } catch (Throwable e) { e.printStackTrace(); easyAdError = EasyAdError.parseErr(EasyAdError.ERROR_SUPPLIER_SELECT); onTotalFailed(); } } /** * 整体失败回调,包括任意场景下的失败,可以在这里统一进行失败信息上报或者延迟上报的处理 */ protected void onTotalFailed() { try { //回调开发者错误信息 if (baseADListener != null) { baseADListener.onAdFailed(easyAdError); if (baseADListener instanceof EASplashListener) { final EASplashListener fListener = (EASplashListener) baseADListener; EAUtil.switchMainThread(new BaseEnsureListener() { @Override public void ensure() { fListener.onAdClose(); } }); } } } catch (Throwable e) { e.printStackTrace(); } } /** * 串行统一处理任务,当前是串行渠道直接loadAd,不是再去选择 */ private void runOrderSuppliers() { callSDKSelected(); if (suppliers != null && suppliers.size() > 0 && currentSdkSupplier != null) { String tag = currentSdkSupplier.tag; try { //移除当前正在执行的渠道 suppliers.remove(0); //查找对应渠道的处理adapter EABaseSupplierAdapter adapter = supplierAdapters.get(tag); if (adapter != null) { adapter.setSDKSupplier(currentSdkSupplier); //区分加载模式 if (isLoadOnly) { adapter.loadOnly(); } else { adapter.loadAndShow(); } } else { EALog.e(EasyAdsConstant.NOT_SUPPORT_SUPPLIER_TIPS); //无对应渠道处理adapter,需要重新调度策略 selectSdkSupplier(); } } catch (Throwable e) { e.printStackTrace(); catchFailed(); } } } private void callSDKSelected() { try { currentSdkTag = currentSdkSupplier.tag; EasyAdsManger.getInstance().currentSupTag = currentSdkTag; EasyAdsManger.getInstance().isSplashSupportZoomOut = false;//初始化开屏v+支持状态 EALog.simple(BTAG + "即将执行SDK :" + currentSdkTag); } catch (Throwable e) { e.printStackTrace(); } } //异常时的 private void catchFailed() { try { this.easyAdError = EasyAdError.parseErr(EasyAdError.ERROR_LOAD_SDK, ""); selectSdkSupplier(); } catch (Throwable e) { e.printStackTrace(); } } /** * 销毁非正在进行的其他渠道,一般是在广告成功展示以后调用。 * * @param supplier 正在进行的渠道 */ protected void destroyOtherSupplier(SdkSupplier supplier) { try { //遍历调用销毁 if (supplierAdapters != null && supplierAdapters.size() > 0 && supplier != null) { for (String key : supplierAdapters.keySet()) { String sTag = supplier.tag; //如果是非当前渠道,进行销毁操作 if (sTag != null && !sTag.equals(key)) { EABaseSupplierAdapter bad = supplierAdapters.get(key); //只销毁优先级在当前渠道之上的渠道 if (bad != null && bad.sdkSupplier != null && bad.sdkSupplier.priority < supplier.priority) { bad.destroy(); } } } } } catch (Throwable e) { EALog.e(BTAG + "destroyOtherSupplier catch Throwable"); e.printStackTrace(); } } public void destroy() { try { //遍历调用销毁 if (supplierAdapters != null && supplierAdapters.size() > 0) { for (String key : supplierAdapters.keySet()) { // 进行销毁操作 EABaseSupplierAdapter bad = supplierAdapters.get(key); if (bad != null) { bad.destroy(); } } } if (getActivity() != null && fromActivityDestroy) { getActivity().getApplication().unregisterActivityLifecycleCallbacks(alcb); } } catch (Throwable e) { EALog.e(BTAG + " do destroy catch Throwable"); e.printStackTrace(); } } //反射调用各个渠道banner adapter初始化方法 protected void initAdapter(String sdkTag, Object adObject) { try { EABaseSupplierAdapter bdAdapter = EAAdapterLoader.getSDKLoader(sdkTag, adType, mSoftActivity, adObject); if (bdAdapter != null && supplierAdapters != null) { supplierAdapters.put(sdkTag, bdAdapter); } } catch (Throwable e) { e.printStackTrace(); } } //在此方法里添加需要支持的adapter protected abstract void initSdkSupplier(); } ================================================ FILE: easyads/src/main/java/com/easyads/core/banner/EABannerListener.java ================================================ package com.easyads.core.banner; import com.easyads.itf.EABaseADListener; public interface EABannerListener extends EABaseADListener { } ================================================ FILE: easyads/src/main/java/com/easyads/core/banner/EABannerSetting.java ================================================ package com.easyads.core.banner; import android.view.ViewGroup; import com.easyads.itf.BaseAdapterEvent; import com.easyads.model.SdkSupplier; /** * banner 位置的一些共有事件接口 */ public interface EABannerSetting extends BaseAdapterEvent { void adapterDidDislike(SdkSupplier sdkSupplier); int getRefreshInterval(); int getCsjExpressViewAcceptedWidth(); int getCsjExpressViewAcceptedHeight(); ViewGroup getContainer(); } ================================================ FILE: easyads/src/main/java/com/easyads/core/banner/EasyAdBanner.java ================================================ package com.easyads.core.banner; import android.app.Activity; import android.view.ViewGroup; import com.easyads.EasyAdsConstant; import com.easyads.core.EasyAdBaseAdspot; import com.easyads.model.EasyAdType; import com.easyads.model.SdkSupplier; import com.easyads.utils.ScreenUtil; public class EasyAdBanner extends EasyAdBaseAdspot implements EABannerSetting { private ViewGroup adContainer; //banner 父布局 private int refreshInterval = 0; //定时刷新时间,单位为秒,仅优量汇、穿山甲生效 private EABannerListener listener;//广告事件回调 public int csjExpressViewAcceptedWidth = 360; //穿山甲模板尺寸宽度,单位dp,一定要和穿山甲后台配置的尺寸匹配 public int csjExpressViewAcceptedHeight = 0; /** * 构造方法 * * @param activity 上下文 * @param adContainer 广告父布局 * @param listener 广告事件回调 */ public EasyAdBanner(Activity activity, final ViewGroup adContainer, EABannerListener listener) { super(activity, listener); try { adType = EasyAdType.BANNER;//赋值广告类型 this.adContainer = adContainer; this.listener = listener; //默认设置为全屏宽度 csjExpressViewAcceptedWidth = ScreenUtil.px2dip(activity, ScreenUtil.getScreenWidth(activity)); } catch (Throwable e) { e.printStackTrace(); } } @Override protected void initSdkSupplier() { try { initAdapter(EasyAdsConstant.SDK_TAG_CSJ, this); initAdapter(EasyAdsConstant.SDK_TAG_YLH, this); initAdapter(EasyAdsConstant.SDK_TAG_BAIDU, this); } catch (Throwable e) { e.printStackTrace(); } } @Override public void adapterDidExposure(SdkSupplier supplier) { super.adapterDidExposure(supplier); //一旦有渠道展示成功了 则将其他渠道进行销毁操作,避免定时器触发问题 if (currentSdkSupplier != null) { destroyOtherSupplier(currentSdkSupplier); } } public void adapterDidDislike(SdkSupplier supplier) { updateSupplier("adapterDidDislike", supplier); if (null != listener) { listener.onAdClose(); } } public EasyAdBanner setRefreshInterval(int refreshInterval) { this.refreshInterval = refreshInterval; return this; } public void setCsjExpressSize(int width, int height) { csjExpressViewAcceptedWidth = width; csjExpressViewAcceptedHeight = height; } public ViewGroup getContainer() { return adContainer; } public int getRefreshInterval() { return this.refreshInterval; } public int getCsjExpressViewAcceptedWidth() { return this.csjExpressViewAcceptedWidth; } public int getCsjExpressViewAcceptedHeight() { return this.csjExpressViewAcceptedHeight; } } ================================================ FILE: easyads/src/main/java/com/easyads/core/draw/EADrawListener.java ================================================ package com.easyads.core.draw; import com.easyads.itf.EABaseADListener; public interface EADrawListener extends EABaseADListener { } ================================================ FILE: easyads/src/main/java/com/easyads/core/draw/EADrawSetting.java ================================================ package com.easyads.core.draw; import android.view.ViewGroup; import com.easyads.itf.BaseAdapterEvent; public interface EADrawSetting extends BaseAdapterEvent { ViewGroup getContainer(); int getCsjExpressHeight(); int getCsjExpressWidth(); } ================================================ FILE: easyads/src/main/java/com/easyads/core/draw/EasyAdDraw.java ================================================ package com.easyads.core.draw; import android.app.Activity; import android.view.ViewGroup; import com.easyads.EasyAdsConstant; import com.easyads.core.EasyAdBaseAdspot; import com.easyads.model.EasyAdType; import com.easyads.utils.EALog; import com.easyads.utils.ScreenUtil; public class EasyAdDraw extends EasyAdBaseAdspot implements EADrawSetting { EADrawListener listener;//广告事件回调 ViewGroup adContainer;//广告父布局 private int csjExpressWidth;//穿山甲模板尺寸宽度,单位dp private int csjExpressHeight; public EasyAdDraw(Activity activity, EADrawListener listener) { super(activity, listener); try { //赋值广告类型 adType = EasyAdType.DRAW; this.listener = listener; //默认赋值为屏幕宽高 csjExpressWidth = ScreenUtil.px2dip(activity, ScreenUtil.getScreenWidth(activity)); csjExpressHeight = ScreenUtil.px2dip(activity, ScreenUtil.getScreenHeight(activity)); } catch (Throwable e) { e.printStackTrace(); } } @Override protected void initSdkSupplier() { try { initAdapter(EasyAdsConstant.SDK_TAG_CSJ, this); initAdapter(EasyAdsConstant.SDK_TAG_KS, this); } catch (Throwable e) { e.printStackTrace(); } } /** * 以下为setting回调项处理 */ public void setAdContainer(final ViewGroup adContainer) { try { this.adContainer = adContainer; //将模板尺寸宽高,自动赋值为父布局的宽高属性 adContainer.post(new Runnable() { @Override public void run() { csjExpressWidth = ScreenUtil.px2dip(getActivity(), adContainer.getWidth()); csjExpressHeight = ScreenUtil.px2dip(getActivity(), adContainer.getHeight()); EALog.devDebug("set csjExpressView as adContainer Width= " + csjExpressWidth + " Height= " + csjExpressHeight); } }); } catch (Throwable e) { e.printStackTrace(); } } @Override public ViewGroup getContainer() { return adContainer; } @Override public int getCsjExpressHeight() { return csjExpressHeight; } @Override public int getCsjExpressWidth() { return csjExpressWidth; } } ================================================ FILE: easyads/src/main/java/com/easyads/core/full/EAFullScreenVideoListener.java ================================================ package com.easyads.core.full; import com.easyads.itf.EABaseADListener; public interface EAFullScreenVideoListener extends EABaseADListener { void onVideoComplete(); void onVideoSkipped(); void onVideoCached(); } ================================================ FILE: easyads/src/main/java/com/easyads/core/full/EAFullScreenVideoSetting.java ================================================ package com.easyads.core.full; import com.easyads.itf.BaseAdapterEvent; import com.easyads.model.SdkSupplier; import com.qq.e.ads.cfg.VideoOption; import com.qq.e.ads.interstitial2.UnifiedInterstitialMediaListener; public interface EAFullScreenVideoSetting extends BaseAdapterEvent { void adapterVideoCached(SdkSupplier sdkSupplier); void adapterVideoComplete(SdkSupplier sdkSupplier); void adapterClose(SdkSupplier sdkSupplier); void adapterVideoSkipped(SdkSupplier sdkSupplier); UnifiedInterstitialMediaListener getYlhMediaListener(); VideoOption getYlhVideoOption(); } ================================================ FILE: easyads/src/main/java/com/easyads/core/full/EasyAdFullScreenVideo.java ================================================ package com.easyads.core.full; import android.app.Activity; import com.easyads.EasyAdsConstant; import com.easyads.core.EasyAdBaseAdspot; import com.easyads.model.EasyAdType; import com.easyads.model.SdkSupplier; import com.qq.e.ads.cfg.VideoOption; import com.qq.e.ads.interstitial2.UnifiedInterstitialMediaListener; public class EasyAdFullScreenVideo extends EasyAdBaseAdspot implements EAFullScreenVideoSetting { private EAFullScreenVideoListener listener;//广告事件回调 private UnifiedInterstitialMediaListener ylhMediaListener;//优量汇视频加载情况监听器 private VideoOption ylhVideoOption;//优量汇视频广告设置项 public EasyAdFullScreenVideo(Activity activity, EAFullScreenVideoListener listener) { super(activity, listener); adType = EasyAdType.FULL;//赋值广告类型 this.listener = listener; } @Override public void initSdkSupplier() { try { initAdapter(EasyAdsConstant.SDK_TAG_CSJ, this); initAdapter(EasyAdsConstant.SDK_TAG_YLH, this); initAdapter(EasyAdsConstant.SDK_TAG_BAIDU, this); initAdapter(EasyAdsConstant.SDK_TAG_KS, this); } catch (Throwable e) { e.printStackTrace(); } } @Override public UnifiedInterstitialMediaListener getYlhMediaListener() { return ylhMediaListener; } @Override public VideoOption getYlhVideoOption() { return ylhVideoOption; } public void setYlhMediaListener(UnifiedInterstitialMediaListener mediaListener) { this.ylhMediaListener = mediaListener; } public void setYlhVideoOption(VideoOption videoOption) { this.ylhVideoOption = videoOption; } public void adapterVideoCached(SdkSupplier supplier) { updateSupplier("adapterVideoCached", supplier); if (null != listener) { listener.onVideoCached(); } } public void adapterVideoComplete(SdkSupplier supplier) { updateSupplier("adapterVideoComplete", supplier); if (null != listener) { listener.onVideoComplete(); } } public void adapterClose(SdkSupplier supplier) { updateSupplier("adapterClose", supplier); if (null != listener) { listener.onAdClose(); } } public void adapterVideoSkipped(SdkSupplier supplier) { updateSupplier("adapterVideoSkipped", supplier); if (null != listener) { listener.onVideoSkipped(); } } } ================================================ FILE: easyads/src/main/java/com/easyads/core/inter/EAInterstitialListener.java ================================================ package com.easyads.core.inter; import com.easyads.itf.EABaseADListener; public interface EAInterstitialListener extends EABaseADListener { } ================================================ FILE: easyads/src/main/java/com/easyads/core/inter/EAInterstitialSetting.java ================================================ package com.easyads.core.inter; import com.easyads.itf.BaseAdapterEvent; import com.easyads.model.SdkSupplier; import com.qq.e.ads.interstitial2.UnifiedInterstitialMediaListener; public interface EAInterstitialSetting extends BaseAdapterEvent { void adapterDidClosed(SdkSupplier sdkSupplier); float getCsjExpressViewWidth(); float getCsjExpressViewHeight(); UnifiedInterstitialMediaListener getYlhMediaListener(); boolean isCsjNew(); } ================================================ FILE: easyads/src/main/java/com/easyads/core/inter/EasyAdInterstitial.java ================================================ package com.easyads.core.inter; import android.app.Activity; import com.easyads.EasyAdsConstant; import com.easyads.core.EasyAdBaseAdspot; import com.easyads.model.EasyAdType; import com.easyads.model.SdkSupplier; import com.qq.e.ads.interstitial2.UnifiedInterstitialMediaListener; public class EasyAdInterstitial extends EasyAdBaseAdspot implements EAInterstitialSetting { private EAInterstitialListener listener; private UnifiedInterstitialMediaListener ylhlistener;//优量汇视频加载情况监听器 private float csjExpressViewWidth = 300;//穿山甲模板尺寸设置,单位dp private float csjExpressViewHeight = 300; private boolean isCsjNew = true;//是否为穿山甲"新插屏广告" public EasyAdInterstitial(Activity activity, EAInterstitialListener listener) { super(activity, listener); adType = EasyAdType.INTR; this.listener = listener; } @Override public void initSdkSupplier() { try { initAdapter(EasyAdsConstant.SDK_TAG_CSJ, this); initAdapter(EasyAdsConstant.SDK_TAG_YLH, this); initAdapter(EasyAdsConstant.SDK_TAG_BAIDU, this); initAdapter(EasyAdsConstant.SDK_TAG_KS, this); } catch (Throwable e) { e.printStackTrace(); } } public void adapterDidClosed(SdkSupplier supplier) { updateSupplier("adapterDidClosed", supplier); if (null != listener) { listener.onAdClose(); } destroy(); } //期望模板广告view的size,单位dp;注意:参数请按照平台勾选的比例去进行请求。现有1:1,3:2 ,2:3 三种比例可供选择。 public void setCsjExpressViewAcceptedSize(float expressViewWidth, float expressViewHeight) { this.csjExpressViewWidth = expressViewWidth; this.csjExpressViewHeight = expressViewHeight; } @Deprecated public void setCsjNew(boolean csjNew) { isCsjNew = csjNew; } public void setYlhMediaListener(UnifiedInterstitialMediaListener listener) { ylhlistener = listener; } public UnifiedInterstitialMediaListener getYlhMediaListener() { return ylhlistener; } public float getCsjExpressViewWidth() { return csjExpressViewWidth; } public float getCsjExpressViewHeight() { return csjExpressViewHeight; } @Override public boolean isCsjNew() { return isCsjNew; } } ================================================ FILE: easyads/src/main/java/com/easyads/core/nati/EANativeExpressListener.java ================================================ package com.easyads.core.nati; import com.easyads.itf.EABaseADListener; public interface EANativeExpressListener extends EABaseADListener { void onAdRenderFailed(); void onAdRenderSuccess(); } ================================================ FILE: easyads/src/main/java/com/easyads/core/nati/EANativeExpressSetting.java ================================================ package com.easyads.core.nati; import android.view.View; import android.view.ViewGroup; import com.easyads.itf.BaseAdapterEvent; import com.easyads.model.SdkSupplier; public interface EANativeExpressSetting extends BaseAdapterEvent { int getExpressViewWidth(); int getExpressViewHeight(); boolean isVideoMute(); void adapterRenderFailed(SdkSupplier sdkSupplier); void adapterRenderSuccess(SdkSupplier sdkSupplier); void adapterDidClosed(SdkSupplier sdkSupplier); void setNativeExpressADView(View expressADView); int getYlhMaxVideoDuration(); ViewGroup getAdContainer(); } ================================================ FILE: easyads/src/main/java/com/easyads/core/nati/EasyAdNativeExpress.java ================================================ package com.easyads.core.nati; import android.app.Activity; import android.view.View; import android.view.ViewGroup; import com.easyads.EasyAdsConstant; import com.easyads.core.EasyAdBaseAdspot; import com.easyads.model.EasyAdType; import com.easyads.model.SdkSupplier; import com.easyads.utils.EALog; import com.easyads.utils.ScreenUtil; public class EasyAdNativeExpress extends EasyAdBaseAdspot implements EANativeExpressSetting { private EANativeExpressListener listener; private int expressViewWidth = 600;//模板尺寸设置,单位dp private int expressViewHeight = 0;//模板尺寸设置,单位dp,高度为0 代表自适应高度 private boolean videoMute = true; //视频静音,仅优量汇生效,穿山甲在后台中配置 private int ylhMaxVideoDuration = 60;//优量汇最大视频时长,单位秒 private ViewGroup adContainer; //广告承载布局 private View expressADView; //广告的实际展示view信息,数据来源于广告SDK回调 public EasyAdNativeExpress(Activity activity, EANativeExpressListener listener) { super(activity, listener); adType = EasyAdType.NATIV; this.listener = listener; } @Override public void initSdkSupplier() { try { initAdapter(EasyAdsConstant.SDK_TAG_CSJ, this); initAdapter(EasyAdsConstant.SDK_TAG_YLH, this); initAdapter(EasyAdsConstant.SDK_TAG_BAIDU, this); initAdapter(EasyAdsConstant.SDK_TAG_KS, this); } catch (Throwable e) { e.printStackTrace(); } } public void adapterDidClosed(SdkSupplier supplier) { updateSupplier("adapterDidClosed", supplier); if (null != listener) { listener.onAdClose(); } } public void adapterRenderFailed(SdkSupplier supplier) { updateSupplier("adapterRenderFailed", supplier); if (null != listener) { listener.onAdRenderFailed(); } } public void adapterRenderSuccess(SdkSupplier supplier) { updateSupplier("adapterRenderSuccess", supplier); if (null != listener) { listener.onAdRenderSuccess(); } } @Override public void destroy() { super.destroy(); try { if (adContainer != null) { adContainer.removeAllViews(); } } catch (Throwable e) { e.printStackTrace(); } } /** * 个性化配置类参数设置 */ // 设置用来展示广告的父布局 public void setAdContainer(ViewGroup container) { adContainer = container; try { adContainer.post(new Runnable() { @Override public void run() { expressViewWidth = ScreenUtil.px2dip(getActivity(), adContainer.getWidth()); expressViewHeight = ScreenUtil.px2dip(getActivity(), adContainer.getHeight()); EALog.devDebug("set expressViewWidth as adContainer Width= " + expressViewWidth + " Height= " + expressViewHeight); } }); } catch (Throwable e) { e.printStackTrace(); } } // 设置视频类广告是否默认静音,默认true public EasyAdNativeExpress setVideoMute(boolean mute) { this.videoMute = mute; return this; } public EasyAdNativeExpress setYlhMaxVideoDuration(int ylhMaxVideoDuration) { this.ylhMaxVideoDuration = ylhMaxVideoDuration; return this; } @Override public ViewGroup getAdContainer() { return adContainer; } @Override public void setNativeExpressADView(View expressADView) { this.expressADView = expressADView; } public int getExpressViewWidth() { return expressViewWidth; } public int getExpressViewHeight() { return expressViewHeight; } @Override public boolean isVideoMute() { return videoMute; } public int getYlhMaxVideoDuration() { return ylhMaxVideoDuration; } public View getExpressADView() { return expressADView; } } ================================================ FILE: easyads/src/main/java/com/easyads/core/reward/EARewardServerCallBackInf.java ================================================ package com.easyads.core.reward; import java.util.Map; public class EARewardServerCallBackInf { public boolean bdRewardVerify; public Map ylhRewardMap; public CsjRewardInf csjInf; public static class CsjRewardInf { public boolean rewardVerify; public int rewardAmount; public String rewardName; public int errorCode; public String errMsg; } } ================================================ FILE: easyads/src/main/java/com/easyads/core/reward/EARewardVideoListener.java ================================================ package com.easyads.core.reward; import com.easyads.itf.EABaseADListener; public interface EARewardVideoListener extends EABaseADListener { void onVideoCached(); void onVideoComplete(); void onVideoSkip(); void onAdReward(); //激励视频返回的服务器回调信息,穿山甲一直支持,优量汇自v4.330.1200 开始支持,百度9.13开始支持 void onRewardServerInf(EARewardServerCallBackInf inf); } ================================================ FILE: easyads/src/main/java/com/easyads/core/reward/EARewardVideoSetting.java ================================================ package com.easyads.core.reward; import com.easyads.itf.BaseAdapterEvent; import com.easyads.model.SdkSupplier; import com.qq.e.ads.rewardvideo.ServerSideVerificationOptions; public interface EARewardVideoSetting extends BaseAdapterEvent { void adapterVideoCached(SdkSupplier sdkSupplier); void adapterAdReward(SdkSupplier sdkSupplier); void adapterVideoSkipped(SdkSupplier sdkSupplier); void postRewardServerInf(EARewardServerCallBackInf inf, SdkSupplier sdkSupplier); void adapterVideoComplete(SdkSupplier sdkSupplier); void adapterDidClosed(SdkSupplier sdkSupplier); String getCsjRewardName(); int getCsjRewardAmount(); String getCsjUserId(); int getCsjOrientation(); String getCsjMediaExtra(); boolean isYlhVolumeOn(); ServerSideVerificationOptions getYlhSSVO(); } ================================================ FILE: easyads/src/main/java/com/easyads/core/reward/EasyAdRewardVideo.java ================================================ package com.easyads.core.reward; import android.app.Activity; import com.easyads.EasyAdsConstant; import com.easyads.core.EasyAdBaseAdspot; import com.easyads.model.EasyAdType; import com.easyads.model.SdkSupplier; import com.qq.e.ads.rewardvideo.ServerSideVerificationOptions; public class EasyAdRewardVideo extends EasyAdBaseAdspot implements EARewardVideoSetting { private EARewardVideoListener listener; private int csjOrientation = ORIENTATION_VERTICAL; //屏幕方向,仅穿山甲支持 private String csjRewardName = ""; //穿山甲奖励名称 private String csjUserId = "";//穿山甲用户id private int csjRewardAmount = 1;//穿山甲奖励数量 private String csjMediaExtra = "";//穿山甲媒体补充信息 private boolean isYlhVO = false; //优量汇是否开启声音 private ServerSideVerificationOptions ylhSSVO = null; //优量汇服务端校验配置信息 public static final int ORIENTATION_VERTICAL = 1; public static final int ORIENTATION_HORIZONTAL = 2; public EasyAdRewardVideo(Activity activity, EARewardVideoListener listener) { super(activity, listener); adType = EasyAdType.REWARD; this.listener = listener; } @Override public void initSdkSupplier() { try { initAdapter(EasyAdsConstant.SDK_TAG_CSJ, this); initAdapter(EasyAdsConstant.SDK_TAG_YLH, this); initAdapter(EasyAdsConstant.SDK_TAG_BAIDU, this); initAdapter(EasyAdsConstant.SDK_TAG_KS, this); } catch (Throwable e) { e.printStackTrace(); } } public void adapterVideoCached(SdkSupplier supplier) { updateSupplier("adapterVideoCached", supplier); if (null != listener) { listener.onVideoCached(); } } public void adapterVideoComplete(SdkSupplier supplier) { updateSupplier("adapterVideoComplete", supplier); if (null != listener) { listener.onVideoComplete(); } } public void adapterDidClosed(SdkSupplier supplier) { updateSupplier("adapterDidClosed", supplier); if (null != listener) { listener.onAdClose(); } } public void adapterAdReward(SdkSupplier supplier) { updateSupplier("adapterAdReward", supplier); if (null != listener) { listener.onAdReward(); } } public void adapterVideoSkipped(SdkSupplier supplier) { updateSupplier("adapterVideoSkipped", supplier); if (null != listener) { listener.onVideoSkip(); } } @Override public void postRewardServerInf(EARewardServerCallBackInf inf, SdkSupplier supplier) { updateSupplier("postRewardServerInf", supplier); if (null != listener) { listener.onRewardServerInf(inf); } } public void setCsjRewardName(String rewardName) { this.csjRewardName = rewardName; } public void setCsjUserId(String userId) { this.csjUserId = userId; } public void setCsjRewardAmount(int rewardAmount) { this.csjRewardAmount = rewardAmount; } public void setCsjOrientation(int csjOrientation) { this.csjOrientation = csjOrientation; } public void setCsjMediaExtra(String mediaExtra) { this.csjMediaExtra = mediaExtra; } public void setYlhVolumeOn(boolean vo) { isYlhVO = vo; } public void setYlhSSVO(ServerSideVerificationOptions options){ ylhSSVO = options; } @Override public boolean isYlhVolumeOn() { return isYlhVO; } public String getCsjMediaExtra() { return csjMediaExtra; } @Override public ServerSideVerificationOptions getYlhSSVO() { return ylhSSVO; } public int getCsjOrientation() { return csjOrientation; } public int getCsjRewardAmount() { return csjRewardAmount; } public String getCsjRewardName() { return csjRewardName; } public String getCsjUserId() { return csjUserId; } } ================================================ FILE: easyads/src/main/java/com/easyads/core/splash/EASplashLifeCallback.java ================================================ package com.easyads.core.splash; public interface EASplashLifeCallback { void onResume(); void onPause(); } ================================================ FILE: easyads/src/main/java/com/easyads/core/splash/EASplashListener.java ================================================ package com.easyads.core.splash; import com.easyads.itf.EABaseADListener; public interface EASplashListener extends EABaseADListener { } ================================================ FILE: easyads/src/main/java/com/easyads/core/splash/EASplashSetting.java ================================================ package com.easyads.core.splash; import android.view.ViewGroup; import com.easyads.itf.BaseAdapterEvent; import com.easyads.model.SdkSupplier; public interface EASplashSetting extends BaseAdapterEvent { int getCsjAcceptedSizeWidth(); int getCsjAcceptedSizeHeight(); float getCsjExpressViewWidth(); float getCsjExpressViewHeight(); boolean getCsjShowAsExpress(); void adapterDidSkip(SdkSupplier sdkSupplier); void adapterDidTimeOver(SdkSupplier sdkSupplier); ViewGroup getAdContainer(); boolean isShowInSingleActivity(); } ================================================ FILE: easyads/src/main/java/com/easyads/core/splash/EasyAdSplash.java ================================================ package com.easyads.core.splash; import android.app.Activity; import android.text.TextUtils; import android.view.ViewGroup; import com.easyads.EasyAdsConstant; import com.easyads.core.EasyAdBaseAdspot; import com.easyads.itf.BaseEnsureListener; import com.easyads.model.EasyAdType; import com.easyads.model.SdkSupplier; import com.easyads.utils.EAUtil; import com.easyads.utils.EALog; import com.easyads.utils.ScreenUtil; public class EasyAdSplash extends EasyAdBaseAdspot implements EASplashSetting { private EASplashListener listener; private ViewGroup adContainer; //穿山甲尺寸,单位px private int csjAcceptedSizeWidth = 1080; private int csjAcceptedSizeHeight = 1920; //穿山甲模板宽高,单位dp private float csjExpressViewWidth = 360; private float csjExpressViewHeight = 640; private String failed_waring = "当前渠道(%1$s)已失败,等待后续渠道结果"; private boolean csjShowAsExpress = false; //设置穿山甲广告是否以个性化模板形式加载广告 private boolean canJump = false; //内部控制变量,是否可以进行广告跳转 private boolean showInSingleActivity = true;//APP实现是否为单独activity中展示开屏广告 public boolean isADSkip = false;//用来判断用户是否选择了跳过广告 public EasyAdSplash(final Activity activity, final ViewGroup adContainer, EASplashListener listener) { super(activity, listener); this.adContainer = adContainer; this.listener = listener; try { //赋值ad类型 adType = EasyAdType.SPLASH; splashLifeCallback = new EASplashLifeCallback() { @Override public void onResume() { if (canJump) { doJump(); } canJump = true; } @Override public void onPause() { canJump = TextUtils.equals(currentSdkTag, EasyAdsConstant.SDK_TAG_CSJ); } }; adContainer.post(new Runnable() { @Override public void run() { csjAcceptedSizeWidth = adContainer.getWidth(); csjAcceptedSizeHeight = adContainer.getHeight(); csjExpressViewWidth = ScreenUtil.px2dip(activity, adContainer.getWidth()); csjExpressViewHeight = ScreenUtil.px2dip(activity, adContainer.getHeight()); EALog.devDebug("set expressViewWidth as adContainer Width= " + csjExpressViewWidth + " Height= " + csjExpressViewHeight); } }); } catch (Throwable e) { e.printStackTrace(); } } @Override public void initSdkSupplier() { try { initAdapter(EasyAdsConstant.SDK_TAG_CSJ, this); initAdapter(EasyAdsConstant.SDK_TAG_YLH, this); initAdapter(EasyAdsConstant.SDK_TAG_BAIDU, this); initAdapter(EasyAdsConstant.SDK_TAG_KS, this); } catch (Throwable e) { e.printStackTrace(); } } @Override public void startLoad() { canJump = false; super.startLoad(); } /** * 统一处理跳转逻辑 */ private void doJump() { try { BaseEnsureListener ensureListener = new BaseEnsureListener() { @Override public void ensure() { EALog.high("[EasyAdSplash] canJump = " + canJump); if (canJump) { if (listener != null) listener.onAdClose(); } else { canJump = true; } } }; EAUtil.switchMainThread(ensureListener); } catch (Throwable e) { e.printStackTrace(); } } @Override public void adapterDidExposure(SdkSupplier supplier) { super.adapterDidExposure(supplier); //防止无法跳转 canJump = true; } public void adapterDidSkip(SdkSupplier supplier) { updateSupplier("adapterDidSkip", supplier); if (supplier != null && supplier.adStatus == EasyAdsConstant.AD_STATUS_LOAD_FAILED) { EALog.w(String.format(failed_waring,supplier.tag)); return; } isADSkip = true; doJump(); } public void adapterDidTimeOver(SdkSupplier supplier) { updateSupplier("adapterDidTimeOver", supplier); if (supplier != null && supplier.adStatus == EasyAdsConstant.AD_STATUS_LOAD_FAILED) { EALog.w(String.format(failed_waring,supplier.tag)); return; } isADSkip = false; doJump(); } /** * 开屏位置公共属性的获取和赋值 */ public boolean isShowInSingleActivity() { return showInSingleActivity; } public void setShowInSingleActivity(boolean single) { showInSingleActivity = single; canJump = single; } /** * 设置穿山甲是否为模板开屏类型 * * @param isExpress 模板标志,true为模板 */ public void setCsjShowAsExpress(boolean isExpress) { csjShowAsExpress = isExpress; } public boolean getCsjShowAsExpress() { return csjShowAsExpress; } public int getCsjAcceptedSizeWidth() { return csjAcceptedSizeWidth; } public int getCsjAcceptedSizeHeight() { return csjAcceptedSizeHeight; } public float getCsjExpressViewWidth() { return csjExpressViewWidth; } public float getCsjExpressViewHeight() { return csjExpressViewHeight; } @Override public ViewGroup getAdContainer() { return adContainer; } } ================================================ FILE: easyads/src/main/java/com/easyads/custom/EABannerCustomAdapter.java ================================================ package com.easyads.custom; import android.app.Activity; import com.easyads.core.EABaseSupplierAdapter; import com.easyads.itf.BaseAdapterEvent; import java.lang.ref.SoftReference; public abstract class EABannerCustomAdapter extends EABaseSupplierAdapter { public EABannerCustomAdapter(SoftReference softReferenceActivity, BaseAdapterEvent baseListener) { super(softReferenceActivity, baseListener); } } ================================================ FILE: easyads/src/main/java/com/easyads/custom/EADrawCustomAdapter.java ================================================ package com.easyads.custom; import android.app.Activity; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import com.easyads.core.EABaseSupplierAdapter; import com.easyads.core.draw.EADrawSetting; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import java.lang.ref.SoftReference; public abstract class EADrawCustomAdapter extends EABaseSupplierAdapter { protected EADrawSetting mDrawSetting; public EADrawCustomAdapter(SoftReference activity, EADrawSetting setting) { super(activity, setting); this.mDrawSetting = setting; } public boolean addADView(View adView) { boolean hasAdded = false; try { if (mDrawSetting != null) { ViewGroup adC = mDrawSetting.getContainer(); if (adC != null) { adC.removeAllViews(); if (adView != null) { ViewParent parent = adView.getParent(); if (parent != null) { ((ViewGroup) parent).removeView(adView); } } adC.addView(adView); hasAdded = true; } else { EALog.e(TAG + "无法展示广告,原因:未设置广告承载布局,请检查 setAdContainer(ViewGroup adContainer) 方法是否有赋值"); } } else { EALog.e(TAG + "无法展示广告,原因:内部处理异常,mDrawSetting 为空"); } } catch (Throwable e) { e.printStackTrace(); } if (!hasAdded) { handleFailed(EasyAdError.ERROR_RENDER_FAILED, "添加广告视图操作失败"); } else { EALog.simple(TAG + "ADView has Added"); } return hasAdded; } } ================================================ FILE: easyads/src/main/java/com/easyads/custom/EAFullScreenCustomAdapter.java ================================================ package com.easyads.custom; import android.app.Activity; import com.easyads.core.EABaseSupplierAdapter; import com.easyads.core.full.EAFullScreenVideoSetting; import java.lang.ref.SoftReference; public abstract class EAFullScreenCustomAdapter extends EABaseSupplierAdapter { EAFullScreenVideoSetting mFullSetting; public EAFullScreenCustomAdapter(SoftReference activity, EAFullScreenVideoSetting setting) { super(activity, setting); mFullSetting = setting; } public void handleCached() { try { if (null != mFullSetting) { mFullSetting.adapterVideoCached(sdkSupplier); } } catch (Throwable e) { e.printStackTrace(); } } } ================================================ FILE: easyads/src/main/java/com/easyads/custom/EAInterstitialCustomAdapter.java ================================================ package com.easyads.custom; import android.app.Activity; import com.easyads.core.EABaseSupplierAdapter; import com.easyads.core.inter.EAInterstitialSetting; import java.lang.ref.SoftReference; public abstract class EAInterstitialCustomAdapter extends EABaseSupplierAdapter { EAInterstitialSetting mInterSetting; public EAInterstitialCustomAdapter(SoftReference activity, EAInterstitialSetting setting) { super(activity, setting); mInterSetting = setting; } } ================================================ FILE: easyads/src/main/java/com/easyads/custom/EANativeExpressCustomAdapter.java ================================================ package com.easyads.custom; import android.app.Activity; import android.view.View; import android.view.ViewGroup; import com.easyads.core.EABaseSupplierAdapter; import com.easyads.core.nati.EANativeExpressSetting; import com.easyads.utils.EALog; import java.lang.ref.SoftReference; public abstract class EANativeExpressCustomAdapter extends EABaseSupplierAdapter { EANativeExpressSetting mNativeSetting; public EANativeExpressCustomAdapter(SoftReference activity, EANativeExpressSetting baseSetting) { super(activity, baseSetting); mNativeSetting = baseSetting; } public void addADView(View adView) { try { ViewGroup adContainer = mNativeSetting.getAdContainer(); if (adContainer == null) { EALog.e("adContainer 不存在,请先调用setAdContainer() 方法设置adContainer"); return; } if (adContainer.getChildCount() > 0 && adContainer.getChildAt(0) == adView) { EALog.high("已添加的布局"); return; } if (adContainer.getChildCount() > 0) { adContainer.removeAllViews(); } if (adView != null) { if (adView.getParent() != null) { ((ViewGroup) adView.getParent()).removeView(adView); } adContainer.setVisibility(View.VISIBLE); EALog.max("add adContainer = " + adContainer.toString()); // 广告可见才会产生曝光,否则将无法产生收益。 adContainer.addView(adView); } } catch (Throwable e) { e.printStackTrace(); } } public void removeADView() { try { ViewGroup adContainer = mNativeSetting.getAdContainer(); if (adContainer == null) { EALog.e("adContainer 不存在"); return; } EALog.max("remove adContainer = " + adContainer.toString()); adContainer.removeAllViews(); setNEView(null); } catch (Throwable e) { e.printStackTrace(); } } public void setNEView(View view) { try { if (view == null) { return; } mNativeSetting.setNativeExpressADView(view); } catch (Throwable e) { e.printStackTrace(); } } } ================================================ FILE: easyads/src/main/java/com/easyads/custom/EARewardCustomAdapter.java ================================================ package com.easyads.custom; import android.app.Activity; import com.easyads.core.EABaseSupplierAdapter; import com.easyads.core.reward.EARewardVideoSetting; import java.lang.ref.SoftReference; public abstract class EARewardCustomAdapter extends EABaseSupplierAdapter { public EARewardVideoSetting rewardSetting; public EARewardCustomAdapter(SoftReference softReferenceActivity, EARewardVideoSetting setting) { super(softReferenceActivity, setting); this.rewardSetting = setting; } public void handleCached() { try { if (null != rewardSetting) { rewardSetting.adapterVideoCached(sdkSupplier); } } catch (Throwable e) { e.printStackTrace(); } } } ================================================ FILE: easyads/src/main/java/com/easyads/custom/EASplashCustomAdapter.java ================================================ package com.easyads.custom; import android.app.Activity; import android.view.ViewGroup; import com.easyads.core.EABaseSupplierAdapter; import com.easyads.core.splash.EASplashSetting; import java.lang.ref.SoftReference; public abstract class EASplashCustomAdapter extends EABaseSupplierAdapter { public EASplashSetting mSplashSetting; public ViewGroup adContainer; public EASplashCustomAdapter(SoftReference activity, final EASplashSetting setting) { super(activity, setting); this.mSplashSetting = setting; try { if (setting != null) { adContainer = setting.getAdContainer(); } } catch (Throwable e) { e.printStackTrace(); } } } ================================================ FILE: easyads/src/main/java/com/easyads/itf/AdCoreAction.java ================================================ package com.easyads.itf; /** * 广告加载核心调用方法,对外聚合调用和内部adapter调用,方法命名一致 */ public interface AdCoreAction { void loadOnly(); void show(); void loadAndShow(); void destroy(); } ================================================ FILE: easyads/src/main/java/com/easyads/itf/BaseAdapterEvent.java ================================================ package com.easyads.itf; import com.easyads.model.EasyAdError; import com.easyads.model.SdkSupplier; /** * adapter 处理基类,所有的广告adapter必须实现的方法 */ public interface BaseAdapterEvent { void adapterDidSucceed(SdkSupplier supplier); void adapterDidExposure(SdkSupplier supplier); void adapterDidClicked(SdkSupplier supplier); void adapterDidFailed(EasyAdError easyAdError, SdkSupplier sdkSupplier);//聚合失败回调,回传sdkSupplier信息 SdkSupplier getSupplierInf(); //获取当前的渠道策略信息 } ================================================ FILE: easyads/src/main/java/com/easyads/itf/BaseEnsureListener.java ================================================ package com.easyads.itf; public interface BaseEnsureListener { void ensure(); } ================================================ FILE: easyads/src/main/java/com/easyads/itf/EABaseADListener.java ================================================ package com.easyads.itf; import com.easyads.model.EasyAdError; public interface EABaseADListener { void onAdSucceed();//成功加载到广告 void onAdExposure();//广告曝光 void onAdClicked();//广告点击 void onAdClose();//广告关闭 void onAdFailed(EasyAdError easyAdError);//广告失败 } ================================================ FILE: easyads/src/main/java/com/easyads/model/EALogLevel.java ================================================ package com.easyads.model; public enum EALogLevel { /** * 可以自定义级别,level值越大,打印的信息越多。 */ CUSTOM(-1), /** * 不打印任何log */ NONE(0), /** * 默认等级,等于SIMPLE等级,打印SDK核心方法日志信息 */ DEFAULT(1), /** * 基础等级,打印SDK核心方法日志信息,方便普通的接入者查看接入效果 */ SIMPLE(2), /** * 高级模式,可打印一些辅助判断的执行信息,方便排查问题 */ HIGH(3), /** * 最大等级,可打印全部信息,比较详细的看到SDK执行信息,用来定位错误信息 */ MAX(10), ; EALogLevel(int ni) { level = ni; } public int level; //日志级别 } ================================================ FILE: easyads/src/main/java/com/easyads/model/EasyAdError.java ================================================ package com.easyads.model; import android.support.annotation.NonNull; public class EasyAdError { public static final String ERROR_DEFAULT = "99"; public static final String ERROR_DATA_NULL = "9901"; //广告返回的数据为空 public static final String ERROR_EXCEPTION_LOAD = "9902"; public static final String ERROR_EXCEPTION_SHOW = "9903"; public static final String ERROR_EXCEPTION_RENDER = "9904";// public static final String ERROR_NONE_SDK = "9905";//未配置SDK渠道 public static final String ERROR_SUPPLIER_SELECT = "9906"; //执行SDK渠道选择是发生异常 public static final String ERROR_NONE_STRATEGY = "9907"; public static final String ERROR_LOAD_SDK = "9908"; public static final String ERROR_CSJ_SKIP = "9909"; public static final String ERROR_CSJ_TIMEOUT = "9910"; public static final String ERROR_BD_FAILED = "9911"; public static final String ERROR_PARAM_FORMAT = "9912"; //参数异常 public static final String ERROR_NO_ACTIVITY = "9913"; //无activity异常 public static final String ERROR_KS_INIT = "9914"; //快手初始化异常 public static final String ERROR_RENDER_FAILED = "9915"; //渲染失败异常 public static final String ERROR_CSJ_INIT_FAILED = "9916"; //穿山甲初始化失败异常 public EasyAdError(String code, String msg) { this.code = code; this.msg = msg; } public String code; public String msg; public static EasyAdError parseErr(int code) { return parseErr(code + ""); } public static EasyAdError parseErr(String code) { return parseErr(code, ""); } public static EasyAdError parseErr(int code, String extMsg) { return parseErr(code + "", extMsg); } public static EasyAdError parseErr(String code, String extMsg) { EasyAdError error; switch (code) { case ERROR_DATA_NULL: error = new EasyAdError(code, "data null ;" + extMsg); break; case ERROR_EXCEPTION_LOAD: error = new EasyAdError(code, "exception when load ;view System.err log for more " + extMsg); break; case ERROR_EXCEPTION_SHOW: error = new EasyAdError(code, "exception when show ;view System.err log for more " + extMsg); break; case ERROR_EXCEPTION_RENDER: error = new EasyAdError(code, "exception when render ;view System.err log for more " + extMsg); break; case ERROR_NONE_STRATEGY: error = new EasyAdError(code, "None Strategy Info: please check setData() function. " + extMsg); break; case ERROR_NONE_SDK: error = new EasyAdError(code, "none sdk to show ;" + extMsg); break; case ERROR_SUPPLIER_SELECT: error = new EasyAdError(code, "策略调度异常,selectSdkSupplier Throwable ;" + extMsg); break; case ERROR_LOAD_SDK: error = new EasyAdError(code, "sdk 启动异常 " + extMsg); break; case ERROR_CSJ_SKIP: error = new EasyAdError(code, "穿山甲SDK加载超时,不再加载后续SDK渠道 " + extMsg); break; case ERROR_CSJ_TIMEOUT: error = new EasyAdError(code, "穿山甲SDK加载超时 " + extMsg); break; case ERROR_BD_FAILED: error = new EasyAdError(code, "百度SDK加载失败 " + extMsg); break; case ERROR_PARAM_FORMAT: error = new EasyAdError(code, "快手SDK加载失败,广告位id类型转换异常 " + extMsg); break; case ERROR_NO_ACTIVITY: error = new EasyAdError(code, "当前activity已被销毁,不再请求广告 " + extMsg); break; case ERROR_KS_INIT: error = new EasyAdError(code, "快手SDK初始化失败 " + extMsg); break; case ERROR_RENDER_FAILED: error = new EasyAdError(code, "广告渲染失败 " + extMsg); break; case ERROR_CSJ_INIT_FAILED: error = new EasyAdError(code, "穿山甲SDK初始化失败 " + extMsg); break; default: //优量汇的详细异常码 String extCode = ""; try { if ("6000".equals(code) && !extMsg.isEmpty()) { int startIndex = extMsg.lastIndexOf("详细码") + 4; extCode = extMsg.substring(startIndex, startIndex + 6); } } catch (Throwable e) { e.printStackTrace(); } error = new EasyAdError(ERROR_DEFAULT + "_" + code + extCode, "err from sdk callback : [" + code + "] " + extMsg); } return error; } @NonNull @Override public String toString() { return "[EasyAdError] errorCode = " + code + " ; errorMsg = " + msg; } } ================================================ FILE: easyads/src/main/java/com/easyads/model/EasyAdType.java ================================================ package com.easyads.model; /** * 广告类型 */ public enum EasyAdType { SPLASH("Splash"), BANNER("Banner"), INTR("Interstitial"), NATIV("NativeExpress"), REWARD("RewardVideo"), DRAW("Draw"), FULL("FullScreenVideo"); EasyAdType(String ni) { name = ni; } public String name; } ================================================ FILE: easyads/src/main/java/com/easyads/model/SdkRule.java ================================================ package com.easyads.model; import com.easyads.EasyAdsConstant; import java.io.Serializable; import java.util.ArrayList; //SDK的加载规则 public class SdkRule implements Serializable { public String tag = ""; // 策略组标记 public ArrayList sort = new ArrayList<>();//广告加载index排序组,优先级由高到低依次排序 public double percent = EasyAdsConstant.DEFAULT_PERCENT;//使用当前规则的概率,建议使用百分值 public int lowLimit = 0;//下限,用来计算是否命中当前策略组 public int highLimit = 0;//上限,用来计算是否命中当前策略组 public ArrayList sortedSuppliers = new ArrayList<>(); //排序后的可执行的渠道列表 } ================================================ FILE: easyads/src/main/java/com/easyads/model/SdkSupplier.java ================================================ package com.easyads.model; import com.easyads.EasyAdsConstant; import java.io.Serializable; public class SdkSupplier implements Comparable, Serializable { public String adspotId; //SDK广告位id public String appId; //SDK应用id public String tag = ""; //SDK标记 public int index = -1;//坐标标记 public int versionTag = -1;//版本标记,预留字段,主要为了区分相同广告类型的不同版本 public String ruleTag = "";//当前渠道所在的策略组标记 public int priority = 1; //优先级,根据策略设定,进行优先级概念的定义,值越小代表优先级越靠前 public int adStatus = EasyAdsConstant.AD_STATUS_DEFAULT; //广告加载状态标记 @Override public int compareTo(SdkSupplier o) { if (this.priority < o.priority) { return -1; } else if (this.priority == o.priority) { return 0; } else { return 1; } } @Override public String toString() { return "SdkSupplier{" + "ruleTag='" + ruleTag + '\'' + ", tag='" + tag + '\'' + ", priority=" + priority + ", adspotid='" + adspotId + '\'' + ", appid='" + appId + '\'' + ", versionTag=" + versionTag + ", adStatus=" + adStatus + '}'; } } ================================================ FILE: easyads/src/main/java/com/easyads/model/StrategyModel.java ================================================ package com.easyads.model; import com.easyads.EasyAdsConstant; import com.easyads.utils.BigDecimalUtil; import com.easyads.utils.EALog; import org.json.JSONArray; import org.json.JSONObject; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; /** * 所有策略信息model,包含所有的广告渠道信息和加载策略 */ public class StrategyModel implements Serializable { public HashMap supplierMap = new HashMap<>(); public ArrayList rules = new ArrayList<>(); public double sumPercent = 0;//概率值总和 /** * 将约定格式的json结构转换为当前model,解析成可执行的策略信息,用于后续策略执行、调度 * * @param json 一定要完全参考demo示例中的json格式 * @return SDK可执行的策略信息 */ public static StrategyModel covert(String json) { StrategyModel strategyModel = new StrategyModel(); try { JSONObject result = new JSONObject(json); JSONArray suppliers = result.getJSONArray("suppliers"); JSONArray setting = result.getJSONArray("rules"); strategyModel.rules = new ArrayList<>(); strategyModel.supplierMap = new HashMap<>(); for (int i = 0; i < suppliers.length(); i++) { SdkSupplier supplier = new SdkSupplier(); JSONObject impObject = suppliers.getJSONObject(i); supplier.tag = impObject.optString("tag"); supplier.adspotId = impObject.optString("adspotId"); supplier.appId = impObject.optString("appId"); supplier.index = impObject.optInt("index"); strategyModel.supplierMap.put(supplier.index, supplier); } for (int i = 0; i < setting.length(); i++) { SdkRule rule = new SdkRule(); JSONObject impObject = setting.getJSONObject(i); rule.tag = impObject.optString("tag"); rule.percent = impObject.optDouble("percent", EasyAdsConstant.DEFAULT_PERCENT); //累加百分值 strategyModel.sumPercent = BigDecimalUtil.add(strategyModel.sumPercent, rule.percent); JSONArray sortList = impObject.optJSONArray("sort"); if (sortList != null) { rule.sort = new ArrayList<>(); //存放排好序后的广告组 rule.sortedSuppliers = new ArrayList<>(); //依次查找sort中元素 for (int j = 0; j < sortList.length(); j++) { int index = sortList.optInt(j); rule.sort.add(index); //从已有的渠道map中找到对应index的渠道,依次添加至排序列表中 SdkSupplier sourceSupplier = strategyModel.supplierMap.get(index); if (sourceSupplier != null) { //需要新建对象,不可直接使用sourceSupplier内容,否则如果相同的index在不同的rule组时,会出现引用源错乱问题。 SdkSupplier supplier = new SdkSupplier(); //赋值优先级,按照for循环执行顺序,起始值1 supplier.priority = j + 1; supplier.ruleTag = rule.tag; //需要依次把对应值重新赋值 supplier.tag = sourceSupplier.tag; supplier.adspotId = sourceSupplier.adspotId; supplier.appId = sourceSupplier.appId; supplier.index = sourceSupplier.index; supplier.versionTag = sourceSupplier.versionTag; EALog.max("[StrategyModel_covert] 已完成解析的渠道:" + supplier.toString()); rule.sortedSuppliers.add(supplier); } } } strategyModel.rules.add(rule); } } catch (Throwable e) { e.printStackTrace(); } return strategyModel; } } ================================================ FILE: easyads/src/main/java/com/easyads/utils/BigDecimalUtil.java ================================================ package com.easyads.utils; import java.math.BigDecimal; public class BigDecimalUtil { /** * 提供精确加法计算的add方法 * * @param value1 被加数 * @param value2 加数 * @return 两个参数的和 */ public static double add(double value1, double value2) { BigDecimal b1 = new BigDecimal(value1); BigDecimal b2 = new BigDecimal(value2); return b1.add(b2).doubleValue(); } /** * 提供精确减法运算的sub方法 * * @param value1 被减数 * @param value2 减数 * @return 两个参数的差 */ public static double sub(double value1, double value2) { BigDecimal b1 = new BigDecimal(value1); BigDecimal b2 = new BigDecimal(value2); return b1.subtract(b2).doubleValue(); } /** * 提供精确乘法运算的mul方法 * * @param value1 被乘数 * @param value2 乘数 * @return 两个参数的积,结果保留两位小数 */ public static double mul(double value1, double value2) { BigDecimal b1 = new BigDecimal(value1); BigDecimal b2 = new BigDecimal(value2); return b1.multiply(b2).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); } public static double div(double value1, double value2) { return div(value1, value2, 0); } /** * 提供精确的除法运算方法div * * == value1(分子)除以value2(分母) * * @param value1 被除数 * @param value2 除数 * @param scale 精确范围 * @return 两个参数的商 */ public static double div(double value1, double value2, int scale) { //如果精确范围小于0,抛出异常信息 if (scale < 0) { scale = 0; } BigDecimal b1 = new BigDecimal(value1); BigDecimal b2 = new BigDecimal(value2); return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); } } ================================================ FILE: easyads/src/main/java/com/easyads/utils/EAAdapterLoader.java ================================================ package com.easyads.utils; import android.app.Activity; import com.easyads.EasyAdsConstant; import com.easyads.core.draw.EADrawSetting; import com.easyads.core.banner.EABannerSetting; import com.easyads.core.EABaseSupplierAdapter; import com.easyads.core.full.EAFullScreenVideoSetting; import com.easyads.core.inter.EAInterstitialSetting; import com.easyads.core.nati.EANativeExpressSetting; import com.easyads.core.reward.EARewardVideoSetting; import com.easyads.core.splash.EASplashSetting; import com.easyads.model.EasyAdType; import java.lang.ref.SoftReference; import java.lang.reflect.Constructor; /** * 通过反射的方式获取对应渠道的各个广告位初始化方法, */ public class EAAdapterLoader { public static String BASE_ADAPTER_PKG_PATH = "com.easyads.supplier."; /** * 利用反射获取渠道adapter * * @param sdkTag 渠道标志 * @param adType 广告类型 * @param activity 软引用上下文 * @param parameterTypes 实体类 * @return 渠道adapter适配器 */ public static EABaseSupplierAdapter getSDKLoader(String sdkTag, EasyAdType adType, SoftReference activity, Object parameterTypes) { try { Class sdkClz = null; String namePath = ""; //根据tag添加对应adapter类信息的路径 switch (sdkTag) { case EasyAdsConstant.SDK_TAG_CSJ: namePath = "csj.Csj"; break; case EasyAdsConstant.SDK_TAG_YLH: namePath = "ylh.Ylh"; break; case EasyAdsConstant.SDK_TAG_BAIDU: namePath = "baidu.BD"; break; case EasyAdsConstant.SDK_TAG_KS: namePath = "ks.KS"; break; } namePath = namePath + adType.name + "Adapter"; //根据广告类型使用对应的接口参数 switch (adType) { case DRAW: sdkClz = EADrawSetting.class; break; case FULL: sdkClz = EAFullScreenVideoSetting.class; break; case SPLASH: sdkClz = EASplashSetting.class; break; case BANNER: sdkClz = EABannerSetting.class; break; case REWARD: sdkClz = EARewardVideoSetting.class; break; case INTR: sdkClz = EAInterstitialSetting.class; break; case NATIV: sdkClz = EANativeExpressSetting.class; break; } return getSDKAdapter(namePath, activity, sdkClz, parameterTypes); } catch (Throwable e) { e.printStackTrace(); } return null; } /** * 弱引用初始化广告的反射获取方法 * * @param clzName * @param activity * @param parClz * @param parameterTypes * @return */ private static EABaseSupplierAdapter getSDKAdapter(String clzName, SoftReference activity, Class parClz, Object... parameterTypes) { EABaseSupplierAdapter result = null; try { String fullClzName = BASE_ADAPTER_PKG_PATH + clzName; Class clz = Class.forName(fullClzName); Object par1; if (parameterTypes.length > 0) { par1 = parameterTypes[0]; Constructor cons1 = clz.getConstructor(SoftReference.class, parClz); result = (EABaseSupplierAdapter) cons1.newInstance(activity, par1); if (result != null) EALog.devDebug("反射获取SDK渠道adapter类,已完成: " + result.toString()); } } catch (Throwable e) { e.printStackTrace(); } return result; } } ================================================ FILE: easyads/src/main/java/com/easyads/utils/EALog.java ================================================ package com.easyads.utils; import android.util.Log; import com.easyads.EasyAdsManger; import com.easyads.model.EALogLevel; import java.io.PrintWriter; import java.io.StringWriter; public class EALog { private static String LOG = "EasyAds-log"; public static void d(String s) { if (isDebug()) { Log.d(LOG, s); } } //打印核心信息 public static void simple(String s) { if (isDebug() && showSimpleLog()) { Log.d(LOG, "" + s); } } //打印调试用信息 public static void high(String s) { if (isDebug() && showHighLog()) { Log.d(LOG, "[H] " + s); } } //打印全部可用信息 public static void max(String s) { if (isDebug() && showAllLog()) { Log.d(LOG, "[A] " + s); } } public static void devDebug(String devText) { if (EasyAdsManger.getInstance().isDev && EasyAdsManger.getInstance().debug) { Log.d(LOG, "[dev] " + devText); } } public static void devDebugAuto(String devText, String debugText) { try { if (isDebug()) { String lt; if (EasyAdsManger.getInstance().isDev) { lt = "[dev] " + devText + debugText; } else { lt = debugText; } Log.d(LOG, lt); } } catch (Throwable e) { e.printStackTrace(); } } public static void w(String s) { if (isDebug()) { Log.w(LOG, s); } } public static void e(String s) { if (isDebug()) { Log.e(LOG, s); } } public static boolean isDebug() { return EasyAdsManger.getInstance().debug; } //是否可以显示基础等级log public static boolean showSimpleLog() { return EasyAdsManger.getInstance().logLevel.level >= EALogLevel.DEFAULT.level; } //是否可以显示高级log信息 public static boolean showHighLog() { return EasyAdsManger.getInstance().logLevel.level >= EALogLevel.HIGH.level; } //是否可以显示全部log public static boolean showAllLog() { return EasyAdsManger.getInstance().logLevel.level >= EALogLevel.MAX.level; } //打印错误信息 public static String getThrowableLog(Throwable paramThrowable) { StringWriter localStringWriter = null; PrintWriter localPrintWriter = null; try { localStringWriter = new StringWriter(); localPrintWriter = new PrintWriter(localStringWriter); paramThrowable.printStackTrace(localPrintWriter); for (Throwable localThrowable = paramThrowable.getCause(); localThrowable != null; localThrowable = localThrowable.getCause()) localThrowable.printStackTrace(localPrintWriter); String str = localStringWriter.toString(); localPrintWriter.close(); return str; } catch (Throwable e2) { return ""; } finally { try { if (localStringWriter != null) localStringWriter.close(); if (localPrintWriter != null) localPrintWriter.close(); } catch (Throwable ignored) { } } } } ================================================ FILE: easyads/src/main/java/com/easyads/utils/EASplashPlusManager.java ================================================ package com.easyads.utils; import android.app.Activity; import com.easyads.EasyAdsConstant; import com.easyads.EasyAdsManger; public class EASplashPlusManager { public static void startZoom(Activity activity) { try { if (EasyAdsManger.getInstance().isSplashSupportZoomOut) { String sdkId = EasyAdsManger.getInstance().currentSupTag; ZoomCall call = null; switch (sdkId) { case EasyAdsConstant.SDK_TAG_YLH: call = reflectZoomMethod("ylh.YlhUtil"); break; case EasyAdsConstant.SDK_TAG_CSJ: call = reflectZoomMethod("csj.CsjUtil"); break; case EasyAdsConstant.SDK_TAG_KS: call = reflectZoomMethod("ks.KSUtil"); break; case EasyAdsConstant.SDK_TAG_BAIDU: call = reflectZoomMethod("baidu.BDUtil"); break; } if (call != null) { call.zoomOut(activity); } } } catch (Throwable e) { e.printStackTrace(); } } private static ZoomCall reflectZoomMethod(String supClzName) { ZoomCall zoomCall = null; try { Class clz = Class.forName(EAAdapterLoader.BASE_ADAPTER_PKG_PATH + supClzName); zoomCall = (ZoomCall) clz.newInstance(); if (zoomCall != null) EALog.devDebug("reflectZoomMethod result = " + zoomCall.toString()); } catch (Throwable e) { e.printStackTrace(); } return zoomCall; } public interface ZoomCall { void zoomOut(Activity activity); } } ================================================ FILE: easyads/src/main/java/com/easyads/utils/EASplashZoomOutLayout.java ================================================ package com.easyads.utils; import android.content.Context; import android.graphics.drawable.GradientDrawable; import android.os.Build; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.widget.FrameLayout; public class EASplashZoomOutLayout extends FrameLayout { private float dX, dY; private int margin; private int maxX; private int maxY; private float moveAccumulateX, moveAccumulateY; private final int touchSlop;//拖动和点击的触发阈值,采用系统的参数,超过该值认为是拖动,低于认为是点击 public EASplashZoomOutLayout(Context context, int m) { super(context); //设置悬浮窗的圆角 GradientDrawable gd = new GradientDrawable(); gd.setCornerRadius(10); this.setBackgroundDrawable(gd); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { setClipToOutline(true); } this.margin = m; touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); this.post(new Runnable() { @Override public void run() { View parent = (View) getParent(); if (parent == null) { return; } int parentWidth = parent.getWidth(); int parentHeight = parent.getHeight(); maxY = parentHeight - EASplashZoomOutLayout.this.getHeight() - margin; maxX = parentWidth - EASplashZoomOutLayout.this.getWidth() - margin; } }); } @Override public boolean onInterceptTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: dX = getX() - event.getRawX(); dY = getY() - event.getRawY(); moveAccumulateX = 0; moveAccumulateY = 0; break; case MotionEvent.ACTION_MOVE: float newX = event.getRawX() + dX; float newY = event.getRawY() + dY; //这里采用累积,防止转一圈回到起点的情况也触发点击 moveAccumulateX += Math.abs(newX - getX()); moveAccumulateY += Math.abs(newY - getY()); //限制浮窗不会超出父布局 newX = newX < margin ? margin : newX > maxX ? maxX : newX; newY = newY < margin ? margin : newY > maxY ? maxY : newY; animate() .x(newX) .y(newY) .setDuration(0) .start(); break; case MotionEvent.ACTION_UP: //拖动吸附,放开手时自动吸附到左右两边 float animationX; float upX = event.getRawX() + dX; if (upX * 2 > maxX) { animationX = maxX; } else { animationX = margin; } animate() .x(animationX) .setDuration(0) .start(); //如果拖动超过一定距离拦截发向子view的点击事件 if (moveAccumulateX > touchSlop || moveAccumulateY > touchSlop) { return true; } //Log.d("fgt","x:"+getX()+" y:"+getY()+ " height:"+parentHeight+" width:"+parentWidth); default: } return super.onInterceptTouchEvent(event); } } ================================================ FILE: easyads/src/main/java/com/easyads/utils/EAUtil.java ================================================ package com.easyads.utils; import android.app.Activity; import android.app.ActivityManager; import android.content.Context; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Process; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import com.easyads.EasyAdsManger; import com.easyads.itf.BaseEnsureListener; import java.lang.ref.SoftReference; import java.util.List; import java.util.UUID; public class EAUtil { public static String getUUID() { try { return UUID.randomUUID().toString().replace("-", ""); } catch (Throwable e) { e.printStackTrace(); return ""; } } /** * 判断页面是否被销毁了 * * @param activity * @return */ public static boolean isActivityDestroyed(Activity activity) { try { if (activity == null) { return true; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { return activity.isDestroyed() || activity.isFinishing(); } else { return activity.isFinishing(); } } catch (Exception e) { e.printStackTrace(); return false; } } public static boolean isActivityDestroyed(SoftReference activity) { try { if (activity == null || activity.get() == null) { return true; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { return activity.get().isDestroyed() || activity.get().isFinishing(); } else { return activity.get().isFinishing(); } } catch (Exception e) { e.printStackTrace(); return false; } } public static void switchMainThread(final BaseEnsureListener ensureListener) { try { boolean isMainThread = Looper.myLooper() == Looper.getMainLooper(); if (isMainThread) { ensureListener.ensure(); } else { //如果是非主线程,需要强制切换到主线程来进行初始化 new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { try { EALog.high("[switchMainThread] force to main thread"); ensureListener.ensure(); } catch (Throwable e) { e.printStackTrace(); } } }); } } catch (Throwable e) { e.printStackTrace(); } } public static void switchMainThreadDelay(final BaseEnsureListener ensureListener, long delayTime) { try { EALog.high("[switchMainThreadDelay] " + delayTime + "ms later force to main thread"); //如果是非主线程,需要强制切换到主线程来进行初始化 new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { @Override public void run() { try { ensureListener.ensure(); } catch (Throwable e) { e.printStackTrace(); } } }, delayTime); } catch (Throwable e) { e.printStackTrace(); } } public static void removeFromParent(View view) { try { if (view != null) { ViewParent vp = view.getParent(); if (vp instanceof ViewGroup) { ((ViewGroup) vp).removeView(view); } } } catch (Throwable e) { e.printStackTrace(); } } /** * 标记开发者身份 * * @return 是否为开发者 */ public static boolean isDev() { return EasyAdsManger.getInstance().isDev && EasyAdsManger.getInstance().debug; } public static void autoClose(final View view) { try { //不执行自动关闭 int closeDur = EasyAdsManger.getInstance().getSplashPlusAutoClose(); if (closeDur < 0) { return; } //延迟给定时间,执行移除布局操作 new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { @Override public void run() { EAUtil.removeFromParent(view); } }, closeDur); } catch (Throwable e) { e.printStackTrace(); } } public static boolean isMainProcess(Context context) { boolean result; try { result = context.getPackageName().equals(getCurrentProcessName(context)); } catch (Throwable e) { e.printStackTrace(); result = false; } return result; } public static String getCurrentProcessName(Context context) { try { int pid = Process.myPid(); String currentProcessName = ""; ActivityManager activityManager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE); List runningAppProcesses = activityManager.getRunningAppProcesses(); for (ActivityManager.RunningAppProcessInfo processInfo : runningAppProcesses) { if (pid == processInfo.pid) { currentProcessName = processInfo.processName; } } return currentProcessName; } catch (Throwable e) { e.printStackTrace(); return ""; } } } ================================================ FILE: easyads/src/main/java/com/easyads/utils/ScreenUtil.java ================================================ package com.easyads.utils; import android.app.Activity; import android.content.Context; public class ScreenUtil { public ScreenUtil() { } public static int dip2px(Activity activity, float dpValue) { return dip2pxC(activity, dpValue); } public static int dip2pxC(Context context, float dpValue) { try { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } catch (Throwable e) { e.printStackTrace(); return -1; } } public static int px2dip(Activity activity, float pxValue) { return px2dipC(activity, pxValue); } public static int px2dipC(Context context, float pxValue) { try { final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } catch (Throwable e) { e.printStackTrace(); return -1; } } public static int getScreenWidth(Activity activity) { return getScreenWidthC(activity); } public static int getScreenWidthC(Context context) { try { return context.getResources() .getDisplayMetrics().widthPixels; } catch (Throwable e) { e.printStackTrace(); return -1; } } public static int getScreenHeight(Activity activity) { return getScreenHeightC(activity); } public static int getScreenHeightC(Context context) { try { return context.getResources() .getDisplayMetrics().heightPixels; } catch (Throwable e) { e.printStackTrace(); return -1; } } } ================================================ FILE: easybd/.gitignore ================================================ /build ================================================ FILE: easybd/build.gradle ================================================ apply plugin: 'com.android.library' apply plugin: 'com.kezong.fat-aar' // JitPack Maven apply plugin: 'com.github.dcendents.android-maven' // Your Group group = 'com.github.bayescom' //maven依赖的项目名称 def pomName = "easyads-bd" def VersionCode = 9352 def VersionName = "${rootProject.ext.EasyAds_version}_" + VersionCode def outputName = pomName + "_" + VersionName + ".aar" android { compileSdkVersion 29 buildToolsVersion "29.0.3" defaultConfig { minSdkVersion 14 targetSdkVersion 29 versionCode VersionCode versionName VersionName consumerProguardFiles 'consumer-rules.pro' } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } lintOptions { abortOnError false } println pomName + " VersionName = " + VersionName } repositories { flatDir { dirs 'libs' } } // If the value is changed to true, the dependencies of the remote dependency will also be embedded in the final aar. // the default value of transitive is false configurations.embed.transitive = true dependencies { compileOnly 'com.android.support:appcompat-v7:28.0.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' def aarName = "Baidu_MobAds_SDK_${VersionCode}" //发布时用embed捆绑aar包 if (rootProject.ext.releaseSDK) { embed(name: aarName, ext: 'aar') } else { api fileTree(dir: 'libs', include: ['*.jar', '*.aar']) } compileOnly project(path: ':easyads') } //android.libraryVariants.all { variant -> // variant.outputs.all { // outputFileName = outputName // } //} ================================================ FILE: easybd/consumer-rules.pro ================================================ -ignorewarnings -dontwarn com.baidu.mobads.sdk.api.** -keepclassmembers class * extends android.app.Activity { public void *(android.view.View); } -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } -keep class com.baidu.mobads.** { *; } -keep class com.style.widget.** {*;} -keep class com.component.** {*;} -keep class com.baidu.ad.magic.flute.** {*;} -keep class com.baidu.mobstat.forbes.** {*;} ================================================ FILE: easybd/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: easybd/src/androidTest/java/com/easyads/supplier/baidu/ExampleInstrumentedTest.java ================================================ package com.easyads.supplier.baidu; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumented test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() { // Context of the app under test. Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); assertEquals("com.easyads.supplier.baidu.test", appContext.getPackageName()); } } ================================================ FILE: easybd/src/main/AndroidManifest.xml ================================================ ================================================ FILE: easybd/src/main/java/com/easyads/supplier/baidu/BDBannerAdapter.java ================================================ package com.easyads.supplier.baidu; import android.app.Activity; import android.view.ViewGroup; import android.widget.RelativeLayout; import com.easyads.core.banner.EABannerSetting; import com.easyads.custom.EABannerCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.baidu.mobads.sdk.api.AdView; import com.baidu.mobads.sdk.api.AdViewListener; import org.json.JSONObject; import java.lang.ref.SoftReference; /** * 横幅会自动刷新,刷新间隔30秒。如果网络异常,下一次加载将回调加载失败,网络恢复,不再执行刷新。故失败逻辑直接进入下一优先级的加载 */ public class BDBannerAdapter extends EABannerCustomAdapter implements AdViewListener { private EABannerSetting setting; private AdView adView; public BDBannerAdapter(SoftReference activity, EABannerSetting baseSetting) { super(activity, baseSetting); setting = baseSetting; } @Override protected void doLoadAD() { if (sdkSupplier != null) { BDUtil.initBDAccount(this); adView = new AdView(getActivity(), sdkSupplier.adspotId); adView.setListener(this); } //必须要添加布局后,才会返回广告 if (null != setting) { ViewGroup adContainer = setting.getContainer(); if (adContainer != null) { adContainer.removeAllViews(); int width = adContainer.getWidth(); EALog.max(TAG + "adContainer width = " + width); if (width <= 0) { width = ViewGroup.LayoutParams.MATCH_PARENT; } adContainer.addView(adView, new RelativeLayout.LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT)); } } } @Override protected void doShowAD() { } @Override protected void doDestroy() { try { if (adView != null) { adView.destroy(); } } catch (Throwable e) { e.printStackTrace(); } } @Override public void onAdReady(AdView adView) { EALog.high(TAG + "onAdReady" + adView); handleSucceed(); } @Override public void onAdShow(JSONObject jsonObject) { EALog.high(TAG + "onAdShow " + jsonObject); handleExposure(); } @Override public void onAdClick(JSONObject jsonObject) { EALog.high(TAG + "onAdClick " + jsonObject); handleClick(); } @Override public void onAdFailed(String s) { EALog.e(TAG + "onAdFailed " + s); handleFailed(EasyAdError.ERROR_BD_FAILED, s); } @Override public void onAdSwitch() { EALog.high(TAG + "onAdSwitch"); } @Override public void onAdClose(JSONObject jsonObject) { EALog.high(TAG + "onAdClose " + jsonObject); if (null != setting) { setting.adapterDidDislike(sdkSupplier); } } } ================================================ FILE: easybd/src/main/java/com/easyads/supplier/baidu/BDFullScreenVideoAdapter.java ================================================ package com.easyads.supplier.baidu; import android.app.Activity; import com.easyads.core.full.EAFullScreenVideoSetting; import com.easyads.custom.EAFullScreenCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.baidu.mobads.sdk.api.FullScreenVideoAd; import java.lang.ref.SoftReference; public class BDFullScreenVideoAdapter extends EAFullScreenCustomAdapter implements FullScreenVideoAd.FullScreenVideoAdListener { private EAFullScreenVideoSetting setting; private FullScreenVideoAd mFullScreenVideoAd; public BDFullScreenVideoAdapter(SoftReference activity, EAFullScreenVideoSetting setting) { super(activity, setting); this.setting = setting; } @Override protected void doLoadAD() { BDUtil.initBDAccount(this); // 全屏视频产品可以选择是否使用SurfaceView进行渲染视频 mFullScreenVideoAd = new FullScreenVideoAd(getActivity(), sdkSupplier.adspotId , this, EasyBDManager.getInstance().fullScreenUseSurfaceView); mFullScreenVideoAd.load(); } @Override protected void doShowAD() { try { boolean isReady = mFullScreenVideoAd != null && mFullScreenVideoAd.isReady(); EALog.simple(TAG + "-doShowAD- isReady = " + isReady); if (mFullScreenVideoAd != null) { mFullScreenVideoAd.show(); } } catch (Throwable e) { e.printStackTrace(); } } @Override public void doDestroy() { } /** * 广告事件回调 */ @Override public void onAdShow() { EALog.high(TAG + "onAdShow"); handleExposure(); } @Override public void onAdClick() { EALog.high(TAG + "onAdClick"); handleExposure(); } @Override public void onAdClose(float playScale) { // 用户关闭了广告 // 说明:关闭按钮在mssp上可以动态配置,媒体通过mssp配置,可以选择广告一开始就展示关闭按钮,还是播放结束展示关闭按钮 // 建议:收到该回调之后,可以重新load下一条广告,最好限制load次数(4-5次即可) // playScale[0.0-1.0],1.0表示播放完成,媒体可以按照自己的设计给予奖励 EALog.high(TAG + "onAdClose,playScale = " + playScale); if (setting != null) setting.adapterClose(sdkSupplier); } @Override public void onAdFailed(String s) { String msg = "onAdFailed" + s; handleFailed(EasyAdError.ERROR_BD_FAILED, msg); } @Override public void onVideoDownloadSuccess() { EALog.high(TAG + "onVideoDownloadSuccess"); handleCached(); } @Override public void onVideoDownloadFailed() { EALog.e(TAG + "onVideoDownloadFailed"); } @Override public void playCompletion() { EALog.high(TAG + "playCompletion"); if (setting != null) setting.adapterVideoComplete(sdkSupplier); } @Override public void onAdSkip(float playScale) { // 用户跳过了广告 // playScale[0.0-1.0],1.0表示播放完成,媒体可以按照自己的设计给予奖励 EALog.high(TAG + "onAdSkip,playScale = " + playScale); if (setting != null) setting.adapterVideoSkipped(sdkSupplier); } @Override public void onAdLoaded() { EALog.high(TAG + "onAdLoaded"); handleSucceed(); try { if (mFullScreenVideoAd != null) { String ecpm = mFullScreenVideoAd.getECPMLevel(); EALog.high(TAG + "mFullScreenVideoAd ECPMLevel = " + ecpm); } } catch (Throwable throwable) { throwable.printStackTrace(); } } } ================================================ FILE: easybd/src/main/java/com/easyads/supplier/baidu/BDInterstitialAdapter.java ================================================ package com.easyads.supplier.baidu; import android.app.Activity; import android.widget.RelativeLayout; import com.easyads.core.inter.EAInterstitialSetting; import com.easyads.custom.EAInterstitialCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.baidu.mobads.sdk.api.AdSize; import com.baidu.mobads.sdk.api.InterstitialAd; import com.baidu.mobads.sdk.api.InterstitialAdListener; import java.lang.ref.SoftReference; public class BDInterstitialAdapter extends EAInterstitialCustomAdapter implements InterstitialAdListener { private EAInterstitialSetting setting; private InterstitialAd mInterAd; // 插屏广告实例,支持单例模式 private AdSize adSize; private RelativeLayout videoLayout; private boolean isAdForVideo = false; public BDInterstitialAdapter(SoftReference activity, EAInterstitialSetting baseSetting) { super(activity, baseSetting); this.setting = baseSetting; adSize = EasyBDManager.getInstance().interstitialType; videoLayout = EasyBDManager.getInstance().interstitialVideoLayout; } @Override protected void doLoadAD() { if (sdkSupplier != null) { BDUtil.initBDAccount(this); String adPlaceId = sdkSupplier.adspotId; if (adSize == null) { mInterAd = new InterstitialAd(getActivity(), adPlaceId); } else { mInterAd = new InterstitialAd(getActivity(), adSize, adPlaceId); } mInterAd.setListener(this); } if (adSize != null) { isAdForVideo = adSize == AdSize.InterstitialForVideoBeforePlay || adSize == AdSize.InterstitialForVideoPausePlay; if (isAdForVideo && videoLayout != null) { RelativeLayout.LayoutParams reLayoutParams = (RelativeLayout.LayoutParams) videoLayout.getLayoutParams(); mInterAd.loadAdForVideoApp(reLayoutParams.width, reLayoutParams.height); } else { mInterAd.loadAd(); } } else { mInterAd.loadAd(); } } @Override protected void doShowAD() { if (mInterAd != null) { if (videoLayout != null && isAdForVideo) { mInterAd.showAdInParentForVideoApp( videoLayout); } else { mInterAd.showAd(); } } } @Override public void doDestroy() { if (mInterAd != null) { mInterAd.destroy(); } } //广告的回调事件 @Override public void onAdReady() { EALog.high(TAG + "onAdReady"); handleSucceed(); } @Override public void onAdPresent() { EALog.high(TAG + "onAdPresent"); handleExposure(); } @Override public void onAdClick(InterstitialAd interstitialAd) { EALog.high(TAG + "onAdClick"); handleClick(); } @Override public void onAdDismissed() { EALog.high(TAG + "onAdDismissed"); if (null != setting) { setting.adapterDidClosed(sdkSupplier); } } @Override public void onAdFailed(String s) { EALog.high(TAG + "onAdFailed ,reason:" + s); handleFailed(EasyAdError.ERROR_BD_FAILED, s); } } ================================================ FILE: easybd/src/main/java/com/easyads/supplier/baidu/BDNativeExpressAdapter.java ================================================ package com.easyads.supplier.baidu; import android.app.Activity; import android.view.View; import com.easyads.core.nati.EANativeExpressSetting; import com.easyads.custom.EANativeExpressCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.baidu.mobads.sdk.api.BaiduNativeManager; import com.baidu.mobads.sdk.api.FeedNativeView; import com.baidu.mobads.sdk.api.NativeResponse; import com.baidu.mobads.sdk.api.RequestParameters; import com.baidu.mobads.sdk.api.StyleParams; import com.baidu.mobads.sdk.api.XAdNativeResponse; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.List; /** * 模板信息流 对应了百度的智能优选信息流广告位 */ public class BDNativeExpressAdapter extends EANativeExpressCustomAdapter implements BaiduNativeManager.FeedAdListener, NativeResponse.AdInteractionListener { private EANativeExpressSetting setting; private RequestParameters parameters; NativeResponse nativeResponse = null; FeedNativeView feedNativeView; public BDNativeExpressAdapter(SoftReference activity, EANativeExpressSetting baseSetting) { super(activity, baseSetting); setting = baseSetting; parameters = EasyBDManager.getInstance().nativeExpressParameters; } @Override protected void doLoadAD() { if (sdkSupplier != null) { BDUtil.initBDAccount(this); /** * Step 1. 创建BaiduNative对象,参数分别为: 上下文context,广告位ID * 注意:请将adPlaceId替换为自己的广告位ID * 注意:信息流广告对象,与广告位id一一对应,同一个对象可以多次发起请求 */ BaiduNativeManager mBaiduNativeManager = new BaiduNativeManager(getActivity(), sdkSupplier.adspotId); mBaiduNativeManager.loadFeedAd(parameters, this); } } @Override protected void doShowAD() { try { StyleParams styleParams; styleParams = EasyBDManager.getInstance().nativeExpressSmartStyle; if (nativeResponse != null) { feedNativeView.setAdData((XAdNativeResponse) nativeResponse); if (styleParams != null) { feedNativeView.changeViewLayoutParams(styleParams); } feedNativeView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { nativeResponse.unionLogoClick(); } }); if (nativeResponse != null) { EALog.max("getImageUrl = " + nativeResponse.getImageUrl()); XAdNativeResponse xad = (XAdNativeResponse) nativeResponse; xad.setAdDislikeListener(new NativeResponse.AdDislikeListener() { @Override public void onDislikeClick() { try { onADClose(); // 点击了负反馈渠道的回调 removeADView(); } catch (Throwable e) { e.printStackTrace(); } } }); /** * registerViewForInteraction()与BaiduNativeManager配套使用 * 警告:调用该函数来发送展现,勿漏! */ List clickViews = new ArrayList<>(); List creativeViews = new ArrayList<>(); nativeResponse.registerViewForInteraction(setting.getAdContainer(),clickViews, creativeViews,this); nativeResponse.setAdPrivacyListener(new NativeResponse.AdPrivacyListener() { @Override public void onADPermissionShow() { EALog.high(TAG + "onADPermissionShow"); } @Override public void onADPermissionClose() { EALog.high(TAG + "onADPermissionClose"); } @Override public void onADPrivacyClick() { EALog.high(TAG + "onADPrivacyClick"); } @Override public void onADFunctionClick() { EALog.high(TAG + "onADFunctionClick"); } }); } } } catch (Throwable e) { e.printStackTrace(); } } @Override public void doDestroy() { } @Override public void onNativeLoad(List list) { EALog.high(TAG + "onNativeLoad"); try { if (list == null || list.size() == 0) { handleFailed(EasyAdError.ERROR_DATA_NULL, ""); } else { nativeResponse = list.get(0); feedNativeView = new FeedNativeView(getActivity()); addADView(feedNativeView); setNEView(feedNativeView); handleSucceed(); } } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.parseErr(EasyAdError.ERROR_EXCEPTION_LOAD)); } } @Override public void onNativeFail(int i, String s,NativeResponse response) { handleFailed(i + "", s); } @Override public void onNoAd(int i, String s,NativeResponse response) { handleFailed(i + "", s); } @Override public void onVideoDownloadSuccess() { EALog.high(TAG + "onVideoDownloadSuccess"); } @Override public void onVideoDownloadFailed() { EALog.e(TAG + "onVideoDownloadFailed"); } @Override public void onLpClosed() { EALog.high(TAG + "onLpClosed"); } public void onADClose() { EALog.high(TAG + "onADClose"); if (null != setting) { setting.adapterDidClosed(sdkSupplier); } } @Override public void onAdClick() { handleClick(); String title = ""; if (nativeResponse != null) { title = nativeResponse.getTitle(); } EALog.high(TAG + "onAdClick: title = " + title); } @Override public void onADExposed() { handleExposure(); String title = ""; if (nativeResponse != null) { title = nativeResponse.getTitle(); } EALog.high(TAG + "onADExposed: title = " + title); } @Override public void onADExposureFailed(int i) { EALog.high(TAG + "onADExposureFailed , inf = " + i); handleFailed(i, "onADExposureFailed"); } @Override public void onADStatusChanged() { EALog.high(TAG + "onADStatusChanged:" + getBtnText(nativeResponse)); } @Override public void onAdUnionClick() { EALog.high(TAG + "onADUnionClick"); handleClick(); } // 下载状态及下载的进度 private String getBtnText(NativeResponse nrAd) { if (nrAd == null) { return ""; } try { if (nrAd.isNeedDownloadApp()) { int status = nrAd.getDownloadStatus(); if (status >= 0 && status <= 100) { return "下载中:" + status + "%"; } else if (status == 101) { return "点击安装"; } else if (status == 102) { return "继续下载"; } else if (status == 103) { return "点击启动"; } else if (status == 104) { return "重新下载"; } else { return "点击下载"; } } } catch (Throwable e) { e.printStackTrace(); } return "查看详情"; } } ================================================ FILE: easybd/src/main/java/com/easyads/supplier/baidu/BDRewardVideoAdapter.java ================================================ package com.easyads.supplier.baidu; import android.app.Activity; import com.easyads.core.reward.EARewardServerCallBackInf; import com.easyads.core.reward.EARewardVideoSetting; import com.easyads.custom.EARewardCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.baidu.mobads.sdk.api.RewardVideoAd; import java.lang.ref.SoftReference; public class BDRewardVideoAdapter extends EARewardCustomAdapter implements RewardVideoAd.RewardVideoAdListener { private RewardVideoAd mRewardVideoAd; public BDRewardVideoAdapter(SoftReference activity, EARewardVideoSetting setting) { super(activity, setting); } @Override protected void doLoadAD() { BDUtil.initBDAccount(this); //使用SurfaceView会无法展示视频,第三个字段需要为false代表不使用SurfaceView mRewardVideoAd = new RewardVideoAd(getActivity(), sdkSupplier.adspotId, this, EasyBDManager.getInstance().rewardUseSurfaceView); mRewardVideoAd.setDownloadAppConfirmPolicy(EasyBDManager.getInstance().rewardDownloadAppConfirmPolicy); mRewardVideoAd.load(); } @Override protected void doShowAD() { if (mRewardVideoAd != null) { // 在跳过按钮后展示弹框 mRewardVideoAd.setShowDialogOnSkip(true); // 展示奖励领取倒计时提示 // mRewardVideoAd.setUseRewardCountdown(true); mRewardVideoAd.show(); } } @Override public void doDestroy() { } //以下为广告回调事件 @Override public void onAdShow() { EALog.high(TAG + "onAdShow"); handleExposure(); } @Override public void onAdClick() { EALog.high(TAG + "onAdClick"); handleClick(); } @Override public void onAdClose(float v) { EALog.high(TAG + "onAdClose " + v); if (null != rewardSetting) { rewardSetting.adapterDidClosed(sdkSupplier); } } @Override public void onAdFailed(String s) { EALog.e(TAG + "onAdFailed ,reason :" + s); handleFailed(EasyAdError.ERROR_BD_FAILED, s); } @Override public void onVideoDownloadSuccess() { EALog.high(TAG + "onVideoDownloadSuccess"); handleCached(); } @Override public void onVideoDownloadFailed() { EALog.e(TAG + "onVideoDownloadFailed"); } @Override public void playCompletion() { EALog.high(TAG + "playCompletion"); if (null != rewardSetting) { rewardSetting.adapterVideoComplete(sdkSupplier); } } @Override public void onAdSkip(float playScale) { // 用户点击跳过, 展示尾帧 // 建议:媒体可以按照自己的设计给予奖励 EALog.high(TAG + " onSkip: playScale = " + playScale); if (null != rewardSetting) { rewardSetting.adapterVideoSkipped(sdkSupplier); } } @Override public void onRewardVerify(boolean rewardVerify) { try { EALog.high(TAG + " onRewardVerify : rewardVerify = " + rewardVerify); EARewardServerCallBackInf inf = new EARewardServerCallBackInf(); inf.bdRewardVerify = rewardVerify; if (null != rewardSetting) { rewardSetting.postRewardServerInf(inf, sdkSupplier); if (rewardVerify) { //激励达成回调 rewardSetting.adapterAdReward(sdkSupplier); } } } catch (Throwable e) { e.printStackTrace(); } } @Override public void onAdLoaded() { EALog.high(TAG + "onAdLoaded"); handleSucceed(); } } ================================================ FILE: easybd/src/main/java/com/easyads/supplier/baidu/BDSplashAdapter.java ================================================ package com.easyads.supplier.baidu; import android.app.Activity; import android.os.Handler; import com.easyads.core.splash.EASplashSetting; import com.easyads.custom.EASplashCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.baidu.mobads.sdk.api.RequestParameters; import com.baidu.mobads.sdk.api.SplashAd; import com.baidu.mobads.sdk.api.SplashInteractionListener; import java.lang.ref.SoftReference; public class BDSplashAdapter extends EASplashCustomAdapter implements SplashInteractionListener { private SplashAd splashAd; private RequestParameters parameters; private boolean isCountingEnd = false;//用来判断是否倒计时走到了最后,false 回调dismiss的话代表是跳过,否则倒计时结束 public BDSplashAdapter(SoftReference softReferenceActivity, EASplashSetting setting) { super(softReferenceActivity, setting); parameters = EasyBDManager.getInstance().splashParameters; } @Override protected void doLoadAD() { initSplash(); splashAd.load(); } private void initSplash() { BDUtil.initBDAccount(this); splashAd = new SplashAd(getActivity(), sdkSupplier.adspotId, parameters, this); } @Override protected void doShowAD() { splashAd.show(adContainer); } @Override public void doDestroy() { if (splashAd != null) { splashAd.destroy(); splashAd = null; } } /** * 以下为监听的广告事件 */ @Override public void onLpClosed() { //落地页关闭回调 EALog.high(TAG + "onLpClosed"); } @Override public void onAdPresent() { EALog.high(TAG + "onAdPresent"); //进行辅助判断倒计时操作的定时任务 // try { // handleExposure(); // // new Handler().postDelayed(new Runnable() { // @Override // public void run() { // isCountingEnd = true; // } // }, 4800); // } catch (Throwable e) { // e.printStackTrace(); // } } @Override public void onAdExposed() { EALog.high(TAG + "onAdExposed"); //进行辅助判断倒计时操作的定时任务 try { handleExposure(); new Handler().postDelayed(new Runnable() { @Override public void run() { isCountingEnd = true; } }, 4800); } catch (Throwable e) { e.printStackTrace(); } } @Override public void onADLoaded() { EALog.high(TAG + "onADLoaded "); handleSucceed(); } @Override public void onAdClick() { EALog.high(TAG + "onAdClick"); handleClick(); } @Override public void onAdCacheSuccess() { EALog.high(TAG + "onAdCacheSuccess"); } @Override public void onAdCacheFailed() { EALog.high(TAG + "onAdCacheFailed"); } @Override public void onAdDismissed() { EALog.high(TAG + "onAdDismissed"); if (mSplashSetting != null) { if (isCountingEnd) { mSplashSetting.adapterDidTimeOver(sdkSupplier); } else { mSplashSetting.adapterDidSkip(sdkSupplier); } } } @Override public void onAdSkip() { EALog.high(TAG + "onAdSkip"); if (mSplashSetting != null) mSplashSetting.adapterDidSkip(sdkSupplier); } @Override public void onAdFailed(String s) { EALog.high(TAG + "onAdFailed reason:" + s); handleFailed(EasyAdError.ERROR_BD_FAILED, s); } } ================================================ FILE: easybd/src/main/java/com/easyads/supplier/baidu/BDUtil.java ================================================ package com.easyads.supplier.baidu; import android.app.Activity; import android.app.ActivityManager; import android.content.Context; import android.text.TextUtils; import com.easyads.EasyAdsManger; import com.easyads.core.EABaseSupplierAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EASplashPlusManager; import com.easyads.utils.EALog; import com.baidu.mobads.sdk.api.BDAdConfig; import com.baidu.mobads.sdk.api.BDDialogParams; import com.baidu.mobads.sdk.api.MobadsPermissionSettings; public class BDUtil implements EASplashPlusManager.ZoomCall { public static void initBDAccount(EABaseSupplierAdapter adapter) { try { if (adapter == null) { EALog.e("[BDUtil] initAD failed BaseSupplierAdapter null"); return; } boolean hasInit = EasyAdsManger.getInstance().hasBDInit; String lastAppId = EasyAdsManger.getInstance().lastBDAID; String resultAppID = adapter.getAppID(); if (TextUtils.isEmpty(resultAppID)) { String msg = "[initCsj] initAD failed AppID null"; adapter.handleFailed(EasyAdError.ERROR_DATA_NULL, msg); EALog.e(msg); return; } EALog.high("[BDUtil.initBDAccount] 百度 appID:" + resultAppID); boolean isSame = lastAppId.equals(resultAppID); //只有当允许初始化优化时,且快手已经初始化成功过,并行初始化的id和当前id一致,才可以不再重复初始化。 if (hasInit && adapter.canOptInit() && isSame) { return; } EALog.simple("[BDUtil] 开始初始化SDK"); Activity activity = adapter.getActivity(); if (getProcessName(activity).startsWith(activity.getPackageName())) { // 初始化信息,初始化一次即可,(此处用startsWith(),可包括激励/全屏视频的进程) // https、视频缓存空间有特殊需求可动态配置,一般取默认值即可,无需设置 BDAdConfig bdAdConfig = new BDAdConfig.Builder() // 1、设置app名称,可选 // .setAppName("网盟demo") // 2、应用在mssp平台申请到的appsid,和包名一一对应,此处设置等同于在AndroidManifest.xml里面设置 .setAppsid(resultAppID) // 3、设置下载弹窗的类型和按钮动效样式,可选 .setDialogParams(new BDDialogParams.Builder() .setDlDialogType(BDDialogParams.TYPE_BOTTOM_POPUP) .setDlDialogAnimStyle(BDDialogParams.ANIM_STYLE_NONE) .build()) .setHttps(false)//如果设置为true,那么banner广告将会无法展示,todo 这是SDK的bug,需要观察后续版本是否解决了 .build(activity); bdAdConfig.init(); // 设置SDK可以使用的权限,包含:设备信息、定位、存储、APP LIST // 注意:建议授权SDK读取设备信息,SDK会在应用获得系统权限后自行获取IMEI等设备信息 // 授权SDK获取设备信息会有助于提升ECPM MobadsPermissionSettings.setPermissionReadDeviceID(true); MobadsPermissionSettings.setPermissionLocation(true); MobadsPermissionSettings.setPermissionStorage(true); MobadsPermissionSettings.setPermissionAppList(true); } EasyAdsManger.getInstance().hasBDInit = true; EasyAdsManger.getInstance().lastBDAID = resultAppID; } catch (Throwable throwable) { throwable.printStackTrace(); } } private static String getProcessName(Context context) { if (context == null) return null; try { ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); for (ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) { if (processInfo.pid == android.os.Process.myPid()) { return processInfo.processName; } } } catch (Throwable e) { e.printStackTrace(); } return null; } @Override public void zoomOut(Activity activity) { EALog.e("[BDUtil] not support zoomOut"); } } ================================================ FILE: easybd/src/main/java/com/easyads/supplier/baidu/EasyBDManager.java ================================================ package com.easyads.supplier.baidu; import android.widget.RelativeLayout; import com.baidu.mobads.sdk.api.AdSize; import com.baidu.mobads.sdk.api.RequestParameters; import com.baidu.mobads.sdk.api.RewardVideoAd; import com.baidu.mobads.sdk.api.StyleParams; /** * baidu 渠道相关位置的特殊配置项 */ public class EasyBDManager { private static EasyBDManager instance; private EasyBDManager() { } public static synchronized EasyBDManager getInstance() { if (instance == null) { instance = new EasyBDManager(); } return instance; } //开屏个性化配置信息 public RequestParameters splashParameters = null; //信息流个性化配置信息 public RequestParameters nativeExpressParameters = null; //信息流布局个性化配置信息 public StyleParams nativeExpressSmartStyle = null; //插屏类型 public AdSize interstitialType = null; //视频贴片类型的插屏布局父布局 public RelativeLayout interstitialVideoLayout = null; //全屏视频是否使用SurfaceView来渲染 public boolean fullScreenUseSurfaceView = false; //激励视频是否使用SurfaceView来渲染 public boolean rewardUseSurfaceView = false; //激励视频下载确认弹框设置,默认永不弹框 public int rewardDownloadAppConfirmPolicy = RewardVideoAd.DOWNLOAD_APP_CONFIRM_NEVER; } ================================================ FILE: easybd/src/main/res/values/strings.xml ================================================ ================================================ FILE: easybd/src/test/java/com/easyads/supplier/baidu/ExampleUnitTest.java ================================================ package com.easyads.supplier.baidu; import org.junit.Test; import static org.junit.Assert.*; /** * Example local unit test, which will execute on the development machine (host). * * @see Testing documentation */ public class ExampleUnitTest { @Test public void addition_isCorrect() { assertEquals(4, 2 + 2); } } ================================================ FILE: easycsj/.gitignore ================================================ /build ================================================ FILE: easycsj/build.gradle ================================================ apply plugin: 'com.android.library' apply plugin: 'com.kezong.fat-aar' // JitPack Maven apply plugin: 'com.github.dcendents.android-maven' // Your Group group = 'com.github.bayescom' def pomName = "easyads-csj" def VersionCode = 4908 def VersionName = "${rootProject.ext.EasyAds_version}_" + VersionCode def outputName = pomName + "_" + VersionName + ".aar" android { compileSdkVersion 29 buildToolsVersion "29.0.3" defaultConfig { minSdkVersion 16 targetSdkVersion 29 versionCode VersionCode versionName VersionName consumerProguardFiles 'consumer-rules.pro' ndk { abiFilters "armeabi", "armeabi-v7a", "x86", "x86_64", "arm64-v8a" } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } lintOptions { abortOnError false } println pomName + " VersionName = " + VersionName } //android.libraryVariants.all { variant -> // variant.outputs.all { // outputFileName = outputName // } //} repositories { flatDir { dirs 'libs' } } // If the value is changed to true, the dependencies of the remote dependency will also be embedded in the final aar. // the default value of transitive is false configurations.embed.transitive = true dependencies { compileOnly 'com.android.support:appcompat-v7:28.0.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' def aarName = "open_ad_sdk_${VersionCode}" //发布时用embed捆绑aar包 if (rootProject.ext.releaseSDK) { embed(name: aarName, ext: 'aar') } else { // api fileTree(dir: 'libs', include: ['*.jar', '*.aar']) api "com.pangle.cn:mediation-sdk:6.1.2.5" } compileOnly project(path: ':easyads') } ================================================ FILE: easycsj/consumer-rules.pro ================================================ #-keep class com.bytedance.sdk.openadsdk.** { *; } #-keep public interface com.bytedance.sdk.openadsdk.downloadnew.** {*;} #-keep class com.pgl.sys.ces.* {*;} #-keep class com.bytedance.embed_dr.** {*;} #-keep class com.bytedance.embedapplog.** {*;} #-dontwarn com.ss.android.socialbase.downloader.** #-dontwarn com.ss.android.crash.log.** #3900版本及以上版本混淆规则如下: #-keep class com.bytedance.sdk.openadsdk.** { *; } #-keep class com.bytedance.frameworks.** { *; } # #-keep class ms.bd.c.Pgl.**{*;} #-keep class com.bytedance.mobsec.metasec.ml.**{*;} # #-keep class com.ss.android.**{*;} # #-keep class com.bytedance.embedapplog.** {*;} #-keep class com.bytedance.embed_dr.** {*;} # #-keep class com.bykv.vk.** {*;} #4000版本混淆,aar中已支持,可以不添加 #-keepclassmembers class * { # *** getContext(...); # *** getActivity(...); # *** getResources(...); # *** startActivity(...); # *** startActivityForResult(...); # *** registerReceiver(...); # *** unregisterReceiver(...); # *** query(...); # *** getType(...); # *** insert(...); # *** delete(...); # *** update(...); # *** call(...); # *** setResult(...); # *** startService(...); # *** stopService(...); # *** bindService(...); # *** unbindService(...); # *** requestPermissions(...); # *** getIdentifier(...); # } # #-keep class com.bytedance.pangle.** {*;} #-keep class com.bytedance.sdk.openadsdk.** { *; } #-keep class com.bytedance.frameworks.** { *; } # #-keep class ms.bd.c.Pgl.**{*;} #-keep class com.bytedance.mobsec.metasec.ml.**{*;} # #-keep class com.ss.android.**{*;} # #-keep class com.bytedance.embedapplog.** {*;} #-keep class com.bytedance.embed_dr.** {*;} # #-keep class com.bykv.vk.** {*;} ================================================ FILE: easycsj/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: easycsj/src/androidTest/java/com/easyads/supplier/csj/ExampleInstrumentedTest.java ================================================ package com.easyads.supplier.csj; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumented test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() { // Context of the app under test. Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); assertEquals("com.easyads.supplier.csj.test", appContext.getPackageName()); } } ================================================ FILE: easycsj/src/main/AndroidManifest.xml ================================================ ================================================ FILE: easycsj/src/main/java/com/easyads/supplier/csj/CSJSplashClickEyeManager.java ================================================ package com.easyads.supplier.csj; import android.animation.Animator; import android.content.Context; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import android.view.animation.OvershootInterpolator; import android.widget.FrameLayout; import com.easyads.utils.EALog; import com.easyads.utils.ScreenUtil; import com.easyads.utils.EASplashZoomOutLayout; import com.bytedance.sdk.openadsdk.TTSplashAd; /** * 点睛广告展示处理逻辑 */ public class CSJSplashClickEyeManager { private static final String TAG = "CSJSplashClickEyeManager"; private static final int LEFT = 0; private static final int RIGHT = 1; private int mClickEyeViewWidth;//悬浮窗的宽度 private int mClickEyeViewHeight;//悬浮窗的高度 private int mClickEyeViewMargin;//悬浮窗最小离屏幕边缘的距离 private int mClickEyeViewMarginBottom;//悬浮窗默认距离屏幕底端的高度 private int mClickEyeViewPos;//悬浮窗默认位于屏幕左面或右面 private int mClickEyeViewAnimationTime;//悬浮窗缩放动画的,单位ms // private int zoomOutMargin;//悬浮窗最小离屏幕边缘的距离 private TTSplashAd mSplashAd; private View mSplashShowView; private int[] mOriginSplashPos = new int[2]; private int mDecorViewWidth; private int mDecorViewHeight; private volatile static CSJSplashClickEyeManager mInstance; private boolean mIsSupportSplashClickEye = false; public interface AnimationCallBack { void animationStart(int animationTime); void animationEnd(); } /** * 单例获取SplashClickEyeManager对象 * * @return */ public static CSJSplashClickEyeManager getInstance() { if (mInstance == null) { synchronized (CSJSplashClickEyeManager.class) { if (mInstance == null) { mInstance = new CSJSplashClickEyeManager(); } } } return mInstance; } private CSJSplashClickEyeManager() { mClickEyeViewPos = RIGHT; mClickEyeViewAnimationTime = 300; } public void init(Context context) { initClickEyeViewData(context); mClickEyeViewMargin = ScreenUtil.dip2pxC(context, 16); mClickEyeViewMarginBottom = ScreenUtil.dip2pxC(context, 100); } private void initClickEyeViewData(Context context) { if (context == null) { return; } if (mSplashAd != null && mSplashAd.getSplashClickEyeSizeToDp() != null) { //使用推荐的点睛宽高 mClickEyeViewWidth = ScreenUtil.dip2pxC(context, mSplashAd.getSplashClickEyeSizeToDp()[0]); mClickEyeViewHeight = ScreenUtil.dip2pxC(context, mSplashAd.getSplashClickEyeSizeToDp()[1]); } else { int deviceWidth = Math.min(ScreenUtil.getScreenHeightC(context), ScreenUtil.getScreenWidthC(context)); //默认的点睛宽高 mClickEyeViewWidth = Math.round(deviceWidth * 0.3f);//屏幕宽度的30%,之前使用PxUtils.dpToPx(context, 90); mClickEyeViewHeight = Math.round(mClickEyeViewWidth * 16 / 9);//根据宽度计算高度,之前使用PxUtils.dpToPx(context, 160); } } public void setSplashInfo(TTSplashAd splashAd, View splashView, View decorView) { this.mSplashAd = splashAd; this.mSplashShowView = splashView; splashView.getLocationOnScreen(mOriginSplashPos); mDecorViewWidth = decorView.getWidth(); mDecorViewHeight = decorView.getHeight(); initClickEyeViewData(splashView.getContext()); } public void clearSplashStaticData() { mSplashAd = null; mSplashShowView = null; } public TTSplashAd getSplashAd() { return mSplashAd; } public ViewGroup startSplashClickEyeAnimationInTwoActivity(final ViewGroup decorView, final ViewGroup splashViewContainer, final AnimationCallBack callBack) { EALog.high(TAG + "startSplashClickEyeAnimationInTwoActivity"); if (decorView == null || splashViewContainer == null) { EALog.e(TAG + " decorView == null || splashViewContainer == null"); return null; } if (mSplashAd == null || mSplashShowView == null) { EALog.e(TAG + "mSplashAd == null || mSplashShowView == null"); return null; } return startSplashClickEyeAnimation(mSplashShowView, decorView, splashViewContainer, callBack); } //开屏点睛动画 public ViewGroup startSplashClickEyeAnimation(final View splash, final ViewGroup decorView, final ViewGroup splashViewContainer, final AnimationCallBack callBack) { if (splash == null || splashViewContainer == null) { EALog.e(TAG + "splash == null || splashViewContainer == null"); return null; } final int[] splashScreenPos = new int[2]; splash.getLocationOnScreen(splashScreenPos); final Context context = splashViewContainer.getContext(); int splashViewWidth = splash.getWidth(); int splashViewHeight = splash.getHeight(); int animationContainerWidth = decorView.getWidth(); int animationContainerHeight = decorView.getHeight(); if (animationContainerWidth == 0) { animationContainerWidth = mDecorViewWidth; } if (animationContainerHeight == 0) { animationContainerHeight = mDecorViewHeight; } float xScaleRatio = (float) mClickEyeViewWidth / splashViewWidth; float yScaleRation = (float) mClickEyeViewHeight / splashViewHeight; final float animationDistX = mClickEyeViewPos == LEFT ? mClickEyeViewMargin : animationContainerWidth - mClickEyeViewMargin - mClickEyeViewWidth; final float animationDistY = animationContainerHeight - mClickEyeViewMarginBottom - mClickEyeViewHeight; //最终位于container的y坐标 removeFromParent(splash); FrameLayout.LayoutParams animationParams = new FrameLayout.LayoutParams(splashViewWidth, splashViewHeight); decorView.addView(splash, animationParams); final EASplashZoomOutLayout splashViewLayout = new EASplashZoomOutLayout(context,mClickEyeViewMargin); splash.setPivotX(0); splash.setPivotY(0); splash.animate() .scaleX(xScaleRatio) .scaleY(yScaleRation) .x(animationDistX) .y(animationDistY) .setInterpolator(new OvershootInterpolator(0)) .setDuration(mClickEyeViewAnimationTime) .setListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { if (callBack != null) { callBack.animationStart(mClickEyeViewAnimationTime); } } @Override public void onAnimationEnd(Animator animation) { removeFromParent(splash); splash.setScaleX(1); splash.setScaleY(1); splash.setX(0); splash.setY(0); int[] clickEyeContainerScreenPos = new int[2]; splashViewContainer.getLocationOnScreen(clickEyeContainerScreenPos); float distX = animationDistX - clickEyeContainerScreenPos[0] + splashScreenPos[0]; float distY = animationDistY - clickEyeContainerScreenPos[1] + splashScreenPos[1]; splashViewLayout.addView(splash, FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); FrameLayout.LayoutParams clickEyeParams = new FrameLayout.LayoutParams(mClickEyeViewWidth, mClickEyeViewHeight); splashViewContainer.addView(splashViewLayout, clickEyeParams); splashViewLayout.setTranslationX(distX); splashViewLayout.setTranslationY(distY); if (callBack != null) { callBack.animationEnd(); } } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); return splashViewLayout; } public static void removeFromParent(View view) { if (view != null) { ViewParent vp = view.getParent(); if (vp instanceof ViewGroup) { ((ViewGroup) vp).removeView(view); } } } public boolean isSupportSplashClickEye() { return mIsSupportSplashClickEye; } public void setSupportSplashClickEye(boolean isSupportSplashClickEye) { this.mIsSupportSplashClickEye = isSupportSplashClickEye; } } ================================================ FILE: easycsj/src/main/java/com/easyads/supplier/csj/CsjBannerAdapter.java ================================================ package com.easyads.supplier.csj; import android.app.Activity; import android.view.View; import android.view.ViewGroup; import com.easyads.EasyAdsManger; import com.easyads.core.banner.EABannerSetting; import com.easyads.custom.EABannerCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.bytedance.sdk.openadsdk.AdSlot; import com.bytedance.sdk.openadsdk.TTAdDislike; import com.bytedance.sdk.openadsdk.TTAdNative; import com.bytedance.sdk.openadsdk.TTNativeExpressAd; import java.lang.ref.SoftReference; import java.util.List; /** * 如果网络异常,不会进行刷新行为,且不会回调失败。当网络正常,会继续定时刷新。视为内部闭环了刷新行为,一旦失败就流转下一优先级 */ public class CsjBannerAdapter extends EABannerCustomAdapter implements TTAdNative.NativeExpressAdListener { private EABannerSetting setting; private long startTime = 0; private TTNativeExpressAd ad; public CsjBannerAdapter(SoftReference activity, final EABannerSetting setting) { super(activity, setting); this.setting = setting; } @Override public void onError(int code, String message) { EALog.high(TAG + " onError: code = " + code + " msg = " + message); handleFailed(code + "", message); } @Override public void onNativeExpressAdLoad(List ads) { try { EALog.high(TAG + "onNativeExpressAdLoad"); if (ads == null || ads.size() == 0) { handleFailed(EasyAdError.ERROR_DATA_NULL, "广告列表数据为空"); return; } ad = ads.get(0); // 加载成功的回调,接入方可在此处做广告的展示,请确保您的代码足够健壮,能够处理异常情况; if (null == ad) { handleFailed(EasyAdError.ERROR_DATA_NULL, "广告数据为空"); return; } bindAdListener(ad); handleSucceed(); } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.parseErr(EasyAdError.ERROR_EXCEPTION_LOAD)); } } private void bindAdListener(TTNativeExpressAd ad) { try { if (null != setting) { ad.setSlideIntervalTime(setting.getRefreshInterval() * 1000); } ad.setExpressInteractionListener(new TTNativeExpressAd.ExpressAdInteractionListener() { @Override public void onAdClicked(View view, int i) { EALog.high(TAG + "ExpressView onAdClicked , type :" + i); handleClick(); } @Override public void onAdShow(View view, int i) { EALog.high(TAG + "ExpressView onAdShow, type :" + i + ",cost time = " + (System.currentTimeMillis() - startTime)); handleExposure(); } @Override public void onRenderFail(View view, String msg, int code) { EALog.high(TAG + "ExpressView onRenderFail ,cost:" + (System.currentTimeMillis() - startTime)); handleFailed(EasyAdError.ERROR_RENDER_FAILED, TAG + code + ", " + msg); } @Override public void onRenderSuccess(View view, float v, float v1) { EALog.high(TAG + "ExpressView onRenderSuccess,cost:" + (System.currentTimeMillis() - startTime)); if (null != setting) { ViewGroup adContainer = setting.getContainer(); if (adContainer != null) { adContainer.removeAllViews(); adContainer.addView(view); } } } }); //使用默认模板中默认dislike弹出样式 ad.setDislikeCallback(getActivity(), new TTAdDislike.DislikeInteractionCallback() { @Override public void onShow() { } @Override public void onSelected(int position, String value, boolean enforce) { if (null != setting) { //用户选择不喜欢原因后,移除广告展示 ViewGroup adContainer = setting.getContainer(); if (adContainer != null) { adContainer.removeAllViews(); } setting.adapterDidDislike(sdkSupplier); } } @Override public void onCancel() { } }); } catch (Throwable e) { e.printStackTrace(); } } @Override public void doDestroy() { try { if (ad != null) ad.destroy(); } catch (Throwable e) { e.printStackTrace(); } } @Override protected void doLoadAD() { CsjUtil.initCsj(this, new CsjUtil.InitListener() { @Override public void success() { //只有在成功初始化以后才能调用load方法,否则穿山甲会抛错导致无法进行广告展示 startLoadAD(); } @Override public void fail(String code, String msg) { handleFailed(code, msg); } }); } private void startLoadAD() { TTAdNative ttAdNative = CsjUtil.getADManger(this).createAdNative(getActivity()); AdSlot adSlot = new AdSlot.Builder() // 必选参数 设置您的CodeId .setCodeId(sdkSupplier.adspotId) //期望模板广告view的size,单位dp .setExpressViewAcceptedSize(setting.getCsjExpressViewAcceptedWidth(), setting.getCsjExpressViewAcceptedHeight()) // 可选参数 设置是否支持deeplink .setSupportDeepLink(true) //请求原生广告时候需要设置,参数为TYPE_BANNER或TYPE_INTERACTION_AD .build(); ttAdNative.loadBannerExpressAd(adSlot, this); } @Override protected void doShowAD() { startTime = System.currentTimeMillis(); if (ad != null) { ad.render(); } } } ================================================ FILE: easycsj/src/main/java/com/easyads/supplier/csj/CsjDrawAdapter.java ================================================ package com.easyads.supplier.csj; import android.app.Activity; import android.view.View; import com.easyads.core.draw.EADrawSetting; import com.easyads.custom.EADrawCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.bytedance.sdk.openadsdk.AdSlot; import com.bytedance.sdk.openadsdk.TTAdNative; import com.bytedance.sdk.openadsdk.TTNativeExpressAd; import java.lang.ref.SoftReference; import java.util.List; public class CsjDrawAdapter extends EADrawCustomAdapter implements TTAdNative.NativeExpressAdListener { TTNativeExpressAd ad; public CsjDrawAdapter(SoftReference activity, EADrawSetting setting) { super(activity, setting); } @Override protected void doLoadAD() { CsjUtil.initCsj(this, new CsjUtil.InitListener() { @Override public void success() { //只有在成功初始化以后才能调用load方法,否则穿山甲会抛错导致无法进行广告展示 startLoadAD(); } @Override public void fail(String code, String msg) { handleFailed(code, msg); } }); } @Override public void doDestroy() { } @Override protected void doShowAD() { if (addADView(ad.getExpressAdView())) { ad.render(); } } private void startLoadAD() { // 创建TTAdNative对象,用于调用广告请求接口 TTAdNative mTTAdNative = CsjUtil.getADManger(this).createAdNative(getActivity().getApplicationContext()); AdSlot adSlot = new AdSlot.Builder() .setCodeId(sdkSupplier.adspotId) .setSupportDeepLink(true) .setExpressViewAcceptedSize(mDrawSetting.getCsjExpressWidth(), mDrawSetting.getCsjExpressHeight()) //期望模板广告view的size,单位dp .setAdCount(1) //请求广告数量为1到3条 // .setAdLoadType(PRELOAD)//推荐使用,用于标注此次的广告请求用途为预加载(当做缓存)还是实时加载,方便后续为开发者优化相关策略 .build(); mTTAdNative.loadExpressDrawFeedAd(adSlot, this); } @Override public void onError(int code, String message) { EALog.high(TAG + "onError" + code + message); handleFailed(code, message); } @Override public void onNativeExpressAdLoad(List ads) { try { EALog.high(TAG + "onNativeExpressAdLoad, ads = " + ads); if (ads == null || ads.isEmpty()) { handleFailed(EasyAdError.ERROR_DATA_NULL, "ads empty"); return; } ad = ads.get(0); // ad.setCanInterruptVideoPlay(false); ad.setExpressInteractionListener(new TTNativeExpressAd.ExpressAdInteractionListener() { //广告点击的回调 @Override public void onAdClicked(View view, int type) { EALog.high(TAG + "onAdClicked, type = " + type); handleClick(); } //广告展示回调 @Override public void onAdShow(View view, int type) { EALog.high(TAG + "onAdShow, type = " + type); handleExposure(); } //广告渲染失败 @Override public void onRenderFail(View view, String msg, int code) { String log = "onRenderFail : code = " + code + ",msg =" + msg; EALog.high(TAG + "onRenderFail, log = " + log); handleFailed(EasyAdError.ERROR_RENDER_FAILED, log); } //广告渲染成功 @Override public void onRenderSuccess(View view, float width, float height) { EALog.high(TAG + "onRenderSuccess, width = " + width + ",height = " + height); } }); handleSucceed(); } catch (Throwable e) { e.printStackTrace(); } } } ================================================ FILE: easycsj/src/main/java/com/easyads/supplier/csj/CsjFullScreenVideoAdapter.java ================================================ package com.easyads.supplier.csj; import android.app.Activity; import android.content.res.Configuration; import com.easyads.EasyAdsManger; import com.easyads.core.full.EAFullScreenVideoSetting; import com.easyads.custom.EAFullScreenCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.bytedance.sdk.openadsdk.AdSlot; import com.bytedance.sdk.openadsdk.TTAdConstant; import com.bytedance.sdk.openadsdk.TTAdNative; import com.bytedance.sdk.openadsdk.TTFullScreenVideoAd; import java.lang.ref.SoftReference; public class CsjFullScreenVideoAdapter extends EAFullScreenCustomAdapter implements TTAdNative.FullScreenVideoAdListener, TTFullScreenVideoAd.FullScreenVideoAdInteractionListener { private EAFullScreenVideoSetting setting; private TTFullScreenVideoAd ttFullScreenVideoAd; public CsjFullScreenVideoAdapter(SoftReference activity, EAFullScreenVideoSetting setting) { super(activity, setting); this.setting = setting; } @Override protected void doLoadAD() { CsjUtil.initCsj(this, new CsjUtil.InitListener() { @Override public void success() { //只有在成功初始化以后才能调用load方法,否则穿山甲会抛错导致无法进行广告展示 startLoadAD(); } @Override public void fail(String code, String msg) { handleFailed(code, msg); } }); } private void startLoadAD() { // 创建TTAdNative对象,用于调用广告请求接口 TTAdNative mTTAdNative = CsjUtil.getADManger(this).createAdNative(getActivity().getApplicationContext()); boolean isPortrait = getActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; int orientation; if (isPortrait) { // 基于当前屏幕方向来设定广告期望方向 orientation = TTAdConstant.VERTICAL; } else { orientation = TTAdConstant.HORIZONTAL; } AdSlot adSlot = new AdSlot.Builder() .setCodeId(sdkSupplier.adspotId) //模板广告需要设置期望个性化模板广告的大小,单位dp,激励视频场景,只要设置的值大于0即可 .setExpressViewAcceptedSize(500, 500) .setSupportDeepLink(true) .setOrientation(orientation)//必填参数,期望视频的播放方向:TTAdConstant.HORIZONTAL 或 TTAdConstant.VERTICAL .build(); //step5:请求广告 mTTAdNative.loadFullScreenVideoAd(adSlot, this); } @Override protected void doShowAD() { ttFullScreenVideoAd.setFullScreenVideoAdInteractionListener(this); ttFullScreenVideoAd.showFullScreenVideoAd(getActivity()); } @Override public void doDestroy() { } /** * 以下为FullScreenVideoAdListener 回调 */ @Override public void onError(int i, String s) { EALog.high(TAG + "onError "); handleFailed(i, s); } @Override public void onFullScreenVideoAdLoad(TTFullScreenVideoAd ttFullScreenVideoAd) { try { EALog.high(TAG + "onFullScreenVideoAdLoad "); this.ttFullScreenVideoAd = ttFullScreenVideoAd; handleSucceed(); } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.parseErr(EasyAdError.ERROR_EXCEPTION_LOAD)); } } @Override public void onFullScreenVideoCached() { EALog.high(TAG + "onFullScreenVideoCached "); } @Override public void onFullScreenVideoCached(TTFullScreenVideoAd ttFullScreenVideoAd) { try { String ad = ""; if (ttFullScreenVideoAd != null) { ad = ttFullScreenVideoAd.toString(); } EALog.high(TAG + "onFullScreenVideoCached( " + ad + ")"); } catch (Throwable e) { e.printStackTrace(); } handleCached(); } /** * 以下为setFullScreenVideoAdInteractionListener 广告事件监听 */ @Override public void onAdShow() { EALog.high(TAG + "onFullScreenVideo onAdShow"); handleExposure(); } @Override public void onAdVideoBarClick() { EALog.high(TAG + "onFullScreenVideo onAdVideoBarClick"); handleClick(); } @Override public void onAdClose() { EALog.high(TAG + "onFullScreenVideo onAdClose"); if (setting != null) setting.adapterClose(sdkSupplier); } @Override public void onVideoComplete() { EALog.high(TAG + "onFullScreenVideo onVideoComplete"); if (setting != null) setting.adapterVideoComplete(sdkSupplier); } @Override public void onSkippedVideo() { EALog.high(TAG + "onFullScreenVideo onSkippedVideo"); if (setting != null) setting.adapterVideoSkipped(sdkSupplier); } } ================================================ FILE: easycsj/src/main/java/com/easyads/supplier/csj/CsjInterstitialAdapter.java ================================================ package com.easyads.supplier.csj; import android.app.Activity; import com.bytedance.sdk.openadsdk.AdSlot; import com.bytedance.sdk.openadsdk.TTAdConstant; import com.bytedance.sdk.openadsdk.TTAdNative; import com.bytedance.sdk.openadsdk.TTFullScreenVideoAd; import com.easyads.core.inter.EAInterstitialSetting; import com.easyads.custom.EAInterstitialCustomAdapter; import com.easyads.itf.BaseEnsureListener; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.easyads.utils.EAUtil; import java.lang.ref.SoftReference; public class CsjInterstitialAdapter extends EAInterstitialCustomAdapter { private EAInterstitialSetting setting; // private TTNativeExpressAd mTTAd; private long startTime = 0; public TTFullScreenVideoAd newVersionAd; public CsjInterstitialAdapter(SoftReference activity, EAInterstitialSetting setting) { super(activity, setting); this.setting = setting; } @Override public void doDestroy() { } @Override protected void doLoadAD() { CsjUtil.initCsj(this, new CsjUtil.InitListener() { @Override public void success() { //只有在成功初始化以后才能调用load方法,否则穿山甲会抛错导致无法进行广告展示 startLoadAD(); } @Override public void fail(String code, String msg) { handleFailed(code, msg); } }); } @Override protected void doShowAD() { EAUtil.switchMainThread(new BaseEnsureListener() { @Override public void ensure() { try { String nullTip = TAG + "请先加载广告或者广告已经展示过"; if (newVersionAd != null) { newVersionAd.showFullScreenVideoAd(getActivity(), TTAdConstant.RitScenes.GAME_GIFT_BONUS, null); newVersionAd = null; } else { EALog.e(nullTip); } } catch (Exception e) { e.printStackTrace(); handleFailed(EasyAdError.parseErr(EasyAdError.ERROR_EXCEPTION_SHOW)); } } }); } private void startLoadAD() { TTAdNative ttAdNative = CsjUtil.getADManger(this).createAdNative(getActivity()); AdSlot adSlot = new AdSlot.Builder() .setCodeId(sdkSupplier.adspotId) .setSupportDeepLink(true) .setExpressViewAcceptedSize(setting.getCsjExpressViewWidth(), setting.getCsjExpressViewHeight()) .build(); ttAdNative.loadFullScreenVideoAd(adSlot, new TTAdNative.FullScreenVideoAdListener() { @Override public void onError(int i, String s) { handleFailed(i, s); } @Override public void onFullScreenVideoAdLoad(TTFullScreenVideoAd ttFullScreenVideoAd) { try { EALog.high(TAG + "onFullScreenVideoAdLoad"); newVersionAd = ttFullScreenVideoAd; if (newVersionAd == null) { handleFailed(EasyAdError.ERROR_DATA_NULL, "new ints ad null"); return; } newVersionAd.setFullScreenVideoAdInteractionListener(new TTFullScreenVideoAd.FullScreenVideoAdInteractionListener() { @Override public void onAdShow() { EALog.high(TAG + "newVersionAd onAdShow"); handleExposure(); } @Override public void onAdVideoBarClick() { EALog.high(TAG + "newVersionAd onAdVideoBarClick"); handleClick(); } @Override public void onAdClose() { EALog.high(TAG + "newVersionAd onAdClose"); if (setting != null) setting.adapterDidClosed(sdkSupplier); } @Override public void onVideoComplete() { EALog.high(TAG + "newVersionAd onVideoComplete"); } @Override public void onSkippedVideo() { EALog.high(TAG + "newVersionAd onSkippedVideo"); } }); handleSucceed(); } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.ERROR_EXCEPTION_LOAD, ""); } } @Override public void onFullScreenVideoCached() { EALog.high(TAG + "onFullScreenVideoCached"); } @Override public void onFullScreenVideoCached(TTFullScreenVideoAd ttFullScreenVideoAd) { try { String ad = ""; if (ttFullScreenVideoAd != null) { ad = ttFullScreenVideoAd.toString(); } EALog.high(TAG + "onFullScreenVideoCached( " + ad + ")"); } catch (Throwable e) { e.printStackTrace(); } } }); } } ================================================ FILE: easycsj/src/main/java/com/easyads/supplier/csj/CsjNativeExpressAdapter.java ================================================ package com.easyads.supplier.csj; import android.app.Activity; import android.view.View; import com.easyads.EasyAdsManger; import com.easyads.core.nati.EANativeExpressSetting; import com.easyads.custom.EANativeExpressCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.bytedance.sdk.openadsdk.AdSlot; import com.bytedance.sdk.openadsdk.TTAdDislike; import com.bytedance.sdk.openadsdk.TTAdNative; import com.bytedance.sdk.openadsdk.TTNativeExpressAd; import java.lang.ref.SoftReference; import java.util.List; public class CsjNativeExpressAdapter extends EANativeExpressCustomAdapter implements TTAdNative.NativeExpressAdListener { TTNativeExpressAd ttNativeExpressAd; private EANativeExpressSetting setting; public CsjNativeExpressAdapter(SoftReference activity, EANativeExpressSetting setting) { super(activity, setting); this.setting = setting; } @Override protected void doLoadAD() { CsjUtil.initCsj(this, new CsjUtil.InitListener() { @Override public void success() { //只有在成功初始化以后才能调用load方法,否则穿山甲会抛错导致无法进行广告展示 startLoadAD(); } @Override public void fail(String code, String msg) { handleFailed(code, msg); } }); } private void startLoadAD() { TTAdNative ttAdNative = CsjUtil.getADManger(this).createAdNative(getActivity()); AdSlot adSlot = new AdSlot.Builder() .setCodeId(sdkSupplier.adspotId) //广告位id .setSupportDeepLink(true) .setAdCount(1) //请求广告数量为1到3条 // .setDownloadType(EasyCsjManger.getInstance().csj_downloadType) .setExpressViewAcceptedSize(setting.getExpressViewWidth(), setting.getExpressViewHeight()) //期望模板广告view的size,单位dp .build(); //加载广告 ttAdNative.loadNativeExpressAd(adSlot, this); } @Override public void doDestroy() { } @Override public void onError(int i, String s) { EALog.high(TAG + "onError"); handleFailed(i, s); } @Override public void onNativeExpressAdLoad(List ads) { try { EALog.high(TAG + "onNativeExpressAdLoad"); if (ads == null || ads.size() == 0) { handleFailed(EasyAdError.ERROR_DATA_NULL, "ads empty"); } else { ttNativeExpressAd = ads.get(0); if (null == ttNativeExpressAd) { handleFailed(EasyAdError.ERROR_DATA_NULL, "ttNativeExpressAd is null "); return; } handleSucceed(); } } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.ERROR_EXCEPTION_LOAD, ""); } } @Override protected void doShowAD() { addADView(ttNativeExpressAd.getExpressAdView()); ttNativeExpressAd.setExpressInteractionListener(new TTNativeExpressAd.ExpressAdInteractionListener() { @Override public void onAdClicked(View view, int i) { EALog.high(TAG + "onAdClicked"); setNEView(view); handleClick(); } @Override public void onAdShow(View view, int i) { EALog.high(TAG + "onAdShow"); setNEView(view); handleExposure(); } @Override public void onRenderFail(View view, String msg, int code) { EALog.high(TAG + "onRenderFail"); setNEView(view); if (null != setting) { setting.adapterRenderFailed(sdkSupplier); } handleFailed(EasyAdError.parseErr(EasyAdError.ERROR_RENDER_FAILED, TAG + code + ", " + msg)); removeADView(); } @Override public void onRenderSuccess(View view, float v, float v1) { EALog.high(TAG + "onRenderSuccess"); setNEView(view); if (null != setting) { setting.adapterRenderSuccess(sdkSupplier); } } }); ttNativeExpressAd.setDislikeCallback(getActivity(), new TTAdDislike.DislikeInteractionCallback() { @Override public void onShow() { } @Override public void onSelected(int i, String s, boolean enforce) { EALog.high(TAG + "DislikeInteractionCallback_onSelected , int i = +" + i + ", String s" + s + ", boolean enforce" + enforce + " ;"); if (null != setting) { setting.adapterDidClosed(sdkSupplier); } removeADView(); } @Override public void onCancel() { } }); ttNativeExpressAd.render(); } } ================================================ FILE: easycsj/src/main/java/com/easyads/supplier/csj/CsjRewardVideoAdapter.java ================================================ package com.easyads.supplier.csj; import android.app.Activity; import android.os.Bundle; import com.easyads.core.reward.EARewardServerCallBackInf; import com.easyads.core.reward.EARewardVideoSetting; import com.easyads.custom.EARewardCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.bytedance.sdk.openadsdk.AdSlot; import com.bytedance.sdk.openadsdk.TTAdNative; import com.bytedance.sdk.openadsdk.TTRewardVideoAd; import java.lang.ref.SoftReference; public class CsjRewardVideoAdapter extends EARewardCustomAdapter implements TTAdNative.RewardVideoAdListener { private EARewardVideoSetting setting; private TTRewardVideoAd ttRewardVideoAd; public CsjRewardVideoAdapter(SoftReference activity, EARewardVideoSetting setting) { super(activity, setting); this.setting = setting; } @Override public void onError(int i, String s) { EALog.high(TAG + "onError," + i + s); handleFailed(EasyAdError.parseErr(i, s)); } @Override public void onRewardVideoAdLoad(TTRewardVideoAd ttRewardVideoAd) { try { EALog.high(TAG + "onRewardVideoAdLoad"); this.ttRewardVideoAd = ttRewardVideoAd; handleSucceed(); } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.parseErr(EasyAdError.ERROR_EXCEPTION_LOAD)); } } @Override public void onRewardVideoCached() { EALog.high(TAG + "onRewardVideoCached"); } @Override public void onRewardVideoCached(TTRewardVideoAd ttRewardVideoAd) { try { String ad = ""; if (ttRewardVideoAd != null) { ad = ttRewardVideoAd.toString(); } EALog.high(TAG + "onRewardVideoCached( " + ad + ")"); } catch (Throwable e) { e.printStackTrace(); } handleCached(); } @Override protected void doLoadAD() { CsjUtil.initCsj(this, new CsjUtil.InitListener() { @Override public void success() { //只有在成功初始化以后才能调用load方法,否则穿山甲会抛错导致无法进行广告展示 startLoadAD(); } @Override public void fail(String code, String msg) { handleFailed(code, msg); } }); } private void startLoadAD() { TTAdNative ttAdNative = CsjUtil.getADManger(this).createAdNative(getActivity()); AdSlot adSlot = new AdSlot.Builder() .setCodeId(sdkSupplier.adspotId) .setSupportDeepLink(true) .setAdCount(1) //设置模板属性 .setExpressViewAcceptedSize(500, 500) //必传参数,表来标识应用侧唯一用户;若非服务器回调模式或不需sdk透传 //可设置为空字符串 .setUserID(setting.getCsjUserId()) .setOrientation(setting.getCsjOrientation()) //设置期望视频播放的方向,为TTAdConstant.HORIZONTAL或TTAdConstant.VERTICAL .setMediaExtra(setting.getCsjMediaExtra()) //用户透传的信息,可不传 .build(); ttAdNative.loadRewardVideoAd(adSlot, this); } @Override public void doDestroy() { } @Override protected void doShowAD() { if (ttRewardVideoAd == null) { EALog.e("无广告内容"); return; } ttRewardVideoAd.setRewardAdInteractionListener(new TTRewardVideoAd.RewardAdInteractionListener() { @Override public void onAdShow() { EALog.high(TAG + "onAdShow"); handleExposure(); } @Override public void onAdVideoBarClick() { EALog.high(TAG + "onAdVideoBarClick"); handleClick(); } @Override public void onAdClose() { EALog.high(TAG + "onAdClose"); if (null != setting) { setting.adapterDidClosed(sdkSupplier); } } @Override public void onVideoComplete() { EALog.high(TAG + "onVideoComplete"); if (null != setting) { setting.adapterVideoComplete(sdkSupplier); } } @Override public void onVideoError() { EALog.high(TAG + "onVideoError"); handleFailed(EasyAdError.parseErr(EasyAdError.ERROR_EXCEPTION_RENDER, "onVideoError")); } @Override public void onRewardVerify(boolean rewardVerify, int rewardAmount, String rewardName, int errorCode, String errMsg) { EALog.high(TAG + " onRewardVerify"); } @Override public void onRewardArrived(boolean isRewardValid, int rewardType, Bundle extraInfo) { EALog.high(TAG + " onRewardArrived"); int mServerErrorCode = extraInfo.getInt(TTRewardVideoAd.REWARD_EXTRA_KEY_ERROR_CODE); String mServerErrorMsg = extraInfo.getString(TTRewardVideoAd.REWARD_EXTRA_KEY_ERROR_MSG); String mRewardName = extraInfo.getString(TTRewardVideoAd.REWARD_EXTRA_KEY_REWARD_NAME); int mRewardAmount = extraInfo.getInt(TTRewardVideoAd.REWARD_EXTRA_KEY_REWARD_AMOUNT); float mRewardPropose = extraInfo.getFloat(TTRewardVideoAd.REWARD_EXTRA_KEY_REWARD_PROPOSE); onAdItemRewardVerify(isRewardValid, rewardType, mRewardAmount, mRewardName, mServerErrorCode, mServerErrorMsg, mRewardPropose); } @Override public void onSkippedVideo() { EALog.high(TAG + "onSkippedVideo"); if (null != setting) { setting.adapterVideoSkipped(sdkSupplier); } } }); ttRewardVideoAd.showRewardVideoAd(getActivity()); } public void onAdItemRewardVerify(boolean rewardVerify, int rewardType, int rewardAmount, String rewardName, int errorCode, String errMsg, float mRewardPropose) { try { EALog.high(TAG + "onRewardVerify; rewardVerify = " + rewardVerify + ",rewardAmount = " + rewardAmount + ",rewardName = " + rewardName + " errorCode:" + errorCode + " errMsg:" + errMsg); EARewardServerCallBackInf inf = new EARewardServerCallBackInf(); EARewardServerCallBackInf.CsjRewardInf csjRewardInf = new EARewardServerCallBackInf.CsjRewardInf(); csjRewardInf.rewardVerify = rewardVerify; csjRewardInf.rewardAmount = rewardAmount; csjRewardInf.rewardName = rewardName; csjRewardInf.errorCode = errorCode; csjRewardInf.errMsg = errMsg; inf.csjInf = csjRewardInf; setting.postRewardServerInf(inf, sdkSupplier); } catch (Throwable e) { e.printStackTrace(); } if (rewardVerify) { if (null != setting) { setting.adapterAdReward(sdkSupplier); } } else if (errorCode != 0) {//如果有异常信息,是否进行异常回调? EALog.e("onRewardVerify error ,Code = " + errorCode + " errMsg" + errMsg); } } } ================================================ FILE: easycsj/src/main/java/com/easyads/supplier/csj/CsjSplashAdapter.java ================================================ package com.easyads.supplier.csj; import android.app.Activity; import android.support.annotation.MainThread; import android.view.View; import android.view.ViewGroup; import com.bytedance.sdk.openadsdk.CSJAdError; import com.bytedance.sdk.openadsdk.CSJSplashAd; import com.bytedance.sdk.openadsdk.CSJSplashCloseType; import com.easyads.EasyAdsManger; import com.easyads.core.splash.EASplashSetting; import com.easyads.custom.EASplashCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EAUtil; import com.easyads.utils.EALog; import com.bytedance.sdk.openadsdk.AdSlot; import com.bytedance.sdk.openadsdk.ISplashClickEyeListener; import com.bytedance.sdk.openadsdk.TTAdManager; import com.bytedance.sdk.openadsdk.TTAdNative; import com.bytedance.sdk.openadsdk.TTAdSdk; import com.bytedance.sdk.openadsdk.TTSplashAd; import java.lang.ref.SoftReference; public class CsjSplashAdapter extends EASplashCustomAdapter { private CSJSplashAd newSplashAd; public CsjSplashAdapter(SoftReference activity, EASplashSetting setting) { super(activity, setting); } @Override public void doDestroy() { } @Override protected void doShowAD() { // initSplashClickEyeData(); if (newSplashAd != null) { //获取SplashView View view = newSplashAd.getSplashView(); //渲染之前判断activity生命周期状态 if (!EAUtil.isActivityDestroyed(softReferenceActivity)) { adContainer.removeAllViews(); //把SplashView 添加到ViewGroup中,注意开屏广告view:width >=70%屏幕宽;height >=50%屏幕宽 adContainer.addView(view); } } } @Override protected void doLoadAD() { CsjUtil.initCsj(this, new CsjUtil.InitListener() { @Override public void success() { //只有在成功初始化以后才能调用load方法,否则穿山甲会抛错导致无法进行广告展示 startLoadAD(); } @Override public void fail(String code, String msg) { handleFailed(code, msg); } }); } private void startLoadAD() { boolean isExpress; if (sdkSupplier.versionTag == 1) { isExpress = false; } else if (sdkSupplier.versionTag == 2) { isExpress = true; } else { if (mSplashSetting != null) { isExpress = mSplashSetting.getCsjShowAsExpress(); } else { isExpress = false; } } EALog.simple(TAG + "是否为模板类型:" + isExpress); AdSlot adSlot; //穿山甲后台暂时不支持开屏模板广告,代码先加上相关判断,不影响现有展示。 if (isExpress) { adSlot = new AdSlot.Builder() .setCodeId(sdkSupplier.adspotId) .setSupportDeepLink(true) .setExpressViewAcceptedSize(mSplashSetting.getCsjExpressViewWidth(), mSplashSetting.getCsjExpressViewHeight()) .setImageAcceptedSize(mSplashSetting.getCsjAcceptedSizeWidth(), mSplashSetting.getCsjAcceptedSizeHeight()) .build(); } else { adSlot = new AdSlot.Builder() .setCodeId(sdkSupplier.adspotId) .setSupportDeepLink(true) .setImageAcceptedSize(mSplashSetting.getCsjAcceptedSizeWidth(), mSplashSetting.getCsjAcceptedSizeHeight()) .build(); } TTAdNative ttAdNative = CsjUtil.getADManger(this).createAdNative(getActivity()); ttAdNative.loadSplashAd(adSlot, new TTAdNative.CSJSplashAdListener() { @Override public void onSplashLoadSuccess(CSJSplashAd splashAd) { EALog.high(TAG + "onSplashLoadSuccess"); } @Override public void onSplashLoadFail(CSJAdError csjAdError) { EALog.high(TAG + "onSplashLoadFail"); newApiAdFailed(csjAdError, EasyAdError.ERROR_EXCEPTION_LOAD, "onSplashLoadFail"); } @Override public void onSplashRenderSuccess(CSJSplashAd csjSplashAd) { EALog.high(TAG + "onAdLoaded"); if (csjSplashAd == null) { String nMsg = TAG + " TTSplashAd null"; EasyAdError error = EasyAdError.parseErr(EasyAdError.ERROR_DATA_NULL, nMsg); handleFailed(error); return; } newSplashAd = csjSplashAd; handleSucceed(); newSplashAd.setSplashAdListener(new CSJSplashAd.SplashAdListener() { @Override public void onSplashAdShow(CSJSplashAd csjSplashAd) { EALog.high(TAG + "onSplashAdShow"); handleExposure(); } @Override public void onSplashAdClick(CSJSplashAd csjSplashAd) { EALog.high(TAG + "onSplashAdClick"); handleClick(); } @Override public void onSplashAdClose(CSJSplashAd csjSplashAd, int closeType) { EALog.high(TAG + "onSplashAdClose"); if (mSplashSetting != null) { if (closeType == CSJSplashCloseType.CLICK_SKIP) { mSplashSetting.adapterDidSkip(sdkSupplier); } else if (closeType == CSJSplashCloseType.COUNT_DOWN_OVER) { mSplashSetting.adapterDidTimeOver(sdkSupplier); } else { mSplashSetting.adapterDidSkip(sdkSupplier); } } } }); CsjUtil.getCPMInfNew(TAG, newSplashAd); } @Override public void onSplashRenderFail(CSJSplashAd csjSplashAd, CSJAdError csjAdError) { EALog.high(TAG + "onSplashRenderFail"); newApiAdFailed(csjAdError, EasyAdError.ERROR_RENDER_FAILED, "onSplashRenderFail"); } }, 5000); } private void newApiAdFailed(CSJAdError csjAdError, String errCodeDefault, String errExt) { try { EasyAdError error; if (csjAdError == null) { error = EasyAdError.parseErr(errCodeDefault, errExt); } else { error = EasyAdError.parseErr(csjAdError.getCode(), csjAdError.getMsg()); } handleFailed(error); } catch (Exception e) { e.printStackTrace(); } } /** * 以下为点睛广告特殊处理 */ //是否进行点睛广告的展示 private void switchSplashClickShow() { try { if (mSplashSetting == null) { return; } if (mSplashSetting.isShowInSingleActivity()) { new CsjUtil().zoomOut(getActivity()); } else { EasyAdsManger.getInstance().isSplashSupportZoomOut = true; } } catch (Throwable e) { e.printStackTrace(); } } //初始化点睛广告数据、类 // private void initSplashClickEyeData() { // try { // if (splashAd == null) { // return; // } // View splashView = splashAd.getSplashView(); // if (splashView == null) { // return; // } // SplashClickEyeListener mSplashClickEyeListener = new SplashClickEyeListener(getActivity(), splashAd, adContainer); // // splashAd.setSplashClickEyeListener(mSplashClickEyeListener); // CSJSplashClickEyeManager csjSplashClickEyeManager = CSJSplashClickEyeManager.getInstance(); // csjSplashClickEyeManager.init(getActivity()); // csjSplashClickEyeManager.setSplashInfo(splashAd, splashView, getActivity().getWindow().getDecorView()); // } catch (Throwable e) { // e.printStackTrace(); // } // } public static class SplashClickEyeListener implements ISplashClickEyeListener { private SoftReference mActivity; private TTSplashAd mSplashAd; private View mSplashContainer; String TAG = "[SplashClickEyeListener] "; public SplashClickEyeListener(Activity activity, TTSplashAd splashAd, View splashContainer) { try { mSplashAd = splashAd; mSplashContainer = splashContainer; mActivity = new SoftReference<>(activity); } catch (Throwable e) { e.printStackTrace(); } } @Override public void onSplashClickEyeAnimationStart() { //开始执行开屏点睛动画 startSplashAnimationStart(); } @Override public void onSplashClickEyeAnimationFinish() { try { //sdk关闭了了点睛悬浮窗 CSJSplashClickEyeManager splashClickEyeManager = CSJSplashClickEyeManager.getInstance(); boolean isSupport = splashClickEyeManager.isSupportSplashClickEye(); if (isSupport) { finishActivity(); } splashClickEyeManager.clearSplashStaticData(); } catch (Throwable e) { e.printStackTrace(); } } @Override public boolean isSupportSplashClickEye(boolean isSupport) { try { EALog.high(TAG + " isSupportSplashClickEye = " + isSupport); CSJSplashClickEyeManager splashClickEyeManager = CSJSplashClickEyeManager.getInstance(); //此处可以修改强制支持,测试点睛广告展现效果 // if (EasyAdsManger.getInstance().isDev) { // EALog.devDebug("csj test SplashClickEye force isSupport = true"); // isSupport = true; // } splashClickEyeManager.setSupportSplashClickEye(isSupport); } catch (Throwable e) { e.printStackTrace(); } return isSupport; } private void finishActivity() { try { if (mActivity.get() == null) { return; } mActivity.get().finish(); } catch (Throwable e) { e.printStackTrace(); } } private void startSplashAnimationStart() { try { if (mActivity.get() == null || mSplashAd == null || mSplashContainer == null) { return; } CSJSplashClickEyeManager splashClickEyeManager = CSJSplashClickEyeManager.getInstance(); ViewGroup content = mActivity.get().findViewById(android.R.id.content); splashClickEyeManager.startSplashClickEyeAnimation(mSplashContainer, content, content, new CSJSplashClickEyeManager.AnimationCallBack() { @Override public void animationStart(int animationTime) { } @Override public void animationEnd() { if (mSplashAd != null) { mSplashAd.splashClickEyeAnimationFinish(); } } }); } catch (Throwable e) { e.printStackTrace(); } } } } ================================================ FILE: easycsj/src/main/java/com/easyads/supplier/csj/CsjUtil.java ================================================ package com.easyads.supplier.csj; import android.app.Activity; import android.content.Context; import android.os.Looper; import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; import com.bytedance.sdk.openadsdk.CSJSplashAd; import com.easyads.EasyAdsManger; import com.easyads.core.EABaseSupplierAdapter; import com.easyads.itf.BaseEnsureListener; import com.easyads.model.EasyAdError; import com.easyads.utils.EASplashPlusManager; import com.easyads.utils.EAUtil; import com.easyads.utils.EALog; import com.bytedance.sdk.openadsdk.ISplashClickEyeListener; import com.bytedance.sdk.openadsdk.TTAdConfig; import com.bytedance.sdk.openadsdk.TTAdConstant; import com.bytedance.sdk.openadsdk.TTAdManager; import com.bytedance.sdk.openadsdk.TTAdSdk; import com.bytedance.sdk.openadsdk.TTSplashAd; import java.lang.ref.SoftReference; import java.util.Arrays; import java.util.Map; public class CsjUtil implements EASplashPlusManager.ZoomCall { /** * 统一处理权限申请设置 * * @param adapter 渠道基础适配器 * @return 穿山甲广告manger */ public static TTAdManager getADManger(EABaseSupplierAdapter adapter) { TTAdManager ttAdManager = null; try { ttAdManager = TTAdSdk.getAdManager(); if (EasyCsjManger.getInstance().csj_askPermission) { ttAdManager.requestPermissionIfNecessary(adapter.getActivity()); } } catch (Throwable e) { e.printStackTrace(); } return ttAdManager; } /** * 穿山甲3450以后版本初始化方法,支持异步初始化以及对应回调方法 * * @param adapter 渠道基础适配器 * @param listener 初始化回调 */ public static void initCsj(final EABaseSupplierAdapter adapter, final InitListener listener) { try { if (adapter == null) { String msg = "[initCsj] initAD failed BaseSupplierAdapter null"; if (listener != null) { listener.fail(EasyAdError.ERROR_DATA_NULL, msg); } EALog.e(msg); return; } boolean hasInit = EasyAdsManger.getInstance().hasCSJInit; String resultAppID = adapter.getAppID(); EALog.high("[CsjUtil.initCsj] 穿山甲 appID:" + resultAppID); if (TextUtils.isEmpty(resultAppID)) { String msg = "[initCsj] initAD failed AppID null"; if (listener != null) { listener.fail(EasyAdError.ERROR_DATA_NULL, msg); } EALog.e(msg); return; } String lastAppId = EasyAdsManger.getInstance().lastCSJAID; boolean isSame = lastAppId.equals(resultAppID); if (hasInit && adapter.canOptInit() && isSame) { if (listener != null) { listener.success(); } return; } EALog.simple("[CsjUtil] 开始初始化SDK"); boolean supportMP = EasyCsjManger.getInstance().csj_supportMultiProcess; int[] directDownloadNetworkType = EasyCsjManger.getInstance().csj_directDownloadNetworkType; //如果未设置下载状态集合,默认4g和wifi下可以下载。 if (directDownloadNetworkType == null || directDownloadNetworkType.length == 0) { directDownloadNetworkType = new int[]{TTAdConstant.NETWORK_STATE_4G, TTAdConstant.NETWORK_STATE_WIFI}; } boolean isMainThread = Looper.myLooper() == Looper.getMainLooper(); if (!isMainThread) { EALog.high("[CsjUtil.initCsj]需要在主线程中调用穿山甲sdk 初始化方法"); } else { EALog.high("[CsjUtil.initCsj]当前在主线程中调用穿山甲sdk 初始化方法"); } EALog.high("[CsjUtil.initCsj] supportMultiProcess = " + supportMP + " directDownloadNetworkType = " + Arrays.toString(directDownloadNetworkType)); final TTAdConfig config = new TTAdConfig.Builder() .appId(resultAppID) .useTextureView(true) //使用TextureView控件播放视频,默认为SurfaceView,当有SurfaceView冲突的场景,可以使用TextureView .appName("")// .titleBarTheme(TTAdConstant.TITLE_BAR_THEME_LIGHT) .allowShowNotify(true) //是否允许sdk展示通知栏提示 // .allowShowPageWhenScreenLock(true) //是否在锁屏场景支持展示广告落地页 .debug(EasyAdsManger.getInstance().debug) //测试阶段打开,可以通过日志排查问题,上线时去除该调用 .directDownloadNetworkType(directDownloadNetworkType) //允许直接下载的网络状态集合 .supportMultiProcess(supportMP) //是否支持多进程,true支持 // .asyncInit(true) //如果是主线程使用异步 .build(); //主线程和非主线程逻辑分开 final String finalResultAppID = resultAppID; EAUtil.switchMainThread(new BaseEnsureListener() { @Override public void ensure() { doInit(adapter.getActivity(), config, listener, finalResultAppID); } }); } catch (Throwable e) { String msg = "穿山甲sdk 初始化异常"; EALog.e(msg); e.printStackTrace(); if (listener != null) { listener.fail(EasyAdError.ERROR_CSJ_INIT_FAILED, msg); } } } private static void doInit(Context context, TTAdConfig config, final InitListener listener, final String appID) { try { TTAdSdk.init(context.getApplicationContext(), config // , new TTAdSdk.InitCallback() { // @Override // public void success() { // EALog.simple("csj init success"); // // EAUtil.switchMainThread(new BaseEnsureListener() { // @Override // public void ensure() { // if (listener != null) { // listener.success(); // } // } // }); // EasyAdsManger.getInstance().lastCSJAID = appID; // EasyAdsManger.getInstance().hasCSJInit = true; // } // // @Override // public void fail(int code, String msg) { // EALog.e("csj init fail : code = " + code + " msg = " + msg); // if (listener != null) { // listener.fail(EasyAdError.ERROR_CSJ_INIT_FAILED, msg); // } // EasyAdsManger.getInstance().hasCSJInit = false; // } // } ); TTAdSdk.start(new TTAdSdk.Callback() { @Override public void success() { EALog.simple("csj init success"); if (listener != null) { listener.success(); } EasyAdsManger.getInstance().lastCSJAID = appID; EasyAdsManger.getInstance().hasCSJInit = true; } @Override public void fail(int code, String msg) { EALog.e("csj init fail : code = " + code + " msg = " + msg); if (listener != null) { listener.fail(code+"", msg); } EasyAdsManger.getInstance().hasCSJInit = false; } }); } catch (Throwable e) { e.printStackTrace(); if (listener != null) { listener.fail(EasyAdError.ERROR_CSJ_INIT_FAILED, "csj init exception"); } } } public static void getCPMInfNew(String TAG, CSJSplashAd newSplashAd) { try { Map extraInfo = newSplashAd.getMediaExtraInfo(); //设置cpm double cpm = 0; if (extraInfo != null) { Object price = extraInfo.get("price"); if (price != null) { if (price instanceof Double) { cpm = (double) price; } } } EALog.devDebug(TAG + " cpm = " + cpm); } catch (Throwable e) { e.printStackTrace(); } } /** * 以下处理点睛广告,到达首页后的展示逻辑 */ @Override public void zoomOut(Activity activity) { EALog.simple("CsjUtil start zoomOut"); CSJSplashClickEyeManager splashClickEyeManager = CSJSplashClickEyeManager.getInstance(); boolean isSupportSplashClickEye = splashClickEyeManager.isSupportSplashClickEye(); if (!isSupportSplashClickEye) { EALog.simple("notSupportSplashClickEye"); return; } View splashClickEyeView = addSplashClickEyeView(activity); if (splashClickEyeView != null) { activity.overridePendingTransition(0, 0); } TTSplashAd splashAd = splashClickEyeManager.getSplashAd(); HomeSplashClickEyeListener splashClickEyeListener = new HomeSplashClickEyeListener(splashClickEyeView, splashAd); if (splashAd != null) { splashAd.setSplashClickEyeListener(splashClickEyeListener); } //根据设定延迟自动关闭小窗口 EAUtil.autoClose(splashClickEyeView); } private View addSplashClickEyeView(Activity activity) { final CSJSplashClickEyeManager splashClickEyeManager = CSJSplashClickEyeManager.getInstance(); final TTSplashAd splashAd = splashClickEyeManager.getSplashAd(); return splashClickEyeManager.startSplashClickEyeAnimationInTwoActivity((ViewGroup) activity.getWindow().getDecorView(), (ViewGroup) activity.findViewById(android.R.id.content), new CSJSplashClickEyeManager.AnimationCallBack() { @Override public void animationStart(int animationTime) { } @Override public void animationEnd() { splashAd.splashClickEyeAnimationFinish(); } }); } static class HomeSplashClickEyeListener implements ISplashClickEyeListener { private SoftReference mSplashView; private SoftReference mSplashAd; public HomeSplashClickEyeListener(View splashView, TTSplashAd splashAd) { mSplashView = new SoftReference<>(splashView); mSplashAd = new SoftReference<>(splashAd); } @Override public void onSplashClickEyeAnimationStart() { } @Override public void onSplashClickEyeAnimationFinish() { //小窗展示五秒后会自动回调此方法,导致页面自动关闭。手动点击窗口上的关闭按钮亦会回调此方法。 //接收点击关闭按钮的事件将开屏点睛移除。 EALog.high("[HomeSplashClickEyeListener] onSplashClickEyeAnimationFinish ; close mSplashView"); if (mSplashView != null && mSplashView.get() != null) { mSplashView.get().setVisibility(View.GONE); EAUtil.removeFromParent(mSplashView.get()); mSplashView = null; mSplashAd = null; } CSJSplashClickEyeManager.getInstance().clearSplashStaticData(); } @Override public boolean isSupportSplashClickEye(boolean isSupport) { return isSupport; } } interface InitListener { void success(); void fail(String code, String msg); } } ================================================ FILE: easycsj/src/main/java/com/easyads/supplier/csj/EasyCsjManger.java ================================================ package com.easyads.supplier.csj; /** * 穿山甲全局属性配置管理类 */ public class EasyCsjManger { private static EasyCsjManger instance; private EasyCsjManger() { } public static synchronized EasyCsjManger getInstance() { if (instance == null) { instance = new EasyCsjManger(); } return instance; } //用于控制开屏广告的点击区域,具体设置值及含义可参考: https://www.pangle.cn/support/doc/611f0f0c1b039f004611e4da public int csj_splashButtonType = -1; //用于控制下载APP前是否弹出二次确认弹窗(适用所有广告类型)。具体设置值及含义可参考: https://www.pangle.cn/support/doc/611f0f0c1b039f004611e4da public int csj_downloadType = -1; //是否支持多进程 public boolean csj_supportMultiProcess = true; //下载类广告允许直接下载的网络状态,如果未设置下载状态集合,默认4g和wifi下可以下载。 public int[] csj_directDownloadNetworkType; //是否允许穿山甲SDK,在必要时进行权限询问,默认不允许 public boolean csj_askPermission = false; } ================================================ FILE: easycsj/src/main/res/values/strings.xml ================================================ ================================================ FILE: easycsj/src/main/res/xml/ea_csj_file_paths.xml ================================================ ================================================ FILE: easycsj/src/test/java/com/easyads/supplier/csj/ExampleUnitTest.java ================================================ package com.easyads.supplier.csj; import org.junit.Test; import static org.junit.Assert.*; /** * Example local unit test, which will execute on the development machine (host). * * @see Testing documentation */ public class ExampleUnitTest { @Test public void addition_isCorrect() { assertEquals(4, 2 + 2); } } ================================================ FILE: easyks/.gitignore ================================================ /build ================================================ FILE: easyks/build.gradle ================================================ apply plugin: 'com.android.library' apply plugin: 'com.kezong.fat-aar' // JitPack Maven apply plugin: 'com.github.dcendents.android-maven' // Your Group group = 'com.github.bayescom' //maven依赖的项目名称 def pomName = "easyads-ks" def VersionCode = 3363 def VersionName = "${rootProject.ext.EasyAds_version}_" + VersionCode def outputName = pomName + "_" + VersionName + ".aar" android { compileSdkVersion 29 buildToolsVersion "29.0.3" defaultConfig { minSdkVersion 16 targetSdkVersion 29 versionCode VersionCode versionName VersionName consumerProguardFiles 'consumer-rules.pro' } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } lintOptions { abortOnError false } compileOptions { sourceCompatibility JavaVersion.VERSION_1_7 targetCompatibility JavaVersion.VERSION_1_7 } println pomName + " VersionName = " + VersionName } repositories { flatDir { dirs 'libs' } } configurations.embed.transitive = true dependencies { api 'com.android.support:appcompat-v7:28.0.0' api "com.android.support:recyclerview-v7:28.0.0" testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' def aarName = "kssdk-ad-" + VersionCode //发布时用embed捆绑aar包 if (rootProject.ext.releaseSDK) { embed(name: aarName, ext: 'aar') } else { api fileTree(dir: 'libs', include: ['*.jar', '*.aar']) } compileOnly project(path: ':easyads') } //android.libraryVariants.all { variant -> // variant.outputs.all { // outputFileName = outputName // } //} ================================================ FILE: easyks/consumer-rules.pro ================================================ ================================================ FILE: easyks/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: easyks/src/androidTest/java/com/easyads/supplier/ks/ExampleInstrumentedTest.java ================================================ package com.easyads.supplier.ks; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumented test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() { // Context of the app under test. Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); assertEquals("com.easyads.supplier.ks.test", appContext.getPackageName()); } } ================================================ FILE: easyks/src/main/AndroidManifest.xml ================================================ ================================================ FILE: easyks/src/main/java/com/easyads/supplier/ks/EasyKSManager.java ================================================ package com.easyads.supplier.ks; import com.kwad.sdk.api.KsVideoPlayConfig; /** * 快手SDK特殊配置管理 */ public class EasyKSManager { private static EasyKSManager instance; private EasyKSManager() { } public static synchronized EasyKSManager getInstance() { if (instance == null) { instance = new EasyKSManager(); } return instance; } private KsVideoPlayConfig defaultConfig = new KsVideoPlayConfig.Builder() .videoSoundEnable(true) .showLandscape(false) .build(); //激励视频播放配置 public KsVideoPlayConfig rewardVideoConfig = defaultConfig; //全屏视频播放配置 public KsVideoPlayConfig fullScreenVideoConfig = defaultConfig; //插屏视频播放配置 public KsVideoPlayConfig interstitialVideoConfig = defaultConfig; } ================================================ FILE: easyks/src/main/java/com/easyads/supplier/ks/KSDrawAdapter.java ================================================ package com.easyads.supplier.ks; import android.app.Activity; import android.support.annotation.Nullable; import android.view.View; import com.easyads.core.draw.EADrawSetting; import com.easyads.custom.EADrawCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.kwad.sdk.api.KsAdSDK; import com.kwad.sdk.api.KsDrawAd; import com.kwad.sdk.api.KsLoadManager; import com.kwad.sdk.api.KsScene; import java.lang.ref.SoftReference; import java.util.List; public class KSDrawAdapter extends EADrawCustomAdapter implements KsDrawAd.AdInteractionListener { private KsDrawAd drawAD; public KSDrawAdapter(SoftReference activity, EADrawSetting setting) { super(activity, setting); } @Override protected void doLoadAD() { KSUtil.initAD(this, new KSUtil.InitListener() { @Override public void success() { //只有在成功初始化以后才能调用load方法,否则穿山甲会抛错导致无法进行广告展示 startLoad(); } @Override public void fail(String code, String msg) { handleFailed(code, msg); } }); } private void startLoad() { //场景设置 KsScene scene = new KsScene.Builder(KSUtil.getADID(sdkSupplier)).build(); KsAdSDK.getLoadManager().loadDrawAd(scene, new KsLoadManager.DrawAdListener() { @Override public void onError(int code, String msg) { EALog.high(TAG + " onError " + code + msg); handleFailed(code, msg); } @Override public void onDrawAdLoad(@Nullable List list) { EALog.high(TAG + "onDrawAdLoad"); try { if (list == null || list.size() == 0 || list.get(0) == null) { handleFailed(EasyAdError.ERROR_DATA_NULL, ""); } else { drawAD = list.get(0); //回调监听 if (drawAD != null) { drawAD.setAdInteractionListener(KSDrawAdapter.this); } handleSucceed(); } } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.ERROR_EXCEPTION_LOAD, ""); } } }); } @Override public void doDestroy() { } @Override protected void doShowAD() { View drawVideoView = drawAD.getDrawView(getActivity()); if (addADView(drawVideoView)) { } } /** * ks回调事件 */ @Override public void onAdClicked() { EALog.high(TAG + " onAdClicked"); handleClick(); } @Override public void onAdShow() { EALog.high(TAG + " onAdShow"); handleExposure(); } @Override public void onVideoPlayStart() { EALog.high(TAG + " onVideoPlayStart"); } @Override public void onVideoPlayPause() { EALog.high(TAG + " onVideoPlayPause"); } @Override public void onVideoPlayResume() { EALog.high(TAG + " onVideoPlayResume"); } @Override public void onVideoPlayEnd() { EALog.high(TAG + " onVideoPlayEnd"); } @Override public void onVideoPlayError() { EALog.high(TAG + " onVideoPlayError"); } } ================================================ FILE: easyks/src/main/java/com/easyads/supplier/ks/KSFullScreenVideoAdapter.java ================================================ package com.easyads.supplier.ks; import android.app.Activity; import android.support.annotation.Nullable; import com.easyads.core.full.EAFullScreenVideoSetting; import com.easyads.custom.EAFullScreenCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.kwad.sdk.api.KsAdSDK; import com.kwad.sdk.api.KsFullScreenVideoAd; import com.kwad.sdk.api.KsLoadManager; import com.kwad.sdk.api.KsScene; import java.lang.ref.SoftReference; import java.util.List; public class KSFullScreenVideoAdapter extends EAFullScreenCustomAdapter implements KsFullScreenVideoAd.FullScreenVideoAdInteractionListener { public EAFullScreenVideoSetting setting; private List list; KsFullScreenVideoAd ad; public KSFullScreenVideoAdapter(SoftReference activity, EAFullScreenVideoSetting baseSetting) { super(activity, baseSetting); setting = baseSetting; } @Override protected void doLoadAD() { KSUtil.initAD(this, new KSUtil.InitListener() { @Override public void success() { //只有在成功初始化以后才能调用load方法,否则穿山甲会抛错导致无法进行广告展示 startLoad(); } @Override public void fail(String code, String msg) { handleFailed(code, msg); } }); } private void startLoad() { KsScene scene = new KsScene.Builder(KSUtil.getADID(sdkSupplier)).build(); // 此为测试posId,请联系快手平台申请正式posId KsAdSDK.getLoadManager().loadFullScreenVideoAd(scene, new KsLoadManager.FullScreenVideoAdListener() { @Override public void onError(int code, String msg) { EALog.high(TAG + " onError " + code + msg); handleFailed(code, msg); } @Override public void onFullScreenVideoResult(@Nullable List list) { EALog.high(TAG + "onFullScreenVideoResult "); } @Override public void onFullScreenVideoAdLoad(@Nullable List adList) { EALog.high(TAG + " onFullScreenVideoAdLoad"); try { list = adList; if (list == null || list.size() == 0 || list.get(0) == null) { handleFailed(EasyAdError.ERROR_DATA_NULL, ""); } else { ad = list.get(0); //回调监听 if (ad != null && ad.isAdEnable()) { ad.setFullScreenVideoAdInteractionListener(KSFullScreenVideoAdapter.this); } handleSucceed(); } } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.ERROR_EXCEPTION_LOAD, ""); } } }); } @Override protected void doShowAD() { ad.showFullScreenVideoAd(getActivity(), EasyKSManager.getInstance().fullScreenVideoConfig); } @Override public void doDestroy() { } //--------广告回调-------- @Override public void onAdClicked() { EALog.high(TAG + " onAdClicked"); handleClick(); } @Override public void onPageDismiss() { EALog.high(TAG + " onPageDismiss"); if (setting != null) { setting.adapterClose(sdkSupplier); } } @Override public void onVideoPlayError(int code, int extra) { String msg = " onVideoPlayError,code = " + code + ",extra = " + extra; EALog.high(TAG + msg); handleFailed(EasyAdError.parseErr(EasyAdError.ERROR_EXCEPTION_RENDER, msg)); } @Override public void onVideoPlayEnd() { EALog.high(TAG + " onVideoPlayEnd"); if (setting != null) { setting.adapterVideoComplete(sdkSupplier); } } @Override public void onVideoPlayStart() { EALog.high(TAG + " onVideoPlayStart"); handleExposure(); } @Override public void onSkippedVideo() { EALog.high(TAG + " onSkippedVideo"); if (setting != null) { setting.adapterVideoSkipped(sdkSupplier); } } } ================================================ FILE: easyks/src/main/java/com/easyads/supplier/ks/KSInterstitialAdapter.java ================================================ package com.easyads.supplier.ks; import android.app.Activity; import android.support.annotation.Nullable; import com.easyads.core.inter.EAInterstitialSetting; import com.easyads.custom.EAInterstitialCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.kwad.sdk.api.KsAdSDK; import com.kwad.sdk.api.KsInterstitialAd; import com.kwad.sdk.api.KsLoadManager; import com.kwad.sdk.api.KsScene; import java.lang.ref.SoftReference; import java.util.List; public class KSInterstitialAdapter extends EAInterstitialCustomAdapter implements KsInterstitialAd.AdInteractionListener { private EAInterstitialSetting setting; KsInterstitialAd interstitialAD; List list; public KSInterstitialAdapter(SoftReference activity, EAInterstitialSetting baseSetting) { super(activity, baseSetting); this.setting = baseSetting; } @Override protected void doShowAD() { interstitialAD.showInterstitialAd(getActivity(), EasyKSManager.getInstance().interstitialVideoConfig); } @Override protected void doLoadAD() { //初始化快手SDK KSUtil.initAD(this, new KSUtil.InitListener() { @Override public void success() { //只有在成功初始化以后才能调用load方法,否则穿山甲会抛错导致无法进行广告展示 startLoad(); } @Override public void fail(String code, String msg) { handleFailed(code, msg); } }); } private void startLoad() { //场景设置 KsScene scene = new KsScene.Builder(KSUtil.getADID(sdkSupplier)).build(); KsAdSDK.getLoadManager().loadInterstitialAd(scene, new KsLoadManager.InterstitialAdListener() { @Override public void onError(int code, String msg) { EALog.high(TAG + " onError " + code + msg); handleFailed(code, msg); } @Override public void onRequestResult(int adNumber) { EALog.high(TAG + "onRequestResult,广告填充数量:" + adNumber); } @Override public void onInterstitialAdLoad(@Nullable List adList) { EALog.high(TAG + "onInterstitialAdLoad"); try { list = adList; if (list == null || list.size() == 0 || list.get(0) == null) { handleFailed(EasyAdError.ERROR_DATA_NULL, ""); } else { interstitialAD = list.get(0); //回调监听 if (interstitialAD != null) { interstitialAD.setAdInteractionListener(KSInterstitialAdapter.this); } handleSucceed(); } } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.ERROR_EXCEPTION_LOAD, ""); } } }); } @Override public void doDestroy() { } /** * 广告事件回调 */ @Override public void onAdClicked() { EALog.high(TAG + " onAdClicked"); handleClick(); } @Override public void onAdShow() { EALog.high(TAG + " onAdShow"); handleExposure(); } @Override public void onAdClosed() { EALog.high(TAG + " onAdClosed"); if (setting != null) { setting.adapterDidClosed(sdkSupplier); } } @Override public void onPageDismiss() { EALog.high(TAG + " onPageDismiss"); if (setting != null) { setting.adapterDidClosed(sdkSupplier); } } @Override public void onVideoPlayError(int code, int extra) { EALog.e(TAG + " onVideoPlayError,code = " + code + ",extra = " + extra); try { if (setting != null) { EasyAdError error = EasyAdError.parseErr(EasyAdError.ERROR_EXCEPTION_RENDER, "onVideoPlayError"); setting.adapterDidFailed(error, sdkSupplier); } } catch (Throwable e) { e.printStackTrace(); } } @Override public void onVideoPlayEnd() { EALog.high(TAG + " onVideoPlayEnd"); } @Override public void onVideoPlayStart() { EALog.high(TAG + " onVideoPlayStart"); } @Override public void onSkippedAd() { EALog.high(TAG + " onSkippedAd"); if (setting != null) { setting.adapterDidClosed(sdkSupplier); } } } ================================================ FILE: easyks/src/main/java/com/easyads/supplier/ks/KSNativeExpressAdapter.java ================================================ package com.easyads.supplier.ks; import android.app.Activity; import android.support.annotation.Nullable; import android.view.View; import com.easyads.core.nati.EANativeExpressSetting; import com.easyads.custom.EANativeExpressCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.kwad.sdk.api.KsAdSDK; import com.kwad.sdk.api.KsFeedAd; import com.kwad.sdk.api.KsLoadManager; import com.kwad.sdk.api.KsScene; import java.lang.ref.SoftReference; import java.util.List; public class KSNativeExpressAdapter extends EANativeExpressCustomAdapter { public EANativeExpressSetting setting; KsFeedAd ad; public KSNativeExpressAdapter(SoftReference activity, EANativeExpressSetting baseSetting) { super(activity, baseSetting); setting = baseSetting; } @Override protected void doLoadAD() { KSUtil.initAD(this, new KSUtil.InitListener() { @Override public void success() { //只有在成功初始化以后才能调用load方法,否则穿山甲会抛错导致无法进行广告展示 startLoad(); } @Override public void fail(String code, String msg) { handleFailed(code, msg); } }); } private void startLoad() { int num = 1; KsScene scene = new KsScene.Builder(KSUtil.getADID(sdkSupplier)).adNum(num).build(); // 此为测试posId,请联系快手平台申请正式posId KsAdSDK.getLoadManager().loadConfigFeedAd(scene, new KsLoadManager.FeedAdListener() { @Override public void onError(int code, String msg) { EALog.high(TAG + " onError ," + code + msg); handleFailed(code, msg); } @Override public void onFeedAdLoad(@Nullable List list) { EALog.high(TAG + " onFeedAdLoad"); try { if (list == null || list.size() == 0 || list.get(0) == null) { handleFailed(EasyAdError.ERROR_DATA_NULL, ""); } else { ad = list.get(0); setNEView(ad.getFeedView(getActivity())); ad.setAdInteractionListener(new KsFeedAd.AdInteractionListener() { @Override public void onAdClicked() { EALog.high(TAG + " onAdClicked "); handleClick(); } @Override public void onAdShow() { EALog.high(TAG + " onAdShow "); handleExposure(); } @Override public void onDislikeClicked() { EALog.high(TAG + " onDislikeClicked "); if (setting != null) { setting.adapterDidClosed(sdkSupplier); } removeADView(); } @Override public void onDownloadTipsDialogShow() { EALog.high(TAG + " onDownloadTipsDialogShow "); } @Override public void onDownloadTipsDialogDismiss() { EALog.high(TAG + " onDownloadTipsDialogDismiss "); } }); handleSucceed(); } } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.ERROR_EXCEPTION_LOAD, ""); } } }); } @Override public void doDestroy() { } @Override protected void doShowAD() { View adv = ad.getFeedView(getActivity()); addADView(adv); } } ================================================ FILE: easyks/src/main/java/com/easyads/supplier/ks/KSRewardVideoAdapter.java ================================================ package com.easyads.supplier.ks; import android.app.Activity; import android.support.annotation.Nullable; import com.easyads.core.reward.EARewardVideoSetting; import com.easyads.custom.EARewardCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.kwad.sdk.api.KsAdSDK; import com.kwad.sdk.api.KsLoadManager; import com.kwad.sdk.api.KsRewardVideoAd; import com.kwad.sdk.api.KsScene; import com.kwad.sdk.api.model.KsExtraRewardType; import java.lang.ref.SoftReference; import java.util.List; public class KSRewardVideoAdapter extends EARewardCustomAdapter implements KsRewardVideoAd.RewardAdInteractionListener { public EARewardVideoSetting setting; KsRewardVideoAd ad; public KSRewardVideoAdapter(SoftReference activity, EARewardVideoSetting baseSetting) { super(activity, baseSetting); setting = baseSetting; } @Override protected void doLoadAD() { KSUtil.initAD(this, new KSUtil.InitListener() { @Override public void success() { //只有在成功初始化以后才能调用load方法,否则穿山甲会抛错导致无法进行广告展示 startLoad(); } @Override public void fail(String code, String msg) { handleFailed(code, msg); } }); } private void startLoad() { KsScene scene = new KsScene.Builder(KSUtil.getADID(sdkSupplier)).build(); // 此为测试posId,请联系快手平台申请正式posId KsAdSDK.getLoadManager().loadRewardVideoAd(scene, new KsLoadManager.RewardVideoAdListener() { @Override public void onError(int code, String msg) { EALog.high(TAG + " onError "); handleFailed(code, msg); } @Override public void onRewardVideoResult(@Nullable List list) { EALog.high(TAG + "onRewardVideoResult "); } // // @Override // public void onRequestResult(int adNumber) { // EALog.high(TAG + "onRequestResult,广告填充数量:" + adNumber); // } @Override public void onRewardVideoAdLoad(@Nullable List list) { EALog.high(TAG + " onRewardVideoAdLoad"); try { if (list == null || list.size() == 0 || list.get(0) == null) { handleFailed(EasyAdError.ERROR_DATA_NULL, ""); } else { ad = list.get(0); if (ad != null) { ad.setRewardAdInteractionListener(KSRewardVideoAdapter.this); } handleSucceed(); } } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.ERROR_EXCEPTION_LOAD, ""); } } }); } @Override public void doDestroy() { } //--------广告回调-------- @Override public void onAdClicked() { EALog.high(TAG + " onAdClicked"); handleClick(); } @Override public void onPageDismiss() { EALog.high(TAG + " onPageDismiss"); if (setting != null) { setting.adapterDidClosed(sdkSupplier); } } @Override public void onVideoPlayError(int code, int extra) { String msg = " onVideoPlayError,code = " + code + ",extra = " + extra; EALog.high(TAG + msg); handleFailed(EasyAdError.ERROR_EXCEPTION_RENDER, msg); } @Override public void onVideoPlayEnd() { EALog.high(TAG + " onVideoPlayEnd"); if (setting != null) { setting.adapterVideoComplete(sdkSupplier); } } @Override public void onVideoSkipToEnd(long l) { EALog.high(TAG + " onVideoSkipToEnd,l=" + l); } @Override public void onVideoPlayStart() { EALog.high(TAG + " onVideoPlayStart"); handleExposure(); } @Override public void onRewardVerify() { EALog.high(TAG + " onRewardVerify"); if (setting != null) { setting.adapterAdReward(sdkSupplier); } } /** * 视频激励分阶段回调(激励广告新玩法,相关政策请联系商务或技术支持) * * @param taskType 当前激励视频所属任务类型 * RewardTaskType.LOOK_VIDEO 观看视频类型 属于浅度奖励类型 * RewardTaskType.LOOK_LANDING_PAGE 浏览落地⻚N秒类型 属于深度奖励类型 * RewardTaskType.USE_APP 下载使用App N秒类型 属于深度奖励类型 * @param currentTaskStatus 当前所完成任务类型,@RewardTaskType中之一 */ @Override public void onRewardStepVerify(int taskType, int currentTaskStatus) { EALog.high(TAG + " onRewardStepVerify , taskType :" + taskType + ",currentTaskStatus = " + currentTaskStatus); } @Override public void onExtraRewardVerify(@KsExtraRewardType int extraRewardType) { EALog.high(TAG + " onExtraRewardVerify , extraRewardType :" + extraRewardType); } //--------广告回调 结束-------- @Override protected void doShowAD() { ad.showRewardVideoAd(getActivity(), EasyKSManager.getInstance().rewardVideoConfig); } } ================================================ FILE: easyks/src/main/java/com/easyads/supplier/ks/KSSplashAdapter.java ================================================ package com.easyads.supplier.ks; import android.app.Activity; import android.support.annotation.NonNull; import android.view.View; import android.view.ViewGroup; import com.easyads.core.splash.EASplashSetting; import com.easyads.custom.EASplashCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EAUtil; import com.easyads.utils.EALog; import com.kwad.sdk.api.KsAdSDK; import com.kwad.sdk.api.KsLoadManager; import com.kwad.sdk.api.KsScene; import com.kwad.sdk.api.KsSplashScreenAd; import java.lang.ref.SoftReference; public class KSSplashAdapter extends EASplashCustomAdapter implements KsSplashScreenAd.SplashScreenAdInteractionListener { private KsSplashScreenAd splashAd; public KSSplashAdapter(SoftReference softReferenceActivity, EASplashSetting baseSetting) { super(softReferenceActivity, baseSetting); } @Override protected void doLoadAD() { KSUtil.initAD(this, new KSUtil.InitListener() { @Override public void success() { //只有在成功初始化以后才能调用load方法,否则穿山甲会抛错导致无法进行广告展示 startLoad(); } @Override public void fail(String code, String msg) { handleFailed(code, msg); } }); } private void startLoad() { //场景设置 KsScene scene = new KsScene.Builder(KSUtil.getADID(sdkSupplier)).build(); // 此为测试posId,请联系快手平台申请正式posId KsAdSDK.getLoadManager().loadSplashScreenAd(scene, new KsLoadManager.SplashScreenAdListener() { @Override public void onError(int code, String msg) { EALog.high(TAG + " onError "); handleFailed(code, msg); } @Override public void onRequestResult(int adNumber) { EALog.high(TAG + "onRequestResult,广告填充数量:" + adNumber); } @Override public void onSplashScreenAdLoad(@NonNull KsSplashScreenAd splashScreenAd) { EALog.high(TAG + "onSplashScreenAdLoad"); try { if (splashScreenAd == null) { String nMsg = TAG + " KsSplashScreenAd null"; handleFailed(EasyAdError.ERROR_DATA_NULL, nMsg); return; } splashAd = splashScreenAd; handleSucceed(); } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.parseErr(EasyAdError.ERROR_EXCEPTION_LOAD)); } } }); } @Override protected void doShowAD() { if (splashAd != null) { //获取SplashView View view = splashAd.getView(getActivity(), this); //渲染之前判断activity生命周期状态 if (!EAUtil.isActivityDestroyed(softReferenceActivity)) { adContainer.removeAllViews(); view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); //把SplashView 添加到ViewGroup中,注意开屏广告view:width >=70%屏幕宽;height >=50%屏幕宽 adContainer.addView(view); } } } @Override public void doDestroy() { } //------广告回调事件------ @Override public void onAdClicked() { EALog.high(TAG + "onAdClicked"); handleClick(); } @Override public void onAdShowError(int code, String extra) { String msg = ",开屏广告显示错误 ,code =" + code + " extra " + extra; EALog.high(TAG + "onAdShowError" + msg); //异常时不触发显示miniWindow splashAd = null; //按照渲染异常进行异常回调 handleFailed(EasyAdError.ERROR_EXCEPTION_RENDER, msg); } @Override public void onAdShowEnd() { EALog.high(TAG + "onAdShowEnd"); if (mSplashSetting != null) { mSplashSetting.adapterDidTimeOver(sdkSupplier); } } @Override public void onAdShowStart() { EALog.high(TAG + "onAdShowStart"); handleExposure(); } @Override public void onSkippedAd() { EALog.high(TAG + "onSkippedAd"); if (mSplashSetting != null) { mSplashSetting.adapterDidSkip(sdkSupplier); } } @Override public void onDownloadTipsDialogShow() { EALog.high(TAG + "onDownloadTipsDialogShow"); } @Override public void onDownloadTipsDialogDismiss() { EALog.high(TAG + "onDownloadTipsDialogDismiss"); } @Override public void onDownloadTipsDialogCancel() { EALog.high(TAG + "onDownloadTipsDialogCancel"); } } ================================================ FILE: easyks/src/main/java/com/easyads/supplier/ks/KSUtil.java ================================================ package com.easyads.supplier.ks; import android.app.Activity; import android.content.Context; import android.text.TextUtils; import com.easyads.EasyAdsManger; import com.easyads.core.EABaseSupplierAdapter; import com.easyads.model.EasyAdError; import com.easyads.model.SdkSupplier; import com.easyads.utils.EASplashPlusManager; import com.easyads.utils.EAUtil; import com.easyads.utils.EALog; import com.kwad.sdk.api.KsAdSDK; import com.kwad.sdk.api.KsInitCallback; import com.kwad.sdk.api.SdkConfig; public class KSUtil implements EASplashPlusManager.ZoomCall { public static synchronized void initAD(EABaseSupplierAdapter adapter, final InitListener initResult) { // public static boolean initAD(EABaseSupplierAdapter adapter) { // boolean hasInit = doInitAD(adapter); // try { // //如果未初始化成功,需要进行异常回调,否则无法进入下一步流程 // if (!hasInit) { // EasyAdError error = EasyAdError.parseErr(EasyAdError.ERROR_KS_INIT); // adapter.handleFailed(error); // } // } catch (Throwable e) { // e.printStackTrace(); // } // return hasInit; // } // // // public static boolean doInitAD(EABaseSupplierAdapter adapter) { // boolean initSuccess = false; try { if (adapter == null) { String eMsg = "initAD failed BaseSupplierAdapter null"; EALog.e(eMsg); if (initResult != null) { initResult.fail(EasyAdError.ERROR_KS_INIT, eMsg); } return; } String resultAppID = adapter.getAppID(); if (TextUtils.isEmpty(resultAppID)) { String msg = "[initCsj] initAD failed AppID null"; // adapter.handleFailed(EasyAdError.ERROR_DATA_NULL, msg); EALog.e(msg); if (initResult != null) { initResult.fail(EasyAdError.ERROR_KS_INIT, msg); } return ; } boolean hasInit = EasyAdsManger.getInstance().hasKSInit; String lastAppId = EasyAdsManger.getInstance().lastKSAID; boolean isSame = lastAppId.equals(resultAppID); //只有当允许初始化优化时,且快手已经初始化成功过,并且初始化的id和当前id一致,才可以不再重复初始化。 if (hasInit && isSame && adapter.canOptInit()) { EALog.simple("[KSUtil.initAD] already init"); if (initResult != null) { initResult.success(); } return ; } EALog.simple("[KSUtil] 开始初始化SDK"); Context context = adapter.getActivity(); String currentProcessName = EAUtil.getCurrentProcessName(context); if (currentProcessName.equals(context.getPackageName())) { EALog.high("[KSUtil.doInitAD] init appId = " + resultAppID); SdkConfig.Builder builder = new SdkConfig.Builder(); builder.appId(resultAppID)// aapId,请联系快手平台申请正式AppId,必填 .showNotification(true) // 是否展示下载通知栏 .debug(EasyAdsManger.getInstance().debug); // builder.appName(appName);// appName,请填写您应用的名称,非必填 // builder.appKey(appKey);// 直播sdk安全验证,接入直播模块必填 // builder.appWebKey(appWebKey);// 直播sdk安全验证,接入直播模块必填 // builder.setInitCallback(new KsInitCallback() { @Override public void onSuccess() { EALog.simple("[KSUtil.initAD] InitCallback onSuccess"); } @Override public void onFail(int i, String s) { String eMsg = "[KSUtil.initAD] InitCallback failed ; onFail"; EALog.e(eMsg); if (initResult != null) { initResult.fail(i + "", eMsg); } } }); final String finalAppId = resultAppID; builder.setStartCallback(new KsInitCallback() { @Override public void onSuccess() { EALog.simple("[KSUtil.initAD] StartCallback onSuccess"); if (initResult != null) { initResult.success(); } EasyAdsManger.getInstance().lastKSAID = finalAppId; EasyAdsManger.getInstance().hasKSInit = true; } @Override public void onFail(int i, String s) { String eMsg = "[KSUtil.initAD] InitCallback failed ; onFail"; EALog.e(eMsg); if (initResult != null) { initResult.fail(i + "", eMsg); } } }); // 建议只在需要的进程初始化SDK即可,如主进程 KsAdSDK.init(context, builder.build()); KsAdSDK.start(); } } catch (Throwable e) { e.printStackTrace(); } } public static long getADID(SdkSupplier supplier) { long id = -1; try { id = Long.parseLong(supplier.adspotId); } catch (Throwable throwable) { throwable.printStackTrace(); } return id; } @Override public void zoomOut(Activity activity) { } interface InitListener { void success(); void fail(String code, String msg); } } ================================================ FILE: easyks/src/main/res/values/strings.xml ================================================ ================================================ FILE: easyks/src/test/java/com/easyads/supplier/ks/ExampleUnitTest.java ================================================ package com.easyads.supplier.ks; import org.junit.Test; import static org.junit.Assert.*; /** * Example local unit test, which will execute on the development machine (host). * * @see Testing documentation */ public class ExampleUnitTest { @Test public void addition_isCorrect() { assertEquals(4, 2 + 2); } } ================================================ FILE: easyylh/.gitignore ================================================ /build ================================================ FILE: easyylh/build.gradle ================================================ apply plugin: 'com.android.library' apply plugin: 'com.kezong.fat-aar' // JitPack Maven apply plugin: 'com.github.dcendents.android-maven' // Your Group group = 'com.github.bayescom' //maven依赖的项目名称 def pomName = "easyads-gdt" def VersionCode = 4492 def VersionName = "${rootProject.ext.EasyAds_version}_${VersionCode}" // //def outputName = pomName + "_" + VersionName + ".aar" android { compileSdkVersion 29 buildToolsVersion "29.0.3" defaultConfig { minSdkVersion 14 targetSdkVersion 29 versionCode VersionCode versionName VersionName consumerProguardFiles 'consumer-rules.pro' } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } lintOptions { abortOnError false } println pomName + " VersionName = " + VersionName } repositories { flatDir { dirs 'libs' } } // If the value is changed to true, the dependencies of the remote dependency will also be embedded in the final aar. // the default value of transitive is false configurations.embed.transitive = true dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' // def aarName = "GDTSDK_${VersionCode}" // def version = '4.542.1412' //发布时用embed捆绑aar包 // if (rootProject.ext.releaseSDK) { // embed(name: aarName, ext: 'aar') // } else { api "com.qq.e.union:union:4.575.1445" // api fileTree(dir: 'libs', include: ['*.jar', '*.aar']) // } compileOnly project(path: ':easyads') } //android.libraryVariants.all { variant -> // variant.outputs.all { // outputFileName = outputName // } //} ================================================ FILE: easyylh/consumer-rules.pro ================================================ ================================================ FILE: easyylh/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: easyylh/src/androidTest/java/com/easyads/supplier/ylh/ExampleInstrumentedTest.java ================================================ package com.easyads.supplier.ylh; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumented test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() { // Context of the app under test. Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); assertEquals("com.easyads.supplier.ylh.test", appContext.getPackageName()); } } ================================================ FILE: easyylh/src/main/AndroidManifest.xml ================================================ ================================================ FILE: easyylh/src/main/java/com/easyads/supplier/ylh/SplashZoomOutManager.java ================================================ package com.easyads.supplier.ylh; import android.animation.Animator; import android.content.Context; import android.view.View; import android.view.ViewGroup; import android.view.animation.OvershootInterpolator; import android.widget.FrameLayout; import com.easyads.utils.EAUtil; import com.easyads.utils.EALog; import com.easyads.utils.ScreenUtil; import com.easyads.utils.EASplashZoomOutLayout; import com.qq.e.ads.splash.SplashAD; public class SplashZoomOutManager { private static final String TAG = "[SplashZoomOutManager] "; private static final int LEFT = 0; private static final int RIGHT = 1; private int zoomOutWidth;//悬浮窗的宽度 private int zoomOutHeight;//悬浮窗的高度 private int zoomOutMargin;//悬浮窗最小离屏幕边缘的距离 private int zoomOutAbove;//悬浮窗默认距离屏幕底端的高度 private int zoomOutPos;//悬浮窗默认位于屏幕左面或右面 private int zoomOutAnimationTime;//悬浮窗缩放动画的,单位ms private SplashAD splashAD; private View splashView; private int originSplashWidth; private int originSplashHeight; private int[] originSplashPos = new int[2]; private int decorViewWidth; private int decorViewHeight; public interface AnimationCallBack { void animationStart(int animationTime); void animationEnd(); } private static final class Holder { private static SplashZoomOutManager instance = new SplashZoomOutManager(); } public static SplashZoomOutManager getInstance() { return Holder.instance; } private SplashZoomOutManager() { } public void initSize(Context context) { try { int deviceWidth = Math.min(ScreenUtil.getScreenHeightC(context), ScreenUtil.getScreenWidthC(context)); EALog.high(TAG + "deviceWidth = " + deviceWidth + ", context = " + context); zoomOutWidth = Math.round(deviceWidth * 0.3f);//屏幕宽度的30%,之前使用PxUtils.dpToPx(context, 90); zoomOutHeight = Math.round(zoomOutWidth * 16 / 9);//根据宽度计算高度,之前使用PxUtils.dpToPx(context, 160); zoomOutMargin = ScreenUtil.dip2pxC(context, 6); zoomOutAbove = ScreenUtil.dip2pxC(context, 100); zoomOutPos = RIGHT; zoomOutAnimationTime = 300; } catch (Throwable e) { e.printStackTrace(); } } /** * 用于开屏v+在两个activity之间传递数据 * * @param splashAD 开屏对应的广告数据 * @param splashView 开屏对应显示view,外部提供开屏container的子view * @param decorView 因为在另一个单独的activity启动时获取不到view尺寸,在这里获取下decorView的尺寸,在展示悬挂的 * activity使用该尺寸布局 */ public void setSplashInfo(SplashAD splashAD, View splashView, View decorView) { this.splashAD = splashAD; this.splashView = splashView; splashView.getLocationOnScreen(originSplashPos); originSplashWidth = splashView.getWidth(); originSplashHeight = splashView.getHeight(); decorViewWidth = decorView.getWidth(); decorViewHeight = decorView.getHeight(); } public void clearStaticData() { splashAD = null; splashView = null; } public SplashAD getSplashAD() { return splashAD; } /** * 开屏采用单独的activity时候,悬浮窗显示在另外一个activity使用该函数进行动画 * 调用前要先调用setSplashInfo设置数据,该函数会使用setSplashInfo设置的数据,并会清除对设置数据的引用 * * @param animationContainer 一般是decorView * @param zoomOutContainer 最终浮窗所在的父布局 * @param callBack 动画完成的回调 */ public ViewGroup startZoomOut(final ViewGroup animationContainer, final ViewGroup zoomOutContainer, final AnimationCallBack callBack) { try { EALog.high(TAG + "zoomOut startZoomOut activity"); if (animationContainer == null || zoomOutContainer == null) { EALog.e(TAG + "zoomOut animationContainer or zoomOutContainer is null"); return null; } if (splashAD == null || splashView == null) { EALog.e(TAG + "zoomOut splashAD or splashView is null"); return null; } //先把view按照原来的尺寸显示出来 int[] animationContainerPos = new int[2]; animationContainer.getLocationOnScreen(animationContainerPos); int x = originSplashPos[0] - animationContainerPos[0]; int y = originSplashPos[1] - animationContainerPos[1]; EAUtil.removeFromParent(splashView); FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(originSplashWidth, originSplashHeight); animationContainer.addView(splashView, layoutParams); EALog.max(TAG + "splashView x = " + x + ",y = " + y); splashView.setX(x); splashView.setY(y); } catch (Throwable e) { e.printStackTrace(); } return startZoomOut(splashView, animationContainer, zoomOutContainer, callBack); } /** * 开屏显示和悬浮窗显示在同一个activity中 * 使用该函数会清除setSplashInfo设置的数据 * * 动画步骤: * 1、把需要动画的view从父布局中移除出来,目的是在动画时可以隐藏其他开屏的view * 2、把splash对应的view加到动画的view里开始动画,因为动画窗口可能比较最终的布局要大 * 3、在动画结束把splash view加到zoomOutContainer里 * * @param splash 开屏对应的view; * @param animationContainer 开屏动画所在的layout * @param zoomOutContainer 动画结束时,最终悬浮窗所在的父布局 * @param callBack 动画结束时的回调,splashAdView无法感知动画的执行时间,需要使用该函数通知动画结束了 */ public ViewGroup startZoomOut(final View splash, final ViewGroup animationContainer, final ViewGroup zoomOutContainer, final AnimationCallBack callBack) { ViewGroup zoomOutView = null; try { clearStaticData();//单例清除下引用的view和ad数据,免得内存泄漏 if (splash == null || zoomOutContainer == null) { return null; } final Context context = zoomOutContainer.getContext(); final int[] splashScreenPos = new int[2]; splash.getLocationOnScreen(splashScreenPos); int fromWidth = splash.getWidth(); int fromHeight = splash.getHeight(); int animationContainerWidth = animationContainer.getWidth(); int animationContainerHeight = animationContainer.getHeight(); if (animationContainerWidth == 0) { animationContainerWidth = decorViewWidth; } if (animationContainerHeight == 0) { animationContainerHeight = decorViewHeight; } EALog.high(TAG + "zoomOut width:" + zoomOutWidth + " height:" + zoomOutHeight); float xScaleRatio = (float) zoomOutWidth / fromWidth; float yScaleRation = (float) zoomOutHeight / fromHeight; final float animationDistX = zoomOutPos == LEFT ? zoomOutMargin : animationContainerWidth - zoomOutMargin - zoomOutWidth; final float animationDistY = animationContainerHeight - zoomOutAbove - zoomOutHeight; //最终位于container的y坐标 EALog.max(TAG + "zoomOut animationContainerWidth:" + animationContainerWidth + " " + "animationContainerHeight:" + animationContainerHeight); EALog.max(TAG + "zoomOut splashScreenX:" + splashScreenPos[0] + " splashScreenY:" + splashScreenPos[1]); EALog.high(TAG + "zoomOut splashWidth:" + fromWidth + " splashHeight:" + fromHeight); EALog.max(TAG + "zoomOut animationDistX:" + animationDistX + " animationDistY:" + animationDistY); EAUtil.removeFromParent(splash); FrameLayout.LayoutParams animationParams = new FrameLayout.LayoutParams(fromWidth, fromHeight); animationContainer.addView(splash, animationParams); zoomOutView = new EASplashZoomOutLayout(context, zoomOutMargin); splash.setPivotX(0); splash.setPivotY(0); final ViewGroup finalZoomOutView = zoomOutView; splash.animate() .scaleX(xScaleRatio) .scaleY(yScaleRation) .x(animationDistX) .y(animationDistY) .setInterpolator(new OvershootInterpolator(0)) .setDuration(zoomOutAnimationTime) .setListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { EALog.high(TAG + "zoomOut onAnimationStart"); if (callBack != null) { callBack.animationStart(zoomOutAnimationTime); } } @Override public void onAnimationEnd(Animator animation) { EALog.high(TAG + "zoomOut onAnimationEnd"); EAUtil.removeFromParent(splash); splash.setScaleX(1); splash.setScaleY(1); splash.setX(0); splash.setY(0); int[] zoomOutContainerScreenPos = new int[2]; zoomOutContainer.getLocationOnScreen(zoomOutContainerScreenPos); float distX = animationDistX - zoomOutContainerScreenPos[0] + splashScreenPos[0]; float distY = animationDistY - zoomOutContainerScreenPos[1] + splashScreenPos[1]; EALog.max(TAG + "zoomOut distX:" + distX + " distY:" + distY); EALog.max(TAG + "zoomOut containerScreenX:" + zoomOutContainerScreenPos[0] + " " + "containerScreenY:" + zoomOutContainerScreenPos[1]); finalZoomOutView.addView(splash, FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); FrameLayout.LayoutParams zoomOutParams = new FrameLayout.LayoutParams(zoomOutWidth, zoomOutHeight); zoomOutContainer.addView(finalZoomOutView, zoomOutParams); finalZoomOutView.setTranslationX(distX); finalZoomOutView.setTranslationY(distY); if (callBack != null) { callBack.animationEnd(); } } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); } catch (Throwable e) { e.printStackTrace(); } return zoomOutView; } } ================================================ FILE: easyylh/src/main/java/com/easyads/supplier/ylh/YlhBannerAdapter.java ================================================ package com.easyads.supplier.ylh; import android.app.Activity; import android.view.ViewGroup; import android.widget.RelativeLayout; import com.easyads.core.banner.EABannerSetting; import com.easyads.custom.EABannerCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.qq.e.ads.banner2.UnifiedBannerADListener; import com.qq.e.ads.banner2.UnifiedBannerView; import com.qq.e.comm.util.AdError; import java.lang.ref.SoftReference; public class YlhBannerAdapter extends EABannerCustomAdapter implements UnifiedBannerADListener { private EABannerSetting setting; private UnifiedBannerView bv; public YlhBannerAdapter(SoftReference activity, EABannerSetting setting) { super(activity, setting); this.setting = setting; } @Override public void onNoAD(AdError adError) { try { int code = -1; String msg = "default onNoAD"; if (adError != null) { code = adError.getErrorCode(); msg = adError.getErrorMsg(); } EALog.e(" onError: code = " + code + " msg = " + msg); EasyAdError easyAdError = EasyAdError.parseErr(code, msg); doBannerFailed(easyAdError); } catch (Throwable e) { e.printStackTrace(); } } @Override public void onADReceive() { try { EALog.high(TAG + "onADReceive"); if (setting != null) { int refreshValue = setting.getRefreshInterval(); EALog.high("refreshValue == " + refreshValue); if (refreshValue > 0) { //当收到广告后,且有设置刷新间隔,代表目前正在刷新中 refreshing = true; } } handleSucceed(); } catch (Throwable e) { e.printStackTrace(); doBannerFailed(EasyAdError.parseErr(EasyAdError.ERROR_EXCEPTION_LOAD)); } } @Override public void onADExposure() { EALog.high(TAG + "onADExposure"); handleExposure(); } @Override public void onADClosed() { EALog.high(TAG + "onADClosed"); if (null != setting) { setting.adapterDidDislike(sdkSupplier); } } @Override public void onADClicked() { EALog.high(TAG + "onADClicked"); handleClick(); } @Override public void onADLeftApplication() { EALog.high(TAG + "onADLeftApplication"); } @Override protected void doLoadAD() { YlhUtil.initAD(this); bv = new UnifiedBannerView(getActivity(), sdkSupplier.adspotId, this); if (setting != null) { int refreshValue = setting.getRefreshInterval(); bv.setRefresh(refreshValue); } /* 发起广告请求,收到广告数据后会展示数据 */ bv.loadAD(); } @Override protected void doShowAD() { if (null != setting) { ViewGroup adContainer = setting.getContainer(); if (adContainer != null) { adContainer.removeAllViews(); adContainer.addView(bv, new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); } } } @Override public void doDestroy() { if (null != bv) { bv.destroy(); } } } ================================================ FILE: easyylh/src/main/java/com/easyads/supplier/ylh/YlhFullScreenVideoAdapter.java ================================================ package com.easyads.supplier.ylh; import android.app.Activity; import com.easyads.core.full.EAFullScreenVideoSetting; import com.easyads.custom.EAFullScreenCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.qq.e.ads.cfg.VideoOption; import com.qq.e.ads.interstitial2.UnifiedInterstitialAD; import com.qq.e.ads.interstitial2.UnifiedInterstitialADListener; import com.qq.e.ads.interstitial2.UnifiedInterstitialMediaListener; import com.qq.e.comm.util.AdError; import java.lang.ref.SoftReference; public class YlhFullScreenVideoAdapter extends EAFullScreenCustomAdapter implements UnifiedInterstitialADListener { private EAFullScreenVideoSetting setting; private UnifiedInterstitialAD iad; private long videoDuration; private long videoStartTime; public YlhFullScreenVideoAdapter(SoftReference activity, EAFullScreenVideoSetting setting) { super(activity, setting); this.setting = setting; } @Override public void onADReceive() { try { EALog.high(TAG + "onADReceive"); handleSucceed(); } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.parseErr(EasyAdError.ERROR_EXCEPTION_LOAD)); } } //虽然有此回调,但是返回该事件的时机不固定。。。 @Override public void onVideoCached() { EALog.high(TAG + "onVideoCached"); handleCached(); } @Override public void onNoAD(AdError adError) { try { int code = -1; String msg = "default onNoAD"; if (adError != null) { code = adError.getErrorCode(); msg = adError.getErrorMsg(); } EALog.high(TAG + " onNoAD"); handleFailed(code, msg); } catch (Throwable e) { e.printStackTrace(); } } @Override public void onADOpened() { EALog.high(TAG + "onADOpened"); } @Override public void onADExposure() { EALog.high(TAG + "onADExposure"); handleExposure(); } @Override public void onADClicked() { EALog.high(TAG + "onADClicked"); handleClick(); } @Override public void onADLeftApplication() { EALog.high(TAG + "onADLeftApplication"); } @Override public void onADClosed() { EALog.high(TAG + "onADClosed"); if (setting != null) { long costTime = System.currentTimeMillis() - videoStartTime; EALog.high(TAG + "costTime == " + costTime + " videoDuration == " + videoDuration); if (costTime < videoDuration) { EALog.high(TAG + " adapterVideoSkipped"); setting.adapterVideoSkipped(sdkSupplier); } EALog.high(TAG + " adapterClose"); setting.adapterClose(sdkSupplier); } } @Override public void onRenderSuccess() { EALog.high(TAG + "onRenderSuccess"); } @Override public void onRenderFail() { EALog.high(TAG + "onRenderFail"); handleFailed(EasyAdError.ERROR_RENDER_FAILED, ""); } @Override protected void doLoadAD() { YlhUtil.initAD(this); iad = new UnifiedInterstitialAD(getActivity(), sdkSupplier.adspotId, this); //用来获取视频时长 iad.setMediaListener(new UnifiedInterstitialMediaListener() { @Override public void onVideoInit() { EALog.high(TAG + " onVideoInit"); if (setting != null && setting.getYlhMediaListener() != null) setting.getYlhMediaListener().onVideoInit(); } @Override public void onVideoLoading() { EALog.high(TAG + " onVideoLoading"); if (setting != null && setting.getYlhMediaListener() != null) setting.getYlhMediaListener().onVideoLoading(); } @Override public void onVideoReady(long l) { EALog.high(TAG + " onVideoReady, videoDuration = " + l); try { if (setting != null && setting.getYlhMediaListener() != null) setting.getYlhMediaListener().onVideoReady(l); videoStartTime = System.currentTimeMillis(); videoDuration = l; } catch (Throwable e) { e.printStackTrace(); } } @Override public void onVideoStart() { EALog.high(TAG + " onVideoStart"); if (setting != null && setting.getYlhMediaListener() != null) setting.getYlhMediaListener().onVideoStart(); } @Override public void onVideoPause() { EALog.high(TAG + " onVideoPause"); if (setting != null && setting.getYlhMediaListener() != null) setting.getYlhMediaListener().onVideoPause(); } @Override public void onVideoComplete() { EALog.high(TAG + " onVideoComplete"); if (setting != null && setting.getYlhMediaListener() != null) setting.getYlhMediaListener().onVideoComplete(); if (null != setting) { setting.adapterVideoComplete(sdkSupplier); } } @Override public void onVideoError(AdError adError) { EALog.high(TAG + " onVideoError "); String msgInf = ""; if (adError != null) { EALog.high(TAG + " ErrorCode: " + adError.getErrorCode() + ", ErrorMsg: " + adError.getErrorMsg()); msgInf = TAG + adError.getErrorCode() + ", " + adError.getErrorMsg(); } if (setting != null && setting.getYlhMediaListener() != null) setting.getYlhMediaListener().onVideoError(adError); handleFailed(EasyAdError.ERROR_RENDER_FAILED, msgInf); } @Override public void onVideoPageOpen() { EALog.high(TAG + "onVideoPageOpen "); if (setting != null && setting.getYlhMediaListener() != null) setting.getYlhMediaListener().onVideoPageOpen(); } @Override public void onVideoPageClose() { EALog.high(TAG + " onVideoPageClose"); if (setting != null && setting.getYlhMediaListener() != null) setting.getYlhMediaListener().onVideoPageClose(); } }); VideoOption videoOption; if (setting != null && setting.getYlhVideoOption() != null) { videoOption = setting.getYlhVideoOption(); } else { videoOption = new VideoOption.Builder().setAutoPlayMuted(false) .setAutoPlayPolicy(VideoOption.AutoPlayPolicy.ALWAYS) .build(); } iad.setMinVideoDuration(0); iad.setMaxVideoDuration(60); iad.setVideoOption(videoOption); iad.loadFullScreenAD(); } @Override public void doDestroy() { } @Override protected void doShowAD() { if (iad != null) { iad.showFullScreenAD(getActivity()); } } } ================================================ FILE: easyylh/src/main/java/com/easyads/supplier/ylh/YlhInterstitialAdapter.java ================================================ package com.easyads.supplier.ylh; import android.app.Activity; import com.easyads.core.inter.EAInterstitialSetting; import com.easyads.custom.EAInterstitialCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.qq.e.ads.interstitial2.UnifiedInterstitialAD; import com.qq.e.ads.interstitial2.UnifiedInterstitialADListener; import com.qq.e.comm.constants.AdPatternType; import com.qq.e.comm.util.AdError; import java.lang.ref.SoftReference; public class YlhInterstitialAdapter extends EAInterstitialCustomAdapter implements UnifiedInterstitialADListener { private EAInterstitialSetting setting; private UnifiedInterstitialAD interstitialAD; public YlhInterstitialAdapter(SoftReference activity, EAInterstitialSetting setting) { super(activity, setting); this.setting = setting; } @Override public void doDestroy() { if (null != interstitialAD) { interstitialAD.destroy(); } } @Override protected void doLoadAD() { YlhUtil.initAD(this); interstitialAD = new UnifiedInterstitialAD(getActivity(), sdkSupplier.adspotId, this); interstitialAD.loadAD(); } @Override protected void doShowAD() { if (null != interstitialAD) { interstitialAD.show(); } } @Override public void onADReceive() { EALog.high(TAG + "onADReceive"); handleSucceed(); // onADReceive之后才能调用getAdPatternType() if (interstitialAD != null && interstitialAD.getAdPatternType() == AdPatternType.NATIVE_VIDEO) { interstitialAD.setMediaListener(setting.getYlhMediaListener()); } } @Override public void onVideoCached() { EALog.high(TAG + "onVideoCached"); } @Override public void onNoAD(AdError adError) { try { int code = -1; String msg = "default onNoAD"; if (adError != null) { code = adError.getErrorCode(); msg = adError.getErrorMsg(); } EALog.high(TAG + "onNoAD " + code + msg); EasyAdError error = EasyAdError.parseErr(code, msg); handleFailed(error); } catch (Throwable e) { e.printStackTrace(); } } @Override public void onADOpened() { EALog.high(TAG + "onADOpened"); } @Override public void onADExposure() { EALog.high(TAG + "onADExposure"); handleExposure(); } @Override public void onADClicked() { EALog.high(TAG + "onADClicked"); handleClick(); } @Override public void onADLeftApplication() { EALog.high(TAG + "onADLeftApplication"); } @Override public void onADClosed() { EALog.high(TAG + "onADClosed"); if (null != setting) { setting.adapterDidClosed(sdkSupplier); } } @Override public void onRenderSuccess() { EALog.high(TAG + "onRenderSuccess"); } @Override public void onRenderFail() { EALog.high(TAG + "onRenderFail"); handleFailed(EasyAdError.parseErr(EasyAdError.ERROR_RENDER_FAILED)); } } ================================================ FILE: easyylh/src/main/java/com/easyads/supplier/ylh/YlhNativeExpressAdapter.java ================================================ package com.easyads.supplier.ylh; import android.app.Activity; import android.view.View; import com.easyads.core.nati.EANativeExpressSetting; import com.easyads.custom.EANativeExpressCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.qq.e.ads.cfg.DownAPPConfirmPolicy; import com.qq.e.ads.cfg.VideoOption; import com.qq.e.ads.nativ.ADSize; import com.qq.e.ads.nativ.NativeExpressAD; import com.qq.e.ads.nativ.NativeExpressADView; import com.qq.e.comm.util.AdError; import java.lang.ref.SoftReference; import java.util.List; import static com.easyads.model.EasyAdError.ERROR_DATA_NULL; public class YlhNativeExpressAdapter extends EANativeExpressCustomAdapter { private EANativeExpressSetting setting; NativeExpressADView adView; boolean isVideoMute = false; public YlhNativeExpressAdapter(SoftReference activity, EANativeExpressSetting setting) { super(activity, setting); try { this.setting = setting; isVideoMute = setting.isVideoMute(); } catch (Throwable e) { e.printStackTrace(); } } @Override protected void doLoadAD() { YlhUtil.initAD(this); int width = setting.getExpressViewWidth(); int height = setting.getExpressViewHeight(); // 验证高度为0时为自适应 ADSize adSize = new ADSize(width, height); NativeExpressAD nativeExpressAd = new NativeExpressAD(getActivity(), adSize, sdkSupplier.adspotId, new NativeExpressAD.NativeExpressADListener() { @Override public void onADLoaded(List list) { onADLoadedEV(list); } @Override public void onRenderFail(NativeExpressADView nativeExpressADView) { onRenderFailEV(nativeExpressADView); } @Override public void onRenderSuccess(NativeExpressADView nativeExpressADView) { onRenderSuccessEV(nativeExpressADView); } @Override public void onADExposure(NativeExpressADView nativeExpressADView) { onADExposureEV(nativeExpressADView); } @Override public void onADClicked(NativeExpressADView nativeExpressADView) { onADClickedEV(nativeExpressADView); } @Override public void onADClosed(NativeExpressADView nativeExpressADView) { onADClosedEV(nativeExpressADView); } @Override public void onADLeftApplication(NativeExpressADView nativeExpressADView) { } @Override public void onNoAD(AdError adError) { onNoADEV(adError); } }); // 这里的Context必须为Activity VideoOption option = new VideoOption.Builder() .setAutoPlayMuted(isVideoMute) .build(); nativeExpressAd.setVideoOption(option); nativeExpressAd.setMaxVideoDuration(setting.getYlhMaxVideoDuration()); // nativeExpressAd.setDownAPPConfirmPolicy(DownAPPConfirmPolicy.NOConfirm); nativeExpressAd.loadAD(1); } public void onADLoadedEV(List list) { EALog.high(TAG + "onADLoadedEV"); boolean isEmpty = list == null || list.isEmpty(); if (isEmpty) { handleFailed(EasyAdError.parseErr(ERROR_DATA_NULL)); return; } boolean isAllDataNull = true; for (NativeExpressADView nativeExpressADView : list) { isAllDataNull = isAllDataNull && nativeExpressADView == null; } if (isAllDataNull) { handleFailed(EasyAdError.parseErr(ERROR_DATA_NULL)); return; } adView = list.get(0); addADView(adView); handleSucceed(); } public void onRenderFailEV(View nativeExpressADView) { EALog.high(TAG + "onRenderFailEV"); setNEView(nativeExpressADView); if (setting != null) setting.adapterRenderFailed(sdkSupplier); handleFailed(EasyAdError.parseErr(EasyAdError.ERROR_RENDER_FAILED)); removeADView(); } public void onRenderSuccessEV(View nativeExpressADView) { EALog.high(TAG + "onRenderSuccessEV"); setNEView(nativeExpressADView); if (setting != null) setting.adapterRenderSuccess(sdkSupplier); } public void onADExposureEV(View nativeExpressADView) { EALog.high(TAG + "onADExposureEV"); setNEView(nativeExpressADView); handleExposure(); } public void onADClickedEV(View nativeExpressADView) { EALog.high(TAG + "onADClickedEV"); setNEView(nativeExpressADView); handleClick(); } public void onADClosedEV(View nativeExpressADView) { EALog.high(TAG + "onADClosedEV"); setNEView(nativeExpressADView); if (setting != null) setting.adapterDidClosed(sdkSupplier); removeADView(); } public void onNoADEV(AdError adError) { EALog.high(TAG + "onNoADEV"); int code = -1; String msg = "default onNoAD"; if (adError != null) { code = adError.getErrorCode(); msg = adError.getErrorMsg(); } handleFailed(code, msg); } @Override public void doDestroy() { } @Override protected void doShowAD() { adView.render(); } } ================================================ FILE: easyylh/src/main/java/com/easyads/supplier/ylh/YlhRewardVideoAdapter.java ================================================ package com.easyads.supplier.ylh; import android.app.Activity; import android.os.SystemClock; import com.easyads.core.reward.EARewardServerCallBackInf; import com.easyads.core.reward.EARewardVideoSetting; import com.easyads.custom.EARewardCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.qq.e.ads.rewardvideo.RewardVideoAD; import com.qq.e.ads.rewardvideo.RewardVideoADListener; import com.qq.e.ads.rewardvideo.ServerSideVerificationOptions; import com.qq.e.comm.util.AdError; import java.lang.ref.SoftReference; import java.util.Map; import static com.easyads.model.EasyAdError.ERROR_EXCEPTION_SHOW; public class YlhRewardVideoAdapter extends EARewardCustomAdapter implements RewardVideoADListener { private EARewardVideoSetting setting; public RewardVideoAD rewardVideoAD; public YlhRewardVideoAdapter(SoftReference activity, EARewardVideoSetting setting) { super(activity, setting); this.setting = setting; } @Override public void onADLoad() { EALog.high(TAG + "onADLoad"); handleSucceed(); } @Override public void onVideoCached() { EALog.high(TAG + "onVideoCached"); handleCached(); } @Override public void onADShow() { EALog.high(TAG + "onADShow"); } @Override public void onADExpose() { EALog.high(TAG + "onADExpose"); handleExposure(); } @Override public void onReward(Map map) { try { EALog.high(TAG + "onReward"); if (setting != null) { setting.adapterAdReward(sdkSupplier); EARewardServerCallBackInf inf = new EARewardServerCallBackInf(); inf.ylhRewardMap = map; setting.postRewardServerInf(inf, sdkSupplier); } } catch (Throwable e) { e.printStackTrace(); } } @Override public void onADClick() { EALog.high(TAG + "onADClick"); handleClick(); } @Override public void onVideoComplete() { EALog.high(TAG + "onVideoComplete"); if (setting != null) { setting.adapterVideoComplete(sdkSupplier); } } @Override public void onADClose() { EALog.high(TAG + "onADClose"); if (setting != null) setting.adapterDidClosed(sdkSupplier); } @Override public void onError(AdError adError) { try { int code = -1; String msg = "default onNoAD"; if (adError != null) { code = adError.getErrorCode(); msg = adError.getErrorMsg(); } EALog.high(TAG + "onError"); handleFailed(code, msg); } catch (Throwable e) { e.printStackTrace(); } } @Override protected void doLoadAD() { YlhUtil.initAD(this); boolean vo = false; ServerSideVerificationOptions ssv0 = null; if (setting != null) { vo = setting.isYlhVolumeOn(); ssv0 = setting.getYlhSSVO(); } rewardVideoAD = new RewardVideoAD(getActivity(), sdkSupplier.adspotId, this, vo); if (ssv0 != null) { rewardVideoAD.setServerSideVerificationOptions(ssv0); } rewardVideoAD.loadAD(); } @Override protected void doShowAD() { if (checkRewardOk()) { rewardVideoAD.showAD(); } else { handleFailed(EasyAdError.parseErr(ERROR_EXCEPTION_SHOW, "RewardNotVis")); } } @Override public void doDestroy() { } public boolean checkRewardOk() { try { return rewardVideoAD.isValid(); } catch (Throwable e) { e.printStackTrace(); } return true; } } ================================================ FILE: easyylh/src/main/java/com/easyads/supplier/ylh/YlhSplashAdapter.java ================================================ package com.easyads.supplier.ylh; import android.app.Activity; import android.os.SystemClock; import com.easyads.EasyAdsManger; import com.easyads.core.splash.EASplashSetting; import com.easyads.custom.EASplashCustomAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.qq.e.ads.splash.SplashAD; import com.qq.e.ads.splash.SplashADZoomOutListener; import com.qq.e.comm.util.AdError; import java.lang.ref.SoftReference; public class YlhSplashAdapter extends EASplashCustomAdapter { private long remainTime = 5000; private boolean isClicked = false; private SplashAD splashAD; public YlhSplashAdapter(SoftReference activity, EASplashSetting setting) { super(activity, setting); } @Override public void doShowAD() { splashAD.showAd(adContainer); } @Override public void doLoadAD() { initAD(); splashAD.fetchAdOnly(); } @Override public void doDestroy() { } private void initAD() { YlhUtil.initAD(this); int timeout = 5000; SplashADZoomOutListener listener = new SplashADZoomOutListener() { @Override public void onZoomOut() { EALog.high(TAG + "onZoomOut "); zoomOut(); } @Override public void onZoomOutPlayFinish() { EALog.high(TAG + "onZoomOutPlayFinish "); } @Override public boolean isSupportZoomOut() { EALog.high(TAG + "isSupportZoomOut "); return true; } @Override public void onADDismissed() { EALog.high(TAG + "onADDismissed "); if (null != mSplashSetting) { //剩余时长在600ms以上,且未点击才按照跳过 if (remainTime >= 600 && !isClicked) { mSplashSetting.adapterDidSkip(sdkSupplier); } else { mSplashSetting.adapterDidTimeOver(sdkSupplier); } } } @Override public void onNoAD(AdError adError) { int code = -1; String msg = "default onNoAD"; if (adError != null) { code = adError.getErrorCode(); msg = adError.getErrorMsg(); } EALog.high(TAG + "onNoAD"); handleFailed(code, msg); preLoad(); } @Override public void onADPresent() { EALog.high(TAG + "onADPresent "); } @Override public void onADClicked() { EALog.high(TAG + "onADClicked "); handleClick(); isClicked = true; } @Override public void onADTick(long l) { EALog.high(TAG + "onADTick :" + l); remainTime = l; } @Override public void onADExposure() { EALog.high(TAG + "onADExposure "); handleExposure(); preLoad(); } @Override public void onADLoaded(long expireTimestamp) { try { EALog.high(TAG + "onADLoaded expireTimestamp:" + expireTimestamp); handleSucceed(); if (splashAD != null) EALog.max(TAG + "getECPMLevel = " + splashAD.getECPMLevel()); long rt = SystemClock.elapsedRealtime(); long expire = expireTimestamp - rt; EALog.high(TAG + "ad will expired in :" + expire + " ms"); } catch (Throwable e) { e.printStackTrace(); handleFailed(EasyAdError.parseErr(EasyAdError.ERROR_EXCEPTION_LOAD)); } } }; splashAD = new SplashAD(getActivity(), sdkSupplier.adspotId, listener, timeout); } private void zoomOut() { try { SplashZoomOutManager zoomOutManager = SplashZoomOutManager.getInstance(); zoomOutManager.initSize(getActivity()); zoomOutManager.setSplashInfo(splashAD, adContainer.getChildAt(0), getActivity().getWindow().getDecorView()); if (mSplashSetting == null) { return; } if (mSplashSetting.isShowInSingleActivity()) { new YlhUtil().zoomOut(getActivity()); } else { EasyAdsManger.getInstance().isSplashSupportZoomOut = true; } } catch (Throwable e) { e.printStackTrace(); } } //当广告曝光、广告失败的时候再执行广告预加载,避免影响当前展示 private void preLoad() { try { //预加载素材,会有频次限制,目前是交给优量汇自己来控制,不做额外频次控制 if (splashAD != null) splashAD.preLoad(); } catch (Throwable e) { e.printStackTrace(); } } } ================================================ FILE: easyylh/src/main/java/com/easyads/supplier/ylh/YlhUtil.java ================================================ package com.easyads.supplier.ylh; import android.app.Activity; import android.text.TextUtils; import android.view.ViewGroup; import com.easyads.EasyAdsManger; import com.easyads.core.EABaseSupplierAdapter; import com.easyads.model.EasyAdError; import com.easyads.utils.EASplashPlusManager; import com.easyads.utils.EAUtil; import com.easyads.utils.EALog; import com.qq.e.ads.splash.SplashAD; import com.qq.e.comm.managers.GDTAdSdk; public class YlhUtil implements EASplashPlusManager.ZoomCall { public static String TAG = "[YlhUtil] "; public static void initAD(EABaseSupplierAdapter adapter) { try { if (adapter == null) { EALog.e(TAG + " initAD failed BaseSupplierAdapter null"); return; } boolean hasInit = EasyAdsManger.getInstance().hasYLHInit; String lastAppId = EasyAdsManger.getInstance().lastYLHAID; String resultAppID = adapter.getAppID(); if (TextUtils.isEmpty(resultAppID)) { String msg = "[initCsj] initAD failed AppID null"; adapter.handleFailed(EasyAdError.ERROR_DATA_NULL, msg); EALog.e(msg); return; } boolean isSame = lastAppId.equals(resultAppID); //只有当允许初始化优化时,且快手已经初始化成功过,并且初始化的id和当前id一致,才可以不再重复初始化。 if (hasInit && adapter.canOptInit() && isSame) { return; } EALog.simple(TAG + " 开始初始化SDK"); //使用新初始化方法 GDTAdSdk.initWithoutStart(adapter.getActivity().getApplicationContext(), resultAppID); // 该接口不会采集用户信息 // 调用initWithoutStart后请尽快调用start,否则可能影响广告填充,造成收入下降 GDTAdSdk.start(new GDTAdSdk.OnStartListener() { @Override public void onStartSuccess() { EALog.simple(TAG + " onStartSuccess"); // 推荐开发者在onStartSuccess回调后开始拉广告 EasyAdsManger.getInstance().hasYLHInit = true; } @Override public void onStartFailed(Exception e) { EALog.e(TAG + " onStartFailed:" + e.toString()); EasyAdsManger.getInstance().hasYLHInit = false; } }); EasyAdsManger.getInstance().hasYLHInit = true; EasyAdsManger.getInstance().lastYLHAID = resultAppID; } catch (Throwable e) { e.printStackTrace(); } } @Override public void zoomOut(Activity activity) { try { EALog.simple(TAG + " start zoomOut"); final SplashZoomOutManager zoomOutManager = SplashZoomOutManager.getInstance(); final SplashAD zoomAd = zoomOutManager.getSplashAD(); final ViewGroup zoomOutView = zoomOutManager.startZoomOut((ViewGroup) activity.getWindow().getDecorView(), (ViewGroup) activity.findViewById(android.R.id.content), new SplashZoomOutManager.AnimationCallBack() { @Override public void animationStart(int animationTime) { } @Override public void animationEnd() { zoomAd.zoomOutAnimationFinish(); } }); if (zoomOutView != null) { activity.overridePendingTransition(0, 0); } EAUtil.autoClose(zoomOutView); } catch (Throwable e) { e.printStackTrace(); } } } ================================================ FILE: easyylh/src/main/res/values/strings.xml ================================================ ================================================ FILE: easyylh/src/main/res/xml/ea_ylh_file_path.xml ================================================ ================================================ FILE: easyylh/src/test/java/com/easyads/supplier/ylh/ExampleUnitTest.java ================================================ package com.easyads.supplier.ylh; import org.junit.Test; import static org.junit.Assert.*; /** * Example local unit test, which will execute on the development machine (host). * * @see Testing documentation */ public class ExampleUnitTest { @Test public void addition_isCorrect() { assertEquals(4, 2 + 2); } } ================================================ FILE: example/.gitignore ================================================ /build ================================================ FILE: example/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 28 defaultConfig { applicationId "com.easyads.demo" minSdkVersion 17 targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" multiDexEnabled true // true时,生成多个dex ndk { abiFilters "armeabi", "armeabi-v7a", "x86", "x86_64", "arm64-v8a" } } buildTypes { debug { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } lintOptions { abortOnError false } // 支持 JDK 1.8 compileOptions { targetCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8 } } repositories { flatDir { dirs 'libs' } dependencies { //引入方式1:gradle线上,简单快速 //EasyAds sdk(必须) implementation "com.github.bayescom:EasyAds-Android:1.2.1" //穿山甲广告SDK(建议) implementation "com.pangle.cn:mediation-sdk:6.1.2.5" //优量汇广告SDK(建议) implementation "com.qq.e.union:union:4.575.1445" //百度广告SDK(可选) implementation(name: "Baidu_MobAds_SDK_9352", ext: "aar") // //快手广告SDK(可选) implementation(name: "kssdk-ad-3363", ext: "aar") // 引入方式2:library方式引入,可以自己修改代码。注意:两种引入方式互斥,不要同时引入 // implementation project(path: ':easyads') // // implementation project(path: ':easyylh') // implementation project(path: ':easycsj') // implementation project(path: ':easybd') // implementation project(path: ':easyks') //自定义-小米广告SDK implementation(name: "mimo-ad-sdk", ext: "aar") //自定义-小米广告SDK需要额外引入gson implementation 'com.google.code.gson:gson:2.8.5' //自定义-华为广告SDK implementation 'com.huawei.hms:ads-lite:13.4.49.301' // 以下部分为公共引用部分 implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:support-v4:28.0.0' implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support:recyclerview-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.6' implementation 'com.github.bumptech.glide:glide:4.2.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.2.0' implementation "com.android.support:multidex:1.0.3" implementation(name: 'circleimageview-2.2.0', ext: 'aar') implementation 'com.github.pedrovgs:lynx:1.1.0' //显示logcat插件 implementation 'com.github.getActivity:ToastUtils:9.6' //toast组件 } } ================================================ FILE: example/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile # #指定代码的压缩级别 -optimizationpasses 5 #包名不混合大小写 -dontusemixedcaseclassnames #不去忽略非公共的库类 -dontskipnonpubliclibraryclasses #优化 不优化输入的类文件 -dontoptimize #混淆时是否做预校验 -dontpreverify #混淆时是否记录日志 -verbose # 混淆时所采用的算法 -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* #保护注解 -keepattributes *Annotation* #如果引用了v4或者v7包 -dontwarn android.support.** #保持 native 方法不被混淆 -keepclasseswithmembernames class * { native ; } #保持自定义控件类不被混淆 -keepclasseswithmembers class * { public (android.content.Context, android.util.AttributeSet); } #保持自定义控件类不被混淆 -keepclassmembers class * extends android.app.Activity { public void *(android.view.View); } # 保持自定义控件类不被混淆 -keepclasseswithmembers class * { public (android.content.Context, android.util.AttributeSet); } # 保持自定义控件类不被混淆 -keepclasseswithmembers class * { public (android.content.Context, android.util.AttributeSet, int); } #保持 Parcelable 不被混淆 -keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *; } #保持 Serializable 不被混淆 -keepnames class * implements java.io.Serializable #保持 Serializable 不被混淆并且enum 类也不被混淆 -keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; private static final java.io.ObjectStreamField[] serialPersistentFields; !static !transient ; !private ; !private ; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve(); } #保持枚举 enum 类不被混淆 如果混淆报错,建议直接使用上面的 -keepclassmembers class * implements java.io.Serializable即可 #-keepclassmembers enum * { # public static **[] values(); # public static ** valueOf(java.lang.String); #} -keepclassmembers class * { public void *ButtonClicked(android.view.View); } #不混淆资源类 -keepclassmembers class **.R$* { public static ; } #避免混淆泛型 如果混淆报错建议关掉 #–keepattributes Signature #关闭所有日志 log, java.io.Print, printStackTrace -assumenosideeffects class android.util.Log { public static *** e(...); public static *** w(...); public static *** i(...); public static *** d(...); public static *** v(...); } -assumenosideeffects class java.io.PrintStream { public *** print(...); public *** println(...); } -assumenosideeffects class java.lang.Throwable { public *** printStackTrace(...); } # 保持包名不被混淆 # -keeppackagenames com.mercury.sdk #重新打包所有重命名的类文件,将它们移动到给定包中。 如果包中没有参数或一个空字符串,包被完全删除。 此选项将覆盖-flattenpackagehierarchy选项。 它可以使处理后的代码更小,更不容易理解。 它的已弃用名称是-defaultpackage。 仅适用于混淆处理。 # -flattenpackagehierarchy # -repackageclasses com.mercury.sdk # -keep class com.mercury.sdk.** {*;} # 异常提示排除操作 -dontwarn com.mercury.sdk.** -dontwarn androidx.** # 预防性配置:骑士卡遇到的和push-sdk一起使用时暴露出来的个别闪退问题 -dontwarn com.taobao.** -dontwarn anet.channel.** -dontwarn anetwork.channel.** -dontwarn org.android.** -dontwarn org.apache.thrift.** -dontwarn com.xiaomi.** -dontwarn com.huawei.** -keepattributes *Annotation* -keep class com.taobao.** {*;} -keep class org.android.** {*;} -keep class anet.channel.** {*;} -keep class com.umeng.** {*;} -keep class com.xiaomi.** {*;} -keep class com.huawei.** {*;} -keep class org.apache.thrift.** {*;} -keep class com.alibaba.sdk.android.**{*;} -keep class com.ut.**{*;} -keep class com.ta.**{*;} -keep public class **.R$*{ public static final int *; } #获取oaid所需相关混淆设置 -keep, includedescriptorclasses class com.asus.msa.SupplementaryDID.** { *; } -keepclasseswithmembernames class com.asus.msa.SupplementaryDID.** { *; } -keep, includedescriptorclasses class com.asus.msa.sdid.** { *; } -keepclasseswithmembernames class com.asus.msa.sdid.** { *; } -keep public class com.netease.nis.sdkwrapper.Utils {public ;} -keep class com.bun.miitmdid.**{*;} -keep class com.bun.lib.**{*;} -keep class com.samsung.android.deviceidservice.**{*;} -keep class a.**{*;} -keep class android.support.** { *; } -keep class com.bytedance.sdk.openadsdk.** { *; } -keep public interface com.bytedance.sdk.openadsdk.downloadnew.** {*;} -keep class com.ss.sys.ces.* {*;} -keep class com.ss.android.**{*;} -keep class com.pgl.sys.ces.* {*;} -keep class com.qq.e.** { public protected *; } -keep class android.support.v4.app.NotificationCompat**{ public *; } -keep class com.bun.miitmdid.** {*;} -keep class com.easyads.supplier.** {*;} -keep class com.kwai.** {*;} ================================================ FILE: example/src/androidTest/java/com/easyads/demo/ExampleInstrumentedTest.java ================================================ package com.easyads.demo; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import com.easyads.utils.BigDecimalUtil; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumented test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.easyads.demo", appContext.getPackageName()); } } ================================================ FILE: example/src/main/AndroidManifest.xml ================================================ ================================================ FILE: example/src/main/assets/banner_config.json ================================================ { "rules": [ { "tag": "A", "sort": [ 1, 2 ], "percent": 50 }, { "tag": "B", "sort": [ 3 ], "percent": 50 } ], "suppliers": [ { "tag": "csj", "adspotId": "947107684", "appId": "5051624", "index": 1 }, { "tag": "ylh", "adspotId": "4080052898050840", "appId": "1101152570", "index": 2 }, { "tag": "bd", "adspotId": "2015351", "appId": "e866cfb0", "index": 3 } ] } ================================================ FILE: example/src/main/assets/draw_config.json ================================================ { "rules": [ { "tag": "A", "sort": [ 1 ], "percent": 50 }, { "tag": "B", "sort": [ 2 ], "percent": 50 } ], "suppliers": [ { "tag": "csj", "adspotId": "946782694", "appId": "5051624", "index": 1 }, { "tag": "ks", "adspotId": "4000000020", "appId": "90009", "index": 2 } ] } ================================================ FILE: example/src/main/assets/full_config.json ================================================ { "rules": [ { "tag": "A", "sort": [ 1, 3 ], "percent": 50 }, { "tag": "B", "sort": [ 2, 4 ], "percent": 50 } ], "suppliers": [ { "tag": "csj", "adspotId": "945065337", "appId": "5051624", "index": 1 }, { "tag": "ylh", "adspotId": "7022203877744399", "appId": "1101152570", "index": 2 }, { "tag": "ks", "adspotId": "90009002", "appId": "90009", "index": 3 }, { "tag": "bd", "adspotId": "7339862", "appId": "e866cfb0", "index": 4 } ] } ================================================ FILE: example/src/main/assets/interstitial_config.json ================================================ { "rules": [ { "tag": "A", "sort": [ 1, 3 ], "percent": 50 }, { "tag": "B", "sort": [ 2, 4 ], "percent": 50 } ], "suppliers": [ { "tag": "csj", "adspotId": "946017941", "appId": "5051624", "index": 1 }, { "tag": "ylh", "adspotId": "4080298282218338", "appId": "1101152570", "index": 2 }, { "tag": "ks", "adspotId": "4000000276", "appId": "90009", "index": 3 }, { "tag": "bd", "adspotId": "2403633", "appId": "e866cfb0", "index": 4 } ] } ================================================ FILE: example/src/main/assets/native_config.json ================================================ { "rules": [ { "tag": "A", "sort": [ 1, 3 ], "percent": 50 }, { "tag": "B", "sort": [ 2, 4 ], "percent": 50 } ], "suppliers": [ { "tag": "csj", "adspotId": "945064154", "appId": "5051624", "index": 1 }, { "tag": "ylh", "adspotId": "4011142740008715", "appId": "1101152570", "index": 2 }, { "tag": "ks", "adspotId": "4000000003", "appId": "90009", "index": 3 }, { "tag": "bd", "adspotId": "6481012", "appId": "e866cfb0", "index": 4 } ] } ================================================ FILE: example/src/main/assets/reward_config.json ================================================ { "rules": [ { "tag": "A", "sort": [ 1, 3 ], "percent": 50 }, { "tag": "B", "sort": [ 2, 4 ], "percent": 50 } ], "suppliers": [ { "tag": "csj", "adspotId": "945065268", "appId": "5051624", "index": 1 }, { "tag": "ylh", "adspotId": "9011264358826997", "appId": "1101152570", "index": 2 }, { "tag": "ks", "adspotId": "90009001", "appId": "90009", "index": 3 }, { "tag": "bd", "adspotId": "5989414", "appId": "e866cfb0", "index": 4 } ] } ================================================ FILE: example/src/main/assets/splash_config.json ================================================ { "rules": [ { "tag": "A", "sort": [ 1, 3 ], "percent": 50 }, { "tag": "B", "sort": [ 2, 4 ], "percent": 50 } ], "suppliers": [ { "tag": "csj", "adspotId": "887477661", "appId": "5051624", "index": 1 }, { "tag": "ylh", "adspotId": "2001447730515391", "appId": "1101152570", "index": 2 }, { "tag": "ks", "adspotId": "4000000042", "appId": "90009", "index": 3 }, { "tag": "bd", "adspotId": "2058622", "appId": "e866cfb0", "index": 4 } ] } ================================================ FILE: example/src/main/assets/splash_cus_config.json ================================================ { "rules": [ { "tag": "A", "sort": [ 5, 1, 3 ], "percent": 50 }, { "tag": "B", "sort": [ 6, 2, 4 ], "percent": 50 } ], "suppliers": [ { "tag": "csj", "adspotId": "887477661", "appId": "5051624", "index": 1 }, { "tag": "ylh", "adspotId": "2001447730515391", "appId": "1101152570", "index": 2 }, { "tag": "ks", "adspotId": "4000000042", "appId": "90009", "index": 3 }, { "tag": "bd", "adspotId": "2058622", "appId": "e866cfb0", "index": 4 }, { "tag": "hw", "adspotId": "testd7c5cewoj6", "appId": "", "index": 5 }, { "tag": "xm", "adspotId": "94f4805a2d50ba6e853340f9035fda18", "appId": "", "index": 6 } ] } ================================================ FILE: example/src/main/java/com/easyads/demo/BannerActivity.java ================================================ package com.easyads.demo; import android.os.Bundle; import android.view.View; import android.widget.RelativeLayout; public class BannerActivity extends BaseActivity { RelativeLayout rl; EasyADController ad; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_banner); rl = findViewById(R.id.banner_layout); //初始化广告处理封装类 ad = new EasyADController(this); //加载banner ad.loadBanner("banner_config.json", rl); } public void loadBanner(View view) { if (ad != null) { ad.destroy(); ad.loadBanner("banner_config.json", rl); } } } ================================================ FILE: example/src/main/java/com/easyads/demo/BaseActivity.java ================================================ package com.easyads.demo; import android.app.Activity; import android.content.Context; import android.hardware.SensorManager; import android.os.Bundle; import android.support.annotation.Nullable; import android.util.Log; import com.easyads.demo.utils.DialogLogcat; import com.easyads.demo.utils.NormalSetting; import com.squareup.seismic.ShakeDetector; public class BaseActivity extends Activity { boolean isRunning = false; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ShakeDetector shakeDetector = new ShakeDetector(new ShakeDetector.Listener() { @Override public void hearShake() { if (NormalSetting.getInstance().showLogcat && isRunning()) { new DialogLogcat(BaseActivity.this).show(); } } }); SensorManager sensorManager = (SensorManager) this.getSystemService(Context.SENSOR_SERVICE); shakeDetector.start(sensorManager); } public boolean isRunning() { Log.d("BaseActivity", "isRunning = " + isRunning); return isRunning; } @Override protected void onResume() { super.onResume(); isRunning = true; } @Override protected void onPause() { super.onPause(); isRunning = false; } } ================================================ FILE: example/src/main/java/com/easyads/demo/DrawActivity.java ================================================ package com.easyads.demo; import android.content.Context; import android.media.MediaPlayer; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.widget.OrientationHelper; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.VideoView; import com.easyads.demo.draw.FullScreenVideoView; import com.easyads.demo.draw.OnViewPagerListener; import com.easyads.demo.draw.ViewPagerLayoutManager; import com.easyads.demo.utils.UIUtils; import java.util.ArrayList; import java.util.List; import de.hdodenhof.circleimageview.CircleImageView; public class DrawActivity extends BaseActivity { RecyclerView mRecyclerView; private LinearLayout mBottomLayout; private RelativeLayout mTopLayout; private static final int TYPE_COMMON_ITEM = 1; private static final int TYPE_AD_ITEM = 2; private int[] imgs = {R.mipmap.video11, R.mipmap.video12, R.mipmap.video13, R.mipmap.video14, R.mipmap.img_video_2}; private int[] videos = {R.raw.video11, R.raw.video12, R.raw.video13, R.raw.video14, R.raw.video_2}; private ViewPagerLayoutManager mLayoutManager; private DrawRecyclerAdapter mRecyclerAdapter; private List mDrawList = new ArrayList<>(); private static final String TAG = "DrawActivity"; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); try { requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); } catch (Throwable ignore) { } setContentView(R.layout.activity_draw); mRecyclerView = findViewById(R.id.recycler); mBottomLayout = findViewById(R.id.bottom); mTopLayout = findViewById(R.id.top); mLayoutManager = new ViewPagerLayoutManager(this, OrientationHelper.VERTICAL, false); //设置正常视频数据 for (int i = 0; i < 10; i++) { if (i % 2 == 0) { //隔一个加载一个广告 TestItem item = new TestItem(TYPE_AD_ITEM); item.ad = new EasyADController(DrawActivity.this); mDrawList.add(item); continue; } int random = (int) (Math.random() * 100); int index = random % videos.length; TestItem item = new TestItem(TYPE_COMMON_ITEM); item.videoId = videos[index]; item.imgId = imgs[index]; mDrawList.add(item); } mRecyclerAdapter = new DrawRecyclerAdapter(this, mDrawList); mRecyclerView.setLayoutManager(mLayoutManager); mRecyclerView.setAdapter(mRecyclerAdapter); initListener(); } private void initListener() { mLayoutManager.setOnViewPagerListener(new OnViewPagerListener() { @Override public void onInitComplete() { Log.d(TAG, "初始化完成"); if (mDrawList != null && mDrawList.size() > 0) { boolean isNormalVideo = mDrawList.get(0).isNormal(); if (isNormalVideo) { playVideo(); } changeBottomTopLayoutVisibility(isNormalVideo); } } @Override public void onPageRelease(boolean isNext, int position) { Log.e(TAG, "释放位置:" + position + " 下一页:" + isNext); if (mDrawList != null && mDrawList.size() > 0) { boolean isNormalVideo = mDrawList.get(position).isNormal(); if (isNormalVideo) { int index = isNext ? 0 : 1; releaseVideo(index); } } } @Override public void onPageSelected(final int position, boolean isBottom) { Log.e(TAG, "选中位置:" + position + " 是否是滑动到底部:" + isBottom); if (mDrawList != null && mDrawList.size() > 0) { boolean isNormalVideo = mDrawList.get(position).isNormal(); if (isNormalVideo) { playVideo(); } changeBottomTopLayoutVisibility(isNormalVideo); } } }); } private void playVideo() { if (isFinishing()) { return; } View itemView = mRecyclerView.getChildAt(0); if (itemView == null) { return; } final FrameLayout videoLayout = itemView.findViewById(R.id.video_layout); final View view = videoLayout.getChildAt(0); if (view == null || !(view instanceof VideoView)) { return; } final VideoView videoView = (VideoView) view; final ImageView imgPlay = itemView.findViewById(R.id.img_play); final ImageView imgThumb = itemView.findViewById(R.id.img_thumb); final MediaPlayer[] mediaPlayer = new MediaPlayer[1]; videoView.start(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { videoView.setOnInfoListener(new MediaPlayer.OnInfoListener() { @Override public boolean onInfo(MediaPlayer mp, int what, int extra) { mediaPlayer[0] = mp; Log.e(TAG, "onInfo"); mp.setLooping(true); imgThumb.animate().alpha(0).setDuration(200).start(); if (mp != null && videoView != null) { //获取视频资源的宽度 int mVideoWidth = mp.getVideoWidth(); //获取视频资源的高度 int mVideoHeight = mp.getVideoHeight(); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) videoView.getLayoutParams(); if (mVideoWidth > 0 && mVideoHeight > 0 && layoutParams != null) { int[] size = UIUtils.getScreenSize(DrawActivity.this.getApplicationContext()); layoutParams.width = mVideoWidth * size[1] / mVideoHeight; layoutParams.height = size[1]; layoutParams.leftMargin = -(layoutParams.width - size[0]) / 2; videoView.setLayoutParams(layoutParams); } } return false; } }); } videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { Log.e(TAG, "onPrepared"); } }); imgPlay.setOnClickListener(new View.OnClickListener() { boolean isPlaying = true; @Override public void onClick(View v) { if (videoView.isPlaying()) { Log.e(TAG, "isPlaying:" + videoView.isPlaying()); imgPlay.animate().alpha(1f).start(); videoView.pause(); isPlaying = false; } else { Log.e(TAG, "isPlaying:" + videoView.isPlaying()); imgPlay.animate().alpha(0f).start(); videoView.start(); isPlaying = true; } } }); } private void releaseVideo(int index) { if (isFinishing()) { return; } View itemView = mRecyclerView.getChildAt(index); if (itemView != null) { final FrameLayout videoLayout = itemView.findViewById(R.id.video_layout); if (videoLayout == null) return; View view = videoLayout.getChildAt(0); if (view instanceof VideoView) { final VideoView videoView = (VideoView) videoLayout.getChildAt(0); final ImageView imgThumb = itemView.findViewById(R.id.img_thumb); final ImageView imgPlay = itemView.findViewById(R.id.img_play); videoView.stopPlayback(); imgThumb.animate().alpha(1).start(); imgPlay.animate().alpha(0f).start(); } } } private void changeBottomTopLayoutVisibility(boolean visibility) { mBottomLayout.setVisibility(visibility ? View.VISIBLE : View.GONE); mTopLayout.setVisibility(visibility ? View.VISIBLE : View.GONE); } public class DrawRecyclerAdapter extends RecyclerView.Adapter { private Context mContext; private List mDataList; DrawRecyclerAdapter(Context context, List dataList) { this.mContext = context; this.mDataList = dataList; } @NonNull @Override public DrawRecyclerAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view_pager, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull DrawRecyclerAdapter.ViewHolder holder, int position) { View view = new View(mContext); TestItem item = null; if (mDataList != null) { item = mDataList.get(position); if (item.type == TYPE_COMMON_ITEM) { holder.img_thumb.setImageResource(item.imgId); view = getView(); ((VideoView) view).setVideoURI(Uri.parse("android.resource://" + getPackageName() + "/" + item.videoId)); holder.videoLayout.removeAllViews(); if (view.getParent() != null) { ((ViewGroup) view.getParent()).removeView(view); } holder.videoLayout.addView(view); } else if (item.type == TYPE_AD_ITEM && item.ad != null) { //请求并展示draw信息流广告 item.ad.loadDraw("draw_config.json",holder.videoLayout); } } if (item != null) { changeUIVisibility(holder, item.type); } } @Override public int getItemCount() { return mDataList.size(); } @Override public int getItemViewType(int position) { Log.d(TAG, "getItemViewType--" + position); return mDataList.get(position).type; } public class ViewHolder extends RecyclerView.ViewHolder { ImageView img_thumb; CircleImageView img_head_icon; ImageView img_play; RelativeLayout rootView; FrameLayout videoLayout; LinearLayout verticalIconLauout; public ViewHolder(View itemView) { super(itemView); img_thumb = itemView.findViewById(R.id.img_thumb); videoLayout = itemView.findViewById(R.id.video_layout); img_play = itemView.findViewById(R.id.img_play); rootView = itemView.findViewById(R.id.root_view); verticalIconLauout = itemView.findViewById(R.id.vertical_icon); img_head_icon = itemView.findViewById(R.id.head_icon); } } } private View getView() { FullScreenVideoView videoView = new FullScreenVideoView(this); FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); videoView.setLayoutParams(layoutParams); return videoView; } private void changeUIVisibility(DrawRecyclerAdapter.ViewHolder holder, int type) { boolean visibilable = true; if (type == TYPE_AD_ITEM) { visibilable = false; } Log.d(TAG, "是否展示:visibilable=" + visibilable); holder.img_play.setVisibility(visibilable ? View.VISIBLE : View.GONE); holder.img_thumb.setVisibility(visibilable ? View.VISIBLE : View.GONE); } private static class TestItem { public int type = 0; public EasyADController ad; public int videoId; public int imgId; public TestItem(int type) { this.type = type; } boolean isNormal() { return type == TYPE_COMMON_ITEM; } } } ================================================ FILE: example/src/main/java/com/easyads/demo/EasyADController.java ================================================ package com.easyads.demo; import android.app.Activity; import android.content.Context; import android.content.res.AssetManager; import android.graphics.Color; import android.util.Log; import android.view.View; import android.view.ViewGroup; import com.easyads.EasyAdsConstant; import com.easyads.core.EasyAdBaseAdspot; import com.easyads.core.banner.EasyAdBanner; import com.easyads.core.banner.EABannerListener; import com.easyads.core.draw.EasyAdDraw; import com.easyads.core.draw.EADrawListener; import com.easyads.core.full.EasyAdFullScreenVideo; import com.easyads.core.full.EAFullScreenVideoListener; import com.easyads.core.inter.EasyAdInterstitial; import com.easyads.core.inter.EAInterstitialListener; import com.easyads.core.nati.EasyAdNativeExpress; import com.easyads.core.nati.EANativeExpressListener; import com.easyads.core.reward.EasyAdRewardVideo; import com.easyads.core.reward.EARewardVideoListener; import com.easyads.core.reward.EARewardServerCallBackInf; import com.easyads.core.splash.EasyAdSplash; import com.easyads.core.splash.EASplashListener; import com.easyads.model.EasyAdError; import com.easyads.utils.EALog; import com.easyads.utils.ScreenUtil; import com.easyads.demo.custom.HuaWeiSplashAdapter; import com.easyads.demo.custom.XiaoMiSplashAdapter; import com.hjq.toast.ToastUtils; import java.io.BufferedReader; import java.io.InputStreamReader; import java.lang.ref.SoftReference; /** * 进一步封装了使用 EasyAds 的广告加载逻辑,方便代码中调用,也为了尽量广告处理代码独立。 * EasyAds不会进行任何的采集和上报处理,如果有统计需求,APP开发者可以在对应的时间回调内进行统一上报处理。 */ public class EasyADController { EasyAdBaseAdspot baseAD; Activity mActivity; //小米渠道是否需要添加为自定义渠道 public boolean cusXiaoMi = false; //华为渠道是否需要添加为自定义渠道 public boolean cusHuaWei = false; /** * 初始化广告处理类 * * @param activity 页面上下文 */ public EasyADController(Activity activity) { mActivity = activity; } /** * 加载开屏广告,开屏推荐使用加载并展示开屏广告方式,所有的广告均支持请求和展示分离,如有必要,可分别调用加载广告和展示广告,可参考"插屏广告"中的处理示例。 * * @param adContainer 广告承载布局,不可为空 * @param logoContainer 底部logo布局,可以为空 * @param singleActivity 是否为单独activity中展示开屏广告 * @param callBack 跳转回调,在回调中进行跳转主页或其他操作 */ public void loadSplash(String jsonFileName, final ViewGroup adContainer, final ViewGroup logoContainer, boolean singleActivity, final SplashCallBack callBack) { //必须:设置开屏核心回调事件的监听器。 EASplashListener listener = new EASplashListener() { @Override public void onAdClose() { if (callBack != null) callBack.jumpMain(); logAndToast(mActivity, "广告关闭"); } @Override public void onAdSucceed() { logAndToast(mActivity, "广告加载成功"); if (logoContainer != null) logoContainer.setVisibility(View.VISIBLE); } @Override public void onAdExposure() { //设置开屏父布局背景色为白色 if (adContainer != null) adContainer.setBackgroundColor(Color.WHITE); logAndToast(mActivity, "广告展示成功"); } @Override public void onAdFailed(EasyAdError error) { logAndToast(mActivity, "广告加载失败 code=" + error.code + " msg=" + error.msg); } @Override public void onAdClicked() { logAndToast(mActivity, "广告点击"); } }; EasyAdSplash easySplash = new EasyAdSplash(mActivity, adContainer, listener); baseAD = easySplash; //注意:如果开屏页是fragment或者dialog实现,这里需要置为false。默认为true,代表开屏和首页为两个不同的activity easySplash.setShowInSingleActivity(singleActivity); if (cusXiaoMi) { //注意:此处自定义渠道的tag,一定要和setData()中配置的tag一致。 easySplash.addCustomSupplier("xm", new XiaoMiSplashAdapter(new SoftReference<>(mActivity), easySplash)); } if (cusHuaWei) { easySplash.addCustomSupplier("hw", new HuaWeiSplashAdapter(new SoftReference<>(mActivity), easySplash)); } //必须:设置策略信息 easySplash.setData(getJson(mActivity, jsonFileName)); //必须:请求并展示广告 easySplash.loadAndShow(); logAndToast(mActivity, "广告请求中"); } /** * 开屏跳转回调 */ public interface SplashCallBack { void jumpMain(); } /** * 加载并展示banner广告 * * @param adContainer banner广告的承载布局 */ public void loadBanner(String jsonFileName, ViewGroup adContainer) { //必须:核心事件监听回调 EABannerListener listener = new EABannerListener() { @Override public void onAdClose() { logAndToast(mActivity, "广告关闭"); } @Override public void onAdExposure() { logAndToast(mActivity, "广告展现"); } @Override public void onAdFailed(EasyAdError error) { logAndToast(mActivity, "广告加载失败 code=" + error.code + " msg=" + error.msg); } @Override public void onAdClicked() { logAndToast(mActivity, "广告点击"); } @Override public void onAdSucceed() { logAndToast(mActivity, "广告加载成功"); } }; EasyAdBanner easyAdBanner = new EasyAdBanner(mActivity, adContainer, listener); baseAD = easyAdBanner; //如果集成穿山甲,这里必须配置,建议尺寸要和穿山甲后台中的"代码位尺寸"宽高比例一致,值单位为dp,这里示例使用的广告位宽高比为640:100。 int adWidth = ScreenUtil.px2dip(mActivity, ScreenUtil.getScreenWidth(mActivity)); int adHeight = (int) (((double) adWidth / (double) 640) * 100); //如果高度传入0代表自适应高度 easyAdBanner.setCsjExpressSize(adWidth, adHeight); //必须:设置策略信息 easyAdBanner.setData(getJson(mActivity, jsonFileName)); //必须:请求并展示广告 easyAdBanner.loadAndShow(); logAndToast(mActivity, "广告请求中"); } /** * 初始话插屏广告。 * 可以选择性先提前加载,然后在合适的时机再调用展示方法 * 或者直接调用加载并展示广告 *

* 注意!!!:穿山甲默认为"新插屏广告" */ public EasyAdInterstitial initInterstitial(String jsonFileName) { //必须:核心事件监听回调 EAInterstitialListener listener = new EAInterstitialListener() { @Override public void onAdSucceed() { logAndToast(mActivity, "广告就绪"); } @Override public void onAdClose() { logAndToast(mActivity, "广告关闭"); } @Override public void onAdExposure() { logAndToast(mActivity, "广告展示"); } @Override public void onAdFailed(EasyAdError error) { logAndToast(mActivity, "广告加载失败 code=" + error.code + " msg=" + error.msg); } @Override public void onAdClicked() { logAndToast(mActivity, "广告点击"); } }; //初始化 EasyAdInterstitial easyInterstitial = new EasyAdInterstitial(mActivity, listener); baseAD = easyInterstitial; //注意:穿山甲默认为"新插屏广告",如果要使用旧版请打开这条设置 // easyInterstitial.setCsjNew(false); //必须:设置策略信息 easyInterstitial.setData(getJson(mActivity, jsonFileName)); return easyInterstitial; } /** * 加载并展示激励视频广告。 * 也可以选择性先提前加载,然后在合适的时机再调用展示方法 */ public EasyAdRewardVideo initReward(String jsonFileName) { //必须:核心事件监听回调 EARewardVideoListener listener = new EARewardVideoListener() { @Override public void onAdSucceed() { logAndToast(mActivity, "广告加载成功"); } @Override public void onAdExposure() { logAndToast(mActivity, "广告展示"); } @Override public void onAdFailed(EasyAdError error) { logAndToast(mActivity, "广告加载失败 code=" + error.code + " msg=" + error.msg); } @Override public void onAdClicked() { logAndToast(mActivity, "广告点击"); } @Override public void onVideoCached() { logAndToast(mActivity, "广告缓存成功"); } @Override public void onVideoComplete() { logAndToast(mActivity, "视频播放完毕"); } @Override public void onVideoSkip() { } @Override public void onAdClose() { logAndToast(mActivity, "广告关闭"); } @Override public void onAdReward() { logAndToast(mActivity, "激励发放"); } @Override public void onRewardServerInf(EARewardServerCallBackInf inf) { //优量汇和穿山甲支持回调服务端激励验证信息,详见RewardServerCallBackInf中字段信息 logAndToast(mActivity, "onRewardServerInf" + inf); } }; //初始化,注意需要时再初始化,不要复用。 EasyAdRewardVideo easyRewardVideo = new EasyAdRewardVideo(mActivity, listener); baseAD = easyRewardVideo; //必须:设置策略信息 easyRewardVideo.setData(getJson(mActivity, jsonFileName)); return easyRewardVideo; } /** * 初始化获取展示全屏视频的广告对象。 * 也可以选择先提前加载,然后在合适的时机再调用展示方法 */ public EasyAdFullScreenVideo initFullVideo(String jsonFileName) { //推荐:核心事件监听回调 EAFullScreenVideoListener listener = new EAFullScreenVideoListener() { @Override public void onAdSucceed() { logAndToast(mActivity, "广告加载成功"); } @Override public void onAdClose() { logAndToast(mActivity, "广告关闭"); } @Override public void onVideoComplete() { logAndToast(mActivity, "视频播放结束"); } @Override public void onVideoSkipped() { logAndToast(mActivity, "跳过视频"); } @Override public void onVideoCached() { //广告缓存成功,可以在此记录状态,但要注意:不一定所有的广告会返回该回调 logAndToast(mActivity, "广告缓存成功"); } @Override public void onAdExposure() { logAndToast(mActivity, "广告展示"); } @Override public void onAdFailed(EasyAdError error) { logAndToast(mActivity, "广告加载失败 code=" + error.code + " msg=" + error.msg); } @Override public void onAdClicked() { logAndToast(mActivity, "广告点击"); } }; //初始化 EasyAdFullScreenVideo easyFullScreenVideo = new EasyAdFullScreenVideo(mActivity, listener); baseAD = easyFullScreenVideo; //必须:设置策略信息 easyFullScreenVideo.setData(getJson(mActivity, jsonFileName)); return easyFullScreenVideo; } public boolean hasNativeShow = false; boolean isNativeLoading = false; /** * 加载并展示原生模板信息流广告 * * @param adContainer 广告的承载布局 */ public void loadNativeExpress(String jsonFileName, ViewGroup adContainer) { if (hasNativeShow) {//同一位置广告,已展示过不再重复发起请求 EALog.d("loadNativeExpress hasNativeShow"); return; } if (isNativeLoading) {//同一位置广告,正在请求中,不再重复请求 EALog.d("loadNativeExpress isNativeLoading"); return; } isNativeLoading = true; if (adContainer.getChildCount() > 0) { adContainer.removeAllViews(); } //推荐:核心事件监听回调 EANativeExpressListener listener = new EANativeExpressListener() { @Override public void onAdSucceed() { logAndToast(mActivity, "广告加载成功"); } @Override public void onAdRenderSuccess() { logAndToast(mActivity, "广告渲染成功"); } @Override public void onAdClose() { logAndToast(mActivity, "广告关闭"); } @Override public void onAdExposure() { hasNativeShow = true; isNativeLoading = false; logAndToast(mActivity, "广告展示"); } @Override public void onAdFailed(EasyAdError error) { isNativeLoading = false; logAndToast(mActivity, "广告加载失败 code=" + error.code + " msg=" + error.msg); } @Override public void onAdRenderFailed() { isNativeLoading = false; logAndToast(mActivity, "广告渲染失败"); } @Override public void onAdClicked() { logAndToast(mActivity, "广告点击"); } }; //初始化 final EasyAdNativeExpress easyNativeExpress = new EasyAdNativeExpress(mActivity, listener); baseAD = easyNativeExpress; easyNativeExpress.setAdContainer(adContainer); //必须:设置策略信息 easyNativeExpress.setData(getJson(mActivity, jsonFileName)); //必须:请求并展示广告 easyNativeExpress.loadAndShow(); logAndToast(mActivity, "广告请求中"); } public EasyAdDraw easyAdDraw; public void loadDraw(String jsonFileName, ViewGroup adContainer) { EADrawListener listener = new EADrawListener() { @Override public void onAdSucceed() { if (easyAdDraw != null) { easyAdDraw.show(); } logAndToast(mActivity, "广告加载成功"); } @Override public void onAdExposure() { logAndToast(mActivity, "广告展示"); } @Override public void onAdClicked() { logAndToast(mActivity, "广告点击"); } @Override public void onAdClose() { //此位置不存在广告关闭动作,当前回调不会触发 logAndToast(mActivity, "广告关闭"); } @Override public void onAdFailed(EasyAdError error) { logAndToast(mActivity, "广告加载失败 code=" + error.code + " msg=" + error.msg); } }; easyAdDraw = new EasyAdDraw(mActivity, listener); baseAD = easyAdDraw; easyAdDraw.setAdContainer(adContainer); //必须:设置策略信息 easyAdDraw.setData(getJson(mActivity, jsonFileName)); //必须:请求并展示广告 easyAdDraw.loadAndShow(); logAndToast(mActivity, "广告请求中"); } /** * 销毁广告 */ public void destroy() { if (baseAD != null) { baseAD.destroy(); baseAD = null; } } /** * 统一处理打印日志,并且toast提示。 * * @param context 上下文 * @param msg 需要显示的内容 */ public static void logAndToast(Context context, String msg) { Log.d("[DemoUtil][logAndToast]", msg); ToastUtils.debugShow(msg); } /** * 获取存放在assets下面的广告json文件内容,建议APP端有条件的话,通过后端下发json配置内容 */ public static String getJson(Context context, String fileName) { StringBuilder stringBuilder = new StringBuilder(); try { AssetManager assetManager = context.getAssets(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(assetManager.open(fileName))); String line; while ((line = bufferedReader.readLine()) != null) { stringBuilder.append(line); } } catch (Throwable e) { e.printStackTrace(); } return stringBuilder.toString(); } } ================================================ FILE: example/src/main/java/com/easyads/demo/FullScreenVideoActivity.java ================================================ package com.easyads.demo; import android.os.Bundle; import android.support.annotation.Nullable; import android.view.View; import com.easyads.core.full.EasyAdFullScreenVideo; public class FullScreenVideoActivity extends BaseActivity { EasyAdFullScreenVideo fullScreenVideo; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_full_screen_video); } public void loadFull(View view) { fullScreenVideo = new EasyADController(this).initFullVideo("full_config.json"); fullScreenVideo.loadOnly(); } public void showFull(View view) { if (fullScreenVideo != null) { fullScreenVideo.show(); } else { EasyADController.logAndToast(this, "需要先调用loadOnly()"); } } public void loadAndShowFull(View view) { new EasyADController(this).initFullVideo("full_config.json").loadAndShow(); } } ================================================ FILE: example/src/main/java/com/easyads/demo/InterstitialActivity.java ================================================ package com.easyads.demo; import android.os.Bundle; import android.view.View; import com.easyads.core.inter.EasyAdInterstitial; public class InterstitialActivity extends BaseActivity { EasyAdInterstitial interstitialAD; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_interstitial); } public void loadAd(View view) { interstitialAD = new EasyADController(this).initInterstitial("interstitial_config.json"); interstitialAD.loadOnly(); } public void showAd(View view) { if (interstitialAD != null) { interstitialAD.show(); } else { EasyADController.logAndToast(this, "需要先调用loadOnly()"); } } public void loadAndShowAd(View view) { new EasyADController(this).initInterstitial("interstitial_config.json").loadAndShow(); } } ================================================ FILE: example/src/main/java/com/easyads/demo/MainActivity.java ================================================ package com.easyads.demo; import android.Manifest; import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.view.View; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.TextView; import com.easyads.EasyAds; import com.baidu.mobads.sdk.api.AdSettings; import com.easyads.demo.custom.CustomActivity; import com.easyads.demo.utils.BaseCallBack; import com.easyads.demo.utils.Constant; import com.easyads.demo.utils.NormalSetting; import com.easyads.demo.utils.UserPrivacyDialog; import com.bytedance.sdk.openadsdk.TTAdSdk; import com.kwad.sdk.api.KsAdSDK; import com.qq.e.comm.managers.status.SDKStatus; import java.util.ArrayList; import java.util.List; public class MainActivity extends BaseActivity { TextView version; CheckBox checkBox; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); version = findViewById(R.id.tv_version); checkBox = findViewById(R.id.cb_log); checkBox.setChecked(NormalSetting.getInstance().showLogcat); checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { NormalSetting.getInstance().showLogcat = isChecked; } }); TextView title = findViewById(R.id.tv_title); title.setText("EasyAds-简单聚合 急速变现"); boolean hasPri = getSharedPreferences(Constant.SP_NAME, Context.MODE_PRIVATE).getBoolean(Constant.SP_AGREE_PRIVACY, false); /** * 注意!:由于工信部对设备权限等隐私权限要求愈加严格,强烈推荐APP提前申请好权限,且用户同意隐私政策后再加载广告 */ if (!hasPri) { UserPrivacyDialog dialog = new UserPrivacyDialog(this); dialog.callBack = new BaseCallBack() { @Override public void call() { //一定要用户授权同意隐私协议后,再申领必要权限 if (Build.VERSION.SDK_INT >= 23 && Build.VERSION.SDK_INT < 29) { checkAndRequestPermission(); } } }; dialog.show(); } version.setText("版本号:EasyAds聚合SDK:v" + EasyAds.getVersion() + "\n穿山甲:v" + TTAdSdk.getAdManager().getSDKVersion() + " 优量汇:v" + SDKStatus.getSDKVersion() + " 百度:v" + AdSettings.getSDKVersion() + " 快手:v" + KsAdSDK.getSDKVersion()); } @TargetApi(Build.VERSION_CODES.M) private void checkAndRequestPermission() { List lackedPermission = new ArrayList(); if (!(checkSelfPermission(Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED)) { lackedPermission.add(Manifest.permission.READ_PHONE_STATE); } if (!(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) { lackedPermission.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); } // 缺少权限,进行申请 if (lackedPermission.size() > 0) { // 请求所缺少的权限,在onRequestPermissionsResult中再看是否获得权限,如果获得权限就可以调用SDK,否则不要调用SDK。 String[] requestPermissions = new String[lackedPermission.size()]; lackedPermission.toArray(requestPermissions); requestPermissions(requestPermissions, 1024); } } public void onBanner(View view) { startActivity(new Intent(this, BannerActivity.class)); } public void onSplash(View view) { Intent intent = new Intent(this, SplashActivity.class); startActivity(intent); } public void onNativeExpress(View view) { startActivity(new Intent(this, NativeExpressActivity.class)); } public void onRewardVideo(View view) { startActivity(new Intent(this, RewardVideoActivity.class)); } public void onNativeExpressRecyclerView(View view) { startActivity(new Intent(this, NativeExpressRecyclerViewActivity.class)); } public void onInterstitial(View view) { startActivity(new Intent(this, InterstitialActivity.class)); } public void onFullVideo(View view) { startActivity(new Intent(this, FullScreenVideoActivity.class)); } public void onSplashDialog(View view) { SplashDialog dialog = new SplashDialog(this); dialog.show(); } public void draw(View view) { startActivity(new Intent(this, DrawActivity.class)); } public void customChannel(View view) { startActivity(new Intent(this, CustomActivity.class)); } } ================================================ FILE: example/src/main/java/com/easyads/demo/MyApplication.java ================================================ package com.easyads.demo; import android.app.Application; import android.content.Context; import android.support.multidex.MultiDex; import com.easyads.EasyAds; import com.easyads.model.EALogLevel; import com.easyads.demo.utils.Constant; import com.hjq.toast.ToastUtils; import com.huawei.hms.ads.HwAds; public class MyApplication extends Application { static MyApplication instance; public static MyApplication getInstance() { if (instance == null) { instance = new MyApplication(); } return instance; } @Override public void onCreate() { super.onCreate(); instance = this; boolean hasPri = getSharedPreferences(Constant.SP_NAME, Context.MODE_PRIVATE).getBoolean(Constant.SP_AGREE_PRIVACY, false); //建议当用户同意了隐私政策以后才调用SDK初始化 if (hasPri) { initSDK(); } } public void initSDK() { //设置debug模式,日志可分等级打印,默认只打印简单的事件信息 EasyAds.setDebug(BuildConfig.DEBUG, EALogLevel.DEFAULT); //自定义渠道-华为广告的初始化,如果不需要自定义可忽略此处 HwAds.init(this); // 初始化 Toast 框架 ToastUtils.init(this); } @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); // 调用MutiDex } } ================================================ FILE: example/src/main/java/com/easyads/demo/NativeExpressActivity.java ================================================ package com.easyads.demo; import android.os.Bundle; import android.view.View; import android.widget.FrameLayout; public class NativeExpressActivity extends BaseActivity { private FrameLayout container; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_native_express); container = findViewById(R.id.native_express_container); new EasyADController(this).loadNativeExpress("native_config.json",container); } public void loadNEAD(View view) { new EasyADController(this).loadNativeExpress("native_config.json",container); } } ================================================ FILE: example/src/main/java/com/easyads/demo/NativeExpressRecyclerViewActivity.java ================================================ package com.easyads.demo; import android.app.Activity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.widget.TextView; import java.util.ArrayList; import java.util.List; /** * 在消息流中接入原生模板广告的示例 */ public class NativeExpressRecyclerViewActivity extends BaseActivity { public static final int MAX_ITEMS = 50; public static int FIRST_AD_POSITION = 1; // 第一条广告的位置 public static int ITEMS_PER_AD = 10; // 每间隔10个条目插入一条广告 private RecyclerView mRecyclerView; private List mNormalDataList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_native_express_recycler_view); mRecyclerView = findViewById(R.id.recycler_view); mRecyclerView.setHasFixedSize(true); mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); initData(); } private void initData() { for (int i = 0; i < MAX_ITEMS; ++i) { mNormalDataList.add(new RycItem("No." + i + " Normal Data")); } //添加广告的数目,这里定义了3条广告。 int maxAD = 4; for (int i = 0; i < maxAD; i++) { int position = FIRST_AD_POSITION + ITEMS_PER_AD * i; if (position <= mNormalDataList.size()) { //将广告的NormalItem定义为默认的空标题item。 //也可以不同item使用不同的广告位id作为广告标识,这样方便区分不同item的广告数据表现。 RycItem adItem = new RycItem(""); //核心步骤1:初始化广告处理类。 adItem.ad = new EasyADController(this); mNormalDataList.add(position, adItem); } } //列表adapter创建,传入要渲染的数据信息 CustomAdapter mAdapter = new CustomAdapter(this, mNormalDataList); mRecyclerView.setAdapter(mAdapter); //一定要加上该配置,防止item复用导致广告重复 mRecyclerView.setItemViewCacheSize(500); } /** * RecyclerView的Adapter */ static class CustomAdapter extends RecyclerView.Adapter { static final int TYPE_DATA = 0; static final int TYPE_AD = 1; private List mData; Activity mActivity; public CustomAdapter(Activity activity, List list) { mActivity = activity; mData = list; } @Override public int getItemCount() { if (mData != null) { return mData.size(); } else { return 0; } } @Override public int getItemViewType(int position) { //核心步骤2:根据是否包含标题内容,来判断是否为广告item return TextUtils.isEmpty(mData.get(position).title) ? TYPE_AD : TYPE_DATA; } @Override public void onBindViewHolder(final CustomViewHolder customViewHolder, final int position) { int type = getItemViewType(position); if (TYPE_AD == type) { //核心步骤3:如果是广告布局,执行广告加载 EasyADController ad = mData.get(position).ad; ad.loadNativeExpress("native_config.json", customViewHolder.container); } else { customViewHolder.title.setText(mData.get(position).getTitle()); } } @Override public CustomViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { int layoutId = (viewType == TYPE_AD) ? R.layout.item_express_ad : R.layout.item_data; View view = LayoutInflater.from(viewGroup.getContext()).inflate(layoutId, null); CustomViewHolder viewHolder = new CustomViewHolder(view); return viewHolder; } static class CustomViewHolder extends RecyclerView.ViewHolder { public TextView title; public ViewGroup container; // 广告承载布局 public CustomViewHolder(View view) { super(view); title = view.findViewById(R.id.title); container = view.findViewById(R.id.express_ad_container); } } } //列表子类 public static class RycItem { private String title; public EasyADController ad; // 广告处理类 public RycItem(String title) { this.title = title; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } } } ================================================ FILE: example/src/main/java/com/easyads/demo/RewardVideoActivity.java ================================================ package com.easyads.demo; import android.os.Bundle; import android.view.View; import com.easyads.core.reward.EasyAdRewardVideo; public class RewardVideoActivity extends BaseActivity { EasyAdRewardVideo rewardVideo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_reward_video); } public void onLoad(View view) { rewardVideo = new EasyADController(this).initReward("reward_config.json"); rewardVideo.loadOnly(); } public void onShow(View view) { if (rewardVideo != null) { rewardVideo.show(); } else { EasyADController.logAndToast(this, "需要先调用loadOnly()"); } } public void loadAndShow(View view) { new EasyADController(this).initReward("reward_config.json").loadAndShow(); } } ================================================ FILE: example/src/main/java/com/easyads/demo/SplashActivity.java ================================================ package com.easyads.demo; import android.app.ActionBar; import android.content.Intent; import android.os.Bundle; import android.view.Window; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.LinearLayout; import com.easyads.demo.utils.Constant; public class SplashActivity extends BaseActivity { LinearLayout logo; FrameLayout adContainer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //全屏设置 requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); ActionBar actionBar = getActionBar(); if (actionBar != null) { actionBar.hide(); } setContentView(R.layout.activity_splash_custom_logo); adContainer = findViewById(R.id.splash_container); logo = findViewById(R.id.ll_logo); //初始化广告处理封装类 EasyADController ad = new EasyADController(this); String jsonName = "splash_config.json"; //自定义渠道部分设置,不需要的可以不添加 ad.cusHuaWei = getIntent().getBooleanExtra(Constant.CUS_HW, false); ad.cusXiaoMi = getIntent().getBooleanExtra(Constant.CUS_XM, false); if (ad.cusHuaWei || ad.cusXiaoMi) { //使用包含自定义渠道的配置 jsonName = "splash_cus_config.json"; } /** * 加载广告 */ ad.loadSplash(jsonName, adContainer, logo, true, new EasyADController.SplashCallBack() { @Override public void jumpMain() { goToMainActivity(); } }); } /** * 跳转到主页面 */ private void goToMainActivity() { startActivity(new Intent(this, SplashToMainActivity.class)); this.finish(); } } ================================================ FILE: example/src/main/java/com/easyads/demo/SplashDialog.java ================================================ package com.easyads.demo; import android.app.Activity; import android.app.Dialog; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.support.annotation.NonNull; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.LinearLayout; public class SplashDialog extends Dialog { LinearLayout logo; FrameLayout adContainer; public SplashDialog(@NonNull final Activity context) { super(context); setContentView(R.layout.activity_splash_custom_logo); //设置window背景,默认的背景会有Padding值,不能全屏。当然不一定要是透明,你可以设置其他背景,替换默认的背景即可。 getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); //一定要在setContentView之后调用,否则无效 getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); adContainer = findViewById(R.id.splash_container); logo = findViewById(R.id.ll_logo); new EasyADController(context).loadSplash("splash_config.json", adContainer, logo, false, new EasyADController.SplashCallBack() { @Override public void jumpMain() { dismiss(); } }); } } ================================================ FILE: example/src/main/java/com/easyads/demo/SplashToMainActivity.java ================================================ package com.easyads.demo; import android.os.Bundle; import android.support.annotation.Nullable; import com.easyads.utils.EASplashPlusManager; public class SplashToMainActivity extends BaseActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash_to_main); //如果需要支持穿山甲、优量汇的特殊开屏(v+、点睛)展示模式,这里需要进行调用适配,否则无法生效。 EASplashPlusManager.startZoom(this); } } ================================================ FILE: example/src/main/java/com/easyads/demo/custom/CustomActivity.java ================================================ package com.easyads.demo.custom; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.view.View; import com.easyads.demo.R; import com.easyads.demo.SplashActivity; import com.easyads.demo.utils.Constant; public class CustomActivity extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_custom); } public void cusHW(View view) { Intent intent = new Intent(this, SplashActivity.class); intent.putExtra(Constant.CUS_HW,true); startActivity(intent); } public void cusXM(View view) { Intent intent = new Intent(this, SplashActivity.class); intent.putExtra(Constant.CUS_XM,true); startActivity(intent); } } ================================================ FILE: example/src/main/java/com/easyads/demo/custom/HuaWeiSplashAdapter.java ================================================ package com.easyads.demo.custom; import android.app.Activity; import android.app.Application; import android.content.pm.ActivityInfo; import android.os.Bundle; import android.os.Handler; import com.easyads.core.splash.EASplashSetting; import com.easyads.custom.EASplashCustomAdapter; import com.easyads.utils.EALog; import com.huawei.hms.ads.AdParam; import com.huawei.hms.ads.splash.SplashAdDisplayListener; import com.huawei.hms.ads.splash.SplashView; import java.lang.ref.SoftReference; public class HuaWeiSplashAdapter extends EASplashCustomAdapter { SplashView splashView; private boolean isCountingEnd = false;//用来辅助判断用户行为,用户是点击了跳过还是倒计时结束,false 回调dismiss的话代表是跳过,否则倒计时结束 String TAG = "[HuaWeiSplashAdapter] "; private Application.ActivityLifecycleCallbacks alcb; public HuaWeiSplashAdapter(SoftReference activity, EASplashSetting splashSetting) { super(activity, splashSetting); if (alcb != null) { //如果已存在,先注销 activity.get().getApplication().unregisterActivityLifecycleCallbacks(alcb); } //注册Lifecycle生命周期监控,用于在合适的生命周期处理广告事件 alcb = new Application.ActivityLifecycleCallbacks() { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { } @Override public void onActivityStarted(Activity activity) { } @Override public void onActivityResumed(Activity activity) { if (getActivity() == activity && splashView != null) { splashView.resumeView(); } } @Override public void onActivityPaused(Activity activity) { if (getActivity() == activity && splashView != null) { splashView.pauseView(); } } @Override public void onActivityStopped(Activity activity) { } @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { } @Override public void onActivityDestroyed(Activity activity) { } }; activity.get().getApplication().registerActivityLifecycleCallbacks(alcb); } @Override protected void doLoadAD() { //请求广告 EALog.simple(TAG + "doLoadAD"); loadAD(); } @Override protected void doShowAD() {//展示广告 adContainer.addView(splashView); } @Override public void doDestroy() {//销毁广告 EALog.simple(TAG + "doDestroy"); if (splashView != null) { splashView.destroyView(); } if (alcb != null) { //先注销监听 getActivity().getApplication().unregisterActivityLifecycleCallbacks(alcb); } } private void loadAD() { int orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; AdParam adParam = new AdParam.Builder().build(); splashView = new SplashView(getActivity()); // 一定要调用基类生命相关的handle周期方法 SplashAdDisplayListener adDisplayListener = new SplashAdDisplayListener() { @Override public void onAdShowed() { //一定要调用此方法,否则对应回调方法无法执行 handleExposure(); try { new Handler().postDelayed(new Runnable() { @Override public void run() { isCountingEnd = true; } }, 4800); } catch (Throwable e) { e.printStackTrace(); } } @Override public void onAdClick() { //一定要调用此方法,否则对应回调方法无法执行 handleClick(); } }; splashView.setAdDisplayListener(adDisplayListener); splashView.load(getPosID(), orientation, adParam, new SplashView.SplashAdLoadListener() { @Override public void onAdFailedToLoad(int errorCode) { super.onAdFailedToLoad(errorCode); EALog.e(TAG + "广告失败:errorCode = " + errorCode); //注意:一定要在广告失败试,执行此方法,否则无法加载后续渠道请求,导致此次请求无广告展示 handleFailed(errorCode + "", ""); } @Override public void onAdLoaded() { super.onAdLoaded(); EALog.high(TAG + "onAdLoaded "); //一定要调用此方法,否则对应回调方法无法执行 handleSucceed(); } @Override public void onAdDismissed() { super.onAdDismissed(); EALog.high(TAG + "onAdDismissed "); //广告关闭事件回调,回调区分用户点击了跳过还是倒计时结束 if (mSplashSetting != null) { if (isCountingEnd) { mSplashSetting.adapterDidTimeOver(sdkSupplier); } else { mSplashSetting.adapterDidSkip(sdkSupplier); } } } }); } } ================================================ FILE: example/src/main/java/com/easyads/demo/custom/XiaoMiSplashAdapter.java ================================================ package com.easyads.demo.custom; import android.app.Activity; import android.os.Handler; import com.easyads.core.splash.EASplashSetting; import com.easyads.custom.EASplashCustomAdapter; import com.easyads.model.EasyAdError; import com.miui.zeus.mimo.sdk.MimoSdk; import com.miui.zeus.mimo.sdk.SplashAd; import java.lang.ref.SoftReference; /** * 自定义小米开屏渠道 */ public class XiaoMiSplashAdapter extends EASplashCustomAdapter { SplashAd mSplashAd; private boolean isCountingEnd = false;//用来判断是否倒计时走到了最后,false 回调dismiss的话代表是跳过,否则倒计时结束 public XiaoMiSplashAdapter(SoftReference activity, EASplashSetting splashSetting) { super(activity, splashSetting); } @Override protected void doLoadAD() { loadAndShowXM(); } @Override public void doDestroy() { //销毁广告 if (mSplashAd != null) { mSplashAd.destroy(); } } @Override protected void doShowAD() { } /** * 初始化小米SDK,并请求和展示广告 */ private void loadAndShowXM() { MimoSdk.init(getActivity()); mSplashAd = new SplashAd(); //执行广告展示方法,并在对应回调中执行对应生命周期回调 mSplashAd.loadAndShow(adContainer, getPosID(), new SplashAd.SplashAdListener() { @Override public void onAdShow() { //一定要调用此方法,否则对应回调方法无法执行 handleExposure(); new Handler().postDelayed(new Runnable() { @Override public void run() { isCountingEnd = true; } }, 4800); } @Override public void onAdClick() { //一定要调用此方法,否则对应回调方法无法执行 handleClick(); } @Override public void onAdDismissed() { //必要回调:广告关闭事件回调,回调区分用户点击了跳过还是计时结束 if (mSplashSetting != null) { if (isCountingEnd) { mSplashSetting.adapterDidTimeOver(sdkSupplier); } else { mSplashSetting.adapterDidSkip(sdkSupplier); } } } @Override public void onAdLoadFailed(int i, String s) { //注意:一定要在广告失败试,执行此方法,否则无法加载后续渠道请求,导致此次请求无广告展示 handleFailed(i + "", s); } @Override public void onAdLoaded() { //一定要调用此方法,否则对应回调方法无法执行 handleSucceed(); } @Override public void onAdRenderFailed() { //注意:一定要在广告失败试,执行此方法,否则无法加载后续渠道请求,导致此次请求无广告展示 handleFailed(EasyAdError.ERROR_RENDER_FAILED, ""); } }); } } ================================================ FILE: example/src/main/java/com/easyads/demo/draw/FullScreenVideoView.java ================================================ package com.easyads.demo.draw; import android.content.Context; import android.util.AttributeSet; import android.widget.VideoView; public class FullScreenVideoView extends VideoView { public FullScreenVideoView(Context context) { super(context); } public FullScreenVideoView(Context context, AttributeSet attrs) { super(context, attrs); } public FullScreenVideoView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = getDefaultSize(0, widthMeasureSpec); int height = getDefaultSize(0, heightMeasureSpec); setMeasuredDimension(width, height); } } ================================================ FILE: example/src/main/java/com/easyads/demo/draw/OnViewPagerListener.java ================================================ package com.easyads.demo.draw; public interface OnViewPagerListener { /* 初始化完成 */ void onInitComplete(); /* 释放的监听 */ void onPageRelease(boolean isNext, int position); /* 选中的监听以及判断是否滑动到底部 */ void onPageSelected(int position, boolean isBottom); } ================================================ FILE: example/src/main/java/com/easyads/demo/draw/ViewPagerLayoutManager.java ================================================ package com.easyads.demo.draw; import android.content.Context; import android.support.annotation.NonNull; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.PagerSnapHelper; import android.support.v7.widget.RecyclerView; import android.view.View; public class ViewPagerLayoutManager extends LinearLayoutManager implements RecyclerView.OnChildAttachStateChangeListener { private PagerSnapHelper mPagerSnapHelper; private OnViewPagerListener mOnViewPagerListener; private int mDrift;// 位移,用来判断移动方向 { mPagerSnapHelper = new PagerSnapHelper(); } public ViewPagerLayoutManager(Context context, int orientation) { this(context, orientation, false); } public ViewPagerLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } @Override public void onAttachedToWindow(RecyclerView recyclerView) { super.onAttachedToWindow(recyclerView); mPagerSnapHelper.attachToRecyclerView(recyclerView); recyclerView.addOnChildAttachStateChangeListener(this); } @Override public void onScrollStateChanged(int state) { if (state == RecyclerView.SCROLL_STATE_IDLE) { View viewIdle = mPagerSnapHelper.findSnapView(ViewPagerLayoutManager.this); if (viewIdle == null) { return; } int positionIdle = getPosition(viewIdle); if (mOnViewPagerListener != null && getChildCount() == 1) { mOnViewPagerListener.onPageSelected(positionIdle, positionIdle == getItemCount() - 1); } } } /** * 监听竖直方向的相对偏移量 */ @Override public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { this.mDrift = dy; return super.scrollVerticallyBy(dy, recycler, state); } /** * 监听水平方向的相对偏移量 */ @Override public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { this.mDrift = dx; return super.scrollHorizontallyBy(dx, recycler, state); } /** * 设置监听 */ public void setOnViewPagerListener(OnViewPagerListener listener) { this.mOnViewPagerListener = listener; } @Override public void onChildViewAttachedToWindow(@NonNull View view) { if (mOnViewPagerListener != null && getChildCount() == 1) { mOnViewPagerListener.onInitComplete(); } } @Override public void onChildViewDetachedFromWindow(@NonNull View view) { if (mDrift >= 0) { if (mOnViewPagerListener != null) mOnViewPagerListener.onPageRelease(true, getPosition(view)); } else { if (mOnViewPagerListener != null) mOnViewPagerListener.onPageRelease(false, getPosition(view)); } } } ================================================ FILE: example/src/main/java/com/easyads/demo/utils/BaseCallBack.java ================================================ package com.easyads.demo.utils; public interface BaseCallBack { void call(); } ================================================ FILE: example/src/main/java/com/easyads/demo/utils/Constant.java ================================================ package com.easyads.demo.utils; public class Constant { public static final String SP_AGREE_PRIVACY = "agree_privacy"; public static final String SP_NAME = "preference"; //标记自定义渠道 public static final String CUS_HW = "cusHW"; public static final String CUS_XM = "cusXM"; } ================================================ FILE: example/src/main/java/com/easyads/demo/utils/DialogLogcat.java ================================================ package com.easyads.demo.utils; import android.app.Dialog; import android.content.Context; import android.graphics.Point; import android.support.annotation.NonNull; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.TextView; import com.easyads.demo.R; import com.github.pedrovgs.lynx.LynxView; public class DialogLogcat extends Dialog { LynxView lynxView; public DialogLogcat(@NonNull Context context) { super(context); setContentView(R.layout.dialog_logcat); lynxView = findViewById(R.id.lv_log); TextView clear = findViewById(R.id.clear); TextView close = findViewById(R.id.close); clear.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //清除之前的记录 lynxView.clear(); } }); close.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); } }); setCanceledOnTouchOutside(false); setCancelable(false); } @Override public void show() { super.show(); try { //宽度全屏 Window window = getWindow(); if (window != null) { WindowManager.LayoutParams lp = window.getAttributes(); Point point = new Point(); window.getWindowManager().getDefaultDisplay().getSize(point); lp.gravity = Gravity.BOTTOM; lp.width = point.x; lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; window.setAttributes(lp); } } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: example/src/main/java/com/easyads/demo/utils/NormalSetting.java ================================================ package com.easyads.demo.utils; public class NormalSetting { static NormalSetting instance; public static NormalSetting getInstance() { if (instance == null) { instance = new NormalSetting(); } return instance; } public boolean showLogcat = true; } ================================================ FILE: example/src/main/java/com/easyads/demo/utils/UIUtils.java ================================================ package com.easyads.demo.utils; import android.app.Activity; import android.content.Context; import android.os.Build; import android.text.TextUtils; import android.util.DisplayMetrics; import android.view.Display; import android.view.DisplayCutout; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import android.view.WindowInsets; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.RelativeLayout; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * Created by bytedance on 2019/9/5. */ public class UIUtils { public static float getScreenWidthDp(Context context){ final float scale = context.getResources().getDisplayMetrics().density; float width = context.getResources().getDisplayMetrics().widthPixels; return width / (scale <= 0 ? 1 : scale) + 0.5f; } //全面屏、刘海屏适配 public static float getHeight(Activity activity) { hideBottomUIMenu(activity); float height; int realHeight = getRealHeight(activity); if (UIUtils.hasNotchScreen(activity)) { height = px2dip(activity, realHeight - getStatusBarHeight(activity)); }else { height = px2dip(activity, realHeight); } return height; } public static void hideBottomUIMenu(Activity activity) { if (activity == null) { return; } try { //隐藏虚拟按键,并且全屏 if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) { // lower api View v = activity.getWindow().getDecorView(); v.setSystemUiVisibility(View.GONE); } else if (Build.VERSION.SDK_INT >= 19) { //for new api versions. View decorView = activity.getWindow().getDecorView(); int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar // | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar | View.SYSTEM_UI_FLAG_IMMERSIVE; decorView.setSystemUiVisibility(uiOptions); activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); } } catch (Exception e) { e.printStackTrace(); } } //获取屏幕真实高度,不包含下方虚拟导航栏 public static int getRealHeight(Context context) { WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = windowManager.getDefaultDisplay(); DisplayMetrics dm = new DisplayMetrics(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { display.getRealMetrics(dm); } else { display.getMetrics(dm); } int realHeight = dm.heightPixels; return realHeight; } //获取状态栏高度 public static float getStatusBarHeight(Context context) { float height = 0; int resourceId = context.getApplicationContext().getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { height = context.getApplicationContext().getResources().getDimensionPixelSize(resourceId); } return height; } public static int px2dip(Context context, float pxValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / (scale <= 0 ? 1 : scale) + 0.5f); } public static int dp2px(Context context, float dp) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dp * scale + 0.5f); } /** * 判断是否是刘海屏 * @return */ public static boolean hasNotchScreen(Activity activity){ return isAndroidPHasNotch(activity) || getInt("ro.miui.notch", activity) == 1 || hasNotchAtHuawei(activity) || hasNotchAtOPPO(activity) || hasNotchAtVivo(activity); } /** * Android P 刘海屏判断 * @param activity * @return */ public static boolean isAndroidPHasNotch(Activity activity){ boolean result = false; if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { DisplayCutout displayCutout = null; try { WindowInsets windowInsets = activity.getWindow().getDecorView().getRootWindowInsets(); if (windowInsets != null) { displayCutout = windowInsets.getDisplayCutout(); } if (displayCutout != null) { result = true; } } catch (Exception e) { e.printStackTrace(); } } return result; } /** * 小米刘海屏判断. * @return 0 if it is not notch ; return 1 means notch * @throws IllegalArgumentException if the key exceeds 32 characters */ public static int getInt(String key, Activity activity) { int result = 0; if (isMiui()){ try { ClassLoader classLoader = activity.getClassLoader(); @SuppressWarnings("rawtypes") Class SystemProperties = classLoader.loadClass("android.os.SystemProperties"); //参数类型 @SuppressWarnings("rawtypes") Class[] paramTypes = new Class[2]; paramTypes[0] = String.class; paramTypes[1] = int.class; Method getInt = SystemProperties.getMethod("getInt", paramTypes); //参数 Object[] params = new Object[2]; params[0] = new String(key); params[1] = new Integer(0); result = (Integer) getInt.invoke(SystemProperties, params); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } return result; } /** * 华为刘海屏判断 * @return */ public static boolean hasNotchAtHuawei(Context context) { boolean ret = false; try { ClassLoader classLoader = context.getClassLoader(); Class HwNotchSizeUtil = classLoader.loadClass("com.huawei.android.util.HwNotchSizeUtil"); Method get = HwNotchSizeUtil.getMethod("hasNotchInScreen"); ret = (boolean) get.invoke(HwNotchSizeUtil); } catch (ClassNotFoundException e) { } catch (NoSuchMethodException e) { } catch (Exception e) { } finally { return ret; } } public static final int VIVO_NOTCH = 0x00000020;//是否有刘海 public static final int VIVO_FILLET = 0x00000008;//是否有圆角 /** * VIVO刘海屏判断 * @return */ public static boolean hasNotchAtVivo(Context context) { boolean ret = false; try { ClassLoader classLoader = context.getClassLoader(); Class FtFeature = classLoader.loadClass("android.util.FtFeature"); Method method = FtFeature.getMethod("isFeatureSupport", int.class); ret = (boolean) method.invoke(FtFeature, VIVO_NOTCH); } catch (ClassNotFoundException e) { } catch (NoSuchMethodException e) { } catch (Exception e) { } finally { return ret; } } /** * O-P-P-O刘海屏判断 * @return */ public static boolean hasNotchAtOPPO(Context context) { String temp = "com.kllk.feature.screen.heteromorphism"; String name = getKllkDecryptString(temp); return context.getPackageManager().hasSystemFeature(name); } public static boolean isMiui() { boolean sIsMiui = false; try { Class clz = Class.forName("miui.os.Build"); if (clz != null) { sIsMiui = true; //noinspection ConstantConditions return sIsMiui; } } catch (Exception e) { // ignore } return sIsMiui; } /** *用于o-p-p-o 版本隐私协议 */ public static String getKllkDecryptString(String encryptionString) { if (TextUtils.isEmpty(encryptionString)) { return ""; } String decryptTag = ""; String decryptCapitalized = "O" + "P" + "P" + "O"; String decrypt = "o" + "p" + "p" + "o"; if (encryptionString.contains("KLLK")) { decryptTag = encryptionString.replace("KLLK", decryptCapitalized); } else if (encryptionString.contains("kllk")) { decryptTag = encryptionString.replace("kllk", decrypt); } return decryptTag; } public static void setViewSize(View view, int width, int height) { if (view.getParent() instanceof FrameLayout) { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) view.getLayoutParams(); lp.width = width; lp.height = height; view.setLayoutParams(lp); view.requestLayout(); } else if (view.getParent() instanceof RelativeLayout) { RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) view.getLayoutParams(); lp.width = width; lp.height = height; view.setLayoutParams(lp); view.requestLayout(); } else if (view.getParent() instanceof LinearLayout) { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) view.getLayoutParams(); lp.width = width; lp.height = height; view.setLayoutParams(lp); view.requestLayout(); } } public static int getScreenWidthInPx(Context context) { DisplayMetrics dm = context.getApplicationContext().getResources().getDisplayMetrics(); return dm.widthPixels; } public static int getScreenHeightInPx(Context context) { DisplayMetrics dm = context.getApplicationContext().getResources().getDisplayMetrics(); return dm.heightPixels; } public static void removeFromParent(View view) { if (view != null) { ViewParent vp = view.getParent(); if (vp instanceof ViewGroup) { ((ViewGroup) vp).removeView(view); } } } /** * 获取全面屏宽高 * @param context * @return */ public static int[] getScreenSize(Context context) { int[] size = new int[]{0,0}; if (context == null){ return size; } WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = windowManager.getDefaultDisplay(); DisplayMetrics dm = new DisplayMetrics(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { display.getRealMetrics(dm); } else { display.getMetrics(dm); } size[0] = dm.widthPixels; size[1] = dm.heightPixels; return size; } } ================================================ FILE: example/src/main/java/com/easyads/demo/utils/UserPrivacyDialog.java ================================================ package com.easyads.demo.utils; import android.app.Dialog; import android.content.Context; import android.support.annotation.NonNull; import android.view.View; import android.widget.TextView; import com.easyads.demo.MyApplication; import com.easyads.demo.R; public class UserPrivacyDialog extends Dialog { public BaseCallBack callBack; public UserPrivacyDialog(@NonNull final Context context) { super(context); setContentView(R.layout.dialog_user_privacy); TextView cont = findViewById(R.id.tv_dup_content); cont.setText("此弹框为模拟APP隐私政策流程弹框,APP开发者在集成SDK时,建议参考demo中初始化相关流程代码,不要过早初始化SDK,当用户'同意'该隐私政策后再执行一系列SDK的初始化或权限申请,以防止合规检测不通过。"); TextView y = findViewById(R.id.tv_dup_yes); TextView n = findViewById(R.id.tv_dup_no); y.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { context.getSharedPreferences(Constant.SP_NAME, Context.MODE_PRIVATE).edit().putBoolean(Constant.SP_AGREE_PRIVACY, true).commit(); /** 注意!!!:一定要在用户同意APP隐私政策要求后再调用此初始化方法。*/ if (callBack != null) { callBack.call(); } MyApplication.getInstance().initSDK(); dismiss(); } }); n.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); System.exit(0); } }); setCanceledOnTouchOutside(false); setCancelable(false); } } ================================================ FILE: example/src/main/res/drawable/circle_big_red.xml ================================================ ================================================ FILE: example/src/main/res/drawable/dialog_white_radius_bg.xml ================================================ ================================================ FILE: example/src/main/res/drawable/ic_launcher_background.xml ================================================ ================================================ FILE: example/src/main/res/drawable/play_arrow.xml ================================================ ================================================ FILE: example/src/main/res/drawable-v24/ic_launcher_foreground.xml ================================================ ================================================ FILE: example/src/main/res/layout/activity_banner.xml ================================================