Repository: Shouheng88/Android-references Branch: master Commit: 82aeb9174f28 Files: 411 Total size: 1.0 MB Directory structure: gitextract_6f4n4z2d/ ├── README.md ├── advanced/ │ ├── .gitignore │ ├── advanced.iml │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── me/ │ │ └── shouheng/ │ │ └── advanced/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── aidl/ │ │ │ └── me/ │ │ │ └── shouheng/ │ │ │ └── advanced/ │ │ │ └── aidl/ │ │ │ ├── INoteManager.aidl │ │ │ └── Note.aidl │ │ ├── debug/ │ │ │ └── AndroidManifest.xml │ │ ├── java/ │ │ │ └── me/ │ │ │ └── shouheng/ │ │ │ └── advanced/ │ │ │ ├── Activity2.java │ │ │ ├── MainActivity.java │ │ │ ├── ModuleAdvancedApp.java │ │ │ ├── aidl/ │ │ │ │ ├── Note.java │ │ │ │ └── NoteService.java │ │ │ ├── callback/ │ │ │ │ ├── ActResultRequest.java │ │ │ │ └── OnActResultEventDispatcherFragment.java │ │ │ ├── keepalive/ │ │ │ │ └── LongLiveService.java │ │ │ ├── messenger/ │ │ │ │ └── MessengerService.java │ │ │ └── remote/ │ │ │ ├── Remote2Activity.java │ │ │ └── RemoteActivity.java │ │ └── res/ │ │ ├── layout/ │ │ │ ├── activity_2.xml │ │ │ ├── activity_advanced.xml │ │ │ └── activity_remote.xml │ │ └── values/ │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── me/ │ └── shouheng/ │ └── advanced/ │ └── ExampleUnitTest.java ├── animations/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── me/ │ │ └── shouheng/ │ │ └── animations/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── debug/ │ │ │ └── AndroidManifest.xml │ │ ├── java/ │ │ │ └── me/ │ │ │ └── shouheng/ │ │ │ └── animations/ │ │ │ ├── DrawableActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── ModuleAnimationsApp.java │ │ │ └── reveal/ │ │ │ └── CircleRevealActivity.java │ │ └── res/ │ │ ├── drawable/ │ │ │ ├── drawable_bitmap.xml │ │ │ ├── drawable_bitmap_no_filter.xml │ │ │ ├── drawable_layer.xml │ │ │ └── ic_launcher_background.xml │ │ ├── drawable-v24/ │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout/ │ │ │ ├── activity_anim.xml │ │ │ ├── activity_circle_reveal.xml │ │ │ └── activity_drawable.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ └── values/ │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── me/ │ └── shouheng/ │ └── animations/ │ └── ExampleUnitTest.java ├── client/ │ ├── .gitignore │ ├── app/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── channel │ │ ├── fabric.properties │ │ ├── gradle/ │ │ │ └── wrapper/ │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── proguard-rules.pro │ │ └── src/ │ │ ├── androidTest/ │ │ │ └── java/ │ │ │ └── me/ │ │ │ └── shouheng/ │ │ │ └── references/ │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── assets/ │ │ │ │ ├── camera.aar │ │ │ │ ├── guokr.articleInline.js │ │ │ │ ├── guokr.base.js │ │ │ │ └── guokr_master.css │ │ │ ├── java/ │ │ │ │ └── me/ │ │ │ │ └── shouheng/ │ │ │ │ └── references/ │ │ │ │ ├── MyApplication.java │ │ │ │ ├── view/ │ │ │ │ │ ├── intro/ │ │ │ │ │ │ ├── AppIntroActivity.java │ │ │ │ │ │ ├── IntroFragment.java │ │ │ │ │ │ ├── IntroSlide1.java │ │ │ │ │ │ ├── IntroSlide2.java │ │ │ │ │ │ ├── IntroSlide3.java │ │ │ │ │ │ └── IntroSlide4.java │ │ │ │ │ └── main/ │ │ │ │ │ └── MainActivity.java │ │ │ │ └── viewmodel/ │ │ │ │ └── MainViewModel.java │ │ │ └── res/ │ │ │ ├── layout/ │ │ │ │ ├── activity_main.xml │ │ │ │ └── fragment_intro_slide.xml │ │ │ ├── mipmap-anydpi-v26/ │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── values/ │ │ │ │ ├── colors.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ └── xml/ │ │ │ ├── provider_paths.xml │ │ │ └── shortcuts.xml │ │ └── test/ │ │ └── java/ │ │ └── me/ │ │ └── shouheng/ │ │ └── references/ │ │ └── ExampleUnitTest.java │ ├── build.gradle │ ├── gradle/ │ │ └── wrapper/ │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradle.properties │ ├── gradlew │ ├── gradlew.bat │ ├── palm.jks │ └── settings.gradle ├── commons/ │ ├── .gitignore │ ├── build.gradle │ ├── libs/ │ │ └── pldroid-player-1.5.0.jar │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── me/ │ │ └── shouheng/ │ │ └── commons/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── me/ │ │ │ └── shouheng/ │ │ │ └── commons/ │ │ │ ├── BaseApplication.java │ │ │ ├── config/ │ │ │ │ ├── BaseConstants.java │ │ │ │ └── Configs.java │ │ │ ├── di/ │ │ │ │ ├── AppComponent.java │ │ │ │ ├── enums/ │ │ │ │ │ ├── ActivityScoped.java │ │ │ │ │ └── ViewModelKey.java │ │ │ │ └── module/ │ │ │ │ └── AppModule.java │ │ │ ├── model/ │ │ │ │ ├── Resource.java │ │ │ │ └── Status.java │ │ │ ├── rxbus/ │ │ │ │ └── RxBus.java │ │ │ ├── tools/ │ │ │ │ ├── ColorUtils.java │ │ │ │ ├── FragmentHelper.java │ │ │ │ ├── LogUtils.java │ │ │ │ ├── NetworkUtils.java │ │ │ │ ├── PalmUtils.java │ │ │ │ ├── StringUtils.java │ │ │ │ ├── TimeUtils.java │ │ │ │ ├── ToastUtils.java │ │ │ │ ├── ViewUtils.java │ │ │ │ ├── glide/ │ │ │ │ │ └── MyAppGlideModule.java │ │ │ │ ├── permission/ │ │ │ │ │ ├── OnGetPermissionCallback.java │ │ │ │ │ └── PermissionUtils.java │ │ │ │ └── theme/ │ │ │ │ ├── SystemUiVisibilityUtil.java │ │ │ │ └── ThemeUtils.java │ │ │ └── view/ │ │ │ ├── activity/ │ │ │ │ ├── BaseActivity.java │ │ │ │ ├── CommonActivity.java │ │ │ │ ├── CommonDaggerActivity.java │ │ │ │ └── UMengActivity.java │ │ │ ├── fragment/ │ │ │ │ ├── CommonDaggerFragment.java │ │ │ │ └── CommonFragment.java │ │ │ └── widget/ │ │ │ ├── CircleImageView.java │ │ │ ├── DividerItemDecoration.java │ │ │ ├── DragSortRecycler.java │ │ │ ├── EmptyView.java │ │ │ ├── SpaceItemDecoration.java │ │ │ ├── SquareFrameLayout.java │ │ │ └── SquareImageView.java │ │ └── res/ │ │ ├── drawable/ │ │ │ ├── bg_toolbar_shade.xml │ │ │ ├── ic_account_circle_black_24dp.xml │ │ │ ├── ic_add_black_24dp.xml │ │ │ ├── ic_autorenew_black_24dp.xml │ │ │ ├── ic_check_circle_black_24dp.xml │ │ │ ├── ic_close_black_24dp.xml │ │ │ ├── ic_favorite_black_24dp.xml │ │ │ ├── ic_launcher_background.xml │ │ │ ├── ic_launcher_foreground.xml │ │ │ ├── ic_widgets_black_24dp.xml │ │ │ ├── item_divider_black.xml │ │ │ ├── item_divider_white.xml │ │ │ └── nav_item_color.xml │ │ ├── layout/ │ │ │ ├── layout_toolbar.xml │ │ │ └── widget_empty_view.xml │ │ ├── values/ │ │ │ ├── attrs.xml │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── xml/ │ │ └── network_security_config.xml │ └── test/ │ └── java/ │ └── me/ │ └── shouheng/ │ └── commons/ │ └── ExampleUnitTest.java ├── eyepetizer/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── me/ │ │ └── shouheng/ │ │ └── eyepetizer/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── debug/ │ │ │ └── AndroidManifest.xml │ │ ├── java/ │ │ │ └── me/ │ │ │ └── shouheng/ │ │ │ └── eyepetizer/ │ │ │ ├── ModuleEyepetizerApp.java │ │ │ ├── mvp/ │ │ │ │ ├── base/ │ │ │ │ │ ├── BasePresenter.java │ │ │ │ │ └── BaseView.java │ │ │ │ ├── contract/ │ │ │ │ │ └── HomeContract.java │ │ │ │ ├── model/ │ │ │ │ │ ├── HomeModel.java │ │ │ │ │ └── bean/ │ │ │ │ │ └── HomeBean.java │ │ │ │ └── presenter/ │ │ │ │ └── HomePresenter.java │ │ │ ├── net/ │ │ │ │ ├── APIRetrofit.java │ │ │ │ └── APIService.java │ │ │ └── ui/ │ │ │ ├── activity/ │ │ │ │ ├── ContainerActivity.java │ │ │ │ └── HomeActivity.java │ │ │ ├── adapter/ │ │ │ │ └── HomeAdapter.java │ │ │ └── fragment/ │ │ │ └── VideoFragment.java │ │ └── res/ │ │ ├── drawable/ │ │ │ └── ic_launcher_background.xml │ │ ├── drawable-v24/ │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout/ │ │ │ ├── activity_container.xml │ │ │ ├── activity_eyepetizer_menu.xml │ │ │ ├── fragment_eye_video.xml │ │ │ └── item_home.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ └── values/ │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── me/ │ └── shouheng/ │ └── eyepetizer/ │ └── ExampleUnitTest.java ├── guokr/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── me/ │ │ └── shouheng/ │ │ └── guokr/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── debug/ │ │ │ └── AndroidManifest.xml │ │ ├── java/ │ │ │ └── me/ │ │ │ └── shouheng/ │ │ │ └── guokr/ │ │ │ ├── ModuleGuokrApp.java │ │ │ ├── model/ │ │ │ │ ├── data/ │ │ │ │ │ ├── GuokrNews.java │ │ │ │ │ └── GuokrNewsContent.java │ │ │ │ └── repository/ │ │ │ │ ├── GuokrRetrofit.java │ │ │ │ └── GuokrService.java │ │ │ ├── view/ │ │ │ │ ├── GuokrNewsActivity.java │ │ │ │ ├── adapter/ │ │ │ │ │ └── GuokrNewsAdapter.java │ │ │ │ └── fragment/ │ │ │ │ ├── NewsDetailFragment.java │ │ │ │ └── NewsListFragment.java │ │ │ └── viewmodel/ │ │ │ └── GuokrViewModel.java │ │ └── res/ │ │ ├── drawable/ │ │ │ └── ic_launcher_background.xml │ │ ├── drawable-v24/ │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout/ │ │ │ ├── activity_guokr_bews.xml │ │ │ ├── fragment_news_detail.xml │ │ │ ├── fragment_news_list.xml │ │ │ └── item_guokr_news.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ └── values/ │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── me/ │ └── shouheng/ │ └── guokr/ │ └── ExampleUnitTest.java ├── knife-annotation/ │ ├── .gitignore │ ├── build.gradle │ └── src/ │ └── main/ │ └── java/ │ └── me/ │ └── shouheng/ │ └── knife/ │ └── annotation/ │ ├── BindView.java │ └── OnClick.java ├── knife-api/ │ ├── .gitignore │ ├── build.gradle │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── me/ │ │ └── shouheng/ │ │ └── knife/ │ │ └── api/ │ │ ├── Injector.java │ │ ├── MyKnife.java │ │ ├── Unbinder.java │ │ └── finder/ │ │ ├── ActivityFinder.java │ │ ├── Finder.java │ │ └── ViewFinder.java │ └── res/ │ └── values/ │ └── strings.xml ├── knife-compiler/ │ ├── .gitignore │ ├── build.gradle │ └── src/ │ └── main/ │ └── java/ │ └── me/ │ └── shouheng/ │ └── knife/ │ └── compiler/ │ ├── BindViewProcessor.java │ ├── TypeUtils.java │ └── model/ │ ├── AnnotatedClass.java │ ├── BindViewField.java │ └── OnClickMethod.java ├── layout/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── me/ │ │ └── shouheng/ │ │ └── layout/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── debug/ │ │ │ └── AndroidManifest.xml │ │ ├── java/ │ │ │ └── me/ │ │ │ └── shouheng/ │ │ │ └── layout/ │ │ │ ├── ModuleLayoutApp.java │ │ │ ├── common/ │ │ │ │ └── NormalTipsFragment.java │ │ │ └── view/ │ │ │ ├── ActivityEditLayout.java │ │ │ ├── DrawerActivity.java │ │ │ ├── LayoutMenuActivity.java │ │ │ ├── adapter/ │ │ │ │ ├── AdapterUtils.java │ │ │ │ └── BeforeAdapter.java │ │ │ ├── bottomsheet/ │ │ │ │ └── BottomSheetActivity.java │ │ │ ├── collapse/ │ │ │ │ └── CollapseBarStructure.java │ │ │ ├── custom/ │ │ │ │ └── CustomView.java │ │ │ ├── navigation/ │ │ │ │ ├── NavigationActivity.java │ │ │ │ └── fragment/ │ │ │ │ └── PagerFragment.java │ │ │ ├── scrolling/ │ │ │ │ └── ScrollingActivity.java │ │ │ ├── support28/ │ │ │ │ ├── BottomAppBarActivity.java │ │ │ │ └── Support28Activity.java │ │ │ ├── swipe/ │ │ │ │ └── SwipeBackDemoActivity.java │ │ │ ├── tabbed/ │ │ │ │ └── TabbedActivity.java │ │ │ └── views/ │ │ │ ├── CustomView.java │ │ │ ├── ViewAnimateActivity.java │ │ │ └── ViewSystemActivity.java │ │ └── res/ │ │ ├── anim/ │ │ │ ├── cycle_7.xml │ │ │ ├── layout_anim.xml │ │ │ └── shake.xml │ │ ├── drawable/ │ │ │ ├── branded_background.xml │ │ │ ├── ic_launcher_background.xml │ │ │ ├── record_anim.xml │ │ │ └── side_nav_bar.xml │ │ ├── drawable-v21/ │ │ │ ├── ic_menu_camera.xml │ │ │ ├── ic_menu_gallery.xml │ │ │ ├── ic_menu_manage.xml │ │ │ ├── ic_menu_send.xml │ │ │ ├── ic_menu_share.xml │ │ │ └── ic_menu_slideshow.xml │ │ ├── drawable-v24/ │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout/ │ │ │ ├── activity_adapter.xml │ │ │ ├── activity_bottom_app_bar.xml │ │ │ ├── activity_bottom_sheet.xml │ │ │ ├── activity_collapse_bar_structure.xml │ │ │ ├── activity_drawer.xml │ │ │ ├── activity_edit_layout.xml │ │ │ ├── activity_layout_menu.xml │ │ │ ├── activity_navigation.xml │ │ │ ├── activity_scrolling.xml │ │ │ ├── activity_support_28.xml │ │ │ ├── activity_swipe_back.xml │ │ │ ├── activity_tabbed.xml │ │ │ ├── activity_view_animate.xml │ │ │ ├── activity_view_system.xml │ │ │ ├── app_bar_drawer.xml │ │ │ ├── bottom_sheet_dialog.xml │ │ │ ├── content_drawer.xml │ │ │ ├── fragment_normal_tips.xml │ │ │ ├── fragment_pager.xml │ │ │ └── nav_header_drawer.xml │ │ ├── menu/ │ │ │ ├── activity_drawer_drawer.xml │ │ │ ├── bottom_navigation.xml │ │ │ └── drawer.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── values/ │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── drawables.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── values-v21/ │ │ └── styles.xml │ └── test/ │ └── java/ │ └── me/ │ └── shouheng/ │ └── layout/ │ └── ExampleUnitTest.java ├── libraries/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── debug/ │ │ └── AndroidManifest.xml │ ├── java/ │ │ └── me/ │ │ └── shouheng/ │ │ └── libraries/ │ │ ├── FingerprintIdentifyActivity.java │ │ ├── MenuActivity.java │ │ ├── ModuleLibraryApp.java │ │ ├── MyKnifeActivity.java │ │ ├── TimberActivity.java │ │ ├── eventbus/ │ │ │ ├── EventBusActivity1.java │ │ │ ├── EventBusActivity2.java │ │ │ └── MessageWrap.java │ │ ├── handler/ │ │ │ ├── FileRecognizeTask.java │ │ │ └── HandlerActivity.java │ │ ├── image/ │ │ │ ├── ClipPictureActivity.java │ │ │ └── ImageCompressActivity.java │ │ ├── rxjava/ │ │ │ ├── DemoFragment.java │ │ │ ├── RxBusActivity.java │ │ │ ├── RxJavaActivity.java │ │ │ └── RxMessage.java │ │ ├── serial/ │ │ │ ├── SerializeActivity.java │ │ │ └── SerializeUtils.java │ │ └── workmanager/ │ │ ├── PeriodicTask.java │ │ ├── SingleTask.java │ │ ├── SingleTask2.java │ │ └── WorkManagerActivity.java │ └── res/ │ ├── drawable/ │ │ └── ic_launcher_background.xml │ ├── drawable-v24/ │ │ └── ic_launcher_foreground.xml │ ├── layout/ │ │ ├── activity_clip_picture.xml │ │ ├── activity_event_bus1.xml │ │ ├── activity_event_bus2.xml │ │ ├── activity_fingerprint_identify.xml │ │ ├── activity_handler.xml │ │ ├── activity_image_compress.xml │ │ ├── activity_menu.xml │ │ ├── activity_my_knife.xml │ │ ├── activity_rx_bus.xml │ │ ├── activity_rxjava.xml │ │ ├── activity_serialize.xml │ │ ├── activity_timber.xml │ │ ├── activity_work_manager.xml │ │ └── fragment_demo.xml │ ├── mipmap-anydpi-v26/ │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ └── values/ │ ├── colors.xml │ ├── strings.xml │ └── styles.xml └── live/ ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src/ ├── androidTest/ │ └── java/ │ └── me/ │ └── shouheng/ │ └── live/ │ └── ExampleInstrumentedTest.java ├── main/ │ ├── AndroidManifest.xml │ ├── debug/ │ │ └── AndroidManifest.xml │ ├── java/ │ │ └── me/ │ │ └── shouheng/ │ │ └── live/ │ │ ├── LiveModuleApp.java │ │ ├── common/ │ │ │ ├── DecimalFormatUtil.java │ │ │ └── widget/ │ │ │ └── FlutteringLayout.java │ │ ├── model/ │ │ │ ├── LiveService.java │ │ │ ├── data/ │ │ │ │ ├── AppStart.java │ │ │ │ ├── Banner.java │ │ │ │ ├── LiveInfo.java │ │ │ │ ├── Recommend.java │ │ │ │ ├── Room.java │ │ │ │ ├── RoomLine.java │ │ │ │ └── StreamSrc.java │ │ │ └── repository/ │ │ │ └── LiveRetrofit.java │ │ ├── view/ │ │ │ ├── Constant.java │ │ │ ├── activity/ │ │ │ │ ├── LiveActivity.java │ │ │ │ └── LiveRoomActivity.java │ │ │ ├── adapter/ │ │ │ │ ├── RecommendAdapter.java │ │ │ │ └── RecommendChildAdapter.java │ │ │ └── fragment/ │ │ │ ├── FullscreenFragment.java │ │ │ ├── RoomFragment.java │ │ │ └── VideoFragment.java │ │ └── viewmodel/ │ │ └── LiveViewModel.java │ └── res/ │ ├── drawable/ │ │ ├── full_room_avatar_bg.xml │ │ ├── ic_arrow_back_black_24dp.xml │ │ ├── ic_card_giftcard_black_24dp.xml │ │ ├── ic_forum_black_24dp.xml │ │ ├── ic_fullscreen_black_24dp.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_mail_black_24dp.xml │ │ ├── ic_more_vert_black_24dp.xml │ │ ├── ic_person_add_black_24dp.xml │ │ ├── ic_send_black_24dp.xml │ │ └── ic_share_black_24dp.xml │ ├── drawable-v24/ │ │ └── ic_launcher_foreground.xml │ ├── layout/ │ │ ├── activity_live.xml │ │ ├── activity_live_room.xml │ │ ├── fragment_fullscreen.xml │ │ ├── fragment_room.xml │ │ ├── fragment_video.xml │ │ ├── item_remmend.xml │ │ ├── item_remmend_child.xml │ │ └── layout_banner.xml │ ├── mipmap-anydpi-v26/ │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ └── values/ │ ├── attrs.xml │ ├── colors.xml │ ├── strings.xml │ └── styles.xml └── test/ └── java/ └── me/ └── shouheng/ └── live/ └── ExampleUnitTest.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================ # Awesome Android > 该项主要用来收集和整理开发过程中经常用到的三方库和控件,并包含一些演示代码。[可以参考下文来了解更多的内容] ## 代码清单 ### 1、整体结构 ``` /----- /----- advanced IPC, AIDL /----- animations CircularReveal, TapTargetView, Ripple, etc /----- client 整体 APP 打包 /----- commons 公共库 /----- eyepetizer 开眼视频, MVP /----- guokr 果壳新闻 /----- knife-annotations ButterKnife 注解 /----- knife-api ButterKnife API /----- knife-compiler ButterKnife 编译器 /----- layout MaterialDesign /----- libraries 指纹识别, EventBus, WorkManager, Knife etc /----- live 全民直播 ``` 注:各个模块借助 `ARouter` 实现了模块化开发,可以通过修改 [gradle.properties](client/gradle.properties) 中的属性来实现各个模块独立打包 ### 2、视频直播 对应于 `live` 模块,该模块主要用来演示**视频直播**相关的功能: 基于《全民直播》的 API 设计的在线视频直播功能;使用了支持包里的 `Palette` 来提取图片的颜色;`MVVM` 架构设计 (在该项目中的使用不符合规范,谨慎参考);使用了pldroid-player作为视频播放的工具。 部分截图:
### 3、果壳新闻 对应于 `guokr` 模块,该模块主要用来演示`OkHttp + Retrofit + RexJava`的开发方式: 基于《果壳网》 API 设计的新闻客户端,包含基本的"列表-详情"结构;MVVM 架构设计 (在该项目中的使用不符合规范,谨慎参考)。
### 4、开眼视频 对应于 `eyepetizer` 模块,该模块主要用来演示**小视频**类型的 APP 相关的功能,同时演示 `MVP` 架构模式在 Android 端的使用方式: MVP 架构设计;基于《开眼视频》的 API 设计视频浏览客户端。 注:项目比较小,功能比较少,主要用来演示核心的网络视频播放功能。 ### 5、MaterialDesign 对应于 `layout` 模块,该模块主要用来整理 MaterialDesign 相关的布局和控件,目前包含的布局有: `Navigation`、`Tabbed`、`Bottom sheet`、`Scrolling`、`Collapse`、`Support 28` 中的部分控件。
### 6、其他 1. 自定义类似于 `ButterKnife` 的库,文件路径包含 [knife-annotation](knife-annotation)、[knife-api](knife-api) 和 [knife-compiler](knife-compiler) 该部分内容需要使用 Java 中的注解以及注解处理,你可以通过这篇文章来了解这部分功能如何实现:[《Java 注解及其在 Android 中的应用》](https://juejin.im/post/5b824b8751882542f105447d) 2. 使用 `RxJava2` 搭建一个 `EventBus`,文件路径在 [rxbus](commons\src\main\java\me\shouheng\commons\rxbus) 该部分使用 `RxJava2` 实现一个类似于 `EventBus` 的全局通信的框架,相关的知识可以通过这篇文章进行了解:[《RxJava2 系列 (3):使用 Subject》](https://juejin.im/post/5b801dfa51882542cb409905) 3. 在该项目中使用了 `MVP` 和 `MVVM` 两种架构设计方式,同时使用了 `ARouter` 来实现了模块化开发,你可以通过这篇文章来了解相关的知识:[《Android 架构设计:MVC、MVP、MVVM和组件化》](https://juejin.im/post/5b7c1706f265da436d7e408e) 4. 如果你希望了解 `OkHttp` 的源码相关的知识,请参考我的这篇文章:[《Andriod 网络框架 OkHttp 源码解析》](https://juejin.im/post/5bc89fbc5188255c713cb8a5) 4. 如果你希望了解 `Retrofit` 的源码相关的知识,其中使用了哪些设计模式等等,请参考我的这篇文章:[《Android 网络框架 Retrofit 源码解析》](https://juejin.im/post/5bd05d5c6fb9a05d2b6dfc46) ================================================ FILE: advanced/.gitignore ================================================ /build ================================================ FILE: advanced/advanced.iml ================================================ ================================================ FILE: advanced/build.gradle ================================================ println isAdvancedModuleApp.toBoolean() apply plugin: "build-time-tracker" if (isAdvancedModuleApp.toBoolean()) { apply plugin: 'com.android.application' } else { apply plugin: 'com.android.library' } apply plugin: 'com.getkeepsafe.dexcount' android { compileSdkVersion rootProject.ext.compileSdkVersion defaultConfig { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true javaCompileOptions { annotationProcessorOptions { arguments = [moduleName: project.getName()] } } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } // use data binding dataBinding { enabled = true } // use java 8 language compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } // use external libs folder sourceSets { main { jniLibs.srcDirs = ['libs'] if (isAdvancedModuleApp.toBoolean()) { manifest.srcFile "src/main/debug/AndroidManifest.xml" } else { manifest.srcFile "src/main/AndroidManifest.xml" } } } // dex count. see https://github.com/KeepSafe/dexcount-gradle-plugin dexcount { format = "list" includeClasses = false includeClassCount = false includeFieldCount = true includeTotalMethodCount = false orderByMethodCount = false verbose = false maxTreeDepth = Integer.MAX_VALUE teamCityIntegration = false teamCitySlug = null runOnEachPackage = true // maxMethodCount = 64000 enabled = true } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' testImplementation 'org.mockito:mockito-inline:2.22.0' androidTestImplementation 'org.mockito:mockito-inline:2.22.0' // router annotationProcessor 'com.alibaba:arouter-compiler:1.1.4' // projects implementation project(':commons') } ================================================ FILE: advanced/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: advanced/src/androidTest/java/me/shouheng/advanced/ExampleInstrumentedTest.java ================================================ package me.shouheng.advanced; import android.content.Context; import org.junit.Test; import org.junit.runner.RunWith; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.action.ViewActions.click; import static androidx.test.espresso.matcher.ViewMatchers.withId; import static org.junit.Assert.assertEquals; /** * 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("me.shouheng.advanced.test", appContext.getPackageName()); } public void testClick(int id) { onView(withId(id)).perform(click()); } } ================================================ FILE: advanced/src/main/AndroidManifest.xml ================================================ ================================================ FILE: advanced/src/main/aidl/me/shouheng/advanced/aidl/INoteManager.aidl ================================================ package me.shouheng.advanced.aidl; import me.shouheng.advanced.aidl.Note; interface INoteManager { Note getNote(long id); void addNote(long id, String name); } ================================================ FILE: advanced/src/main/aidl/me/shouheng/advanced/aidl/Note.aidl ================================================ package me.shouheng.advanced.aidl; parcelable Note; ================================================ FILE: advanced/src/main/debug/AndroidManifest.xml ================================================ ================================================ FILE: advanced/src/main/java/me/shouheng/advanced/Activity2.java ================================================ package me.shouheng.advanced; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import me.shouheng.advanced.databinding.Activity2Binding; import me.shouheng.commons.view.activity.CommonActivity; /** * Created on 2018/12/26. */ public class Activity2 extends CommonActivity { public static final String EXTRA_KEY_NAME = "EXTRA"; @Override protected int getLayoutResId() { return R.layout.activity_2; } @Override protected void doCreateView(Bundle savedInstanceState) { getBinding().cfrm.setOnClickListener(v -> { Intent intent = new Intent(); intent.putExtra(EXTRA_KEY_NAME, getBinding().et.getText().toString()); setResult(Activity.RESULT_OK, intent); finish(); }); } } ================================================ FILE: advanced/src/main/java/me/shouheng/advanced/MainActivity.java ================================================ package me.shouheng.advanced; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.launcher.ARouter; import androidx.annotation.Nullable; import me.shouheng.advanced.aidl.INoteManager; import me.shouheng.advanced.aidl.Note; import me.shouheng.advanced.aidl.NoteService; import me.shouheng.advanced.callback.ActResultRequest; import me.shouheng.advanced.databinding.ActivityAdvancedBinding; import me.shouheng.advanced.keepalive.LongLiveService; import me.shouheng.advanced.messenger.MessengerService; import me.shouheng.commons.config.BaseConstants; import me.shouheng.commons.tools.LogUtils; import me.shouheng.commons.tools.ToastUtils; import me.shouheng.commons.view.activity.CommonActivity; /** * @author shouh * @version $Id: MainActivity, v 0.1 2018/10/22 21:36 shouh Exp$ */ @Route(path = BaseConstants.ADVANCED_MENU) public class MainActivity extends CommonActivity { private INoteManager noteManager; private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { noteManager = INoteManager.Stub.asInterface(service); try { Note note = noteManager.getNote(100); LogUtils.d(note); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; public static final int MSG_REPLAY_ID = 0x0012; public static final String MSG_EXTRA_REPLAY_STRING = "__extra_replay_string"; private boolean serviceConnected = false; private Messenger boundServiceMessenger = null; private final Messenger receiveMessenger = new Messenger(new ReceiveMessHandler()); private ServiceConnection msgConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { boundServiceMessenger = new Messenger(service); serviceConnected = true; } @Override public void onServiceDisconnected(ComponentName name) { serviceConnected = false; } }; private static class ReceiveMessHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_REPLAY_ID: ToastUtils.makeToast("收到返回结果:"+msg.getData().getString(MSG_EXTRA_REPLAY_STRING)); break; } } } @Override protected int getLayoutResId() { return R.layout.activity_advanced; } @Override protected void doCreateView(Bundle savedInstanceState) { /* 开启远程的活动 */ getBinding().btnRemote.setOnClickListener(v -> ARouter.getInstance().build(BaseConstants.ADVANCED_REMOTE) .withString(BaseConstants.ADVANCED_REMOTE_ARG_CONTENT, "Simple advanced content") .navigation()); getBinding().btnRemote2.setOnClickListener(v -> ARouter.getInstance().build(BaseConstants.ADVANCED_REMOTE2) .withString(BaseConstants.ADVANCED_REMOTE2_ARG_CONTENT, "Simple advanced content 2") .navigation()); /* 两种获取程序执行结果的效果的对比 */ getBinding().btnResult.setOnClickListener(v -> { Intent intent = new Intent(this, Activity2.class); ActResultRequest request = new ActResultRequest(this); /* 实际的原理就是通过一个没有背景的 fragment 来发起请求并在其中处理请求并进行回调 * 问题:当不保留活动的时候 GG */ request.startForResult(intent, 1, (resultCode, data) -> { String result = data.getStringExtra(Activity2.EXTRA_KEY_NAME); ToastUtils.makeToast(result); }); }); getBinding().btnResult2.setOnClickListener(v -> { /* 正常的启动活动并获取结果的逻辑,即使不保留活动也正常运行 */ Intent intent = new Intent(this, Activity2.class); startActivityForResult(intent, 0); }); /* 跨进程获取笔记信息 */ getBinding().btnDisplay.setOnClickListener(v -> { try { Note note = noteManager.getNote(100); ToastUtils.makeToast(note + ""); } catch (RemoteException e) { e.printStackTrace(); } }); /* 启动前台服务 */ getBinding().btnFore.setOnClickListener(v -> { Intent intent = new Intent(this, LongLiveService.class); startService(intent); }); /* 使用 Messenger 发送消息 */ getBinding().btnMessenger.setOnClickListener(v -> { Message message = Message.obtain(null, MessengerService.MSG_SAY_SOMETHING); message.replyTo = receiveMessenger; Bundle bundle = new Bundle(); bundle.putString(MessengerService.MSG_EXTRA_COMMAND, "11111"); message.setData(bundle); try { boundServiceMessenger.send(message); } catch (RemoteException ex) { ex.printStackTrace(); } }); /* 绑定 NoteService */ // Intent intent = new Intent(this, NoteService.class); // bindService(intent, connection, Context.BIND_AUTO_CREATE); /* 绑定 MessengerService */ Intent intent1 = new Intent(this, MessengerService.class); bindService(intent1, msgConn, Context.BIND_AUTO_CREATE); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case 0: assert data != null; String result = data.getStringExtra(Activity2.EXTRA_KEY_NAME); ToastUtils.makeToast(result); break; default: // do nothing } } @Override protected void onDestroy() { super.onDestroy(); unbindService(connection); if (serviceConnected) { unbindService(msgConn); } } } ================================================ FILE: advanced/src/main/java/me/shouheng/advanced/ModuleAdvancedApp.java ================================================ package me.shouheng.advanced; import com.alibaba.android.arouter.launcher.ARouter; import me.shouheng.commons.BaseApplication; /** * @author shouh * @version $Id: ModuleGuokrApp, v 0.1 2018/6/6 22:30 shouh Exp$ */ public class ModuleAdvancedApp extends BaseApplication { private static ModuleAdvancedApp application; public static ModuleAdvancedApp getContext() { return application; } @Override public void onCreate() { super.onCreate(); application = this; ARouter.init(this); } } ================================================ FILE: advanced/src/main/java/me/shouheng/advanced/aidl/Note.java ================================================ package me.shouheng.advanced.aidl; import android.os.Parcel; import android.os.Parcelable; /** * @author shouh * @version $Id: Note, v 0.1 2018/10/22 22:27 shouh Exp$ */ public class Note implements Parcelable { public final long id; public final String name; public Note(long id, String name) { this.id = id; this.name = name; } protected Note(Parcel in) { id = in.readLong(); name = in.readString(); } public static final Creator CREATOR = new Creator() { @Override public Note createFromParcel(Parcel in) { return new Note(in); } @Override public Note[] newArray(int size) { return new Note[size]; } }; @Override public String toString() { return "Note{" + "id=" + id + ", name='" + name + '\'' + '}'; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(id); dest.writeString(name); } } ================================================ FILE: advanced/src/main/java/me/shouheng/advanced/aidl/NoteService.java ================================================ package me.shouheng.advanced.aidl; import android.app.Notification; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Message; import androidx.annotation.Nullable; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import io.reactivex.Observable; import io.reactivex.functions.Consumer; import me.shouheng.advanced.MainActivity; import me.shouheng.advanced.R; import me.shouheng.commons.tools.LogUtils; /** * @author shouh * @version $Id: NoteService, v 0.1 2018/10/23 21:20 shouh Exp$ */ public class NoteService extends Service { private CopyOnWriteArrayList notes = new CopyOnWriteArrayList<>(); private Binder binder = new INoteManager.Stub() { @Override public Note getNote(long id) { return Observable.fromIterable(notes).filter(note -> note.id == id).singleOrError().blockingGet(); } @Override public void addNote(long id, String name) { notes.add(new Note(id, name)); } }; @Override public void onCreate() { super.onCreate(); notes.add(new Note(100, "Note 100")); notes.add(new Note(101, "Note 101")); LogUtils.d("========== onCreate()"); Notification notification = new Notification.Builder(getApplicationContext()) .setContentTitle("Content Title") .setContentText("Content Text") .setSmallIcon(R.drawable.ic_launcher_foreground) .build(); Intent intent = new Intent(this, MainActivity.class); notification.contentIntent = PendingIntent.getActivity(this, 0, intent, 0); startForeground(1, notification); } @Nullable @Override public IBinder onBind(Intent intent) { LogUtils.d("========== onBind()"); Observable.interval(1, TimeUnit.SECONDS).subscribe(new Consumer() { @Override public void accept(Long aLong) throws Exception { LogUtils.d("========== " + aLong); } }); return binder; } @Override public void onRebind(Intent intent) { super.onRebind(intent); LogUtils.d("========== onRebind()"); } @Override public boolean onUnbind(Intent intent) { LogUtils.d("========== onUnbind()"); return super.onUnbind(intent); } @Override public void onDestroy() { super.onDestroy(); LogUtils.d("========== onDestroy()"); } } ================================================ FILE: advanced/src/main/java/me/shouheng/advanced/callback/ActResultRequest.java ================================================ package me.shouheng.advanced.callback; import android.content.Intent; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.FragmentManager; /** * Created on 2018/12/26. */ public class ActResultRequest { private OnActResultEventDispatcherFragment fragment; /** * new 一个 ActResultRequest 的时候会初始化一个没有背景的 Fragment * * @param activity */ public ActResultRequest(AppCompatActivity activity) { fragment = getEventDispatchFragment(activity); } private OnActResultEventDispatcherFragment getEventDispatchFragment(AppCompatActivity activity) { final FragmentManager fragmentManager = activity.getSupportFragmentManager(); OnActResultEventDispatcherFragment fragment = findEventDispatchFragment(fragmentManager); if (fragment == null) { fragment = new OnActResultEventDispatcherFragment(); fragmentManager .beginTransaction() .add(fragment, OnActResultEventDispatcherFragment.TAG) .commitAllowingStateLoss(); fragmentManager.executePendingTransactions(); } return fragment; } private OnActResultEventDispatcherFragment findEventDispatchFragment(FragmentManager manager) { return (OnActResultEventDispatcherFragment) manager.findFragmentByTag(OnActResultEventDispatcherFragment.TAG); } /** * 当调用 startForResult 的时候,实际上是调用 fragment 的 startForResult 方法 * * @param intent * @param requestCode * @param callback */ public void startForResult(Intent intent, int requestCode, Callback callback) { fragment.startForResult(intent, requestCode, callback); } public interface Callback { void onActivityResult(int resultCode, Intent data); } } ================================================ FILE: advanced/src/main/java/me/shouheng/advanced/callback/OnActResultEventDispatcherFragment.java ================================================ package me.shouheng.advanced.callback; import android.content.Intent; import android.os.Bundle; import android.util.SparseArray; import androidx.fragment.app.Fragment; /** * Created on 2018/12/26. */ public class OnActResultEventDispatcherFragment extends Fragment { public static final String TAG = "on_act_result_event_dispatcher"; private SparseArray mCallbacks = new SparseArray<>(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); } /** * 这里的 fragment 调用自己的 startActivityForResult 方法来获取结果 * * @param intent * @param requestCode * @param callback */ public void startForResult(Intent intent, int requestCode, ActResultRequest.Callback callback) { mCallbacks.put(requestCode, callback); startActivityForResult(intent, requestCode); } /** * 当拿到结果之后从列表中找出回调并调用 * * @param requestCode * @param resultCode * @param data */ @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); ActResultRequest.Callback callback = mCallbacks.get(requestCode); mCallbacks.remove(requestCode); if (callback != null) { callback.onActivityResult(resultCode, data); } } } ================================================ FILE: advanced/src/main/java/me/shouheng/advanced/keepalive/LongLiveService.java ================================================ package me.shouheng.advanced.keepalive; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.IBinder; import java.util.concurrent.TimeUnit; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; import io.reactivex.Observable; import me.shouheng.advanced.MainActivity; import me.shouheng.advanced.R; import me.shouheng.commons.tools.LogUtils; /** * 通过设置为前台服务的形式进行保活 * 另外还有在 onStartCommand() 中返回 START_STICKY * * Created on 2018/12/26. */ public class LongLiveService extends Service { @Override public void onCreate() { /* 启动前台服务保活 */ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); CharSequence name = "FOREGROUND"; String description = ""; NotificationChannel channel; int importance = NotificationManager.IMPORTANCE_DEFAULT; channel = new NotificationChannel("ChannelId", name, importance); channel.setDescription(description); notificationManager.createNotificationChannel(channel); } Notification notification = new NotificationCompat.Builder(getApplicationContext(), "ChannelId") .setContentTitle("Content Title 2") .setContentText("Content Text 2") .setSmallIcon(R.drawable.ic_launcher_foreground) .build(); Intent intent = new Intent(this, MainActivity.class); notification.contentIntent = PendingIntent.getActivity(this, 0, intent, 0); startForeground(2, notification); /* 每隔 1 秒的时间输出一个日志 */ Observable.interval(1, TimeUnit.SECONDS).subscribe(aLong -> LogUtils.d("LongLive " + aLong)).isDisposed(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { /* 返回 START_STICKY,可以做到在“正在运行的服务”中停止的时候自启 */ return START_STICKY; } @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public boolean onUnbind(Intent intent) { return super.onUnbind(intent); } @Override public void onRebind(Intent intent) { // do nothing } @Override public void onDestroy() { Intent intent = new Intent(this, LongLiveService.class); startService(intent); } } ================================================ FILE: advanced/src/main/java/me/shouheng/advanced/messenger/MessengerService.java ================================================ package me.shouheng.advanced.messenger; import android.app.Service; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import java.lang.ref.WeakReference; import androidx.annotation.Nullable; import me.shouheng.commons.tools.ToastUtils; import static me.shouheng.advanced.MainActivity.MSG_EXTRA_REPLAY_STRING; import static me.shouheng.advanced.MainActivity.MSG_REPLAY_ID; /** * @author WngShhng (shouheng2015@gmail.com) * @version $Id: MessengerService, v 0.1 2019/2/16 14:38 shouh Exp$ */ public class MessengerService extends Service { public static final int MSG_SAY_SOMETHING = 11; public static final String MSG_EXTRA_COMMAND = "__extra_command"; private static class MessengerHandler extends Handler { private WeakReference serviceRef; MessengerHandler(Service service) { this.serviceRef = new WeakReference<>(service); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_SOMETHING: ToastUtils.makeToast("远程服务收到消息,内容是:" + msg.getData().getString(MSG_EXTRA_COMMAND)); // 向客户端返回消息 Messenger client = msg.replyTo; Message message = Message.obtain(null, MSG_REPLAY_ID); Bundle bundle = new Bundle(); bundle.putString(MSG_EXTRA_REPLAY_STRING, "00000"); message.setData(bundle); try { client.send(message); } catch (RemoteException e) { e.printStackTrace(); } break; } } } private Messenger messenger = new Messenger(new MessengerHandler(this)); @Nullable @Override public IBinder onBind(Intent intent) { ToastUtils.makeToast("MessengerService bound!"); return messenger.getBinder(); } } ================================================ FILE: advanced/src/main/java/me/shouheng/advanced/remote/Remote2Activity.java ================================================ package me.shouheng.advanced.remote; import android.os.Bundle; import com.alibaba.android.arouter.facade.annotation.Route; import me.shouheng.advanced.R; import me.shouheng.advanced.databinding.ActivityRemoteBinding; import me.shouheng.commons.config.BaseConstants; import me.shouheng.commons.view.activity.CommonActivity; /** * @author shouh * @version $Id: Remote2Activity, v 0.1 2018/10/22 22:15 shouh Exp$ */ @Route(path = BaseConstants.ADVANCED_REMOTE2) public class Remote2Activity extends CommonActivity { @Override protected int getLayoutResId() { return R.layout.activity_remote; } @Override protected void doCreateView(Bundle savedInstanceState) { String content = getIntent().getStringExtra(BaseConstants.ADVANCED_REMOTE2_ARG_CONTENT); getBinding().tvContent.setText(content); } } ================================================ FILE: advanced/src/main/java/me/shouheng/advanced/remote/RemoteActivity.java ================================================ package me.shouheng.advanced.remote; import android.os.Bundle; import com.alibaba.android.arouter.facade.annotation.Route; import me.shouheng.advanced.R; import me.shouheng.advanced.databinding.ActivityRemoteBinding; import me.shouheng.commons.config.BaseConstants; import me.shouheng.commons.view.activity.CommonActivity; /** * @author shouh * @version $Id: RemoteActivity, v 0.1 2018/10/22 22:03 shouh Exp$ */ @Route(path = BaseConstants.ADVANCED_REMOTE) public class RemoteActivity extends CommonActivity { @Override protected int getLayoutResId() { return R.layout.activity_remote; } @Override protected void doCreateView(Bundle savedInstanceState) { String content = getIntent().getStringExtra(BaseConstants.ADVANCED_REMOTE_ARG_CONTENT); getBinding().tvContent.setText(content); } } ================================================ FILE: advanced/src/main/res/layout/activity_2.xml ================================================ ================================================ FILE: advanced/src/main/res/layout/activity_advanced.xml ================================================ ================================================ FILE: advanced/src/main/res/layout/activity_remote.xml ================================================ ================================================ FILE: advanced/src/main/res/values/strings.xml ================================================ Advanced ================================================ FILE: advanced/src/main/res/values/styles.xml ================================================ ================================================ FILE: advanced/src/test/java/me/shouheng/advanced/ExampleUnitTest.java ================================================ package me.shouheng.advanced; 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: animations/.gitignore ================================================ /build ================================================ FILE: animations/build.gradle ================================================ println isAnimationsModuleApp.toBoolean() if (isAnimationsModuleApp.toBoolean()) { apply plugin: 'com.android.application' } else { apply plugin: 'com.android.library' } android { compileSdkVersion rootProject.ext.compileSdkVersion defaultConfig { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions { annotationProcessorOptions { arguments = [moduleName: project.getName()] } } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } // use data binding dataBinding { enabled = true } // use java 8 language compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } // use external libs folder sourceSets { main { jniLibs.srcDirs = ['libs'] if (isAnimationsModuleApp.toBoolean()) { manifest.srcFile "src/main/debug/AndroidManifest.xml" } else { manifest.srcFile "src/main/AndroidManifest.xml" } } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.1.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' // router annotationProcessor 'com.alibaba:arouter-compiler:1.1.4' // CircularReveal implementation ('com.github.ozodrukh:CircularReveal:2.0.1@aar') { transitive = true } // target view implementation 'com.getkeepsafe.taptargetview:taptargetview:1.12.0' // ripple implementation 'com.balysv:material-ripple:1.0.2' // projects implementation project(':commons') } ================================================ FILE: animations/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: animations/src/androidTest/java/me/shouheng/animations/ExampleInstrumentedTest.java ================================================ package me.shouheng.animations; import android.content.Context; import androidx.test.InstrumentationRegistry; import androidx.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.getTargetContext(); assertEquals("me.shouheng.animations", appContext.getPackageName()); } } ================================================ FILE: animations/src/main/AndroidManifest.xml ================================================ ================================================ FILE: animations/src/main/debug/AndroidManifest.xml ================================================ ================================================ FILE: animations/src/main/java/me/shouheng/animations/DrawableActivity.java ================================================ package me.shouheng.animations; import android.os.Bundle; import com.alibaba.android.arouter.facade.annotation.Route; import me.shouheng.animations.databinding.ActivityDrawableBinding; import me.shouheng.commons.config.BaseConstants; import me.shouheng.commons.view.activity.CommonActivity; /** * Created by WngShhng on 2018/10/17. */ @Route(path = BaseConstants.ANIMATIONS_DRAWABLE) public class DrawableActivity extends CommonActivity { @Override protected int getLayoutResId() { return R.layout.activity_drawable; } @Override protected void doCreateView(Bundle savedInstanceState) { } } ================================================ FILE: animations/src/main/java/me/shouheng/animations/MainActivity.java ================================================ package me.shouheng.animations; import android.graphics.Color; import android.graphics.Rect; import android.os.Bundle; import android.view.Gravity; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.launcher.ARouter; import com.balysv.materialripple.MaterialRippleLayout; import com.getkeepsafe.taptargetview.TapTarget; import com.getkeepsafe.taptargetview.TapTargetSequence; import me.shouheng.animations.databinding.ActivityAnimBinding; import me.shouheng.commons.config.BaseConstants; import me.shouheng.commons.tools.PalmUtils; import me.shouheng.commons.tools.ToastUtils; import me.shouheng.commons.view.activity.CommonActivity; @Route(path = BaseConstants.ANIMATIONS_MENU) public class MainActivity extends CommonActivity { @Override protected int getLayoutResId() { return R.layout.activity_anim; } @Override protected void doCreateView(Bundle savedInstanceState) { getBinding().btnReveal.setOnClickListener(v -> ARouter.getInstance().build(BaseConstants.ANIMATIONS_CIRCLE_REVEAL).navigation()); getBinding().btnToast.setOnClickListener(v -> showToastWithIcon()); getBinding().tvDrawable.setOnClickListener(v -> ARouter.getInstance().build(BaseConstants.ANIMATIONS_DRAWABLE).navigation()); configMaterialRipple(); configTargetView(); } private void showToastWithIcon() { Toast toast = new Toast(this); toast.setDuration(Toast.LENGTH_LONG); LinearLayout ly = new LinearLayout(MainActivity.this); ly.setGravity(Gravity.CENTER_VERTICAL); ImageView iv = new ImageView(MainActivity.this); iv.setImageResource(R.mipmap.ic_launcher); TextView tv = new TextView(MainActivity.this); tv.setText("Test toast message"); ly.addView(tv); ly.addView(iv); toast.setView(ly); toast.show(); } private void configMaterialRipple() { MaterialRippleLayout.on(getBinding().tvRipple) .rippleAlpha(0.2f) .rippleHover(true) .rippleColor(Color.BLUE) .create(); MaterialRippleLayout.on(getBinding().tvRipple2) .rippleColor(Color.parseColor("#FF0000")) .rippleAlpha(0.2f) .rippleHover(true) .create(); getBinding().tvRipple.setOnClickListener(v -> ToastUtils.makeToast("Ripple1")); getBinding().tvRipple2.setOnClickListener(v -> ToastUtils.makeToast("Ripple2")); } private void configTargetView() { Rect rect = new Rect(100, 100, 100, 100); new TapTargetSequence(this) .targets( TapTarget.forView(getBinding().btnReveal, "Gonna"), TapTarget.forView(getBinding().btnToast, "You", "Up") .dimColorInt(Color.RED) .outerCircleColorInt(Color.GREEN) .targetCircleColorInt(Color.BLUE) .textColorInt(Color.WHITE), TapTarget.forBounds(rect, "Down", ":^)") .cancelable(false) .icon(PalmUtils.getDrawableCompact(R.drawable.ic_account_circle_black_24dp))) .listener(new TapTargetSequence.Listener() { @Override public void onSequenceFinish() { ToastUtils.makeToast("onSequenceFinish"); } @Override public void onSequenceStep(TapTarget lastTarget, boolean targetClicked) { ToastUtils.makeToast("onSequenceStep"); } @Override public void onSequenceCanceled(TapTarget lastTarget) { ToastUtils.makeToast("onSequenceCanceled"); } }).start(); } } ================================================ FILE: animations/src/main/java/me/shouheng/animations/ModuleAnimationsApp.java ================================================ package me.shouheng.animations; import com.alibaba.android.arouter.launcher.ARouter; import me.shouheng.commons.BaseApplication; /** * @author shouh * @version $Id: ModuleGuokrApp, v 0.1 2018/6/6 22:30 shouh Exp$ */ public class ModuleAnimationsApp extends BaseApplication { private static ModuleAnimationsApp application; public static ModuleAnimationsApp getContext() { return application; } @Override public void onCreate() { super.onCreate(); application = this; ARouter.init(this); } } ================================================ FILE: animations/src/main/java/me/shouheng/animations/reveal/CircleRevealActivity.java ================================================ package me.shouheng.animations.reveal; import android.animation.Animator; import android.os.Bundle; import android.view.animation.AccelerateDecelerateInterpolator; import com.alibaba.android.arouter.facade.annotation.Route; import io.codetail.animation.ViewAnimationUtils; import me.shouheng.animations.R; import me.shouheng.animations.databinding.ActivityCircleRevealBinding; import me.shouheng.commons.config.BaseConstants; import me.shouheng.commons.view.activity.CommonActivity; @Route(path = BaseConstants.ANIMATIONS_CIRCLE_REVEAL) public class CircleRevealActivity extends CommonActivity { @Override protected int getLayoutResId() { return R.layout.activity_circle_reveal; } @Override protected void doCreateView(Bundle savedInstanceState) { getBinding().btn1.setOnClickListener(v -> animate()); } private void animate() { int cx = (getBinding().vTop.getLeft() + getBinding().vTop.getRight()) / 2; int cy = (getBinding().vTop.getTop() + getBinding().vTop.getBottom()) / 2; // get the final radius for the clipping circle int dx = Math.max(cx, getBinding().vTop.getWidth() - cx); int dy = Math.max(cy, getBinding().vTop.getHeight() - cy); float finalRadius = (float) Math.hypot(dx, dy); // Android native animator Animator animator = ViewAnimationUtils.createCircularReveal(getBinding().vTop, cx, cy, 0, finalRadius); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.setDuration(1500); animator.start(); } } ================================================ FILE: animations/src/main/res/drawable/drawable_bitmap.xml ================================================ ================================================ FILE: animations/src/main/res/drawable/drawable_bitmap_no_filter.xml ================================================ ================================================ FILE: animations/src/main/res/drawable/drawable_layer.xml ================================================ ================================================ FILE: animations/src/main/res/drawable/ic_launcher_background.xml ================================================ ================================================ FILE: animations/src/main/res/drawable-v24/ic_launcher_foreground.xml ================================================ ================================================ FILE: animations/src/main/res/layout/activity_anim.xml ================================================ ================================================ FILE: animations/src/main/res/layout/activity_circle_reveal.xml ================================================ ================================================ FILE: animations/src/main/res/layout/activity_drawable.xml ================================================ ================================================ FILE: animations/src/main/res/mipmap-anydpi-v26/ic_launcher.xml ================================================ ================================================ FILE: animations/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml ================================================ ================================================ FILE: animations/src/main/res/values/strings.xml ================================================ animations ================================================ FILE: animations/src/main/res/values/styles.xml ================================================ ================================================ FILE: animations/src/test/java/me/shouheng/animations/ExampleUnitTest.java ================================================ package me.shouheng.animations; 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: client/.gitignore ================================================ # Built application files *.apk *.ap_ # Files for the ART/Dalvik VM *.dex # Java class files *.class # Generated files bin/ gen/ out/ # Gradle files .gradle/ build/ .idea # Local configuration file (sdk path, etc) local.properties # Proguard folder generated by Eclipse proguard/ # Log Files *.log # Android Studio Navigation editor temp files .navigation/ # Android Studio captures folder captures/ # IntelliJ *.iml .idea/workspace.xml .idea/tasks.xml .idea/gradle.xml .idea/dictionaries .idea/libraries # Keystore files # Uncomment the following line if you do not want to check your keystore files in. #*.jks # External native build folder generated in Android Studio 2.2 and later .externalNativeBuild # Google Services (e.g. APIs or Firebase) google-services.json # Freeline freeline.py freeline/ freeline_project_description.json ================================================ FILE: client/app/.gitignore ================================================ /build ================================================ FILE: client/app/build.gradle ================================================ apply plugin: 'com.android.application' apply plugin: 'io.fabric' apply plugin: 'walle' apply plugin: 'AndResGuard' apply plugin: 'com.getkeepsafe.dexcount' android { compileSdkVersion rootProject.ext.compileSdkVersion defaultConfig { applicationId "me.shouheng.references" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" // enable multi dex multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions { annotationProcessorOptions { arguments = [ moduleName : project.getName() ] } } manifestPlaceholders = [UMENG_CHANNEL_VALUE: "umeng"] ndk { // 只保留 armeabi 和 armeabi-v7a 架构的 so 库,推送中有 so 库,容易导致 UnsatisfiedLinkError abiFilters 'armeabi', 'armeabi-v7a' } } signingConfigs { release { keyAlias 'key0' storeFile file('../palm.jks') storePassword '123456' keyPassword '123456' } debug { } } buildTypes { release { signingConfig signingConfigs.release minifyEnabled false // minify shrinkResources false // enable shrink resources proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } // config released apk name applicationVariants.all { variant -> variant.outputs.all { output -> outputFileName = defaultConfig.applicationId + "-" + variant.baseName + "-" + defaultConfig.versionName + "-" + defaultConfig.versionCode + ".apk" } } } productFlavors.all { flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name] } // config release types flavorDimensions "default" productFlavors { alpha { dimension "default" } beta { dimension "default" } prod { dimension "default" } } // use data binding dataBinding { enabled = true } // use java 8 language compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } // use external libs folder sourceSets { main { jniLibs.srcDirs = ['libs'] } } dexOptions { jumboMode = true } // walle walle { apkOutputFolder = new File("${project.buildDir}/outputs/channels") apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk' channelFile = new File("${project.getProjectDir()}/channel") } // andResGuard andResGuard { mappingFile = null use7zip = true useSign = true keepRoot = false whiteList = [ // for fabric "R.string.com.crashlytics.*", // for google-services "R.string.google_app_id", "R.string.gcm_defaultSenderId", "R.string.default_web_client_id", "R.string.ga_trackingId", "R.string.firebase_database_url", "R.string.google_api_key", "R.string.google_crash_reporting_api_key" ] compressFilePattern = [ "*.png", "*.jpg", "*.jpeg", "*.gif", ] sevenzip { artifact = 'com.tencent.mm:SevenZip:1.2.13' } } // dex count dexcount { format = "list" includeClasses = false includeClassCount = false includeFieldCount = true includeTotalMethodCount = false orderByMethodCount = false verbose = false maxTreeDepth = Integer.MAX_VALUE teamCityIntegration = false teamCitySlug = null runOnEachPackage = true // maxMethodCount = 64000 enabled = true } } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.1.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' // app intro implementation 'com.github.paolorotolo:appintro:1.3.0' // material drawer implementation "com.mikepenz:materialdrawer:6.1.1" // awesome fonts and icons implementation 'com.mikepenz:google-material-typeface:3.0.1.2.original@aar' implementation 'com.mikepenz:fontawesome-typeface:5.0.13.0@aar' // router annotationProcessor 'com.alibaba:arouter-compiler:1.1.4' // projects if (!isGuokrModuleApp.toBoolean()) { implementation project(':guokr') } if (!isLiveModuleApp.toBoolean()) { implementation project(':live') } if (!isLayoutModuleApp.toBoolean()) { implementation project(':layout') } if (!isLibraryModuleApp.toBoolean()) { implementation project(':libraries') } if (!isEyepetizerModuleApp.toBoolean()) { implementation project(':eyepetizer') } if (!isAdvancedModuleApp.toBoolean()) { implementation project(':advanced') } if (!isAnimationsModuleApp.toBoolean()) { implementation project(':animations') } implementation project(':commons') } ================================================ FILE: client/app/channel ================================================ meituan # 美团 samsungapps #三星 hiapk anzhi xiaomi # 小米 91com gfan appchina nduoa 3gcn mumayi 10086com wostore 189store lenovomm hicloud meizu wandou # Google Play # googleplay # 百度 baidu # # 360 360cn # # 应用宝 myapp ================================================ FILE: client/app/fabric.properties ================================================ #Contains API Secret used to validate your application. Commit to internal source control; avoid making secret public. #Wed Jun 06 19:20:45 CST 2018 apiSecret=e4664a037aa796ecf2f8c00b5160724ef5056a17cd29ee650f42b1bc0bc067e6 ================================================ FILE: client/app/gradle/wrapper/gradle-wrapper.properties ================================================ #Wed Jun 06 19:41:09 CST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip ================================================ FILE: client/app/gradlew ================================================ #!/usr/bin/env sh ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn () { echo "$*" } die () { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Escape application args save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } APP_ARGS=$(save "$@") # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then cd "$(dirname "$0")" fi exec "$JAVACMD" "$@" ================================================ FILE: client/app/gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: client/app/proguard-rules.pro ================================================ #指定压缩级别 -optimizationpasses 5 #不跳过非公共的库的类成员 -dontskipnonpubliclibraryclassmembers -dontskipnonpubliclibraryclasses #混淆时采用的算法 -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* #把混淆类中的方法名也混淆了 -useuniqueclassmembernames #优化时允许访问并修改有修饰符的类和类的成员 -allowaccessmodification #将文件来源重命名为“SourceFile”字符串 -renamesourcefileattribute SourceFile #保留行号 -keepattributes SourceFile,LineNumberTable # 是否使用大小写混合 -dontusemixedcaseclassnames #混淆时是否记录日志 -verbose #忽略警告,避免打包时某些警告出现 -ignorewarnings #预校验 -dontpreverify #保护注解 -keepattributes *Annotation* #保护JS回调接口 -keepattributes *JavascriptInterface* #保留JavascriptInterface中的方法 -keepclassmembers class * { @android.webkit.JavascriptInterface ; } #记录生成的日志数据,gradle build时在本项目根目录输出 #apk 包内所有 class 的内部结构 -dump class_files.txt #未混淆的类和成员 -printseeds seeds.txt #列出从 apk 中删除的代码 -printusage unused.txt #混淆前后的映射 -printmapping mapping.txt #保持所有实现 Serializable 接口的类成员 -keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; private static final java.io.ObjectStreamField[] serialPersistentFields; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve(); } # 保持 Parcelable 不被混淆 -keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *; } # 保持枚举 enum 类不被混淆 -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } #Fragment不需要在AndroidManifest.xml中注册,需要额外保护下 -keep public class * extends android.support.v4.app.Fragment -keep public class * extends android.app.Fragment #Social Style Date and Time Formatting for Java -keep class org.ocpsoft.prettytime.i18n.** #如果有引用v4、v7包可以添加下面这行 -keep public class * extends android.support.* #解决ActionBar上面的搜索按钮的空指针问题 -keep class android.support.v7.widget.SearchView { *; } #过滤泛型 -keepattributes Signature # 保持测试相关的代码 -dontnote junit.framework.** -dontnote junit.runner.** -dontwarn android.test.** -dontwarn android.support.test.** -dontwarn org.junit.** # proguard rules for rx-java -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { long producerIndex; long consumerIndex; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { rx.internal.util.atomic.LinkedQueueNode producerNode; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { rx.internal.util.atomic.LinkedQueueNode consumerNode; } -dontwarn sun.misc.Unsafe # Umeng 混淆配置 -keep class com.umeng.** {*;} -keepclassmembers class * { public (org.json.JSONObject); } # Retrofit 混淆配置 -dontnote retrofit2.Platform -dontnote retrofit2.Platform$IOS$MainThreadExecutor -dontwarn retrofit2.Platform$Java8 -keepattributes Signature -keepattributes Exceptions # okhttp 混淆配置 -dontwarn okio.** # Gson 混淆配置 # TODO 自定义数据模型的bean目录,如果使用了 Retrofit,必须增加该配置 # SwipeRecyclerView -keepclasseswithmembers class android.support.v7.widget.RecyclerView$ViewHolder { public final View *; } -dontwarn com.yanzhenjie.recyclerview.swipe.** -keep class com.yanzhenjie.recyclerview.swipe.** {*;} # 弹出菜单不混淆,需要反射调用其中的字段 -keep class android.support.v7.widget.PopupMenu { *; } # Glide -keep public class * implements com.bumptech.glide.module.GlideModule -keep public class * extends com.bumptech.glide.module.AppGlideModule -keep public enum com.bumptech.glide.load.ImageHeaderParser$** { **[] $VALUES; public *; } # sensor -dontwarn com.sensorsdata.analytics.android.** -keep class com.sensorsdata.analytics.android.** { *; } -keep class **.R$* { ; } -keepnames class * implements android.view.View$OnClickListener -keep public class * extends android.content.ContentProvider -keepnames class * extends android.view.View -keep class * extends android.app.Fragment { public void setUserVisibleHint(boolean); public void onHiddenChanged(boolean); public void onResume(); public void onPause(); } -keep class android.support.v4.app.Fragment { public void setUserVisibleHint(boolean); public void onHiddenChanged(boolean); public void onResume(); public void onPause(); } -keep class * extends android.support.v4.app.Fragment { public void setUserVisibleHint(boolean); public void onHiddenChanged(boolean); public void onResume(); public void onPause(); } # DataBinding -dontwarn android.databinding.** -keep class android.databinding.** { *; } # TODO 保持 databinding 目录 # MultiDex -keep class com.sensorsdata.analytics.android.** { *; } ================================================ FILE: client/app/src/androidTest/java/me/shouheng/references/ExampleInstrumentedTest.java ================================================ package me.shouheng.references; import android.content.Context; import androidx.test.InstrumentationRegistry; import androidx.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.getTargetContext(); assertEquals("me.shouheng.references", appContext.getPackageName()); } } ================================================ FILE: client/app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: client/app/src/main/assets/guokr.articleInline.js ================================================ (function(){define("ga",{setGa:function(){(function(e,t,n,r,i,s,o){e.GoogleAnalyticsObject=i,e[i]=e[i]||function(){(e[i].q=e[i].q||[]).push(arguments)},e[i].l=1*new Date,s=t.createElement(n),o=t.getElementsByTagName(n)[0],s.async=1,s.src=r,o.parentNode.insertBefore(s,o)})(window,document,"script","//www.google-analytics.com/analytics.js","ga"),ga("create","UA-19521615-1","guokr.com"),ga("send","pageview")}}),define("tmpl",[],function(){function e(e){return String(e).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}var t={entities:e};return function(n,r){/\W/.test(n)||(n=document.getElementById(n).innerHTML);var i=new Function("funcs, obj","var p=[],print=function(){p.push.apply(p,arguments);};with(funcs){with(obj){p.push('"+n.replace(/[\r\t\n]/g," ").split("<%").join(" ").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',entities($1),'").replace(/\t:=(.*?)%>/g,"',$1,'").split(" ").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}}return p.join('');");return r?i(t,r):function(e){return i(t,e)}}}),require(["ga","tmpl"],function(e,t){function n(){var e;if(typeof GuokrBridge=="undefined")return;e=JSON.parse(GuokrBridge.getRecommendArticles());if(e&&e.length){var n=document.getElementById("relaTmpl").innerHTML,r=t(n,{articles:e});document.getElementById("relaListWrap").innerHTML=r}}(function(e){e.fn.scrollLoading=function(t){var n={attr:"data-src"},r=e.extend({},n,t||{});r.cache=[],e(this).each(function(){var t=this.nodeName.toLowerCase(),n=e(this).attr(r.attr);if(!n)return;var i={obj:e(this),tag:t,url:n};r.cache.push(i)});var i=function(){var t=e(window).scrollTop(),n=t+e(window).height();return e.each(r.cache,function(e,r){var i=r.obj,s=r.tag,o=r.url;if(i){post=i.position().top,posb=post+i.height();if(post>t&&postt&&posb0&&b-1 in a)}function d(a){var b=oa[a]={};return fa.each(a.match(ha)||[],function(a,c){b[c]=!0}),b}function e(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=fa.expando+Math.random()}function f(a,c,d){var e;if(d===b&&1===a.nodeType)if(e="data-"+c.replace(sa,"-$1").toLowerCase(),d=a.getAttribute(e),"string"==typeof d){try{d="true"===d?!0:"false"===d?!1:"null"===d?null:+d+""===d?+d:ra.test(d)?JSON.parse(d):d}catch(f){}pa.set(a,c,d)}else d=b;return d}function g(){return!0}function h(){return!1}function i(){try{return T.activeElement}catch(a){}}function j(a,b){for(;(a=a[b])&&1!==a.nodeType;);return a}function k(a,b,c){if(fa.isFunction(b))return fa.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return fa.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(Ca.test(b))return fa.filter(b,a,c);b=fa.filter(b,a)}return fa.grep(a,function(a){return ba.call(b,a)>=0!==c})}function l(a,b){return fa.nodeName(a,"table")&&fa.nodeName(1===b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function m(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function n(a){var b=Na.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function o(a,b){for(var c=a.length,d=0;c>d;d++)qa.set(a[d],"globalEval",!b||qa.get(b[d],"globalEval"))}function p(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(qa.hasData(a)&&(f=qa.access(a),g=qa.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)fa.event.add(b,e,j[e][c])}pa.hasData(a)&&(h=pa.access(a),i=fa.extend({},h),pa.set(b,i))}}function q(a,c){var d=a.getElementsByTagName?a.getElementsByTagName(c||"*"):a.querySelectorAll?a.querySelectorAll(c||"*"):[];return c===b||c&&fa.nodeName(a,c)?fa.merge([a],d):d}function r(a,b){var c=b.nodeName.toLowerCase();"input"===c&&Ka.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}function s(a,b){if(b in a)return b;for(var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=_a.length;e--;)if(b=_a[e]+c,b in a)return b;return d}function t(a,b){return a=b||a,"none"===fa.css(a,"display")||!fa.contains(a.ownerDocument,a)}function u(b){return a.getComputedStyle(b,null)}function v(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=qa.get(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&t(d)&&(f[g]=qa.access(d,"olddisplay",z(d.nodeName)))):f[g]||(e=t(d),(c&&"none"!==c||!e)&&qa.set(d,"olddisplay",e?c:fa.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function w(a,b,c){var d=Ua.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function x(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=fa.css(a,c+$a[f],!0,e)),d?("content"===c&&(g-=fa.css(a,"padding"+$a[f],!0,e)),"margin"!==c&&(g-=fa.css(a,"border"+$a[f]+"Width",!0,e))):(g+=fa.css(a,"padding"+$a[f],!0,e),"padding"!==c&&(g+=fa.css(a,"border"+$a[f]+"Width",!0,e)));return g}function y(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=u(a),g=fa.support.boxSizing&&"border-box"===fa.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Qa(a,b,f),(0>e||null==e)&&(e=a.style[b]),Va.test(e))return e;d=g&&(fa.support.boxSizingReliable||e===a.style[b]),e=parseFloat(e)||0}return e+x(a,b,c||(g?"border":"content"),d,f)+"px"}function z(a){var b=T,c=Xa[a];return c||(c=A(a,b),"none"!==c&&c||(Ra=(Ra||fa("