From 870ae16bbc39a9daec34183e0d70882738214bf5 Mon Sep 17 00:00:00 2001 From: shenliming <348515494@qq.com> Date: Tue, 1 Aug 2023 16:34:44 +0800 Subject: [PATCH] Code Architecture Refactoring. 1. hide Provider is called and delete the singleton of the provider; 2. provider is now in TrackerContext; 3. add shutdown method; 3. user spotless format code; --- .github/workflows/ci.yml | 5 +- build.gradle | 39 ++- config/checkstyle/checkstyle.sh | 23 -- config/checkstyle/checkstyle.xml | 171 --------- demos/demo-autotrack/build.gradle | 42 +-- .../autotrack/AutotrackEntryActivity.java | 3 +- .../activity/ActionMenuViewActivity.java | 3 +- .../activity/AddPeopleDialogActivity.java | 3 +- .../autotrack/activity/ClickTestActivity.java | 3 +- .../activity/DialogTestActivity.java | 3 +- .../activity/ExpandableListSubActivity.java | 3 +- .../activity/ExpandableListViewActivity.java | 3 +- .../activity/HideFragmentActivity.java | 3 +- .../autotrack/activity/LambdaActivity.java | 3 +- .../activity/NavFragmentActivity.java | 3 +- .../activity/NestedFragmentActivity.java | 3 +- .../activity/RecyclerViewImpActivity.java | 3 +- .../activity/TabFragmentActivity.java | 3 +- .../autotrack/activity/ToolBarActivity.java | 3 +- .../activity/ViewImpressionActivity.java | 3 +- .../autotrack/activity/WebViewActivity.java | 3 +- .../autotrack/activity/X5WebViewActivity.java | 3 +- .../activity/data/LoginDataSource.java | 3 +- .../activity/data/LoginRepository.java | 3 +- .../three/autotrack/activity/data/Result.java | 3 +- .../activity/data/model/LoggedInUser.java | 3 +- .../ui/dashboard/DashboardFragment.java | 3 +- .../ui/dashboard/DashboardViewModel.java | 3 +- .../activity/ui/home/HomeFragment.java | 3 +- .../activity/ui/home/HomeViewModel.java | 3 +- .../activity/ui/login/LoggedInUserView.java | 3 +- .../activity/ui/login/LoginActivity.java | 3 +- .../activity/ui/login/LoginFormState.java | 3 +- .../activity/ui/login/LoginResult.java | 3 +- .../activity/ui/login/LoginViewModel.java | 3 +- .../ui/login/LoginViewModelFactory.java | 3 +- .../activity/ui/main/PageViewModel.java | 3 +- .../activity/ui/main/PlaceholderFragment.java | 3 +- .../ui/main/SectionsPagerAdapter.java | 3 +- .../notifications/NotificationsFragment.java | 3 +- .../notifications/NotificationsViewModel.java | 3 +- .../fragments/FragmentLifecycleCallback.java | 3 +- .../fragments/FragmentLifecycleMonitor.java | 3 +- .../autotrack/fragments/GreenFragment.java | 3 +- .../autotrack/fragments/OrangeFragment.java | 3 +- .../autotrack/fragments/RedFragment.java | 3 +- .../fragments/base/BaseFragment.java | 3 +- .../three/autotrack/utils/DialogUtil.java | 3 +- demos/demo-core/build.gradle | 32 +- .../java/com/gio/test/three/ModuleEntry.java | 3 +- demos/demo/build.gradle | 172 ++++----- .../com/growingio/autotest/EventsTest.java | 3 +- .../autotest/TestTrackConfiguration.java | 3 +- .../autotest/TrackAndroidJUnitRunner.java | 3 +- .../autotest/TrackTestRunListener.java | 3 +- .../growingio/autotest/WebServicesTest.java | 3 +- .../autotest/autotracker/PageEventsTest.java | 5 +- .../autotracker/ViewChangeEventsTest.java | 3 +- .../autotracker/ViewClickEventsTest.java | 3 +- .../autotracker/hybrid/HybridEventsTest.java | 36 +- .../hybrid/HybridSpecialScenarioTest.java | 3 +- .../impression/ImpressionEventsTest.java | 3 +- ...nReceivedViewImpressionEventsListener.java | 5 +- ...pReceivedViewImpressionEventsListener.java | 3 +- ...TrackConfigurationImpressionScaleTest.java | 3 +- .../webservices/CircleServiceTest.java | 5 +- .../webservices/DebuggerServiceTest.java | 3 +- .../com/growingio/autotest/help/Awaiter.java | 3 +- .../autotest/help/BeforeAppOnCreate.java | 3 +- .../growingio/autotest/help/Condition.java | 3 +- .../help/ConditionTimeoutException.java | 3 +- .../growingio/autotest/help/DataHelper.java | 3 +- .../autotest/help/EventsTestDataHelper.java | 3 +- .../com/growingio/autotest/help/JsonUtil.java | 49 +++ .../autotest/help/MockEventsApiServer.java | 5 +- .../growingio/autotest/help/MockServer.java | 3 +- .../growingio/autotest/help/TrackHelper.java | 3 +- .../autotest/help/Uninterruptibles.java | 3 +- .../tracker/CustomDataUploadIntervalTest.java | 3 +- .../tracker/DataCollectionEnabledTest.java | 3 +- .../DefaultDataUploadIntervalTest.java | 3 +- .../autotest/tracker/IdMappingTest.java | 3 +- .../autotest/tracker/SessionEventsTest.java | 7 +- .../autotest/tracker/TrackEventsTest.java | 3 +- .../tracker/TrackWebServicesTest.java | 32 -- .../com/gio/test/three/DemoApplication.java | 6 +- .../java/com/gio/test/three/MainActivity.java | 3 +- .../gio/test/three/OtherProcessService.java | 3 +- .../gio/test/three/core/TrackActivity.java | 3 +- gio-sdk/autotracker/build.gradle | 84 ++--- .../sdk/autotrack/GrowingAutoAppModule.java | 3 +- gio-sdk/tracker/build.gradle | 76 ++-- .../android/sdk/track/GrowingAppModule.java | 3 +- {config/checkstyle => gradle}/java.header | 4 +- gradle/libs.versions.toml | 3 + growingio-advert/build.gradle | 85 +++-- .../advert/AdvertActivateDataLoader.java | 77 +++-- .../android/advert/AdvertConfig.java | 21 +- .../growingio/android/advert/AdvertData.java | 21 +- .../advert/AdvertLibraryGioModule.java | 10 +- .../growingio/android/advert/AdvertUtils.java | 40 +-- .../android/advert/ActivateTest.java | 71 ++-- .../android/advert/RobolectricActivity.java | 2 +- growingio-annotation/build.gradle | 6 +- growingio-annotation/compiler/build.gradle | 38 +- .../compiler/libs/tracker-core-test-3.5.0.jar | Bin 537238 -> 0 bytes .../compiler/libs/tracker-core-test-4.0.0.jar | Bin 0 -> 693437 bytes .../compiler/AppModuleGenerator.java | 22 +- .../compiler/ConfigurationGenerator.kt | 66 ++-- .../compiler/GioModuleProcessor.java | 3 +- .../compiler/GioTrackerGenerator.kt | 108 +++--- .../annotation/compiler/IndexerGenerator.java | 3 +- .../sdk/annotation/compiler/ProcessUtils.kt | 21 +- .../test/java/android/app/Application.java | 2 +- .../test/java/android/content/Context.java | 21 +- .../annotation/compiler/EmptyAppModuleTest.kt | 7 +- .../annotation/compiler/EmptyModuleTest.kt | 8 +- .../sdk/annotation/compiler/TestUtil.java | 3 +- .../compiler/rule/CompilationProvider.java | 3 +- .../compiler/sample/EmptyConfig.java | 2 +- .../EmptyAppModuleTest/EmptyAppGioModule.java | 3 +- .../GeneratedGioModuleImpl.java | 26 +- .../EmptyAppModuleTest/GrowingTracker.java | 31 +- .../TestTrackConfiguration.java | 21 +- .../EmptyLibraryGioModule.java | 3 +- ...gio_android_sdk_EmptyLibraryGioModule.java | 15 + .../json-serializer/build.gradle | 22 +- .../json/serializer/JsonFactoryGenerator.kt | 69 ++-- .../serializer/JsonSerializerGenerator.kt | 147 ++++---- .../serializer/JsonSerializerProcessor.java | 21 +- .../json/serializer/ProcessUtils.kt | 21 +- .../sdk/annotation/GIOAppModule.java | 3 +- .../growingio/sdk/annotation/GIOConfig.java | 3 +- .../sdk/annotation/GIOLibraryModule.java | 3 +- .../growingio/sdk/annotation/GIOTracker.java | 2 +- .../sdk/annotation/compiler/Index.java | 3 +- .../sdk/annotation/json/JsonAlias.java | 21 +- .../sdk/annotation/json/JsonIgnore.java | 21 +- .../sdk/annotation/json/JsonSerializer.java | 21 +- growingio-apm/build.gradle | 87 +++-- .../com/growingio/android/apm/ApmConfig.java | 21 +- .../growingio/android/apm/ApmDataLoader.java | 32 +- .../android/apm/ApmEventBuilder.java | 21 +- .../android/apm/ApmLibraryGioModule.java | 10 +- .../com/growingio/android/apm/ApmLogger.java | 21 +- .../com/growingio/android/apm/ApmTracker.java | 63 +--- .../com/growingio/android/apm/GrowingApm.java | 55 --- .../com/growingio/android/apm/ApmTest.java | 63 ++-- growingio-autotracker-core/build.gradle | 114 +++--- .../sdk/autotrack/AutotrackConfig.java | 3 +- .../sdk/autotrack/AutotrackOptions.java | 21 +- .../android/sdk/autotrack/Autotracker.java | 94 +++-- .../autotrack/AutotrackerLibraryModule.java | 3 +- .../android/sdk/autotrack/IgnorePolicy.java | 3 +- .../impression/ImpressionProvider.java | 61 ++-- .../autotrack/impression/ViewImpression.java | 3 +- .../autotrack/inject/ActivityInjector.java | 13 +- .../autotrack/inject/DialogClickProvider.java | 64 ++-- .../sdk/autotrack/inject/DialogInjector.java | 29 +- .../autotrack/inject/FragmentInjector.java | 21 +- .../autotrack/inject/FragmentV4Injector.java | 21 +- .../autotrack/inject/InjectorProvider.java | 249 ++++++++++++++ .../autotrack/inject/MenuItemInjector.java | 29 +- .../autotrack/inject/UcWebViewInjector.java | 30 +- .../autotrack/inject/ViewChangeInjector.java | 29 +- .../autotrack/inject/ViewChangeProvider.java | 83 +++-- .../autotrack/inject/ViewClickInjector.java | 39 +-- .../autotrack/inject/ViewClickProvider.java | 131 +++---- .../sdk/autotrack/inject/WebViewInjector.java | 30 +- .../autotrack/inject/X5WebViewInjector.java | 30 +- .../sdk/autotrack/page/ActivityPage.java | 15 +- .../sdk/autotrack/page/FragmentPage.java | 15 +- .../android/sdk/autotrack/page/Page.java | 5 +- .../sdk/autotrack/page/PageProvider.java | 48 ++- .../sdk/autotrack/page/SuperFragment.java | 3 +- .../sdk/autotrack/page/WindowPage.java | 3 +- .../shadow/AlertControllerShadow.java | 3 +- .../shadow/ListMenuItemViewShadow.java | 3 +- .../android/sdk/autotrack/util/ClassUtil.java | 3 +- .../sdk/autotrack/util/HurtLocker.java | 3 +- .../sdk/autotrack/view/PageHelper.java | 6 +- .../autotrack/view/ScreenElementHelper.java | 21 +- .../sdk/autotrack/view/ViewAttributeUtil.java | 3 +- .../sdk/autotrack/view/ViewNodeProvider.java | 63 ++-- .../sdk/autotrack/view/ViewNodeRenderer.java | 21 +- .../sdk/autotrack/view/ViewNodeV3.java | 23 +- .../autotrack/view/ViewNodeV3Renderer.java | 41 ++- .../sdk/autotrack/view/ViewNodeV4.java | 21 +- .../autotrack/view/ViewNodeV4Renderer.java | 43 ++- .../android/sdk/autotrack/view/ViewUtil.java | 18 +- .../android/sdk/autotrack/AutotrackTest.java | 33 +- .../sdk/autotrack/RobolectricActivity.java | 3 +- .../sdk/autotrack/ShadowThreadUtils.java | 28 +- .../android/sdk/autotrack/UtilTest.java | 14 +- .../click/TrackMainThreadShadow.java | 21 +- .../sdk/autotrack/click/ViewClickTest.java | 29 +- .../sdk/autotrack/hybrid/HybridTest.java | 8 +- .../autotrack/impression/ImpressionTest.java | 28 +- .../android/sdk/autotrack/page/PageTest.java | 23 +- .../sdk/autotrack/view/ViewNodeV3Test.java | 37 +- .../sdk/autotrack/view/ViewNodeV4Test.java | 192 +++++++++++ growingio-data/database/build.gradle | 84 ++--- .../android/database/DatabaseDataFetcher.java | 21 +- .../android/database/DatabaseDataLoader.java | 9 +- .../database/DatabaseLibraryModule.java | 11 +- .../database/DeprecatedEventSQLite.java | 7 +- .../database/EventDataContentProvider.java | 3 +- .../android/database/EventDataManager.java | 16 +- .../database/EventDataSQLiteOpenHelper.java | 3 +- .../android/database/EventDataTable.java | 10 +- .../growingio/android/database/DbTest.java | 24 +- growingio-data/encoder/build.gradle | 76 ++-- .../android/encoder/EncoderDataFetcher.java | 3 +- .../android/encoder/EncoderDataLoader.java | 3 +- .../encoder/EncoderLibraryGioModule.java | 9 +- .../android/encoder/EncoderTest.java | 15 +- growingio-data/json/build.gradle | 72 ++-- .../android/json/JsonDataFetcher.java | 7 +- .../android/json/JsonDataLoader.java | 3 +- .../android/json/JsonLibraryModule.java | 9 +- .../json/src/test/java/JsonDataTest.java | 40 +-- growingio-data/protobuf/build.gradle | 120 +++---- .../protobuf/EventProtocolTransfer.java | 50 ++- .../android/protobuf/ProtobufDataFetcher.java | 21 +- .../android/protobuf/ProtobufDataLoader.java | 21 +- .../protobuf/ProtobufLibraryModule.java | 27 +- .../protobuf/src/main/proto/event_v3.proto | 4 +- .../android/protobuf/ProtocolDataTest.java | 48 +-- .../android/protobuf/ProtocolTest.java | 32 +- growingio-flutter/build.gradle | 83 +++-- .../android/flutter/FlutterDataLoader.java | 46 ++- .../flutter/FlutterLibraryGioModule.java | 17 +- .../flutter/FlutterMethodInterface.java | 21 +- .../flutter/FlutterPluginProvider.java | 61 ++-- .../android/flutter/ExampleUnitTest.java | 21 +- growingio-hybrid/build.gradle | 87 +++-- .../android/hybrid/HybridBridgeLoader.java | 31 +- .../android/hybrid/HybridBridgeProvider.java | 60 ++-- .../android/hybrid/HybridDomLoader.java | 31 +- .../hybrid/HybridLibraryGioModule.java | 19 +- .../android/hybrid/HybridTransformer.java | 3 +- .../android/hybrid/HybridTransformerImp.java | 17 +- .../android/hybrid/NativeBridge.java | 15 +- .../android/hybrid/OnDomChangedListener.java | 3 +- .../android/hybrid/SuperWebView.java | 3 +- .../WebViewBridgeJavascriptInterface.java | 16 +- .../WebViewJavascriptBridgeConfiguration.java | 3 +- .../growingio/android/hybrid/HybridTest.java | 105 ++---- .../android/hybrid/RobolectricActivity.java | 3 +- growingio-network/okhttp3/build.gradle | 79 +++-- .../android/okhttp3/OkHttpDataFetcher.java | 3 +- .../android/okhttp3/OkHttpDataLoader.java | 3 +- .../okhttp3/OkhttpLibraryGioModule.java | 10 +- .../okhttp3/SecurityExceptionInterceptor.java | 8 +- .../growingio/android/okhttp3/MockServer.java | 3 +- .../android/okhttp3/Okhttp3Test.java | 21 +- growingio-network/urlconnection/build.gradle | 74 ++-- .../android/urlconnection/HttpException.java | 3 +- .../android/urlconnection/LogTime.java | 3 +- .../UrlConnectionDataLoader.java | 3 +- .../urlconnection/UrlConnectionFetcher.java | 3 +- .../urlconnection/UrlConnectionGioModule.java | 10 +- .../uriconnection/UrlConnectionTest.java | 26 +- growingio-network/volley/build.gradle | 85 +++-- .../android/volley/VolleyDataFetcher.java | 3 +- .../android/volley/VolleyDataLoader.java | 3 +- .../volley/VolleyLibraryGioModule.java | 10 +- .../android/volley/VolleyRequestFactory.java | 3 +- .../growingio/android/volley/VolleyTest.java | 24 +- growingio-sdk-bom/build.gradle | 76 ++-- growingio-sdk-bom/publishMaven.gradle | 156 ++++----- growingio-tools/oaid/build.gradle | 87 +++-- .../oaid/resources/OaidHelper1010.java | 3 +- .../oaid/resources/OaidHelper1013.java | 3 +- .../oaid/resources/OaidHelper1025.java | 3 +- .../oaid/resources/OaidHelper1100.java | 3 +- .../growingio/android/oaid/IOaidHelper.java | 3 +- .../android/oaid/OaidCertHelper.java | 24 +- .../growingio/android/oaid/OaidConfig.java | 21 +- .../android/oaid/OaidDataFetcher.java | 3 +- .../android/oaid/OaidDataLoader.java | 55 +-- .../android/oaid/OaidDirectlyHelper.java | 21 +- .../android/oaid/OaidLibraryGioModule.java | 10 +- .../com/growingio/android/oaid/OaidTest.java | 39 ++- growingio-tools/snappy/build.gradle | 64 ++-- .../android/snappy/BufferRecycler.java | 4 +- .../com/growingio/android/snappy/Memory.java | 2 +- .../growingio/android/snappy/SlowMemory.java | 2 +- .../com/growingio/android/snappy/Snappy.java | 2 +- .../android/snappy/SnappyCompressor.java | 3 +- .../android/snappy/SnappyInternalUtils.java | 3 +- .../growingio/android/snappy/XORUtils.java | 3 +- .../growingio/android/snappy/SnappyTest.java | 2 +- growingio-tracker-core/build.gradle | 116 +++---- .../growingio/android/sdk/AppGioModule.java | 3 +- .../growingio/android/sdk/Configurable.java | 3 +- .../android/sdk/CoreConfiguration.java | 3 +- .../android/sdk/GeneratedGioModule.java | 3 +- .../android/sdk/LibraryGioModule.java | 13 +- .../com/growingio/android/sdk/Tracker.java | 199 ++++++----- .../growingio/android/sdk/TrackerContext.java | 96 ++++-- .../growingio/android/sdk/track/ErrorLog.java | 55 --- .../android/sdk/track/SDKConfig.java | 3 +- .../android/sdk/track/TrackMainThread.java | 325 ++++++------------ .../sdk/track/async/EmptyDisposable.java | 31 -- .../sdk/track/async/HandlerDisposable.java | 86 ----- .../sdk/track/events/ActivateEvent.java | 30 +- .../sdk/track/events/AppClosedEvent.java | 8 +- .../sdk/track/events/AttributesBuilder.java | 21 +- .../sdk/track/events/AutotrackEventType.java | 3 +- .../events/ConversionVariablesEvent.java | 3 +- .../android/sdk/track/events/CustomEvent.java | 3 +- .../track/events/EventBuildInterceptor.java | 3 +- .../track/events/EventFilterInterceptor.java | 3 +- .../events/LoginUserAttributesEvent.java | 3 +- .../android/sdk/track/events/PageEvent.java | 9 +- .../track/events/PageLevelCustomEvent.java | 3 +- .../sdk/track/events/TrackEventGenerator.java | 3 +- .../sdk/track/events/TrackEventType.java | 3 +- .../sdk/track/events/ViewElementEvent.java | 5 +- .../android/sdk/track/events/VisitEvent.java | 10 +- .../track/events/VisitorAttributesEvent.java | 3 +- .../events/base/BaseAttributesEvent.java | 3 +- .../sdk/track/events/base/BaseEvent.java | 58 ++-- .../sdk/track/events/base/BaseField.java | 21 +- .../helper/DefaultEventFilterInterceptor.java | 22 +- .../track/events/helper/JsonSerializable.java | 21 +- .../events/hybrid/HybridCustomEvent.java | 21 +- .../track/events/hybrid/HybridPageEvent.java | 21 +- .../events/hybrid/HybridViewElementEvent.java | 21 +- .../android/sdk/track/ipc/IDataSharer.java | 5 +- .../sdk/track/ipc/MultiProcessDataSharer.java | 15 +- .../android/sdk/track/ipc/ProcessLock.java | 3 +- .../android/sdk/track/ipc/SharedEntry.java | 3 +- .../track/{async => listener}/Callback.java | 12 +- .../track/listener/IActivityLifecycle.java | 3 +- .../sdk/track/listener/ListenerContainer.java | 3 +- .../track/listener/ListenerDispatcher.java | 21 +- .../OnConfigurationChangeListener.java | 21 +- .../listener/OnTrackMainInitSDKCallback.java | 22 -- .../listener/OnUserIdChangedListener.java | 3 +- .../sdk/track/listener/TrackThread.java | 21 +- .../event/ActivityLifecycleEvent.java | 3 +- .../android/sdk/track/log/BaseLogger.java | 3 +- .../android/sdk/track/log/CacheLogger.java | 3 +- .../sdk/track/log/CircularFifoQueue.java | 3 +- .../android/sdk/track/log/DebugLogger.java | 3 +- .../android/sdk/track/log/ErrorLogger.java | 3 +- .../android/sdk/track/log/ILogger.java | 3 +- .../android/sdk/track/log/LogItem.java | 3 +- .../android/sdk/track/log/Logger.java | 3 +- .../sdk/track/middleware/EventDatabase.java | 21 +- .../sdk/track/middleware/EventDbResult.java | 21 +- .../sdk/track/middleware/EventFlutter.java | 21 +- .../sdk/track/middleware/EventHttpSender.java | 45 ++- .../sdk/track/middleware/EventSender.java | 33 +- .../android/sdk/track/middleware/GEvent.java | 3 +- .../sdk/track/middleware/IEventNetSender.java | 3 +- .../sdk/track/middleware/OaidHelper.java | 21 +- .../sdk/track/middleware/SendResponse.java | 3 +- .../sdk/track/middleware/Serializer.java | 3 +- .../sdk/track/middleware/advert/Activate.java | 21 +- .../track/middleware/advert/AdvertResult.java | 21 +- .../sdk/track/middleware/advert/DeepLink.java | 21 +- .../middleware/advert/DeepLinkCallback.java | 21 +- .../sdk/track/middleware/apm/EventApm.java | 21 +- .../middleware/format/EventByteArray.java | 21 +- .../middleware/format/EventFormatData.java | 21 +- .../middleware/format/FormatDataFetcher.java | 21 +- .../track/middleware/http/EventEncoder.java | 21 +- .../track/middleware/http/EventResponse.java | 21 +- .../sdk/track/middleware/http/EventUrl.java | 21 +- .../middleware/http/HttpDataFetcher.java | 21 +- .../track/middleware/hybrid/HybridBridge.java | 21 +- .../track/middleware/hybrid/HybridDom.java | 21 +- .../track/middleware/hybrid/HybridJson.java | 21 +- .../webservice}/Circler.java | 5 +- .../webservice}/Debugger.java | 5 +- .../webservice}/WebService.java | 5 +- .../sdk/track/modelloader/DataFetcher.java | 3 +- .../track/modelloader/LoadDataFetcher.java | 21 +- .../sdk/track/modelloader/ModelLoader.java | 3 +- .../track/modelloader/ModelLoaderFactory.java | 3 +- .../modelloader/ModelLoaderRegistry.java | 3 +- .../track/modelloader/TrackerRegistry.java | 23 +- .../providers/ActivityStateProvider.java | 91 +++-- .../sdk/track/providers/AppInfoProvider.java | 58 ++-- .../providers/ConfigurationProvider.java | 51 ++- .../sdk/track/providers/DeepLinkProvider.java | 57 +-- .../track/providers/DeviceInfoProvider.java | 87 +++-- .../track/providers/EventBuilderProvider.java | 220 ++++++++++++ .../track/providers/EventStateProvider.java | 89 ----- .../PersistentDataProvider.java | 128 ++++--- .../sdk/track/providers/SessionProvider.java | 109 +++--- .../TimingEventProvider.java} | 84 ++--- .../TrackerLifecycleProvider.java} | 17 +- .../TrackerLifecycleProviderFactory.java | 84 +++++ .../sdk/track/providers/UserInfoProvider.java | 51 +-- .../android/sdk/track/utils/ActivityUtil.java | 3 +- .../sdk/track/utils/ClassExistHelper.java | 3 +- .../android/sdk/track/utils/ConstantPool.java | 3 +- .../android/sdk/track/utils/DeviceUtil.java | 3 +- .../android/sdk/track/utils/NetworkUtil.java | 3 +- .../android/sdk/track/utils/ObjectUtils.java | 3 +- .../sdk/track/utils/PermissionUtil.java | 7 +- .../android/sdk/track/utils/SysTrace.java | 12 +- .../android/sdk/track/utils/SystemUtil.java | 3 +- .../android/sdk/track/utils/ThreadUtils.java | 97 ------ .../Timer.java => utils/TimerEvent.java} | 41 ++- .../android/sdk/track/view/DecorView.java | 3 +- .../view/OnViewStateChangedListener.java | 3 +- .../sdk/track/view/ScreenshotUtil.java | 13 +- .../{webservices/widget => view}/TipView.java | 7 +- .../sdk/track/view/ViewStateChangedEvent.java | 3 +- .../track/view/ViewTreeStatusListener.java | 67 ++-- .../track/view/ViewTreeStatusObserver.java | 3 +- .../android/sdk/track/view/WindowHelper.java | 10 +- .../sdk/track/view/WindowManagerShadow.java | 3 +- .../message/ClientInfoMessage.java | 67 ---- .../webservices/message/QuitMessage.java | 40 --- .../webservices/message/ReadyMessage.java | 88 ----- .../growingio/android/sdk/TrackerTest.java | 19 +- .../android/sdk/track/AsyncTest.java | 50 --- .../growingio/android/sdk/track/IpcTest.java | 3 +- .../growingio/android/sdk/track/LogTest.java | 3 +- .../android/sdk/track/ModelLoaderTest.java | 3 +- .../android/sdk/track/events/EventsTest.java | 21 +- .../sdk/track/middleware/EventSenderTest.java | 58 ++-- .../sdk/track/middleware/HttpSenderTest.java | 34 +- .../track/middleware/TestModelFactory.java | 21 +- .../EventBuilderProviderFilterTest.java} | 55 +-- .../sdk/track/providers/ProviderTest.java | 69 ++-- .../track/providers/RobolectricActivity.java | 2 +- .../android/sdk/track/timer/TimerTest.java | 10 +- ...Test.java => TimingEventProviderTest.java} | 41 ++- .../android/sdk/track/utils/JsonUtil.java | 3 +- .../android/sdk/track/utils/UtilsTest.java | 27 +- .../track/view/FakeWindowManagerGlobal.java | 3 +- .../TipViewTest.java} | 70 +--- .../android/sdk/track/view/ViewTest.java | 21 +- growingio-webservice/circler/build.gradle | 88 ++--- .../{screenshot => }/CircleScreenshot.java | 43 +-- .../android/circler/CirclerDataLoader.java | 23 +- .../circler/CirclerLibraryGioModule.java | 22 +- .../android/circler/CirclerService.java | 86 ++--- .../{screenshot => }/ScreenshotProvider.java | 213 ++++++++---- .../android/circler/ThreadSafeTipView.java | 58 ++-- .../android/circler/WebSocketHandler.java | 53 +-- .../android/circler/CirclerTest.java | 56 +-- .../android/circler/RobolectricActivity.java | 4 +- .../android/circler/ScreenShotTest.java | 60 ++-- .../android/circler/shadow/ShadowHandler.java | 21 +- .../circler/shadow/ShadowThreadUtils.java | 25 +- .../android/circler/shadow/ShadowWH.java | 21 +- growingio-webservice/debugger/build.gradle | 84 ++--- .../android/debugger/DebuggerDataLoader.java | 21 +- ...rapper.java => DebuggerEventProvider.java} | 73 ++-- .../debugger/DebuggerLibraryGioModule.java | 23 +- .../android/debugger/DebuggerScreenshot.java | 17 +- .../android/debugger/DebuggerService.java | 104 +++--- .../android/debugger}/LoggerDataMessage.java | 28 +- .../android/debugger/ScreenshotProvider.java | 194 ++++++++--- .../android/debugger/ThreadSafeTipView.java | 52 ++- .../android/debugger/WebSocketHandler.java | 31 +- .../growingio/android/debugger}/WsLogger.java | 5 +- .../android/debugger/DebuggerTest.java | 102 +++--- .../android/debugger/RobolectricActivity.java | 2 +- .../android/debugger/ScreenShotTest.java | 45 +-- .../debugger/shadow/ShadowHandler.java | 21 +- .../debugger/shadow/ShadowThreadUtils.java | 25 +- .../android/debugger/shadow/ShadowWH.java | 21 +- 471 files changed, 6069 insertions(+), 6366 deletions(-) delete mode 100755 config/checkstyle/checkstyle.sh delete mode 100644 config/checkstyle/checkstyle.xml create mode 100644 demos/demo/src/androidTest/java/com/growingio/autotest/help/JsonUtil.java delete mode 100644 demos/demo/src/androidTest/java/com/growingio/autotest/tracker/TrackWebServicesTest.java rename {config/checkstyle => gradle}/java.header (79%) delete mode 100644 growingio-annotation/compiler/libs/tracker-core-test-3.5.0.jar create mode 100644 growingio-annotation/compiler/libs/tracker-core-test-4.0.0.jar delete mode 100644 growingio-apm/src/main/java/com/growingio/android/apm/GrowingApm.java create mode 100644 growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/InjectorProvider.java create mode 100644 growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/view/ViewNodeV4Test.java delete mode 100644 growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ErrorLog.java delete mode 100644 growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/async/EmptyDisposable.java delete mode 100644 growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/async/HandlerDisposable.java rename growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/{async => listener}/Callback.java (80%) delete mode 100644 growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/OnTrackMainInitSDKCallback.java rename growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/{webservices => middleware/webservice}/Circler.java (95%) rename growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/{webservices => middleware/webservice}/Debugger.java (92%) rename growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/{webservices => middleware/webservice}/WebService.java (84%) create mode 100644 growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/EventBuilderProvider.java delete mode 100644 growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/EventStateProvider.java rename growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/{ipc => providers}/PersistentDataProvider.java (55%) rename growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/{timer/TimerCenter.java => providers/TimingEventProvider.java} (63%) rename growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/{async/Disposable.java => providers/TrackerLifecycleProvider.java} (65%) create mode 100644 growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/TrackerLifecycleProviderFactory.java delete mode 100644 growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ThreadUtils.java rename growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/{timer/Timer.java => utils/TimerEvent.java} (58%) rename growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/{webservices/widget => view}/TipView.java (97%) delete mode 100644 growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/message/ClientInfoMessage.java delete mode 100644 growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/message/QuitMessage.java delete mode 100644 growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/message/ReadyMessage.java delete mode 100644 growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/AsyncTest.java rename growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/{TrackMainFilterTest.java => providers/EventBuilderProviderFilterTest.java} (67%) rename growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/timer/{TimerCenterTest.java => TimingEventProviderTest.java} (77%) rename growingio-tracker-core/src/{main => test}/java/com/growingio/android/sdk/track/utils/JsonUtil.java (96%) rename growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/{webservice/WebServiceTest.java => view/TipViewTest.java} (50%) rename growingio-webservice/circler/src/main/java/com/growingio/android/circler/{screenshot => }/CircleScreenshot.java (79%) rename growingio-webservice/circler/src/main/java/com/growingio/android/circler/{screenshot => }/ScreenshotProvider.java (50%) rename growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/{DebuggerEventWrapper.java => DebuggerEventProvider.java} (65%) rename {growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/log => growingio-webservice/debugger/src/main/java/com/growingio/android/debugger}/LoggerDataMessage.java (74%) rename {growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/log => growingio-webservice/debugger/src/main/java/com/growingio/android/debugger}/WsLogger.java (96%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b23f6f2b..5c68241b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,15 +39,12 @@ jobs: - name: Before script run: | chmod +x gradlew - chmod +x config/checkstyle/checkstyle.sh chmod +x gradle/publishAllToMavenLocal.sh - name: Run Gradle command run: | + bash ./gradlew spotlessCheck bash ./gradle/publishAllToMavenLocal.sh - bash ./config/checkstyle/checkstyle.sh -# ./gradlew lint -# ./gradlew assembleDebug - name: Run tests uses: reactivecircus/android-emulator-runner@v2 diff --git a/build.gradle b/build.gradle index e31734b6..02de8d08 100644 --- a/build.gradle +++ b/build.gradle @@ -25,6 +25,8 @@ plugins { alias libs.plugins.protobuf apply false alias libs.plugins.growingio.plugin apply false + + alias libs.plugins.spotless apply false } apply from: "${rootProject.projectDir}/gradle/sonarcloud.gradle" @@ -43,22 +45,31 @@ allprojects { } } -subprojects { project -> - apply plugin: 'checkstyle' +//./gradlew spotlessCheck ==> check code +//./gradlew spotlessApply ==> format code +subprojects { + apply plugin: "com.diffplug.spotless" + spotless { + java { + target '**/*.java' + licenseHeaderFile rootProject.file('gradle/java.header') - task checkstyle(type: Checkstyle, group: 'check') { - configFile file("${rootDir}/config/checkstyle/checkstyle.xml") - configProperties.checkstyleJavaHeader = file("${rootDir}/config/checkstyle/java.header") - source 'src/main/java', 'src/test/java', 'src/androidTest/java' - ignoreFailures false - showViolations true - include '**/*.java' - classpath = files() - } + removeUnusedImports() + } + kotlin { + target '**/*.kt' + targetExclude("$buildDir/**/*.kt") + + ktlint() + licenseHeaderFile rootProject.file('gradle/java.header') + } + groovyGradle { + target '*.gradle' + greclipse() + } - afterEvaluate { - if (project.tasks.findByName('check')) { - check.dependsOn('checkstyle') + format 'xml', { + target '**/*.xml' } } } diff --git a/config/checkstyle/checkstyle.sh b/config/checkstyle/checkstyle.sh deleted file mode 100755 index afee127a..00000000 --- a/config/checkstyle/checkstyle.sh +++ /dev/null @@ -1,23 +0,0 @@ - -./gradlew :growingio-annotation:checkstyle \ -&& ./gradlew :growingio-annotation:compiler:checkstyle \ -&& ./gradlew :growingio-tracker-core:checkstyle \ -&& ./gradlew :growingio-autotracker-core:checkstyle \ -&& ./gradlew :growingio-data:json:checkstyle \ -&& ./gradlew :growingio-data:protobuf:checkstyle \ -&& ./gradlew :growingio-data:encoder:checkstyle \ -&& ./gradlew :growingio-data:database:checkstyle \ -&& ./gradlew :growingio-hybrid:checkstyle \ -&& ./gradlew :growingio-network:okhttp3:checkstyle \ -&& ./gradlew :growingio-network:urlconnection:checkstyle \ -&& ./gradlew :growingio-network:volley:checkstyle \ -&& ./gradlew :growingio-webservice:debugger:checkstyle \ -&& ./gradlew :growingio-webservice:circler:checkstyle \ -&& ./gradlew :growingio-apm:checkstyle \ -&& ./gradlew :growingio-flutter:checkstyle \ -&& ./gradlew :growingio-advert:checkstyle \ -&& ./gradlew :growingio-tools:oaid:checkstyle \ -&& ./gradlew :growingio-tools:snappy:checkstyle \ -&& ./gradlew :demo:checkstyle \ -&& ./gradlew :demo-core:checkstyle \ -&& ./gradlew :demo-autotrack:checkstyle \ No newline at end of file diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml deleted file mode 100644 index 056ef9bc..00000000 --- a/config/checkstyle/checkstyle.xml +++ /dev/null @@ -1,171 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/demos/demo-autotrack/build.gradle b/demos/demo-autotrack/build.gradle index 61268919..d56e0436 100644 --- a/demos/demo-autotrack/build.gradle +++ b/demos/demo-autotrack/build.gradle @@ -1,33 +1,33 @@ apply plugin: 'com.android.library' android { - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - } - - lintOptions { - abortOnError false - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + } + + lintOptions { + abortOnError false + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } } dependencies { - implementation libs.bundles.android.support + implementation libs.bundles.android.support - implementation(name: 'tbs_sdk_44051', ext: 'jar') + implementation(name: 'tbs_sdk_44051', ext: 'jar') - debugApi project(':gio-sdk:autotracker') - releaseApi libs.growingio.autotracker - //api "com.growingio.android:autotracker:3.3.1" + debugApi project(':gio-sdk:autotracker') + releaseApi libs.growingio.autotracker + //api "com.growingio.android:autotracker:3.3.1" - implementation project(':demo-core') + implementation project(':demo-core') - compileOnly libs.androidx.appcompat + compileOnly libs.androidx.appcompat } diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/AutotrackEntryActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/AutotrackEntryActivity.java index 4cbdc36b..4063e0b9 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/AutotrackEntryActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/AutotrackEntryActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack; import android.app.Activity; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ActionMenuViewActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ActionMenuViewActivity.java index 73cd8787..1ba7a4c3 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ActionMenuViewActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ActionMenuViewActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity; import android.app.Activity; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/AddPeopleDialogActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/AddPeopleDialogActivity.java index 191b4ef5..cc2ce42b 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/AddPeopleDialogActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/AddPeopleDialogActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity; import android.app.Activity; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ClickTestActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ClickTestActivity.java index db13ab5b..5eb152f4 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ClickTestActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ClickTestActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity; import android.app.Activity; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/DialogTestActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/DialogTestActivity.java index ef7a34e0..5e146bb4 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/DialogTestActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/DialogTestActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity; import android.app.Activity; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ExpandableListSubActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ExpandableListSubActivity.java index f0382d96..8eb3d860 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ExpandableListSubActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ExpandableListSubActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity; import android.annotation.SuppressLint; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ExpandableListViewActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ExpandableListViewActivity.java index 233e0311..6eb99497 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ExpandableListViewActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ExpandableListViewActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity; import android.annotation.SuppressLint; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/HideFragmentActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/HideFragmentActivity.java index 3c55e870..cb6bd84d 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/HideFragmentActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/HideFragmentActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity; import android.os.Bundle; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/LambdaActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/LambdaActivity.java index 8d82dc9f..9ca8ed74 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/LambdaActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/LambdaActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity; import android.app.Activity; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/NavFragmentActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/NavFragmentActivity.java index d5fcdfcc..6b5176fe 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/NavFragmentActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/NavFragmentActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity; import android.os.Bundle; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/NestedFragmentActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/NestedFragmentActivity.java index 4a3a6e0f..d8974fc9 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/NestedFragmentActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/NestedFragmentActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity; import android.os.Bundle; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/RecyclerViewImpActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/RecyclerViewImpActivity.java index 2a523237..75fd193f 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/RecyclerViewImpActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/RecyclerViewImpActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity; import android.app.Activity; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/TabFragmentActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/TabFragmentActivity.java index dd7f68d9..14216bae 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/TabFragmentActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/TabFragmentActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity; import android.os.Bundle; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ToolBarActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ToolBarActivity.java index 13686fb1..7ca87f09 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ToolBarActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ToolBarActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity; import android.app.Activity; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ViewImpressionActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ViewImpressionActivity.java index d617cf50..eb9f0d6b 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ViewImpressionActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ViewImpressionActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity; import android.app.Activity; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/WebViewActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/WebViewActivity.java index 992f5937..a64df67b 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/WebViewActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/WebViewActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity; import android.app.Activity; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/X5WebViewActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/X5WebViewActivity.java index 32042f62..cca5143e 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/X5WebViewActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/X5WebViewActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity; import android.app.Activity; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/data/LoginDataSource.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/data/LoginDataSource.java index ac0634e4..792c80eb 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/data/LoginDataSource.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/data/LoginDataSource.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.data; import com.gio.test.three.autotrack.activity.data.model.LoggedInUser; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/data/LoginRepository.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/data/LoginRepository.java index d9e7c8e6..4a2b7d14 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/data/LoginRepository.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/data/LoginRepository.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.data; import com.gio.test.three.autotrack.activity.data.model.LoggedInUser; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/data/Result.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/data/Result.java index 087acec4..9ba3d7aa 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/data/Result.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/data/Result.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.data; /** diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/data/model/LoggedInUser.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/data/model/LoggedInUser.java index 35ee195a..76faff7d 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/data/model/LoggedInUser.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/data/model/LoggedInUser.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.data.model; /** diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/dashboard/DashboardFragment.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/dashboard/DashboardFragment.java index 56bb98e4..9f3a508d 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/dashboard/DashboardFragment.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/dashboard/DashboardFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.ui.dashboard; import android.arch.lifecycle.Observer; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/dashboard/DashboardViewModel.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/dashboard/DashboardViewModel.java index d2e0cb8c..a2a977ce 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/dashboard/DashboardViewModel.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/dashboard/DashboardViewModel.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.ui.dashboard; import android.arch.lifecycle.LiveData; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/home/HomeFragment.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/home/HomeFragment.java index c5c51f5b..395cf7ac 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/home/HomeFragment.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/home/HomeFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.ui.home; import android.arch.lifecycle.Observer; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/home/HomeViewModel.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/home/HomeViewModel.java index 043de919..9dd550a4 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/home/HomeViewModel.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/home/HomeViewModel.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.ui.home; import android.arch.lifecycle.LiveData; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoggedInUserView.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoggedInUserView.java index c4ec7b1d..f73ea002 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoggedInUserView.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoggedInUserView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.ui.login; /** diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginActivity.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginActivity.java index 5b2f548d..cb4760d5 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginActivity.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.ui.login; import android.app.Activity; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginFormState.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginFormState.java index 686c451a..ec4ca31d 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginFormState.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginFormState.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.ui.login; import android.support.annotation.Nullable; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginResult.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginResult.java index fc5ba28b..9017f0f5 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginResult.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginResult.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.ui.login; import android.support.annotation.Nullable; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginViewModel.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginViewModel.java index df67942c..31968138 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginViewModel.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginViewModel.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.ui.login; import android.arch.lifecycle.LiveData; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginViewModelFactory.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginViewModelFactory.java index fc175edf..88e3cfef 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginViewModelFactory.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/login/LoginViewModelFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.ui.login; import android.arch.lifecycle.ViewModel; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/main/PageViewModel.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/main/PageViewModel.java index d3bc2b1d..9f108135 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/main/PageViewModel.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/main/PageViewModel.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.ui.main; import android.arch.core.util.Function; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/main/PlaceholderFragment.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/main/PlaceholderFragment.java index 0ef61ce8..c3c05fef 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/main/PlaceholderFragment.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/main/PlaceholderFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.ui.main; import android.arch.lifecycle.Observer; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/main/SectionsPagerAdapter.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/main/SectionsPagerAdapter.java index c63415a0..81e65bc7 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/main/SectionsPagerAdapter.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/main/SectionsPagerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.ui.main; import android.content.Context; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/notifications/NotificationsFragment.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/notifications/NotificationsFragment.java index 85cfd859..28d82a2c 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/notifications/NotificationsFragment.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/notifications/NotificationsFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.ui.notifications; import android.arch.lifecycle.Observer; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/notifications/NotificationsViewModel.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/notifications/NotificationsViewModel.java index 917baa51..f6ff3d6a 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/notifications/NotificationsViewModel.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/activity/ui/notifications/NotificationsViewModel.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.activity.ui.notifications; import android.arch.lifecycle.LiveData; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/FragmentLifecycleCallback.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/FragmentLifecycleCallback.java index 8042bbd2..f33dc841 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/FragmentLifecycleCallback.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/FragmentLifecycleCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.fragments; import android.support.v4.app.Fragment; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/FragmentLifecycleMonitor.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/FragmentLifecycleMonitor.java index fda84dc8..6afbaeea 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/FragmentLifecycleMonitor.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/FragmentLifecycleMonitor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.fragments; import android.annotation.SuppressLint; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/GreenFragment.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/GreenFragment.java index aeaca013..15047c98 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/GreenFragment.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/GreenFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.fragments; import android.os.Bundle; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/OrangeFragment.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/OrangeFragment.java index f6a18045..44e75610 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/OrangeFragment.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/OrangeFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.fragments; import android.os.Bundle; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/RedFragment.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/RedFragment.java index 8776abb4..168747af 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/RedFragment.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/RedFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.fragments; import android.os.Bundle; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/base/BaseFragment.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/base/BaseFragment.java index d5ff58e7..2cbbd50f 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/base/BaseFragment.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/fragments/base/BaseFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.fragments.base; import android.os.Bundle; diff --git a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/utils/DialogUtil.java b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/utils/DialogUtil.java index 84370698..8b2fe223 100644 --- a/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/utils/DialogUtil.java +++ b/demos/demo-autotrack/src/main/java/com/gio/test/three/autotrack/utils/DialogUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.autotrack.utils; import android.app.AlertDialog; diff --git a/demos/demo-core/build.gradle b/demos/demo-core/build.gradle index 14f370fd..d0ce9c05 100644 --- a/demos/demo-core/build.gradle +++ b/demos/demo-core/build.gradle @@ -1,23 +1,23 @@ apply plugin: 'com.android.library' android { - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - } + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } - sourceSets.main { - jniLibs.srcDirs = ['libs'] - assets.srcDirs = ['assets'] - } + sourceSets.main { + jniLibs.srcDirs = ['libs'] + assets.srcDirs = ['assets'] + } - lintOptions { - abortOnError false - } + lintOptions { + abortOnError false + } } diff --git a/demos/demo-core/src/main/java/com/gio/test/three/ModuleEntry.java b/demos/demo-core/src/main/java/com/gio/test/three/ModuleEntry.java index ad8dc002..dac6da14 100644 --- a/demos/demo-core/src/main/java/com/gio/test/three/ModuleEntry.java +++ b/demos/demo-core/src/main/java/com/gio/test/three/ModuleEntry.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three; import java.lang.annotation.Documented; diff --git a/demos/demo/build.gradle b/demos/demo/build.gradle index 33e99956..f7d2fb42 100644 --- a/demos/demo/build.gradle +++ b/demos/demo/build.gradle @@ -1,99 +1,99 @@ plugins { - id 'com.android.application' - id 'jacoco' - id 'com.growingio.android.autotracker' + id 'com.android.application' + id 'jacoco' + id 'com.growingio.android.autotracker' } android { - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - applicationId "com.gio.test.three" - minSdkVersion 21 - targetSdkVersion buildConfiguration.targetSdkVersion - - testInstrumentationRunner "com.growingio.autotest.TrackAndroidJUnitRunner" - - testInstrumentationRunnerArguments listener: "com.growingio.autotest.TrackTestRunListener" - - ndk { - abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86" - } - multiDexEnabled true - } - - lintOptions { - abortOnError false - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - buildTypes { - debug { - testCoverageEnabled = true - multiDexKeepFile file('multidex-config.txt') - } - release { - multiDexKeepFile file('multidex-config.txt') - } - } - - testOptions { - unitTests.all { - jacoco { - includeNoLocationClasses = true - } - } - unitTests.returnDefaultValues = true - execution 'ANDROIDX_TEST_ORCHESTRATOR' - } + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + applicationId "com.gio.test.three" + minSdkVersion 21 + targetSdkVersion buildConfiguration.targetSdkVersion + + testInstrumentationRunner "com.growingio.autotest.TrackAndroidJUnitRunner" + + testInstrumentationRunnerArguments listener: "com.growingio.autotest.TrackTestRunListener" + + ndk { + abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86" + } + multiDexEnabled true + } + + lintOptions { + abortOnError false + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + buildTypes { + debug { + testCoverageEnabled = true + multiDexKeepFile file('multidex-config.txt') + } + release { + multiDexKeepFile file('multidex-config.txt') + } + } + + testOptions { + unitTests.all { + jacoco { + includeNoLocationClasses = true + } + } + unitTests.returnDefaultValues = true + execution 'ANDROIDX_TEST_ORCHESTRATOR' + } } growingAutotracker { - logEnabled true - skipDependencyCheck true - //includePackages "com.growingio.android.plugin" - //excludePackages "com.cpacm" + logEnabled true + skipDependencyCheck true + //includePackages "com.growingio.android.plugin" + //excludePackages "com.cpacm" } dependencies { - androidTestUtil libs.orchestrator - androidTestImplementation libs.bundles.androidx.test - androidTestImplementation libs.bundles.espresso - androidTestImplementation libs.mockito.core - //androidTestImplementation libs.truth - androidTestImplementation libs.okhttp3.mockwebserver - androidTestImplementation libs.powermock.reflect - androidTestImplementation libs.androidx.appcompat - androidTestImplementation libs.support.appcompat - androidTestImplementation(name: 'tbs_sdk_44051', ext: 'jar') - - implementation libs.support.appcompat - implementation libs.support.constraint - implementation libs.support.multidex - implementation(name: 'tbs_sdk_44051', ext: 'jar') - implementation(name: 'oaid_sdk_1.0.25', ext: 'aar') - implementation project(':demo-core') - implementation project(":demo-autotrack") - - debugImplementation project(":growingio-tools:oaid") - releaseImplementation libs.growingio.oaid - - debugImplementation project(':growingio-apm') - releaseImplementation libs.growingio.apm - - debugImplementation project(':growingio-advert') - releaseImplementation libs.growingio.advert - - //debugImplementation project(':growingio-data:json') - //releaseImplementation libs.growingio.json - - compileOnly libs.androidx.appcompat - - // leakcanary 避免发版前未使用Profiler进行内存泄漏检测 - debugImplementation libs.square.leakCanary + androidTestUtil libs.orchestrator + androidTestImplementation libs.bundles.androidx.test + androidTestImplementation libs.bundles.espresso + androidTestImplementation libs.mockito.core + //androidTestImplementation libs.truth + androidTestImplementation libs.okhttp3.mockwebserver + androidTestImplementation libs.powermock.reflect + androidTestImplementation libs.androidx.appcompat + androidTestImplementation libs.support.appcompat + androidTestImplementation(name: 'tbs_sdk_44051', ext: 'jar') + + implementation libs.support.appcompat + implementation libs.support.constraint + implementation libs.support.multidex + implementation(name: 'tbs_sdk_44051', ext: 'jar') + implementation(name: 'oaid_sdk_1.0.25', ext: 'aar') + implementation project(':demo-core') + implementation project(":demo-autotrack") + + debugImplementation project(":growingio-tools:oaid") + releaseImplementation libs.growingio.oaid + + debugImplementation project(':growingio-apm') + releaseImplementation libs.growingio.apm + + debugImplementation project(':growingio-advert') + releaseImplementation libs.growingio.advert + + //debugImplementation project(':growingio-data:json') + //releaseImplementation libs.growingio.json + + compileOnly libs.androidx.appcompat + + // leakcanary 避免发版前未使用Profiler进行内存泄漏检测 + debugImplementation libs.square.leakCanary } apply from: "${rootProject.projectDir}/gradle/jacoco.gradle" diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/EventsTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/EventsTest.java index c23f0108..d67fdfe9 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/EventsTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/EventsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest; import com.growingio.autotest.help.MockEventsApiServer; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/TestTrackConfiguration.java b/demos/demo/src/androidTest/java/com/growingio/autotest/TestTrackConfiguration.java index 76292716..a292baeb 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/TestTrackConfiguration.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/TestTrackConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest; import com.growingio.android.sdk.autotrack.AutotrackConfiguration; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/TrackAndroidJUnitRunner.java b/demos/demo/src/androidTest/java/com/growingio/autotest/TrackAndroidJUnitRunner.java index b1347a80..67e2dc88 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/TrackAndroidJUnitRunner.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/TrackAndroidJUnitRunner.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest; import android.app.Application; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/TrackTestRunListener.java b/demos/demo/src/androidTest/java/com/growingio/autotest/TrackTestRunListener.java index c8ff7abe..4dfc4bd8 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/TrackTestRunListener.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/TrackTestRunListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest; import android.util.Log; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/WebServicesTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/WebServicesTest.java index 42ee65c8..e7ba3255 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/WebServicesTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/WebServicesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest; import com.google.common.truth.Truth; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/PageEventsTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/PageEventsTest.java index 3810f415..2fce5474 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/PageEventsTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/PageEventsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.autotracker; import android.util.Log; @@ -38,12 +37,12 @@ import com.gio.test.three.autotrack.fragments.GreenFragment; import com.google.common.truth.Truth; import com.growingio.android.sdk.autotrack.GrowingAutotracker; -import com.growingio.android.sdk.track.utils.JsonUtil; import com.growingio.autotest.EventsTest; import com.growingio.autotest.TestTrackConfiguration; import com.growingio.autotest.help.Awaiter; import com.growingio.autotest.help.BeforeAppOnCreate; import com.growingio.autotest.help.DataHelper; +import com.growingio.autotest.help.JsonUtil; import com.growingio.autotest.help.MockEventsApiServer; import com.growingio.autotest.help.TrackHelper; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/ViewChangeEventsTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/ViewChangeEventsTest.java index d2f55ed7..4337ba47 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/ViewChangeEventsTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/ViewChangeEventsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.autotracker; import androidx.test.core.app.ActivityScenario; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/ViewClickEventsTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/ViewClickEventsTest.java index 1c49feac..e951aced 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/ViewClickEventsTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/ViewClickEventsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.autotracker; import android.os.Build; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/hybrid/HybridEventsTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/hybrid/HybridEventsTest.java index 4dea77b0..e4548831 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/hybrid/HybridEventsTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/hybrid/HybridEventsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.autotracker.hybrid; import android.text.TextUtils; @@ -31,15 +30,16 @@ import com.growingio.android.hybrid.HybridBridgeProvider; import com.growingio.android.hybrid.OnDomChangedListener; import com.growingio.android.hybrid.SuperWebView; +import com.growingio.android.sdk.autotrack.GrowingAutotracker; import com.growingio.android.sdk.track.SDKConfig; -import com.growingio.android.sdk.track.async.Callback; -import com.growingio.android.sdk.track.ipc.PersistentDataProvider; -import com.growingio.android.sdk.track.utils.JsonUtil; +import com.growingio.android.sdk.track.listener.Callback; +import com.growingio.android.sdk.track.providers.PersistentDataProvider; import com.growingio.autotest.EventsTest; import com.growingio.autotest.TestTrackConfiguration; import com.growingio.autotest.help.Awaiter; import com.growingio.autotest.help.BeforeAppOnCreate; import com.growingio.autotest.help.DataHelper; +import com.growingio.autotest.help.JsonUtil; import com.growingio.autotest.help.MockEventsApiServer; import com.growingio.autotest.help.TrackHelper; @@ -78,7 +78,7 @@ public class HybridEventsTest extends EventsTest { public static void beforeAppOnCreate() { DataHelper.deleteEventsDatabase(); DemoApplication.setConfiguration(TestTrackConfiguration.getTestConfig() - .setIdMappingEnabled(true)); + .setIdMappingEnabled(true)); } @Override @@ -431,42 +431,44 @@ protected void onReceivedHybridFormSubmitEvents(JSONArray jsonArray) throws JSON @Test public void hybridUserIdChangeTest() { + PersistentDataProvider persistentDataProvider = GrowingAutotracker.get().getContext().getProvider(PersistentDataProvider.class); getEventsApiServer().setCheckUserId(false); getEventsApiServer().setCheckSessionId(false); WebView webView = launchMockWebView(); - String loginUserId = PersistentDataProvider.get().getLoginUserId(); + String loginUserId = persistentDataProvider.getLoginUserId(); String newUserId = loginUserId + "hybrid"; TrackHelper.postToUiThread(() -> webView.evaluateJavascript("javascript:setUserId(\"" + newUserId + "\")", null)); - Awaiter.untilTrue(() -> newUserId.equals(PersistentDataProvider.get().getLoginUserId())); + Awaiter.untilTrue(() -> newUserId.equals(persistentDataProvider.getLoginUserId())); TrackHelper.postToUiThread(() -> webView.evaluateJavascript("javascript:clearUserId()", null)); - Awaiter.untilTrue(() -> TextUtils.isEmpty(PersistentDataProvider.get().getLoginUserId())); + Awaiter.untilTrue(() -> TextUtils.isEmpty(persistentDataProvider.getLoginUserId())); - String loginUserKey = PersistentDataProvider.get().getLoginUserKey(); + String loginUserKey = persistentDataProvider.getLoginUserKey(); String newUserKey = loginUserKey + "hybridUserKey"; TrackHelper.postToUiThread(() -> - webView.evaluateJavascript("javascript:setUserIdAndUserKey(\"" + newUserId + "\" , \"" + newUserKey + "\")", null)); - Awaiter.untilTrue(() -> newUserId.equals(PersistentDataProvider.get().getLoginUserId()) && - newUserKey.equals(PersistentDataProvider.get().getLoginUserKey())); + webView.evaluateJavascript("javascript:setUserIdAndUserKey(\"" + newUserId + "\" , \"" + newUserKey + "\")", null)); + Awaiter.untilTrue(() -> newUserId.equals(persistentDataProvider.getLoginUserId()) && + newUserKey.equals(persistentDataProvider.getLoginUserKey())); TrackHelper.postToUiThread(() -> webView.evaluateJavascript("javascript:clearUserIdAndUserKey()", null)); - Awaiter.untilTrue(() -> TextUtils.isEmpty(PersistentDataProvider.get().getLoginUserId()) && - TextUtils.isEmpty(PersistentDataProvider.get().getLoginUserKey())); + Awaiter.untilTrue(() -> TextUtils.isEmpty(persistentDataProvider.getLoginUserId()) && + TextUtils.isEmpty(persistentDataProvider.getLoginUserKey())); } @Test public void hybridDomChangedTest() throws Exception { + HybridBridgeProvider hybridBridgeProvider = GrowingAutotracker.get().getContext().getProvider(HybridBridgeProvider.class); AtomicBoolean receivedEvent = new AtomicBoolean(false); WebView webView = launchMockWebView(); SuperWebView superWebView = (SuperWebView) Whitebox.invokeConstructor(Class.forName("com.growingio.android.hybrid.SuperWebView$SystemWebView"), webView); - HybridBridgeProvider.get().registerDomChangedListener(new OnDomChangedListener() { + hybridBridgeProvider.registerDomChangedListener(new OnDomChangedListener() { @Override public void onDomChanged() { TrackHelper.postToUiThread(() -> - HybridBridgeProvider.get().getWebViewDomTree(superWebView, new Callback() { + hybridBridgeProvider.getWebViewDomTree(superWebView, new Callback() { @Override public void onSuccess(JSONObject result) { receivedEvent.set(result != null); diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/hybrid/HybridSpecialScenarioTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/hybrid/HybridSpecialScenarioTest.java index 2750f6cc..347c9437 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/hybrid/HybridSpecialScenarioTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/hybrid/HybridSpecialScenarioTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.autotracker.hybrid; import static androidx.test.espresso.Espresso.onData; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/impression/ImpressionEventsTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/impression/ImpressionEventsTest.java index ebd2a0d3..4fd7383c 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/impression/ImpressionEventsTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/impression/ImpressionEventsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.autotracker.impression; import android.view.View; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/impression/OnReceivedViewImpressionEventsListener.java b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/impression/OnReceivedViewImpressionEventsListener.java index a9f61d44..d529f90a 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/impression/OnReceivedViewImpressionEventsListener.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/impression/OnReceivedViewImpressionEventsListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.autotracker.impression; import com.growingio.android.sdk.track.events.PageLevelCustomEvent; -import com.growingio.android.sdk.track.utils.JsonUtil; import com.growingio.android.sdk.track.utils.ObjectUtils; +import com.growingio.autotest.help.JsonUtil; import com.growingio.autotest.help.MockEventsApiServer; import org.json.JSONArray; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/impression/StopReceivedViewImpressionEventsListener.java b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/impression/StopReceivedViewImpressionEventsListener.java index 317c7e5e..9035b9bb 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/impression/StopReceivedViewImpressionEventsListener.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/impression/StopReceivedViewImpressionEventsListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.autotracker.impression; import com.google.common.truth.Truth; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/impression/TrackConfigurationImpressionScaleTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/impression/TrackConfigurationImpressionScaleTest.java index ff5acec6..3db0ea70 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/impression/TrackConfigurationImpressionScaleTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/impression/TrackConfigurationImpressionScaleTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.autotracker.impression; import android.view.View; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/webservices/CircleServiceTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/webservices/CircleServiceTest.java index ed37a977..fffa228f 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/webservices/CircleServiceTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/webservices/CircleServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.autotracker.webservices; import android.content.Intent; @@ -32,7 +31,7 @@ import com.growingio.android.sdk.track.view.DecorView; import com.growingio.android.sdk.track.view.WindowHelper; import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.webservices.widget.TipView; +import com.growingio.android.sdk.track.view.TipView; import com.growingio.autotest.TestTrackConfiguration; import com.growingio.autotest.WebServicesTest; import com.growingio.autotest.help.Awaiter; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/webservices/DebuggerServiceTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/webservices/DebuggerServiceTest.java index 6858f71c..0e5649da 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/webservices/DebuggerServiceTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/autotracker/webservices/DebuggerServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.autotracker.webservices; import android.content.Intent; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/help/Awaiter.java b/demos/demo/src/androidTest/java/com/growingio/autotest/help/Awaiter.java index 1e78bd98..28cf1d58 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/help/Awaiter.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/help/Awaiter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.help; import com.google.common.util.concurrent.Uninterruptibles; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/help/BeforeAppOnCreate.java b/demos/demo/src/androidTest/java/com/growingio/autotest/help/BeforeAppOnCreate.java index 3a67ae35..6687b9ce 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/help/BeforeAppOnCreate.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/help/BeforeAppOnCreate.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.help; import java.lang.annotation.ElementType; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/help/Condition.java b/demos/demo/src/androidTest/java/com/growingio/autotest/help/Condition.java index a29f33d4..c6f9b37e 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/help/Condition.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/help/Condition.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.help; public interface Condition { diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/help/ConditionTimeoutException.java b/demos/demo/src/androidTest/java/com/growingio/autotest/help/ConditionTimeoutException.java index a6be4ad8..29981f79 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/help/ConditionTimeoutException.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/help/ConditionTimeoutException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.help; public class ConditionTimeoutException extends RuntimeException { diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/help/DataHelper.java b/demos/demo/src/androidTest/java/com/growingio/autotest/help/DataHelper.java index 8ce8525c..2c55c29c 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/help/DataHelper.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/help/DataHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.help; import androidx.test.core.app.ApplicationProvider; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/help/EventsTestDataHelper.java b/demos/demo/src/androidTest/java/com/growingio/autotest/help/EventsTestDataHelper.java index 79d19833..d7840442 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/help/EventsTestDataHelper.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/help/EventsTestDataHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.help; import android.content.Context; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/help/JsonUtil.java b/demos/demo/src/androidTest/java/com/growingio/autotest/help/JsonUtil.java new file mode 100644 index 00000000..b4f98d11 --- /dev/null +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/help/JsonUtil.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.growingio.autotest.help; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +public final class JsonUtil { + private JsonUtil() { + } + + public static Map copyToMap(JSONObject jsonObject) { + if (jsonObject == null) + return null; + Map map = new HashMap<>(); + Iterator keys = jsonObject.keys(); + while (keys.hasNext()) { + String key = keys.next(); + try { + if (jsonObject.get(key) == JSONObject.NULL) { + map.put(key, null); + } else { + map.put(key, String.valueOf(jsonObject.get(key))); + } + } catch (JSONException e) { + // ignore + } + } + return map; + } + +} diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/help/MockEventsApiServer.java b/demos/demo/src/androidTest/java/com/growingio/autotest/help/MockEventsApiServer.java index b6e6e325..0f4b38ef 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/help/MockEventsApiServer.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/help/MockEventsApiServer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.help; import android.net.Uri; @@ -23,7 +22,6 @@ import com.google.common.truth.Truth; import com.growingio.android.sdk.track.events.AutotrackEventType; import com.growingio.android.sdk.track.events.TrackEventType; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; import org.json.JSONArray; import org.json.JSONException; @@ -128,7 +126,6 @@ private void checkBaseEventBody(JSONArray jsonArray) throws JSONException { if (mIsCheckDomain) { Truth.assertThat(jsonObject.getString("domain")).isEqualTo("com.gio.test.three"); } - Truth.assertThat(jsonObject.getString("urlScheme")).isEqualTo(ConfigurationProvider.core().getUrlScheme()); Truth.assertThat(jsonObject.getString("appState")).isIn(Arrays.asList("FOREGROUND", "BACKGROUND")); Truth.assertThat(jsonObject.getLong("eventSequenceId")).isGreaterThan(0); } diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/help/MockServer.java b/demos/demo/src/androidTest/java/com/growingio/autotest/help/MockServer.java index 44e6e75e..49a06709 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/help/MockServer.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/help/MockServer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.help; import java.io.IOException; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/help/TrackHelper.java b/demos/demo/src/androidTest/java/com/growingio/autotest/help/TrackHelper.java index 2ce80197..ffc180c6 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/help/TrackHelper.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/help/TrackHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.help; import android.os.Handler; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/help/Uninterruptibles.java b/demos/demo/src/androidTest/java/com/growingio/autotest/help/Uninterruptibles.java index f405a6da..3b2eae01 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/help/Uninterruptibles.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/help/Uninterruptibles.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.help; import java.util.concurrent.TimeUnit; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/CustomDataUploadIntervalTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/CustomDataUploadIntervalTest.java index 07886c21..739501f7 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/CustomDataUploadIntervalTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/CustomDataUploadIntervalTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.tracker; import androidx.test.ext.junit.runners.AndroidJUnit4; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/DataCollectionEnabledTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/DataCollectionEnabledTest.java index 58c9f975..b3a85fe2 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/DataCollectionEnabledTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/DataCollectionEnabledTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.tracker; import androidx.test.ext.junit.rules.ActivityScenarioRule; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/DefaultDataUploadIntervalTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/DefaultDataUploadIntervalTest.java index 468c8de7..910e7b8c 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/DefaultDataUploadIntervalTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/DefaultDataUploadIntervalTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.tracker; import androidx.test.ext.junit.rules.ActivityScenarioRule; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/IdMappingTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/IdMappingTest.java index 4b66bb96..6b02f413 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/IdMappingTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/IdMappingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.tracker; import androidx.test.core.app.ActivityScenario; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/SessionEventsTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/SessionEventsTest.java index 251ead3a..0ebc1063 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/SessionEventsTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/SessionEventsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.tracker; import android.content.Context; @@ -34,7 +33,6 @@ import com.google.common.truth.Truth; import com.growingio.android.sdk.autotrack.GrowingAutotracker; import com.growingio.android.sdk.track.BuildConfig; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; import com.growingio.autotest.EventsTest; import com.growingio.autotest.TestTrackConfiguration; import com.growingio.autotest.help.Awaiter; @@ -193,8 +191,7 @@ protected void onReceivedAppClosedEvents(JSONArray jsonArray) throws JSONExcepti } }); - long delayTime = ConfigurationProvider.core().getSessionInterval(); - Uninterruptibles.sleepUninterruptibly(delayTime * 1000 + 1, TimeUnit.MILLISECONDS); + Uninterruptibles.sleepUninterruptibly(30 * 1000 + 1, TimeUnit.MILLISECONDS); //To State RESUMED scenario.moveToState(Lifecycle.State.RESUMED); diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/TrackEventsTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/TrackEventsTest.java index 1f4e1273..3106fc40 100644 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/TrackEventsTest.java +++ b/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/TrackEventsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.autotest.tracker; import androidx.test.ext.junit.rules.ActivityScenarioRule; diff --git a/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/TrackWebServicesTest.java b/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/TrackWebServicesTest.java deleted file mode 100644 index 93d3a7da..00000000 --- a/demos/demo/src/androidTest/java/com/growingio/autotest/tracker/TrackWebServicesTest.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.growingio.autotest.tracker; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.LargeTest; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@LargeTest -public class TrackWebServicesTest { - @Test - public void mobileLogTest() { - // TODO: 2020/10/13 - } -} diff --git a/demos/demo/src/main/java/com/gio/test/three/DemoApplication.java b/demos/demo/src/main/java/com/gio/test/three/DemoApplication.java index 5e1cbf3e..58857e08 100644 --- a/demos/demo/src/main/java/com/gio/test/three/DemoApplication.java +++ b/demos/demo/src/main/java/com/gio/test/three/DemoApplication.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three; import android.annotation.SuppressLint; @@ -115,7 +114,8 @@ public void onReceive(Map params, int error, long appAwakePassed .addPreloadComponent(new ApmLibraryGioModule(), apmConfig) // .addPreloadComponent(new ProtobufLibraryModule()) // .addPreloadComponent(new EncoderLibraryGioModule()) - .addPreloadComponent(new OaidLibraryGioModule()); + .addPreloadComponent(new OaidLibraryGioModule()) + .downgrade(); } enableStrictMode(); diff --git a/demos/demo/src/main/java/com/gio/test/three/MainActivity.java b/demos/demo/src/main/java/com/gio/test/three/MainActivity.java index 145a0c08..d96256a1 100644 --- a/demos/demo/src/main/java/com/gio/test/three/MainActivity.java +++ b/demos/demo/src/main/java/com/gio/test/three/MainActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three; import android.annotation.SuppressLint; diff --git a/demos/demo/src/main/java/com/gio/test/three/OtherProcessService.java b/demos/demo/src/main/java/com/gio/test/three/OtherProcessService.java index ad836653..9e51a997 100644 --- a/demos/demo/src/main/java/com/gio/test/three/OtherProcessService.java +++ b/demos/demo/src/main/java/com/gio/test/three/OtherProcessService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three; import android.app.Service; diff --git a/demos/demo/src/main/java/com/gio/test/three/core/TrackActivity.java b/demos/demo/src/main/java/com/gio/test/three/core/TrackActivity.java index e1b311a9..dd09faa3 100644 --- a/demos/demo/src/main/java/com/gio/test/three/core/TrackActivity.java +++ b/demos/demo/src/main/java/com/gio/test/three/core/TrackActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.gio.test.three.core; import android.app.Activity; diff --git a/gio-sdk/autotracker/build.gradle b/gio-sdk/autotracker/build.gradle index ff222715..7535dc11 100644 --- a/gio-sdk/autotracker/build.gradle +++ b/gio-sdk/autotracker/build.gradle @@ -1,55 +1,55 @@ apply plugin: 'com.android.library' android { - namespace 'com.growingio.android.sdk.autotracker' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } - - buildTypes { - debug { - testCoverageEnabled = true - } - - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - consumerProguardFiles 'consumer-proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } - lintOptions { - abortOnError false - } + namespace 'com.growingio.android.sdk.autotracker' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } + + buildTypes { + debug { + testCoverageEnabled = true + } + + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + consumerProguardFiles 'consumer-proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } + lintOptions { + abortOnError false + } } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation fileTree(dir: 'libs', include: ['*.jar']) - debugApi project(':growingio-tracker-core') - debugApi project(':growingio-autotracker-core') - debugApi project(':growingio-network:okhttp3') - debugApi project(':growingio-data:database') - debugApi project(':growingio-data:protobuf') - debugApi project(':growingio-webservice:circler') - debugApi project(':growingio-webservice:debugger') - debugApi project(':growingio-hybrid') + debugApi project(':growingio-tracker-core') + debugApi project(':growingio-autotracker-core') + debugApi project(':growingio-network:okhttp3') + debugApi project(':growingio-data:database') + debugApi project(':growingio-data:protobuf') + debugApi project(':growingio-webservice:circler') + debugApi project(':growingio-webservice:debugger') + debugApi project(':growingio-hybrid') - releaseApi libs.bundles.growingio.autotracker.sdk + releaseApi libs.bundles.growingio.autotracker.sdk - compileOnly libs.androidx.appcompat - compileOnly libs.support.appcompat + compileOnly libs.androidx.appcompat + compileOnly libs.support.appcompat - releaseImplementation libs.growingio.annotation - debugImplementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + releaseImplementation libs.growingio.annotation + debugImplementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/gio-sdk/autotracker/src/main/java/com/growingio/android/sdk/autotrack/GrowingAutoAppModule.java b/gio-sdk/autotracker/src/main/java/com/growingio/android/sdk/autotrack/GrowingAutoAppModule.java index af0292f2..4f3364b2 100644 --- a/gio-sdk/autotracker/src/main/java/com/growingio/android/sdk/autotrack/GrowingAutoAppModule.java +++ b/gio-sdk/autotracker/src/main/java/com/growingio/android/sdk/autotrack/GrowingAutoAppModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack; import com.growingio.android.sdk.AppGioModule; diff --git a/gio-sdk/tracker/build.gradle b/gio-sdk/tracker/build.gradle index bf420468..b2a80c20 100644 --- a/gio-sdk/tracker/build.gradle +++ b/gio-sdk/tracker/build.gradle @@ -1,51 +1,51 @@ apply plugin: 'com.android.library' android { - namespace 'com.growingio.android.sdk.tracker' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } - - buildTypes { - debug { - testCoverageEnabled = true - } - - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - consumerProguardFiles 'consumer-proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } - lintOptions { - abortOnError false - } + namespace 'com.growingio.android.sdk.tracker' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } + + buildTypes { + debug { + testCoverageEnabled = true + } + + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + consumerProguardFiles 'consumer-proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } + lintOptions { + abortOnError false + } } dependencies { - compileOnly fileTree(dir: '../thirdLibs', include: ['*.jar']) + compileOnly fileTree(dir: '../thirdLibs', include: ['*.jar']) - debugApi project(':growingio-tracker-core') - debugApi project(':growingio-network:okhttp3') - debugApi project(':growingio-data:database') - debugApi project(':growingio-data:protobuf') - debugApi project(':growingio-webservice:debugger') + debugApi project(':growingio-tracker-core') + debugApi project(':growingio-network:okhttp3') + debugApi project(':growingio-data:database') + debugApi project(':growingio-data:protobuf') + debugApi project(':growingio-webservice:debugger') - releaseApi libs.bundles.growingio.tracker.sdk + releaseApi libs.bundles.growingio.tracker.sdk - compileOnly libs.support.appcompat + compileOnly libs.support.appcompat - releaseImplementation libs.growingio.annotation - debugImplementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + releaseImplementation libs.growingio.annotation + debugImplementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/gio-sdk/tracker/src/main/java/com/growingio/android/sdk/track/GrowingAppModule.java b/gio-sdk/tracker/src/main/java/com/growingio/android/sdk/track/GrowingAppModule.java index 23a02c5c..174745ac 100644 --- a/gio-sdk/tracker/src/main/java/com/growingio/android/sdk/track/GrowingAppModule.java +++ b/gio-sdk/tracker/src/main/java/com/growingio/android/sdk/track/GrowingAppModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track; import com.growingio.android.sdk.AppGioModule; diff --git a/config/checkstyle/java.header b/gradle/java.header similarity index 79% rename from config/checkstyle/java.header rename to gradle/java.header index 3b374551..fab16924 100644 --- a/config/checkstyle/java.header +++ b/gradle/java.header @@ -1,7 +1,7 @@ /* - * Copyright \(C\) \d\d\d\d Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 \(the "License"\); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index eb42f1bf..63fe7841 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,6 +7,8 @@ navigation = "2.5.3" pluginGradle = "7.4.1" # 4.2.2-7.4.1 kotlin = "1.8.20" +spotless = "6.20.0" + # https://developer.android.com/jetpack/androidx/releases/compose-compiler # composeBom = "2023.04.00" @@ -39,6 +41,7 @@ sonarqube = { id = "org.sonarqube", version = "3.4.0.2513" } protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } growingio-plugin = { id = "com.growingio.android.autotracker", version.ref = "growingioPlugin" } +spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } [libraries] # [growingio] diff --git a/growingio-advert/build.gradle b/growingio-advert/build.gradle index 635fe3d4..ea8917ac 100644 --- a/growingio-advert/build.gradle +++ b/growingio-advert/build.gradle @@ -1,57 +1,56 @@ plugins { - id 'com.android.library' + id 'com.android.library' } android { - namespace 'com.growingio.android.advert' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - consumerProguardFiles 'consumer-rules.pro' - } - } - - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } - - testOptions { - unitTests.all { - jacoco { - jvmArgs '-noverify' - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - } - } + namespace 'com.growingio.android.advert' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + consumerProguardFiles 'consumer-rules.pro' + } + } + + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } + + testOptions { + unitTests.all { + jacoco { + jvmArgs '-noverify' + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } } dependencies { - testImplementation libs.bundles.test - testImplementation libs.androidx.test.core + testImplementation libs.bundles.test + testImplementation libs.androidx.test.core - compileOnly fileTree(include: ['*.jar'], dir: '../thirdLibs') - implementation project(':growingio-tracker-core') + compileOnly fileTree(include: ['*.jar'], dir: '../thirdLibs') + implementation project(':growingio-tracker-core') - implementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + implementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" diff --git a/growingio-advert/src/main/java/com/growingio/android/advert/AdvertActivateDataLoader.java b/growingio-advert/src/main/java/com/growingio/android/advert/AdvertActivateDataLoader.java index 4e673bcb..43f6f7a9 100644 --- a/growingio-advert/src/main/java/com/growingio/android/advert/AdvertActivateDataLoader.java +++ b/growingio-advert/src/main/java/com/growingio/android/advert/AdvertActivateDataLoader.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.advert; import android.app.Activity; @@ -42,11 +41,9 @@ import com.growingio.android.sdk.track.modelloader.LoadDataFetcher; import com.growingio.android.sdk.track.modelloader.ModelLoader; import com.growingio.android.sdk.track.modelloader.ModelLoaderFactory; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; import com.growingio.android.sdk.track.providers.ConfigurationProvider; import com.growingio.android.sdk.track.providers.DeviceInfoProvider; import com.growingio.android.sdk.track.providers.SessionProvider; -import com.growingio.android.sdk.track.utils.ThreadUtils; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -61,18 +58,25 @@ */ public class AdvertActivateDataLoader implements ModelLoader { - private final static int DEEPLINK_TYPE_NONE = 0; //doesn't contain deeplink. private final static int DEEPLINK_TYPE_URI = 1; //web jump,contains ads params. private final static int DEEPLINK_TYPE_SHORT = 2; //short scheme,doesn't contain data. + + private final TrackerContext context; + + public AdvertActivateDataLoader(TrackerContext context) { + this.context = context; + } + @Override public LoadData buildLoadData(Activate activate) { - AdvertConfig config = ConfigurationProvider.get().getConfiguration(AdvertConfig.class); + ConfigurationProvider configurationProvider = context.getConfigurationProvider(); + AdvertConfig config = configurationProvider.getConfiguration(AdvertConfig.class); if (config == null) config = new AdvertConfig(); String deepLinkHost = config.getDeepLinkHost(); if (deepLinkHost == null || deepLinkHost.isEmpty()) { - deepLinkHost = ConfigurationProvider.core().getDataCollectionServerHost(); + deepLinkHost = configurationProvider.core().getDataCollectionServerHost(); } DeepLinkCallback callback = config.getDeepLinkCallback(); boolean isInApp = false; @@ -80,14 +84,20 @@ public LoadData buildLoadData(Activate activate) { isInApp = true; callback = activate.getCallback(); } - return new LoadData<>(new ActivateDataFetcher(activate.getUri(), deepLinkHost, callback, isInApp)); + return new LoadData<>(new ActivateDataFetcher(context, activate.getUri(), deepLinkHost, callback, isInApp)); } public static class Factory implements ModelLoaderFactory { + private final TrackerContext context; + + public Factory(TrackerContext context) { + this.context = context; + } + @Override public ModelLoader build() { - return new AdvertActivateDataLoader(); + return new AdvertActivateDataLoader(this.context); } } @@ -98,7 +108,10 @@ public static class ActivateDataFetcher implements DataFetcher { private final Uri deepLinkHost; private final boolean isInApp; - public ActivateDataFetcher(Uri uri, String adHost, DeepLinkCallback callback, boolean isInApp) { + private TrackerContext trackerContext; + + public ActivateDataFetcher(TrackerContext trackerContext, Uri uri, String adHost, DeepLinkCallback callback, boolean isInApp) { + this.trackerContext = trackerContext; this.deepLinkHost = Uri.parse(adHost); this.uri = uri; this.deepLinkCallback = callback; @@ -202,17 +215,19 @@ void onParseDeeplinkArgs(Uri data) { private void sendDeepLinkCallback(int finalErrorCode, Map params, long wakeTime) { if (deepLinkCallback == null) return; - ThreadUtils.runOnUiThread(() -> deepLinkCallback.onReceive(params, finalErrorCode, System.currentTimeMillis() - wakeTime)); + TrackMainThread.trackMain().runOnUiThread(() -> deepLinkCallback.onReceive(params, finalErrorCode, System.currentTimeMillis() - wakeTime)); } private void requestDeepLinkParamsByTrackId(String trackId, long wakeTime, boolean isInApp) { - String projectId = ConfigurationProvider.core().getProjectId(); - String dataSourceId = ConfigurationProvider.core().getDataSourceId(); + ConfigurationProvider configurationProvider = trackerContext.getConfigurationProvider(); + DeviceInfoProvider deviceInfoProvider = trackerContext.getDeviceInfoProvider(); + String projectId = configurationProvider.core().getProjectId(); + String dataSourceId = configurationProvider.core().getDataSourceId(); String deepType = isInApp ? "inapp" : "defer"; String url = AdvertUtils.getRequestDeepLinkUrl(deepLinkHost.toString(), deepType, projectId, dataSourceId, trackId); - EventUrl eventUrl = new EventUrl(url, System.currentTimeMillis()).addHeader("User-Agent", DeviceInfoProvider.get().getUserAgent()); + EventUrl eventUrl = new EventUrl(url, System.currentTimeMillis()).addHeader("User-Agent", deviceInfoProvider.getUserAgent()); //.addHeader("ip", AdvertUtils.getIP()); - TrackerContext.get().loadData(eventUrl, EventUrl.class, EventResponse.class, new LoadDataFetcher.DataCallback() { + trackerContext.getRegistry().loadData(eventUrl, EventUrl.class, EventResponse.class, new LoadDataFetcher.DataCallback() { @Override public void onDataReady(EventResponse data) { try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { @@ -263,13 +278,14 @@ private void activateDevice() { * 2. 打开隐私政策后,发送设备激活事件,同时发送到用户自定义实现的 {@link DeepLinkCallback} 中 */ private void activateDeviceWithClipBoard() { - AdvertConfig config = ConfigurationProvider.get().getConfiguration(AdvertConfig.class); + ConfigurationProvider configurationProvider = trackerContext.getConfigurationProvider(); + AdvertConfig config = configurationProvider.getConfiguration(AdvertConfig.class); if (config != null && !config.isReadClipBoardEnable()) { activateDevice(); return; } - Activity activity = ActivityStateProvider.get().getForegroundActivity(); + Activity activity = trackerContext.getActivityStateProvider().getForegroundActivity(); //Android 10 限制剪切板获取时机,只有输入法或者焦点APP才有权限获取剪切板 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && activity != null) { activity.getWindow().getDecorView().post(this::checkClipBoardAndSendActivateEvent); @@ -279,8 +295,10 @@ private void activateDeviceWithClipBoard() { } private void checkClipBoardAndSendActivateEvent() { + ConfigurationProvider configurationProvider = trackerContext.getConfigurationProvider(); + String urlScheme = configurationProvider.core().getUrlScheme(); final AdvertData tempData = new AdvertData(); - final boolean success = AdvertUtils.checkClipBoard(tempData); + final boolean success = AdvertUtils.checkClipBoard(trackerContext.getBaseContext(), tempData, urlScheme); if (success) { activateDeviceDefer(tempData); final Map params = new HashMap<>(); @@ -292,7 +310,7 @@ private void checkClipBoardAndSendActivateEvent() { // 若无最新的剪贴板信息,则加载上一次未发送的激活信息 String data = AdvertUtils.getActivateInfo(); if (data != null && !TextUtils.isEmpty(data)) { - AdvertData cacheData = AdvertUtils.parseClipBoardInfo(data); + AdvertData cacheData = AdvertUtils.parseClipBoardInfo(data, urlScheme); if (cacheData != null) tempData.copy(cacheData); activateDeviceDefer(tempData); final Map params = new HashMap<>(); @@ -327,7 +345,8 @@ private void sendReengage(AdvertData data) { private final static String TAG = "AdvertModule"; private void sendEventToMain(ActivateEvent.Builder eventBuilder) { - SessionProvider.get().checkSessionIntervalAndSendVisit(); + SessionProvider sessionProvider = trackerContext.getProvider(SessionProvider.class); + sessionProvider.checkSessionIntervalAndSendVisit(); TrackMainThread.trackMain().postEventToTrackMain(eventBuilder); } diff --git a/growingio-advert/src/main/java/com/growingio/android/advert/AdvertConfig.java b/growingio-advert/src/main/java/com/growingio/android/advert/AdvertConfig.java index e4e3a43b..875eb558 100644 --- a/growingio-advert/src/main/java/com/growingio/android/advert/AdvertConfig.java +++ b/growingio-advert/src/main/java/com/growingio/android/advert/AdvertConfig.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.advert; import static com.growingio.android.advert.AdvertUtils.DEEPLINK_AD_HOST; diff --git a/growingio-advert/src/main/java/com/growingio/android/advert/AdvertData.java b/growingio-advert/src/main/java/com/growingio/android/advert/AdvertData.java index 8ef679ab..3bd9dabd 100644 --- a/growingio-advert/src/main/java/com/growingio/android/advert/AdvertData.java +++ b/growingio-advert/src/main/java/com/growingio/android/advert/AdvertData.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.advert; import com.growingio.android.sdk.track.middleware.advert.DeepLinkCallback; diff --git a/growingio-advert/src/main/java/com/growingio/android/advert/AdvertLibraryGioModule.java b/growingio-advert/src/main/java/com/growingio/android/advert/AdvertLibraryGioModule.java index 31cc6079..a642667a 100644 --- a/growingio-advert/src/main/java/com/growingio/android/advert/AdvertLibraryGioModule.java +++ b/growingio-advert/src/main/java/com/growingio/android/advert/AdvertLibraryGioModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +15,10 @@ */ package com.growingio.android.advert; -import android.content.Context; - import com.growingio.android.sdk.LibraryGioModule; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.middleware.advert.Activate; import com.growingio.android.sdk.track.middleware.advert.AdvertResult; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import com.growingio.sdk.annotation.GIOLibraryModule; /** @@ -31,7 +29,7 @@ @GIOLibraryModule public class AdvertLibraryGioModule extends LibraryGioModule { @Override - public void registerComponents(Context context, TrackerRegistry registry) { - registry.register(Activate.class, AdvertResult.class, new AdvertActivateDataLoader.Factory()); + public void registerComponents(TrackerContext context) { + context.getRegistry().register(Activate.class, AdvertResult.class, new AdvertActivateDataLoader.Factory(context)); } } diff --git a/growingio-advert/src/main/java/com/growingio/android/advert/AdvertUtils.java b/growingio-advert/src/main/java/com/growingio/android/advert/AdvertUtils.java index c48c99d7..7e16b1f0 100644 --- a/growingio-advert/src/main/java/com/growingio/android/advert/AdvertUtils.java +++ b/growingio-advert/src/main/java/com/growingio/android/advert/AdvertUtils.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.advert; import static android.content.Context.CLIPBOARD_SERVICE; @@ -26,10 +25,9 @@ import android.os.Build; import android.text.TextUtils; -import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.middleware.advert.DeepLinkCallback; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; import org.json.JSONException; import org.json.JSONObject; @@ -105,15 +103,15 @@ static AdvertData parseDeeplinkResponse(String body) { * @param data json格式 * @return 广告参数对象 */ - static AdvertData parseClipBoardInfo(String data) { + static AdvertData parseClipBoardInfo(String data, String urlScheme) { try { JSONObject jsonObject = new JSONObject(data); if (!"gads".equals(jsonObject.getString("type"))) { Logger.e(TAG, "非剪贴板数据!"); return null; } - if (!ConfigurationProvider.core().getUrlScheme().equals(jsonObject.getString("scheme"))) { - Logger.e(TAG, "非此应用的延迟深度链接, urlsheme 不匹配,期望为:" + ConfigurationProvider.core().getUrlScheme() + ", 实际为:" + jsonObject.getString("scheme")); + if (!urlScheme.equals(jsonObject.getString("scheme"))) { + Logger.e(TAG, "非此应用的延迟深度链接, urlsheme 不匹配,期望为:" + urlScheme + ", 实际为:" + jsonObject.getString("scheme")); return null; } AdvertData info = new AdvertData(); @@ -144,20 +142,20 @@ static AdvertData parseClipBoardInfo(String data) { * @return true 是 Deffer 延迟深度链接,拿到了有效的剪贴板数据 * false 是普通打开,剪贴板里没有广告组写进去的数据 */ - static boolean checkClipBoard(AdvertData info) { + static boolean checkClipBoard(Context context, AdvertData info, String urlScheme) { try { - @SuppressLint("WrongConstant") ClipboardManager cm = (ClipboardManager) TrackerContext.get().getSystemService(CLIPBOARD_SERVICE); + @SuppressLint("WrongConstant") ClipboardManager cm = (ClipboardManager) context.getSystemService(CLIPBOARD_SERVICE); ClipData clipData = cm != null ? cm.getPrimaryClip() : null; if (clipData == null) return false; if (clipData.getItemCount() == 0) return false; ClipData.Item item = clipData.getItemAt(0); - CharSequence charSequence = item.coerceToText(TrackerContext.get()); + CharSequence charSequence = item.coerceToText(context); if (charSequence == null || charSequence.length() == 0) return false; String data = parseZwsData(charSequence); if (data == null) return false; - AdvertData advertData = parseClipBoardInfo(data); + AdvertData advertData = parseClipBoardInfo(data, urlScheme); if (advertData != null) { info.copy(advertData); //保存剪切板数据 @@ -223,7 +221,7 @@ static void setDeviceActivated() { @SuppressLint("WrongConstant") private static SharedPreferences getSharedPreferences() { - return TrackerContext.get().getSharedPreferences(PREF_FILE_NAME, Context.MODE_PRIVATE); + return TrackMainThread.trackMain().getContext().getSharedPreferences(PREF_FILE_NAME, Context.MODE_PRIVATE); } static void setActivateInfo(String activateInfo) { diff --git a/growingio-advert/src/test/java/com/growingio/android/advert/ActivateTest.java b/growingio-advert/src/test/java/com/growingio/android/advert/ActivateTest.java index 33d27026..b01f6973 100644 --- a/growingio-advert/src/test/java/com/growingio/android/advert/ActivateTest.java +++ b/growingio-advert/src/test/java/com/growingio/android/advert/ActivateTest.java @@ -1,24 +1,22 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.advert; import static org.robolectric.Shadows.shadowOf; -import android.app.Activity; import android.app.Application; import android.content.ClipData; import android.content.ClipboardManager; @@ -31,16 +29,15 @@ import com.google.common.truth.Truth; import com.growingio.android.sdk.Configurable; import com.growingio.android.sdk.CoreConfiguration; +import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.middleware.advert.Activate; import com.growingio.android.sdk.track.middleware.advert.AdvertResult; import com.growingio.android.sdk.track.middleware.advert.DeepLinkCallback; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; import com.growingio.android.sdk.track.providers.DeviceInfoProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProviderFactory; import com.growingio.android.sdk.track.utils.ConstantPool; -import com.growingio.android.sdk.track.utils.ThreadUtils; import org.junit.Before; import org.junit.Test; @@ -61,15 +58,18 @@ @Config(manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) public class ActivateTest { - private final Application context = ApplicationProvider.getApplicationContext(); - private Activity tempActivity; + private final Application application = ApplicationProvider.getApplicationContext(); + private TrackerContext context; @Before public void setup() { - TrackerContext.init(context); Map, Configurable> sModuleConfigs = new HashMap<>(); - ConfigurationProvider.initWithConfig(new CoreConfiguration(ConstantPool.UNKNOWN, "growing.test"), sModuleConfigs); - tempActivity = Robolectric.buildActivity(RobolectricActivity.class).setup().create().get(); + TrackerLifecycleProviderFactory.create().createConfigurationProviderWithConfig(new CoreConfiguration(ConstantPool.UNKNOWN, "growing.test"), sModuleConfigs); + + Tracker tracker = new Tracker(application); + context = tracker.getContext(); + + Robolectric.buildActivity(RobolectricActivity.class).setup().create().get(); } @@ -77,14 +77,12 @@ public void setup() { @LooperMode(LooperMode.Mode.PAUSED) public void activate() { AdvertLibraryGioModule module = new AdvertLibraryGioModule(); - TrackerRegistry trackerRegistry = TrackerContext.get().getRegistry(); - module.registerComponents(context, trackerRegistry); + module.registerComponents(context); AdvertUtils.clearAdvertSharedPreferences(); Truth.assertThat(AdvertUtils.isDeviceActivated()).isFalse(); - ActivityStateProvider.get().onActivityCreated(tempActivity, null); - TrackerContext.get().executeData(Activate.activate(), Activate.class, AdvertResult.class); + context.getRegistry().executeData(Activate.activate(), Activate.class, AdvertResult.class); // 等待 view.post 完成 shadowOf(Looper.getMainLooper()).idle(); Truth.assertThat(AdvertUtils.isDeviceActivated()).isTrue(); @@ -99,12 +97,11 @@ public void deeplinkUri() { Truth.assertThat(error).isEqualTo(0); Truth.assertThat(params.toString()).isEqualTo("{name=cpacm}"); }); - ConfigurationProvider.get().addConfiguration(config); + context.getConfigurationProvider().addConfiguration(config); AdvertLibraryGioModule module = new AdvertLibraryGioModule(); - TrackerRegistry trackerRegistry = TrackerContext.get().getRegistry(); - module.registerComponents(context, trackerRegistry); + module.registerComponents(context); Uri uri = Uri.parse("growing.test://customPath?deep_link_id=1111&deep_click_id=2222&deep_click_time=3333&deep_params={\"name\":\"cpacm\"}"); - TrackerContext.get().executeData(Activate.deeplink(uri), Activate.class, AdvertResult.class); + context.getRegistry().executeData(Activate.deeplink(uri), Activate.class, AdvertResult.class); shadowOf(Looper.getMainLooper()).idle(); } @@ -115,10 +112,9 @@ public void clipBoardTest() { config.setDeepLinkCallback((params, error, appAwakePassedTime) -> { Truth.assertThat(params.toString()).isEqualTo("{name=cpacm}"); }); - ConfigurationProvider.get().addConfiguration(config); + context.getConfigurationProvider().addConfiguration(config); AdvertLibraryGioModule module = new AdvertLibraryGioModule(); - TrackerRegistry trackerRegistry = TrackerContext.get().getRegistry(); - module.registerComponents(context, trackerRegistry); + module.registerComponents(context); String clipData = "{\n" + " \"type\":\"gads\", \n" + @@ -131,7 +127,7 @@ public void clipBoardTest() { ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); cm.setPrimaryClip(ClipData.newPlainText(null, convertToZws(clipData))); - TrackerContext.get().executeData(Activate.activate(), Activate.class, AdvertResult.class); + context.getRegistry().executeData(Activate.activate(), Activate.class, AdvertResult.class); shadowOf(Looper.getMainLooper()).idle(); } @@ -171,15 +167,14 @@ public void zwsConvertTest() { public void shortUrlTest() { AdvertConfig config = new AdvertConfig(); config.setDeepLinkHost("https://www.google.com"); - ConfigurationProvider.get().addConfiguration(config); + context.getConfigurationProvider().addConfiguration(config); AdvertLibraryGioModule module = new AdvertLibraryGioModule(); - TrackerRegistry trackerRegistry = TrackerContext.get().getRegistry(); - module.registerComponents(context, trackerRegistry); + module.registerComponents(context); AdvertUtils.clearAdvertSharedPreferences(); Uri uri = Uri.parse("https://www.google.com/eujsasjf"); - TrackerContext.get().executeData(Activate.handleDeeplink(uri, new DeepLinkCallback() { + context.getRegistry().executeData(Activate.handleDeeplink(uri, new DeepLinkCallback() { @Override public void onReceive(Map params, int error, long appAwakePassedTime) { Truth.assertThat(error).isEqualTo(DeepLinkCallback.ERROR_NET_FAIL); @@ -196,7 +191,7 @@ public String getUserAgent() { } } - @Implements(ThreadUtils.class) + @Implements(TrackMainThread.class) public static class ShadowThreadUtils { @Implementation public static void runOnUiThread(Runnable r) { diff --git a/growingio-advert/src/test/java/com/growingio/android/advert/RobolectricActivity.java b/growingio-advert/src/test/java/com/growingio/android/advert/RobolectricActivity.java index 6841d1bb..1742834a 100644 --- a/growingio-advert/src/test/java/com/growingio/android/advert/RobolectricActivity.java +++ b/growingio-advert/src/test/java/com/growingio/android/advert/RobolectricActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/growingio-annotation/build.gradle b/growingio-annotation/build.gradle index e0963613..811b78e1 100644 --- a/growingio-annotation/build.gradle +++ b/growingio-annotation/build.gradle @@ -1,10 +1,10 @@ plugins { - id 'java-library' + id 'java-library' } java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/growingio-annotation/compiler/build.gradle b/growingio-annotation/compiler/build.gradle index 0d725c47..30a59085 100644 --- a/growingio-annotation/compiler/build.gradle +++ b/growingio-annotation/compiler/build.gradle @@ -1,14 +1,14 @@ //import org.gradle.internal.jvm.Jvm plugins { - id 'java-library' - id 'kotlin' - id 'jacoco' + id 'java-library' + id 'kotlin' + id 'jacoco' } java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } //test { @@ -16,27 +16,27 @@ java { //} jacocoTestReport{ - dependsOn test // report is always generated after tests run + dependsOn test // report is always generated after tests run } dependencies { - // About test: we couldn't use compile-testing in android project unless import jre's rt.jar. - testImplementation libs.junit - testImplementation("com.google.testing.compile:compile-testing:0.21.0") { - // We don't use this and including it requires us to list it separatel which would be - // confusing. - exclude group: "com.google.auto.value", module: "auto-value" - } - testImplementation files('libs/tracker-core-test-3.5.0.jar') + // About test: we couldn't use compile-testing in android project unless import jre's rt.jar. + testImplementation libs.junit + testImplementation("com.google.testing.compile:compile-testing:0.21.0") { + // We don't use this and including it requires us to list it separatel which would be + // confusing. + exclude group: "com.google.auto.value", module: "auto-value" + } + testImplementation files('libs/tracker-core-test-4.0.0.jar') - implementation libs.kotlin.stdlib + implementation libs.kotlin.stdlib - annotationProcessor libs.google.auto.service - compileOnly libs.google.auto.service.annotations - implementation libs.square.javapoet + annotationProcessor libs.google.auto.service + compileOnly libs.google.auto.service.annotations + implementation libs.square.javapoet - implementation project(":growingio-annotation") + implementation project(":growingio-annotation") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/growingio-annotation/compiler/libs/tracker-core-test-3.5.0.jar b/growingio-annotation/compiler/libs/tracker-core-test-3.5.0.jar deleted file mode 100644 index c9470d3344ee49e988517b60b91029c0d9d733b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 537238 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⋘XMfqtt3+18(@>9|fC{&6zOSCihFX9`W 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

=55i{rP%LVeVH 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

fw5BD) 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�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 T`mmDY-`oi}I`tR0dW8Q2USbjr diff --git a/growingio-annotation/compiler/libs/tracker-core-test-4.0.0.jar b/growingio-annotation/compiler/libs/tracker-core-test-4.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..4448c9573c9d7854cfceaae57b1df1c06cf9d625 GIT binary patch literal 693437 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�O*D*Y>p!{0SL5*IE{e#ewr;3G*_IV z1!9nPi?eiCB+(o0p;KZAVb;TRuNa|AVpP_eH3o;FswfsExY|uEn8^MWEsL62WWCQI zE1nsYm`kTIdGPX*+nGfG`6VWkVze>;W@-%TsJOV|RjQGSf=|Cn8#Nly=;41yCkwGZ zn9)kR(BZiwmAy!hos7K+BEXx!h+BuwK#Vz>yv)L_3~s^TD^(#@CmO(R!kL*=nW7X{ z4sWshkWxT=PXbd6$p8irP)iyh4xu7U1kU6DVz;eWH9>Fn0{O0^-;`Szm6IuSmMftX zfT8p>0B=H_7&l!3ej9+l-3#}39mwelE?_`=Y9Xh(w1M2)m^;413*NAkzFVxzhDk5T-)z!#zCK;7#8b@nl;mFHd)d0m} zwp7y<37MD-`kBY^TRnPLJI?qg9;QDH#i%sY5jqgsM|J4mydhID`goBT6ld|LuI{m} zhODa*>uOXCx^q-qpH=er-$B|0{>#4)o?)ScsenBl+^H()O{GBg_9&!?J2bkJQ!d`c zPflrccNBw4-o*I|$r?(izl_sGl|korh7W@po#n?#jqWLfZt@%-kaS}h847r)q@tJu zz%=jGD6P>LxTl6(2oyNOK!nb7CMM1?V8m4z?rdk^j1OBaCr0z;K&(@`@oXyv%mL1A zX|DRc=Zws4|4B@R7*UQvY^>fxb7)%?jDx0=QXpTt>!$NZHW&B_uP$O@rO)h* z3t`L^YkLD@8NC@3VGWOrogL2%q%I)He`+3zX4vr9_P(tSOQ>OA-sv=}&L{(4)z^nf z3a<8w2$@|1jcf~aSkRO7RSTW_J*B?`^xka8wQTfg+GVsN)5*1 zjA}c9uu1H?4b`x_d-?>|Op-Rt3arSPY^S6U$EU*l{quYJ7wAM)Xaf0SnR0*@V@1U~ zM}={o4>LW8j=K=d*#~);<2uGe5YHRs^JTclh{bgUbY&~y-+^M~f zD-Aqmig8(jOO1D#22kYz5dIRb2dXe4>lsbU)J~RmLId#B+bcuy@%tCro7SjA%4y4(O z3v3t2a!)#fGuOG4k%k(lQkk0Ue%d9=Rcf44t9z6JpEdw8NyOHz11QZL5uLa?2p22W zyEmc;?n==nGl{9UXFyYp-&N1xkNnL?MofpFCoDhz~HlQFl2V$}JVNtF+{hywPgQ(@aJN!U?cpu4lnq7(q=eHwpB*rs~O#+C|k#h zt|bv#ij1kWjFrX9HCmz3N-Wv-cI-I@p@G#=v%5O?9Bn&#@>EY-`=Pe|9jCh64tHR_ zbF5PgA5N&QP!PKvt8|(XWL~A{o@1R|dr$S9?CFRQ0>RDu&cnEGhS&T4&c0K<2ihWp z*lqoI$I;%-Bi*OkkL*Pmq7#+;&~C?bE<3_fk)@hZnwlvk;LNx8aTf8gPVy5~tDH1e za_U^DMJWt7?}SDzyf@`q9b7Pi38#Sef}X&3KUHf%1T_PIPvpMD8I3o(Sikw2m}lBV^a_)jUneVMqii#AV?NR1wg}H z1MWh!*gg6ldgSY`-2zt5f30|7verb>nVe&<0dzxzmD~zQqxq;c-aKXW{(ysP0s4pI zEAN=8;NOhFCkNX?w731t8g`3jW-v{jD+*;RT3{%-mU@h-K_%|*g$)uNQr0X%s{*$~ z&QWC!2;VUSfmGST!{|la^)Z}e2Z`Wbb$oSShI`%ddpYha9QT#@5?&?f{AgxC*nl(7 z1I6s&tvWJq5u=3C+2|rQ7!`JBuED6_zB&iT5P7zWNp2~+PXHMIvCuFXP2?KJ&UBx( zaZH&;ng%1LySZk2KJ(ccM58EDRThi_7?67hmK;2=&=mev4WJkcVEj)B^(_0cNimG3 zVdlUTR}Dc`5%-vHi9G)4ZcIe{w-+xKTBTm3`5#J{@i?AIxPj+gBvvn>xsoLm*(@Q_ z+1=aM*4>VYaNVR<5l@?7h|ReV#t8I1{idUl>AajDaOy|(JjyD~d+U^Q|^K#CP zD(7LHhiu^)B=|FM(Mj_Pc^bo0=^PA5BXHqO`GQz8Qke4sUMHjC3m;^d+|=~Vlc}1% z*`>msz@5{p!IgA$eKk8Cm3tkSE6fV8GK?88vucbWp8cIGau$lwZ5-c5z|TxMp;0b~ z!QFUq4et_rVQzoHTaKDrWK{tU(&+>FA}LKQzX`a2-~>{C5+6FL1oN2dKCk z3Hj;SC|2LkYV;hNlRsCAC%>T4^BP@=&;*a&t9-bo(RDt*5Cs)pc|eD%dB zmeOD1t6$`+Uy8yJ`pbO#6^*{iPrk-CzoOCC`TPxyzRBlbrrLQ)RHS}%%G z2T>9gnuy>NQ3?nm%7-#dKqcXt6*K|eL>6-jY=sAN>^F8|**qx+<19Aq$#nlXqBr*? z&!lb{PmCwPxSXcrkPaG~4EpWqv2Xg|ng0puoPqi?B=*#p z%}H4#ZCyE8+}>f{la+(8M@Eh%O+JHNnP?Uh8%s9lbLBfG8_s^b zH!&2y#5)DKa#&l9a*(@EI1?W`2dNlo^Wv8dBV}U}DX+vqB&All0c~hzMtuCfz(`7+ ztLyQGsZ(Ak$kfu4w?pBGF&=e@e96ZM*yr;GVwfsW8` zC|oC97uqs}9HSojMCX0fbUI5($U2iz(FT=U9FZHGPAfq(Vz!5M(I6J!ypP z^f6sDie&+^B{{~jtg8|bb+KHm2$00W`}KiLX4HUyVDI~NY_PVBTM3>RyxFU0qK9vcq~pk^Bd(vY@IMtR9i^G%)7 zJ4C0qGba9tk(-SzAsgiMpfXyIHN<&0B$;TNePXsY^n>eVfb%A?nGZMXVvE?COMxJ^ z>AWYNj!+clAP%5r;&#s7t$^xcm)NR{-TZo+*rOw!{4rg$i)u~m1$nT6t>_T@KufV- zN8rm-0S0wWqJVqz#9^fCUfqyEE)PzvihcNW$V>+{aYz?k;;@cb7?A%67u-WnY2p?g z9_T-UoR@P%knE=T8S`%mi9xW_R@=DQ~8E?2#UID2N zT?@t@D=+Dk6Kzh-3-gBiLFO_3Wvzpu-@qQ%Y{~18q95c8>4}JBU&lP_4@f6ify}gH z9;Y8|N9JGFdvHpyF6>u}KK%i^zxKO*qbA?v+?hvBj=MzK$}(4obl54UlnW@7j5s^-CuZwE;;_&|4>8EqHjuYvl zTiHK2brj&jKf4Gy$S{1I+}cdt1qX-7@FkO<=G=Nc%yKxUa_8q(lW{_{`zP-~K{~`_ z)XE714KbTG3_HL*6y)Km`_`#7zuuEqRfaa^{f6`H!lfmpyuwmS0g} z(nZAeYC#JTd_5zusaL}ZvjP5+Yv3`tRk}EK!cXoXTBQqgJ%RxI9$8Q2Kl^}DPt*k~ z@zg^H_)AXt6EqMKevN?^w{Wx>$&zFJdT=%bl(vm`}o`kzFXwt-O8xW4*0c+VAI3b;NwCQ}SuJe)Y>&-y%E%;iM&rjZpCl5+^p<+%G$36RPc!tHNfzffa zI%A|G`QHoduRB4j83WY{%FW~)+9Km9Rkupjvr6SBT&i8xJFLF{9YEzib`NS?JV9%o zrqhZ9w_7B*Mg7#mj>fI*XgmiO&}qF)>keO|_3)+MaQU^rj3KY#Mz~nt6x%c*9h4c` z1l>GATaXTCE0T*TFxzcfs&R4w^t%Y}<4YJD_hEFr4t?l;jN%8Z+HFGrU`jGg?lJ0V z(p3HSR{ZuLe$}byhqP3q$6ulOD^cTjS~UHbLDM~|b{zSQ0kP^SYOxXD0mLy9cH(4V z%_)G2@o+a1RG=>y>E)b#;f*k+KLo$ghcVJ0h4b&5EW8PhzpB6SmA5bO6SFVCI6kW> z>}a5O%{=05HsMt#eg_bL9EiUYh`$?%zh@>8R~a&STya`JZZYOP705kyjoLqfEI!w1 zuOLq0Q~_dkoUr?~LhAi+RQLe6%Ll=upOTpXZEm^HqUJ&%`(9%l6_H?e4lNRnT<|5F zxl413mANQn-{I9?LE9mD#eSOv$|d?~Fq{vANB@khy5d#URw>o=emQ>G$KnIPDC8K) z{F~W&rn~e3s{uJs|0&?d=zP%VdHb5bLIs>Tk_2(}8PrXo9+~5j*hL zPf+($l2;=&(-Ayafx7hA^imo6K8~^Rvv7yMj1lxHjG)heFF)gG$bD9A_Mr|xWzY=s z9h7T}S!H8jc>rga`jLt3d0O~YWCq|gOUT4|3$s%v{;bY z4&==q^B~?qio997nUtI(R6#wA$s&xOIGaXQa|2R^$zC6l^NHf-Uq%o6iiD{8V3~z* z8S(^jPiN`qGLnKYf}gTT#Bmu+B92mElJs%+a031nOx9m_P@>cVT&e(9g;=;XFzq+M z)mT#@Sfq0fR2@N$)FD)A81)^6ThF~1tz(X2YOG>v48Hdu5Cg%BE8q{5MlV&Q*@83U z&z_WqOR!DiCI>*Xe=vccLCQib5Rv7S>-t&5GjIypFk%Go4=s8Ez2H_5bREQ!Q{hrL z#NR1yN00bJ{LFp!kMJ$$Bl}~w&*1jkFTQ1NB~t9*Zx3D#xVDpv4&I`Q9X%S2wYJ+K zi9Vvq#1;{{D~d>pQxUqm6tPh82%YA0e}o1il;F!Vd>G`zSw1BB^?RaVr9)BBdW4@L zoy9PX^3}b3mFBB4zRK{`IA5LTs|!&aE+S1(6w>8=Q5GvB^!hST>wyTp0U1#v^u`E1 z6rqRt#Ydtb%%gnu80K1y-U3lfE?!mA^qi3c;Z!_{HF{()m11W}T!zNu_|8y*@`Ero zxQ1z@1mU9R_+CTcXuspCZx}kWe$H=^Jd-rH5iNl?dwduw@tJUC(2N%Gcq75`GpY8< zSiI+KU{!kk%P_d>8-rSOJFqJ{%yd=>1y}JphL}q;@M5eWUGPQ?ObD4y4Sg)7ha_P);JZG9a&{QlpDCT)aI#Hl#Dk4Yaq9Sc8%R~08m z@LY#iEN;r>J2u~m%KHHx$T~s;SL+B(z+odFHt_+Wh8n$HN2ck=HF~FxT-xu}v1G?D zPw-U>U%iK~TKVd|e6^OZ-p5z#_zHRLVb_w%KiS)?je0lJ%^H2sFgBKsW7~u_?D&+C z8LiO#1Bt}wDYhPh@yctS0#5OKQ7&)gL?G`g9rqc`7t-|tAFEkF=X7{XBG~W|iWnf8 zJs?jSVDr0ysVk+C?J#7&P`7O(1L;&!{>tDwrF6jG*#jQE4Q$6nSY~ggWp%3iq)mHs5IIj~ zGU?=L?6(8qoNS?uxqzYE{- zLZ`ebRs`)UvMgVr61AXV?XX(0b9(DRi1BBkTQ#+6)4T^7Pa&g-plM(+xdUoXg)xW! zGsY2xs{|dAs%*8ayBEl&9f(FOMEB={5jD|s!Z;5Y7jnWlfRx?}57u~aWYnCd<||lc zuD*gb@|r6kWL=om)N0|)HC8LP*Wu;+QR~-Z<@NvqHQpe>BOM@vuxdU>HX>JV4o@qR znsaz=!SacpaTA7tfC^`4n!f|@@NXMfXIhEnXcf}78k%dItFIs{EzUJpsFu%jSaXdt zE3NHjUV>i1i5!ZdOp9V=8p8sz9%`5+P`)%l<+2{iQf_*+P~KpnyTOQx=QRvRvUAI* z*ma;LmH{#D4ws=zn8zXV$dfrA+R6o8SUF>Fl7&xU{Ty4wH`l45h)&=N32N2j#e8Eu zE;Ao5!Q(RPv2H$IipRS3xZHf)h{xsDS^q+z~Eyue+0<|Jx&o)8SYhS>u4Svy1V0*4qc7sM)4cbnnfj z&?n+hNjLKq4wbZpuW+cOt$c+;C2ixYZBZPyYqSH&FEqkVioT1-ijLzQ-F>Hyc64;_ zM|^$2=-=HCZPbT*JN9)PjZhm_zv_W03`UTR*tPOD7TNP)y=LL4bL4z-EQ!s&?0u*) z7cDgXkuJ8{8=|*%BR{3whSjx3_eOTW$ISVWvJS&U+%}Yqk6|hbkHlFft5&OgwNeBM zBZEA}=UVG;H&R2*#2hxI908kxE;n3V2vzm?&QDuIFsa1E{y$1$GUcV50+Qt(p_DO` zFJ;zyDB#z|y@|9+X`03+^wd!=;8}npIb+;UjAxbL^!#iQ?iCJXN=_6d{_LpQjMEJmWKjnPNVdT;XKon3JzSw8I&4 zQ<)MVR^U5jRosG^ZY%S2$T!=8)(*hm3GsCoW>9mMY%v5}wrsP2%WC^IxK?;<02VC$ zl=22^FlOpXjN&5H9Joh zofE)%3xF*HAg{W&{zn}j7aR32f~d%I|3<5q+=TmC_+vSs5)HyxMB334tGseR-`(#! z@h3VF|J{NQrXmNhx01=!c8{R;>N+A;N)~JnrEOtd%%;&@22z5l! zhxSF`*Z?_VA$3Ms#EnoF-yDumH=mD0sE5zDMChnSz5Hb#e{rl76+h006MVRp4=4F> zn?|?83c!Wksj(fPNT#>eu%iH62d#}i4|_Pbx$I>Qo{{n7xzrmz)du^R2^D&Y+KXM# zk>L73*VO8do>$FX;_z|H;glBGi_=xS3}uS4U}Glasqt}yGsCW?EJ2jYL_GV_DcCi9 zg~#K8=N4BrtO+G2^1R~LqADFdor>YFIgAEo zApwW|S-F*WtG8=SQA0eXQD-#>2Qa#XvQ@5z^WtbSB_hM`QqtT!YTH=T!+?>-na0sG48}vAI=uB>TUxyDJezJvr4y(cg z!^&6d>I5AZ`xB!aOwLMasHevWy5SBQ*Rf8=;XEID_;8f7@mJe@#rklXVjq{gX^Ju{ zldUC3%uaxSF@rz*^iM&@1GciCT|PqN3hsW&T8>|P?{Re zC~6e2;K*s}w`~0ICz!c4$&Bcziy+GhEH+pX$+>5hbZ##vY!TLSF|kE>-6O*C=cVAA z%>c)1ksWd+0)OGpQRu5wZse22KD4;E#bKqHuEMfl7R1|XtR&cFN>l-ey`1rt^*Q5XwXKumTUujfa)y!zw(iG#<_}AI9(y z`|g&)T`}CvJM>1NF24sz70Eoja;GD#?|2nhjfMUKh^8wc$F9e!Z3n2hA2d9QIq428 zmj?06J^1PzWP4sTDUIt8?wCxd3D!DmQ379aX3~(Xco~(foIQK?9u4sEBX}^5Ov}$1 z1lVN|V0QEB7*<8B4aSu&ujb$izJho*7gun>!c`rvV)ClquG3avJI|=oax@uRCL+Km z28`wdo8`b~EehWX!|4vJMs@-XyAHdoI_xs+4{Uwl+Ne6f9r^Vd;6op}0lH9y|3(zY z+U;l)JoV;-%nR%_r&o~^1KsOQp>6N zXt<47PTdOw!A9?L>I0wt#IqkohvUC?d=QXR!vm##C^eQ)$tg@8<5@s0*Qh>1^P(X6 ze2o@xR>XyzGiZ@U4H_+(nPrEs{Jbl$w3f3jn9Yg}Ka2t#9NcsPi~_#3IJlhU@U2q@ zcIP3wKWN?DFr9H~!9`#fd9uAEs@-)q+$1JV7GMZKUVn#ZTTjoa_O2tn9Z)ltvb-A< zk{b>FxKYPIuMUu1-oV8T!R*u;b_1M&*l~Ze|DM2ed~O7#bQF|I?hXMKsB*-~nFu6f z!zCMXZvrP_maR3#LRYCsF(7DM@B%B{7Gn}y0xgYcTGgPbnT0z}e93A%2C)Gwi9wXU zNMW{C6|0$$fhaz>+yS6e0RSJ2LE)$X*i$m5LGQ;49Um7Pr5735t%sQf0Q2jHLz^{V;(vs7U*Gpi&=A12mXioL1uyVWmCv;#M<@aoW3eG za;$SN)QiX>QjWlB#F`uVmf7UW|Ad2Q&PcbJlI0|Dx*2zW1xNFkPNvVIUF+%GoG8Cd zY)Uo|os&8S9dT|V1pm%GQm4UC)U8QR6YS_)=Er%= zmxCv;Sz;%iF#{@*T7Sis;D~LorFqci&zFN<*|%BJ<}``Saa0OwlSS0RGePkoW)&s) zW5qK(&jFzh;%*Tw^s5q&yP1~w2{v@X|zeB%`P2rcTgF@-9ddY#NeG#C2>!1HAGPSe6_?!rl*x~ zmtVM=cL&bj1f>#el6So<5`g@o0*_{$Hpmc8keV8+@kBnJr~ExqG3{D3!vu z%$u%*T~@+CJtbkr2f9W#>o5Xu(ZM3OVh*Go)18$V2-bm9n@Smt`WvHt4N5lm9PL9k zIG6=zc7Y&*8Bdd^xr+f$la5So`QY}zjKvMjxysz;!(24lJu{1{pk2p}BGx@cm|`Jj z8Vn-MLI^k5 zWCZIrg`*d+<^?>D^)^0$mqPic0CN!`tlJhz&kU}-O9N)@p%r39Uk>u|w#y(NAk;w} zr%-c1fCda|PQz~_F9cADHMSur@K(ul6QoKd*F`|82EgA2`Ek2ft{ePt*|&ZgTQBe* zdm;wqn3j|G9*)*Yq&+g+=b}*Q@n+K8O7|kWJdJTE{&*Q0`g|FzTNn7ik$3#~K8;?- zjsmlUiq5U0GxM8F5u7+nu6x|w>$=m+-MoaTT7I}xP z=dj5dmZieI{4F43Ht^C6_R*7GV3^$;F%DhHu?)(#QzpyedZjZ#TRI)Tgdd$DU%d%_ zy%?E{$KgxnB&=cTLYx?OA~d7Ld}G6^JvgXbybDZk(pE1T>UX0&^> z31H^nE1)a5kePuR!)L*E(=kT!?b(l}RqN^;0HOWLq*Qq@q&ktXJUg=RvWQNh0<|#TX!ABe^x4mPKe}`Y$b~=mM)&3M+h##`Nkj*0amlHaDj9tODQ@769 zOLQa%dD=f6vdmVkb50V(vrkpDM#_l$?`KG#;v9RQG5x?~l_bAb75)f4qa*3Z&m#`h zg}cRcb)*5=q$4lQXLNdwp1}gfMNZXCP;;9c=A7oDkqqL^yN5Gx=#UKrJYPE-AL~wB z1jkp2=kq7{EO5JKO-?9B;A%JRFjl0Q=olW&U`Cp9l2IQAt&$<6!^oi1sob_T?o_14 zNX4&t6MI(5q?2-E4mKd8w;JhH6$#ohSo$r*`iqx??6%K~`L*(H9TwK>VVi`_{tA5_S*?l%GWh(0y0}gyf)1}nIwDS&_B1ZAjp)l%+TFb7 zIz78j13LT>UYa^d@B}<9L&28 zq+5^5Za|#iMyzLVLR{b`MzT!A1hO3(p=E)JZyp#9ic$baKAgV!dzUq*Tybn*g zX%w{m7PJEvv`$>9_Yb0>4&fSljvunEw(C{4qm;tpQdd(~^B)v_1-(c0n!~b9yK&?` zl0e~Hd@b6v8cD+{5&02rM)S?#h)}jv7dd*$0aT(BYLr8=s!B&wYaw1ME5kjm78T@R z5=IGqkgee0;CR5@Xbv7R-lHgT7)2&g+FXJ8;qDiy4qddq8xQ6o|312m(VsT2L0qrw zQdbG7!4{mbep#?4wum2iU%33*AIq8`A76u`GOFEGgy|9J2YR4UxCI03C;M2}rfH zyJ%CmCd|)1O3PN)geRzRYjIuim#Fkfs>BFGx4%xyLUiJhu(R&6cLBG#YSB;v?qW|t~ZdmN-W59BU@G#63LOK7MI;0E`jA`eJX>;zuTr~;32 zCdE>N6m!7FO#Uu8DKjIR_lr_l+KK4`IMPjDhqBhQy<&++!GUZ$f{1GX~sSFyP*fp7r>Y2Hc(0 zW{@p=z}<;H>mXM4fE$#=I*TJS#NPsqnL8as-`S78)4uwpr)j6jnbdH5>7%q{4LZ(x z<~$gFFNFs_N>PIo$&vPlvhyJET`dnWlZh&~%_lJ5ya(O+z0fni4;tS0W9In)`qz6g zQ#^%X^dXA`b1^8@AgaOeQ``rbSv}{V)pHJ_Pb7_MR#Es>xlgNDgn{x3l&m3$o9B%B z@z(YsV5B1>KuN6m*=Dc^8$+eW{|Nf~M=_i~E-_L52f9_N>ep(%0dwbFs5#}ZeF~HCr$LTqWMNABs5c7ZaSulZ_`RuNJ_6V&JPXmN zJEDHc6ZdYcK(+XfvseU`&c`z;8Dl2h6gf+gNAq3J9tm?aMNSI$1dn_VLx0JypK9yt zJKDMDSYJnPgk_@W@y?DD@Cxi~M}S=vek|(#KwI~I>>01mj6OJc)L0e)AlSPck><2z zy<)8Rq)~RulqsEKxdOhRTxWRuYTo%6)R3*s`?p6N_ocXxI_}kW*7o~s3JXb77)k*I zmXWzvI+8B3i=Q$&%|=l*($r-d!PvhBb1$B9SF3e*wPeo^j=X}opZ^Zyg@6Qr4;(Vi zv0vQrIGp4VsW~P)ooOcbbi8G#GCbDV-!rknUW@^uE)`c76W z^d{5uxIMVx*bge8&6E%F+k@87GPc@^4_CVd_C5BkRF;lqf#0ig;01Rv@cJXqZa>m} zyyIwZ=aKGH$J>r}w(aTa=w;{PCBef|{7{n)&W4Y2&;WivSmuz>sBtDEZ(KWT82T-a z848~GU^0ZIKh<x`E?Sq`SZ3gU3Z=hdxP@?+HtUitR z-!>}-GEn|Fazsey@R^corZ4?LF8W{ZZIFNs8>Ui#b%cO=~N-2+wxwaH)#HCQDL}(d1T_c&n3Ra)4WcA`IjhZyp(Ayc4+zz&es$^zF zL0^?)@Ch`#y1Y&ub7j=&ak@;Ix}Lx@Z0-^QPC< zb5;o>F$CkH0Bm*uC{+MtY|*@|(kDpsmcGpKu~9m93Y(AUsQ{?EKVB$_SOj}D)2acj z#AnWQYsMrML#11a|C*t(^&~sr@y)Jtb74{EKX#%hd>D9Uvz92x0~M}Di%Zd1OL!*Z z$qmU(;1q&|sTB{PcaDrC((Sz27VlqCXnKm^6XNto&Xg3q8-uc62VczA=}v|Bnw*KD z1)3LK%HAwKE$9a5cW2Q`9pQ6LI!1SuvA56^c=+_W8a!(X@AM{x;lelLO5GbRC;UX@ z{w@ZWU^Z26ux<8g3&Fe<`%6gjQ^n(#NKk6JQen63EyM}O zIkosgocTTGygV-|!&$MMC0F2=h?m=*>2H3z2lFQX9l-~McRU}xf`^HEXweqnjC}#SbhvDF=ZUFVPCYuDypQVe??wQ3?i|h2n9X%~bgr7>QWA1^4xhRI z&Ci?~X+79`q`N@g=(hxwR=r>{?TyOMjLoTj9GY;9kEvu!zR{XCjZVy@vSZpR30BZ^ z9!L&iImisl5XqDhpbmGo62MBYQg6J(dzjC|wdq+pUw&RHB|Ai%`IhZG!qq^5WiAG*HpiO-ak36|oQL~*C^zOy zlsNzauyDsU<}1!M&0e%~<<>PJi3IWnBi{}jCOF?OZ+gac@*+}(e8$LotRtf{;<3ym z&5Q`w%SNODh_F_n5x*=2BFlUbQQ4|Zl$?lQla)3gvf4nzk$eT=V~7H&QWAac=HYF_ zQCC1{K%j=IiUR`Dw;;`QEC2*lss&^S$^6QEUMDj#v+!OCxp)Oc`6CDeTHzJmsrQqa z0jxs!Zw~+joEN3)Un)I1N082dHz7!QQ@B(kt*ORYv?`>}*NNoNko*GbEpIMGkJewXDW`EvB8 z#zDP($sq(`*30KvROLw6;JY;Bgpp$s*C_U1nL-M&KPo5$eHC#FMNq9_DJFYxDejdb zHUuWdeYsq&t2xp%3|5roM0Nf*hQ9dE;1c}TUSJ;v<<$VtT-dzl;l-= zOXt*SV^?|Sn`*NK=aZf|NAFfSpzTX0c)RlTIqXM{C}`e*+!+=@}s)_Cj+c{q_dn;O6*7<>r(!ERg!eX2)kEo1M8;v<8tO2Q4UkMt|0s=*rW ztZU+Fp7X_W)LU2%ZXWvF93VKR!-%^%KL1ZYn*TW7<-Y>rU;L0Umy(`FS@GW^{$;XS zfh!cBRuD)}Ytmo~F^PA)Sgc)y~t*2!=VtWNR;v`z|R(_OdC z1T%)*KBX-M8o)&-3&q(8Ud#M7Dz_96IrbEA#^Uhq&a>pwWCr#WaL3}*OL;Ghl?lgK z%2*sMzH%`BfpeeNJ`ASMe;xQepv>Uc&K`yjUnCfqInJ>GhFxr_qVa|quE|iwgW1;I zLe06B2_Sfggdgta0F^y;N~YVuuHb$sU@n+yY>ppDt5$-3^7w)DJqlfffYct;Fyvd$ zbs(P0AU)ZPa|5i?h@NKeYWOPz<#c}42@b(;ikJyr8C+-QR}ZuM!8vipE#5OViAr}} z*0HgT*WO}=*T!r==H$y*4dHj!<_;mHGXevLSj|>uwYdW)7*;l9wWR}YF>$blQ2;~* z6krP0UVOtsx^trlhNTS1bD$0+@NM}9lxJ+M=HKQr9C^haM+|`#ks;`~c*kn~RvDL( zs{y}(r?aS!`K}1FKpj+X;cVmb@$ZNCOd&wZufhH7L;dWq^YtIS@DfnkNV|Wm0a69x z3Pxg0D@XzcZPb)z18cwG0s^E8$JE!%Jk0x3_>5^Z(ZXsa;*=U-~v2 z8~Lw*?JauZg9)YjH)a5j>LF1Ip}Cz8d->48hkbA*iqHYx(W5g$2Q@mxfm2;kWC%SR zrEWUH*-?5lx+OwKBh<_9_3?Mdcsmsg)e{<>gfKWN4NT7M3an_VvQ#<=jar;HjE9yu zlgzN$QBNn%B#=LSh>@Nd;nLQaxU?zoN)Eb^&LSZAoQ8laWj^c9Vd@H;5IYy>2tfBK za#Z5-sEZ;Mcyh!^Io``$A>xyzx7bj?Zw&6WX<>zm)}<5ifu3|~G?C6+>Ku_u<-W80 z5PMzcQ=7^+5q^+@QT`$_qhoQP^{X3*ad~L(`4ARvnH?BL#y*~FG`dZr+tF1dnT8YC zT61_5yH~9|I4!D|W|9^x-nDZ&mIjxDIr>LHRiQ<8Q%Et5pVItgjU1^8GK*Npv2Nzg zdgsjszHvGoygn!%ev`0?R$%W^t3uf_XNC2f;SUMV8y2Q;n9Bz!Y0(A&374It54?x7 z59(O--vKQE5-0`)Wue8N-l-!Y>nV-y)+tV>8MA(DJa6tWAUpF&rZt@yYeg?VlN=n! zUK)~}MS6}L?K|4Wo8XjowRP|9ZEx!VGT0klM^5F!1!8cPn9YHJo zY2zM?E=#xSkA7wq9*{x%TUPxfvtTF0qlq!;lY;1_#b95#C~v(2UbP_a3&0*AJuS7& z2dwKgfaXm=f@&)L#t)1lvoTS!nHgynq2FSC6neWnqwC8vd!VuQ3o${^RiRF}^Hd)G8H&UzFH>=> z>IxNc@Xjpm7ufR_#~3;q=jhguqPl^`K$?uB#>^>Ci@X;=-iwaHG^;RdZ;M330$zsAh==r&cY>serP?K$Y6PZ z*%Zvz-1ltxB(L_QhLZifby`X9kz+^O5mPlQxU*u;8pAo%QGbs?)-@fP-bssWhb2dC z>>5u&6oHY9{S(vKu8w!)dfbs&ahR zWoq|Q7tsr~bk9C=w6mkTudR8@zt{l zeUgRqPecrN8Tc#9-SrUVR16MJ{58Bx#=3!Ha`?Uu|J6J0=izPiVf+^39xTu3zw(UU zi!=KntGF2!Aw~crg!6J&>~|A(MhIU)%6ObhuJCH%8e!Q{s+JwRsHm6o&ssoR2PVHB zcx^y=8zE-igyn{5+vl~ya@?_)xdQUJEm(8e-uQ4sHfHcjxV~9}Qo?nq0ZKr0WXU`W zK=FhrT-yM)=LE1iHvmNGWMQy7CxA7%0hHzdur~()cZ3XAm4$CkeZt-d<(MifFr8Q7 zJZpI=L_PS*cm=!bnW_-NJvrpF5JzN-G58tx-0laX92K;U1KoMzW+%06Q7E5x@nJXS z$S4rr6Gf=Q-UxN@&Atfj=kozO*^==GJurRWpn0!Rf`qTU06B zD`85aMIbkXqtj3k1&D&5mOS(0aWr_@v7#w3f_J)^SnVEBu;^&&A+y!G%PgoqUEI+2 zV)kL73JJS|sxc=641*I#m*2)YZ*DgvcO7}G@6_ln9lI;tjip@5Hk`0V_fFI9q(c>i z8<+7($0k#F=DYo5Dn)OqVIAsE$oXgam z0oQ&$y-ZM0pASD}s89d+Z=1grtMJKIKsPF29#O3YtgSSHy*0Q4m|y!@JiwO`Pbs|q z2HI7#Os9Dats13*v!=;NH2rm-^t)Bo0FJ^3<1_?hTZ*?;)K^X0fGq@_QpJ92U@K@rQ5rwZ*!+N6w;1&U3Rov_C_G=W zHa#T6&=InK0V+#2ih!va1n^=#ONH#mppQO*%P0chc{f#D>hiLNFDv9Fc2%`v`Wb89 z-_yo=Dcp&dVez&cxUTqr?0pGf9ankoKUd3KU5zYRlJ`xvt+1w0bq3(XD3ty{(=? z@BAhugy&d$yz`%vsTjm*E_bHcC+~|->45=+C6NB<7w>@p|NiNhWJU}K=-_(P&OLOw z^Cfn-Ron~x`=^59Ujfdg8$05y2y!ex{nmFr{Yr=)`D;VLm?(&te?P?1y`I=Q&gv78 zNFpK^%;$N?#P+32O(UZvE=4Fg6-jg?tCWf7P+7;B{#!f!)&Paz7$O9ohP38FfJKc2 zcn5dwgcY7EhR!wq??~f~T54f;!AAn4v?oXVi;?;<8X_&RO1CUT|TYAt!3gi9)_0&Qobk=b=i$T{gwEl9Q{ZSI+4ga^H7V1ggw7l$z^1+T~lwE#O> z$`~Lsj0K}pM#Jy5>VYg01Jc^~cxPN*UGw;9$K{+l?nxJ!7n8oSCb4+}cmEE8QV30aM!> z(x{5ajdcXIb9RJ&g4LK}z5?ZF=ctAx)i4KGN&{#D-XMAv$zMU1#URpYA*ZO( z`C{ByYP0KNuhn5(nZx_U@!TyG#=ktuwC{R^9=`!)TGs5z=QGW`rL6E!E5crC#P?!z z;NW*Ga!iICF8rzpCol`^HDX3DkdBPvty7L5j;|%h1ZwgY-k^AtiWVU1#j%Q)p6qs__kZ}t2pQmnZJmteCqH6E&??c~1gOY;K9&ZV9Mf^yYpr5>(O6A4x@LdDL37L6uj47-bFG>TfbA@TuTS zrh!+OfxFYu(wV9US_wBFdF%lyv911?qLwhB+5{zB7oypcTfl;wX_r*)e(M9kpQmt{@)dWu7$h6Sgt{SS@8u3Q;czKj$ z&q*!RY?Cy575?&4y6b?jK&cn`@;nfrW}AFTro@1N2Cqih1ceLgybfAS%?Q7x*tDo`{-<`&`VO`llv~M6s1ABkyC*j3I?nGT| zG-(r9%8OENc2i0Ne;5eDS)2!4z)BDXqCDUNJKj|BqKX&bg0PPc_P&9?d0{w^*v_dT z{RInv!N5bpyz?q-7X zLM|I949aMK|H-c2qg?|%bbMx{XXOEn9Uc9TjvN%(#!@6Cz3#v;D0E2_`h*I{3}PRGp$41y7LjF+PVHgy$W{rXCSKZ z3+3D0tq2S$*e(fUP=Z2F0h0vv0qGF;6Z8YpJG(R} zdKer7wt9S4imt|X!k46kWGR*F4y2i&+2)`eMf&tK5|}sJdIwdm6y?l@OOk|3I(%PL zdGD4s^B}vg2QRV!@_TBb7pYch8-dB_9Ie=>d(fI`s+e_)nL~DE5prk%S7!4LY{7A~ zfOLDcYC#GuC`Summ*=TnYdsoJ`%>^kRA?{vj7PO?dgs>19C|6~`bb*S!AJMlUtb04 zn}GVjm1Nbo67{V{eJfGlT3>zapI{W6{#C2~dFxvqQC}76n~3^gf0$L@2Gq9+_0d>j zbB^^jMa+a6)K_cQ<(&yzF%!09L{^{=+|g$P&fCES7NMu>F*8chlJyujItKB&0=x&$ zRd|Z$8aP+-d;ssSy|_#|!Mv;wS_uBpuP?mg(+Ls&no&W_1yx}t^~I4!4ybAB$*9bA zB5Ic$ZR=oImQZrM0?R4F3H1#f?dmqZOW0aG)>Wb@1?k6CFpkA86}L zazC78%Z|>rqb;4SNvXxv;nvpn-3V!u~~m3*_V@h->_}ZCV(%KJgKN zA3_MdgoQ$f04~D>;!YhH0W}S=O9rN(d(HdY58Jt4j%&+vU4iQfo@)ku$0k0R=O^O2 z+H*|-N3G|Yu9uTN*NhWFqgTp&xM+r~%9MiWRk;>Tp9Cq1vj&tXO{-Rz@|&vjv+*}S zvt`@sAR*ze6@_Ac3rBm>x3>1C&mwq&4~GT5ifhZTD!1{lmxp~k?C0SC5A8fq@0gS; z$*CXab%!aP^!SI1US+%(Z)J@9rM@M2+FaDZ;7WS>y3-e7!i9i3r5D@uMe-uPJjQ## zM|L*ExRe38QajV<2GS?HhSCUrG*Uo`$sWGeErWSmh_5Ens3>;q-l!LZ28Ieu!J_Gn zJoQdb5yI1*Ihfy$(1L7Djfc9{4S=3COKuRG^c|sU*Lqn+c5`PA-88P!lZQ!r%IsX+ zR)|{52;2r)&U9uXj;etuv8|AsV{l_>hqdy5oOtI2i!+D!lIIc;*NE6Lz)|7mb{3#o ze%-e9kcwL8T&!UTyFyfewZ?(D)*B;lT`)%8E8~t~6v6I@3}TBRgI`3pPvxZGBN-eE z6EuXsSKA0%z*9)U1cw%BNWpd}zW{ovqiwCnK%unl*hg2*MHa|#fLuSuNk$mZ_^y`? z@wuX4$DP4d0*JVdqocj4!ptzN(s6nAs0M+`q0&ZkdfN0W!FP$A1mCVYYi^L7puV1J zpY-Ng(raC%z?vQAb*br}MYH6#!;Olu>dESHv$;nC0o`M4y-f_*sB5TH4(r#AqZ?mI4~!mpF3u+2e3Cp0OPD8u$4+v zrC)%4t7gLt29$doL!ZFJbY8$)0PKMXU~1x0vJCM0$Z#6-q=l775k)^ZYTNGh&-ymIHD6h!?5sy<<;}b6VT6MK);G0 zfDd{Al<6r7b>Mn<%V_{^?U!Uiu<*5>!XYxCf|LfKGbUe@je)K?p?cZHA#dc#Akl|z2K-P8!D^4n-7)-dr^|$nXcWdsb`FP% zq|Hvkj2e7L8Sgm^Ces-P(zRyU z4O5U=Od$-4C3TFijp!#N=#i()Dq)#J+e+j$GB3?k6H|zAGIp9jf0{Txgy6Jsc?B4Q z#ozKUia2+`WxKkJ7vJqLtl5F;3h^2LW?AmByV?|r>jO=zS*#@32JPlbuNZ24s`=F* z&FFcIx#5K6rMXu%zaHun)M`EtN75kkapqIyNYle|!Cnwv)%?7WS*=u8Jmb@{!BwIn zf?JB~WrNU7Rr6~LzqtYu?qZulr#;vMsGfY+mQcr^V7NYBr-sR-JuTKSB8cNWVOT0P z*S7|67J?*UIM@;ps1=}bCxJ|z1$uJ+!j2|01)IS83KZ& zwWd$mzM#`kl_vIBm8REP6evwzm)W2Yol=opSpZ64{1*h!8VZ1IB=bSy-lbE`k0XO7 zbWZJUp28)W+P8++4wYtB?FT#n+~zYXvr{|AL;F0HYg*N<{Yp<^xAyIs+Fi~7!M%h1 zJ`!436S}(JZq1d!!dHhBE`m;uqtlw1FZ^y#hNdl(+n z{B@$h7^Ol{ehdB}gpa169EGx?DB~UlN_Ia<*ZtC-B;mT4u>`M_!%Tr@h4(>Hj`HGI zQm*2itCMn^*ViQFTAr_C=JiaSNXiX7cO~UUQ%(|QH*cm>_%u%OaGIBAm;yb$P;TPo z%}n+3aF&NY1Pn3-!4iw)7E=az9yDdhl=D8%twRB99-9b?7$?w18RD)GUR#I9F3OH> zVWh{_mHByW1u=f^kqz^qNn?3x#dm`Zb|yn_gt``}&2*IdAw2+VnX%5QOeL3VU&U{F zR7aQDLn}DIvG-F97R(G=(~`U7Zm=jBHLWH0$h~2#GK;;^VO}kHmE4XC#dKP7pS+s4 zUt`H@nQb$pmfX)P4;vck=XHg=-hznojh4Jg-fYQRczA$*yl<7aS@Iw+-!AX4HZOwLcKiE@J@@Bx5PyV*0?diLDVZ3su<)tm1)(>2hMzU6$n?_|1p$#ED8K{OCz z_yD91PAvzgGkkFrGT7>JBz?k&FhEeFhk36>OD;@##=sJi2!tVQB?x75LzZ&;rze`(#uPqY5q1+9iO(mG;iN zJH$H=zGtbYrTk%;pyOwz7&H@^6=iRIgn104iI|#|2(MX%B;4f~X|o#18iudLd6H9n zGB51nQ=Cl3ketFyH>Bmu*|r zkcM0fDadJXMYlj%{4xytoe+WCg`wvdYCpWYm6i~fayJAB+{%fmW20F9bSzNtx>OkE1J6(Z|?}YaTTN? zSEDm-H;-q!c^vWqYO`p*%ns{rmPz85a+QgipdY*f0rGVxJ??_f$wIu7MOs`FJ|F3j zhxw>9j61U-z~ON5C0P;!0=gU2U2R2!DGOK#26+o+{Ojc1vLz@Bxc!mT14-zu_+x?C z)GQpaMzT@7h5^?$!MhMUc2N(7h$=O9M3oeWleghb*us5ltLaMEBD`Uyd-g;a0GHWlybUIX#r<79IMtl)?>~(#tGeO*Gz8bNzSF#!1co2z z>gc{X%x%x>Wj9t&y+W|{ks7)jc%R@ag2drPup?}$JAI+&WV)^I6v8xb8I}D#H_(5f zryFjGO*=E?u%)faLx*Vh*P3~s&=n~AM%PbqRQMU|zL3$1^x+%`)Dzd;F|XE?*fxu6 za}o47y~0ox%vv$NM#gnVb67OTPBHU_V1#`UmV$nlw2N^EDnm2aYmw_jl!FmpW{k3= z0+ntDY{nn9T4Ohk*A5KS=N;mH2a8nrmBY$78>ZO|xq=vBjI4sxWTviSYKlw(?%@u$ zIOpyN$m+1Y0wUkVw<&bH?YuYa+*}CPt_boLis>9SvYiChD0?w5WLu06W0d7&^Jw^Q zr1Ibx$+RgH6|tc=Syj58-PrQPJAA;;ftXMpNqSicwJ&wjB0Ra393$^@&*vNY(IGm zt=+p4XKL!yPP^J-tq35T{`8=#n-o|lX0GlJjT!YYEyuG?U#BVkpdL8QvM@Xa`|L4C ztE4%at+8T7TH)6388n6zS)ho;6`)f!0GIOt%kVggB~&YQ;lrN7C3fK?>STy|*6tQY zB-xP*uT`(B$bS3ecv#g&kb)^3s$>gOI8@12rV!1dO13f8VhWfNP@NUB6X7cmxgye8 zAC63e$|~ci4v281Z71||5a(3D1jKbSb8Lf*kxA1$t)E_mlx^P2)tKy%R>vg^T>J&P zCt!h;9|2u~6}uzd*R8j!RlATuHiIA|$fXsJcfM!=;Mj2jqFM~?4&z|Tal<8gCj#yH zsa}XAf_X71R4j)bYS;E%J-rBg5~P2|Xwx)b+DN`kWAt|%X7MnahrI&-1o$V%plij-0T9EeZ9`KinN$fx!oja#s=3y!go3f@~_+I;NBJ}og9 zdH)#KZ+9tp&?Xg1XjQ62R`iC@bjW028`p1>sE(+W8)rJcoNgZG8t&7-)6K)w%gWPE z@}v_Oz}EXba27C|G7UoNcHAdju-vxbLHu^Lc{}zmR~v3VX>Yjsq}p)PvEb2^3Z2w7 zrLq*{!!q=4IeOHD?_mY7>q_LT0x{%R>MU3E2`=#z=@Z(AX#WV%07z+jkwmdJQz2}P z^w%jEn)IOi{Sw?^*j*xfmYlBIkA9|TUPA)RDM?Wt*aZ8zpe>MX?Cw6k#ZwXDxT}h= zK!gK;El?JK?V$ixhXcU&o>>5Pg#uU;4gmH*SpfEi0-&owks2#6fmE&uB)9(uJ%vl` z{_6>}-Zy~|V;DnBV#%3Sn{Z7RAQDbhmKPyj?qR3^31Dg0COw_S?4u8z~ZKX7B|=1#x}Lm#he?HG`DGx?j((E z8st=xrZx?7rd$q6Pf~6&X=YO^y(W!pj!Pde`Z-O`@o)`|22N#L!$xf2QzBL(`Q z5aPVjlLKjZKhv5u2hUfB(Ti?dq0bx^uV885@-r%O{hn<5Fhr++rOCoNZju@Vynxs2m#|(=`OMvIZT%P zaH0E9#5Zh<71h2Jz|q*0s8DOXDfd}GWU%kqsO@|1H|2Gfyk6d5Gpm-oQQl<9o8^AI zhK%xrHay5s8WfuHARVSgvE;Ypt%UYApF$lG(hifQrtYhxJCY*HXwAnX?y8Wli?l8D+K;DIU&46aXm%ME4;PJWcsf zkSDz+Hh%TLNiW+Ly*I&_0$*3aE@p4X!FJ~pSrRh9QNuaeCk1gn0Al7dU4!jV17n@* zQzyT;-hNBkj7q;mH^iy;J!T^;TNLAcXk3+)rhK?CdYLin4~i5oO27TRip$j8q~;@O zP2uuT8QUB>JZNUM60&GVPF?~fLKCjEV+J|NIJP;2rEPO+0J9eS)MUkP*|s^_Kn0)- z#)hKx$$gG(4s{DpOHvu+l<@)vIyAE>cQU!lEjGMWnH5f^ZKk6#E1gUl>>#L|%AEMT z%*ExzXBctgVVTreUGuEeUd^nhXtk5_SgPDDuS9cotY`*<6G$?WkOx$Q7n%h@{vrtQ zS3&x)8S;nS;MA`~&b45)`@w154SB&UG4`**sB;jte&arS5YbO%l4G#PFi+V6R+DU~ zCFZQ!+S+;u!m&#bT=bM>3&-asj1ILZci7`I1-3QdnWqlRG^8w*qQwq&&PCn~q+ptZ z)Xb&TFi@J+{;bSK__;ZoikDrIx=kh1N}iFq2JC+4F*VPS=Vbn-(rLxhN|#M5y(IO| z$O5pLA9~^Yjnj%R$wJSdT!6XrA~c3?c01muU^d0` zL(d2=t!QVkWvzWD5%U?=JqOZ5XZpLb&r~k1r(kqL+6A>iTOUHH@96Cs988193$Y51 z8<;q}^;LCveF=;p|8(S(1ZETGUoqw=OtSDx;Tg_8M1t@~nA>=3`)6*$v;6JD{rq0T z_VYcxu(1ie4A>`3gSj9#ZJc4sOzy@!iyJl0Hf4^76RP1bK{@1OMC0Xk?~mKN451Z>F6;C!Hx@T*re}w&8u??3OC|*4LWacOF7*>+@wocQ=opO;*jz+)6Rd* zh1_26h|Kj6#J5`gXuj()E=fG)M7^?bLmQXWVY%ccCk`QLf|8nuD{wuqM*#Va^cpN) zTu4)DvtT2$TQH$$Qgnd_LP|pbmWBJQ4Z)>jGrI-5g=c#Tm)O-8Wz<#NH<6)6Ny|(} z-%K=y;Y*WJCzXh&OCR*C75=f0zk1i}u)6cN1NZY<;R7rCVhYaZa6zxbLhi)Z^N)xuKWowtW>z;b>a@!PcpKknoFsGPiRJmi@in#GeAGj#G>se(My2Zl`nt;g!k zwH<4I5!U-@P%9TpGRRbY{jtHv{t=-(_&bj0^I9<@1hoMau#MrM8>#bMYRWR=gT@r) zH$1H1VWr8Po*wS9GqxmhZ5iP~ykwG7Yh>UPmzI1tgfAeX;biCkln+Lp6uQ{H`vwaM;)9QESV5BO5Sgt>*W$g~V~cnu2IGv*`VN z*#%ozkj*)+3ZJzRUyrT%#7>7q)Row7b39f(Q#R0-Vk5&VZ=$W?=0agp0xJOf3LFwD zT*`aH*_T2N_(KkuvgLBPzGM5uLW&Ra<;uZR7b!o43eY8c6&9>wd77|-TboWro~@wK-Vy}04_Tzf${YoaChk~X6L;LW zbc$+^Cd$g}_OL|^`$nRy40@Bk-!|#aJut8 z(R~u4`!W}D_VA^2a{*x*bQoC>Ai9T3czFy67$usCI>wy|i0kL#n%0Ea(*`nyY0y<% zA$372^OoVhGZs`l{2f4nnCop6W8-k<0m4$zl=CTDsE3pbY7el2T_bLl>6MF))ubsy zFow(9#wa>)e<`~L4ANvj#=%b70NBf4& zotyHlv3YbKZQIWxGuM@WhSw&<8ET(QXzF&@`nvhVFBXbiuDc)9AV3fOn+vbdA)FCV0nfQiIp` zl9>+aO%N$uq3pf|`NZud4Yr%2?v2KVXZ!Uy&43(6Z|P33u0Y=gOJS2k z_bm2-ovri<6x?&T>lC`Bp9lp~M7#7Bpdh~(h&Y02Y+ynY z=vJ&S?Uk~`0^QxH(9~}ZB@_}nE)80Pj*o}pxY9uRqjw-#ZG=MxjVM*)AmRq%G$I|^ z>Cse&J3b{8X1Eq-BHRhSxZY&tiU7sof9GJOt^<_0D&m61fD^!W5<&49Hd-eV3u z6^X18P&LrcaV8*6#I+ZB6+NyVU_}}yE>IO{ASHGM8ptePCyJD&f&mYluGIks?T(1l zO3_6VNNo|Cy*P-}KKj}R&;J_+g1>7~AkUQ{N=E4SQ+p+S#G+zJr!Y1L%Rww~<4cp_ zq*Dwhjeyc7H$d6Xn9?aZW-_L96}MAK$<^gB4?NDpH82lk&UFNU(9+Z8gefLpLLzM;XU%!jAMObV~XVT#?*r^C0g@aRfN!qQ5}iGW!R z2A~$)dqxR~RnBh5!(etOVqN7ZA)(bXwMUovj3jN3uCiw(MUO7?DLRN*9_OWHa3iWv z@<5t@(T#&`#VTb7Ev)l&FSEklTsCX`wZ$D|di zgY>#ugRy2nl~R=43eT(M7@VoN=H^qyfU*J}T+JACbR{zo9q`S%LJdTR8j=L8{?ix$Lnf3GU;_Sjb2wPRhVW@8K3HQI$q@rt#}QebSd35 zKwxL}jP*c(>UJhv;9@{P=UY*`cZ<>0xF)+j9sKJmu-k)jw+sc@?H?`u>5P?_CH(D1 zftZA)AH8@rGJYwtfenzpl9C?zswuxmmiMb=az=iihp(CP^`v}*$#0g+AIP^%`NO1K zNWnY*+r0jxq}-O2JCgG9q`V?2cP8boq}-mAyOVNHQtnO4E0c0xQeJJ!A1CE|DS2A{ zBq@KIl5mUuXl`vJxl*tt0Xg_O|F_mJfoGHsR zDpGRTn81rlrmC2l$U`+o#He9jEiWgfjLF87lrhzqmNKRrGnhBiG-f4@*-Xw!8Fj{7 z9w6*LW6Z}3FzR_&U>XZeW07e<+nzkv+ck8mf8Z=6SYaru(z$QPszV7oBy%?uO6L*x za3ABLlrqI2rDgDc8|>-tQ|a`Dbl*_tZRgU+A3`*4#6moaI0FogSlZoxwyOuxQp(N` z^md#)lRk@wl3nM{bquM{H0VjU0O=3F8k6$xAqU*Y(JkuQS7w=FT7qn zI*155>ApSbp3`UGwrwEjUb)UX*3&(N_fxL>uzdg$n9S9I{%#hwLr&{vn*iJ1#kR2m zcMRyVeZK27+l=w+89LvMq_Gp->Fw`3ZC|G#*U1#I<=4!oBS3N%x}8N9%33s* z|5m^?r`$J*=;G%PFPw<@N8;Mm?XAv5}eo>71o zt_|4`v`vUVk0xO^8-Bd*2dKY?&}oH;5yeC5D}ND0jGxqXa;WD*&(LjZVQ_-K$MI5e zqcyjLf#4*?s~+qb9Kyit#A4(E-#+Rjb%7rWg#qYQY7IrCdl&BL{+ce_BV2(!r2%mg zc7{IHbNc)MmZ<(d7wqT)P8Tdf*k*$O?7~Lo=Q^u{{Ese%a@*FT3ppKYHjO2w(O?>l zrm@sCmYK$KEVSx7!J4}>eX8qxFVMlFQP?$SJ#}Jz@k>)5gZ>2k(LXZ0U5V{CoR80M z$>Jrjtuo}mSS)c=6JdX&g82ADiuf#PCQuls9=2WL<9g}(=|hHddV9Ed>oxLP%Ez!} z#;VasPvXD9CMQhtAz(646?V*XOKy^zO{2*&RkMZY5 z4btU~L0|!~jB5$`ZbH6}se6!;Azof@Twxg}m^Xk8UyU0qqs!QC8aG*H~{EXARKFI;xra zuI&Y?F!pI7;@TW`R~Xge+%wi;NYy7U)@v!Ym>zBXT%bXu7ek8e77j&i*q}Oo^OG%62#xUM#mBvopFH?R31K&Q@spz*%39UE5bTEx**99#b6DQ6 zUSZ$DF79pUqx$U17$)9t>y8*IWcN*k0wDoIit zml6sDZ74p8H)2TBh<(|CXM6xqurYdeW<9PMoFlGmfITPC?MeVr?5M-?kK;Bk(@@Vr zQ2rpI`%ZV<2J-)GhO6hqvKIj|^|4(ErM_}Y$*g4JNgMt zTCk%18A$`cf)L>R-0u-L;1UQ;iz}33$Rn|El|ZcO5ZF*iFZ81x44``lWOMTTzyM}* zCxkW!dU|_%un3I0nUJRC+O=5b2naj<{i<&>ZD-rk8q0qPJ(2S@pX>*L)i*?vkUOl# zFyPOsw%!XI({5Dl$72cM~uaq_V=IcA}i0ynBg!oEQ03|G(^8cM{rcmu0jMh zs6OK<*}V@5uL9VGYC`xq(xu?z`-qabSjXHybQ)kd=>I^DPok63iGbl8527RW^pr+Ir zw3Ir7u2E-DZR!k~O`SmjsWZ3-2+i+DI)PmXX{L-Qw8(iQa&CdNnWl3yu=`D-;aMqy z`l48c(jSIjesywKJ5?27e`%@!N}vWQmtq5+Xa=^+GAiIcIRPd zEQY)dX>AI}dk^A@)xRCpvij?Brp9G*u=+sF)hH##M7!o{Y{gZhYSoY@+qJrNF^6@% z13!WOMXRzN#@WC&*zZD@S^Gtt+4;$hUy({!x0gJ_*kvEWhDVJU0_qcrRlk+$%76;P zn1PySdHUKVy4Kv%IZ3~??=zFh2&5tOEiK=4zWxNM@h@PukV_a>I#gEyNYODBZ zsdz>vKtH6eD+AY6f$NEZ>*~ODP2jpVa6Kt-JvneaC2&18a6K(>Jw0$e!+%YiNj<65 z&ZtHm26wJNur4c%`LfNZmqW$^xxrW{=Zr-#*jS8h%a_QbMuR+VG|F?vQu&s#O#aGP z?wJNZ&YA|y;WW^5|9u!}FkcvSzWhgV#h1UadKNajn*ETJ6o0IGj+?A=lXKnVyiBs_ zV<@cWhtf1cCs=N*mdVB%7}>0a-NrgZYgq4@D;w?Rp-TB+;M?FmHk302ej4Q|{GpwX zVKS=Lmi!YwJy?b&)?+f1enrX~5NB{cK2bEMUhUJh;CV?_FZ>W@&Z1#i+`x#DOD;*n z!|V8mbXrla&Ib{md2E4HwmKLCLL~N*fq@ za@(-9sKoYR*?}?$W2wuO^X^U*C>oYl+`tyRo*TRLjor9`jc7eL_UJ0wa07ubJva91 z8~bph)^}sSzHtCIrulBPVo|g)qF3D97VH>q+D0cNQK5*n6FMI1l*kKKc4@v3Ma?P+D2P8x_ z)sQM0pO$Mc$#ojm^|*x%c=fGh^$j|=3%S^iSLa$!%Z-=hq`uSbu4NBWrEF}*`n?6~ z_f~}HX^~aNcFfgQX@T^i&DbrS#vVCg?3Hc^8*YM_VbC}zFN1vH9!M76XmrTKMyFhY z9O1K&Abc6JgKuK(`a1*c3uFetAEsXuKC}{b!K#HRF`= z9WdoTGj2+h8aF3q8oh~i##!~99M)6Z`A!n+<-_vZ_yB4X^W`J*QGBi~#xLY!@^LI3 zHyPiMPsk_n1>S=R_P9KO59G7P9r7vpw3HgJ!JQ{%7~fI`CjeUJ8%sPY^_4HclDh~N zil$sLq5OoB`pgTc%#)Q+cb~5LHTQ~aEPerQyQP@|$T8&^6WUE(5-29*o(I{AD63-O+Vg+Q0hK=lxt z+Kp(znWP%s083r;txSZBTRmvF#I7Op<>AXzaElH3f_xD`G&=rWe8oETCHXP{C**nT zYM{e5oxxKNz#3zwd`G^EfV9)0lJAx>e-PFwOTS&&17}12no%&C8ODawCvR@DMKyk@ zyWbK+zLA1K%m>NmJc(9L$mxgQ#drsN0`E)Sw6}X4$bm0 zrf_JMk28fsvwVUn9Gc~mOySThk28fsvpm5R4$bl@rf_JMPcwx>vpmTZ4$U&m6b{XD zi76bKds?!UhDn);hBU}bMPbm&A!XG>@639mpt#Bd@h z&-)&CMfOSg3b@7Aqpj_oCpxb_)S84fht~=trolzNE|ceTu-CZUwi@9MpU}Jh15cn^x+!DKP|N@;j+gjW!7Q5|O^kX>VIr`Lu}yQO-KY}#`7zEOhTAXH`Np~OFGt9r-* zGx8rLPtK@_DI?0rZOiNnWy^%LN6<{U9wl#s+FT04J_wZg{7&0uiVt{1qm(N_@Mp`` zs&N$%P;1GgS1e|`(|T;C#!+_3en$!>141!8I*OQ+lFbf}VHE)!a*fzLR~ad7w(OWA zs0K+(SI53{Wr@=21WkKP`6?E9uS;>3)_n|)#LyurBZKq+b+f<69Z`2$h=hd0ejX0+ zaEOO1c{t2NhitVFwI8V?JRIfW81t@T>S`X2^Kgw^YhgcZ9B$;L+E=NYDYd!MDW=p8 zOOQKK^@~G4^B|ov<@YW5ntUD9))LtVwE{E*sS_l*MD8+iHUJ@T746pLs`vC&dzeUB_b{)r@-WS^@-XY{ zymFkc!|(dN*qObWGco)IW~Y6_?HML(RvzYVRvxBuRvu<{RvspLRvzW;dPJ*mz6tN^ z`kmlM&hk*r4`PJ7_)-G!W`glK2a$6E)&yd_8mt9ndw&eEveD2u+AgtAN@^G)H9Thx zLqLNwq(GHCR}F*D!nt}_YIv?4mPtHM9+oLQb9)w^r(;VNo@WlrES_f%%N(BThGj0# z^M++U&-KHyfait7vWVx!!?J|uhGA*sdFik$<9Ye8H1SNS9M7wUWi`)hhGi{fIYmm9 zFaZ7tY!ABL4H*WF@_qReeei0(l+aP7R9TJo~z=gg{z68UOuft^IJ1`a*LXBF!{l_SE z0aqAil)H!*avRcGBF~)Lk#h%f5`bU;!YgpZ%fZGnj1LDF5{kVb0b56AGNC%8m`T`( zuo4ZZ%&x>h)u8~&LIB(s3gE7A0OcV7?hgfUcQ}BG5CCrs1#k~cQ1loy*<(Ovdg8|e z{eQqyxFpm6SBBRPnP=7jKIj494&bZ80YGM(1>oUO0QZFhfV49Uz$2jmULD&1X~F)# z&r{g#|7*f)pA}O3V;%r*|6dypU`_~tkAwoaAKEkL^|`W;xzq!Ar|%~`h27@AKD6-s zVBt@N7Jfr`(-(v^{gMZO+xIty16ULS;4`5B-V_c1vgWLJ|M^e=Zw?0l#XuH-FNOkm zOE>_iF0ueT9}3`sZ~#pq0DeCd0JhxBynnrf@$+5z$-w*nrl+v`{@)f_cy+Mww?hj* z7~b@?Ax;0T2Y~zj-yRO2C1hPyi2w1ArDI3&3B80(dwa05l|7 z0RAonK(W&M>d*HeUL(na2~eu6e+YWB4N#LbYMCZg^coHQOhIwt#5lNa0`$BjKD4GT zVQn+93u+OxiChHUt0+WLUhl*2{e#Mpj)C8>GbM=B%|XAi`+olCU+t_B;qNdYj7fqS z<@fIk-eOEmfe*Qx0q5_5J1U*b;3D?Ql-wY%O2NwW)k%2`FJ7CJ`+0sH53f(bPT-A6 zc@r<*oRqgD}7Z2}F$|Fg650j7b@ZO}n zkLUL%Ie`3x_fP0D9^{#;T%Z&HwXpFCFvdxJ0V@I@YemxnL$@MRvJ=K)@L z_tE?Adigyb;DUF(e2s^%^8h!z>*bp~`~eT&;=_N))VF!~BOboP!yohTT^_#2!=Lc* zr+oPPO#K-Tf6l{SnDPTt{*o`_uT1$N&wp*o-z&VMpmZz zuQ3U_GFz&T!~U~Dub$GOes*R&Hl|J81i;!Z2*y>R;c{S7oTd#s!jO(lXoeB@2Co^> z_iKC|{2ghBA%zhyUY^h{-^{kxiu)o87TQI9QKNg(sEHDcE8!JyW`}ZFkntXg_+ae- zE~n(Q1c!Y=dFzJ04)Pe)DAryZ zB9W8x2>o8jGUis(nizNs`%6`c}i`y{)Nh$2z`xOakVlb zRDI*aJ+?+@Z`4-g`RjW8mh#kpj7|F!*6g;ZX4A4fPc!#CRYhUV-5J$fz9*0Qd{D16 zrVZ#rOaZW3Vpn=r1d`2b-)+wVD)&@G1Uv;Dcf>>RB48K;Hfm@8dF4GG3T!8y+lkO6 z2t)}s?j(LNjDxUMvx&gU$T@?nkPtdFDA&AXIV7=;NV5bISr#vY4EC&Dc=QlDgr!61 zR03ud{!vJt388~kNrOuKCB(tSkb!@>vAP6-RbW$T9&4;t+hgct3b_z^n%rz1R)Q63 z2X^kOZrPdGS6ye?`>M12MBInnC6hHs!l07LNl3y9ate}Dk%ToRb5%SS8`ew*)-2pd zlosBfjU=RQQ`mqy=RP-Wud@ryb28^6bDo`9?_@4OX1$%c@DVWa)r*j`@RBShz$GfD zfhju&Oup{UQe;Bjq&mLL$y|<1$dFWKlasjunUD*q%#}{&DrBy-GgmvAYmm9x&Rpwc zu0tkdGphLd32%ovY-9BXSr{^j_9yUC#dyy6T`0DS zAki0?8!pFjDx%U(gB*T3Vi(OoAQo=SJ{xMUIZ#~9g-&uF^gZ)2zv>}}UjTvnLdf?Q z!Rmeq1QQLA?Ki^UahZGpu)dBEK;Omm{vqV@KY_;JUjgY?um&xWjYg$xGNxn8rh2); zXu`%i*zg^n!gixwcEIAZ)i^1;j6T_8T!hu=y|T}Ez3eyMF73vna?tpwTnW3(!^U%R z1g-BhzJyls8&FY~j4#{Y0H!IFHUp}cUxo@`2tpLj>giImuJ%7=-<;aoi4$wpUrkNz zc5EUw9*~=Q2RznuGby&6`l~kz~q_Cw6^0p&2L8W#e1%qSc?L-O| z#z?gy1t~gGyO4r7eD|>IK?(YF4Rhkh((Mz1E#D#V{(p&YmzAuqBT`8=Zpcrxv-hA$V8}mEbA}M>b=c?83Ts z2`k+L*h*j+#4DjnQ9bq!S*E4WE({nuw*Rws>b8ubfxtCCvaShbqH2BS$uQ|W|CtW~l>W2qnDh0+8U?Z^XtWrJ=OZj4! znbKr(kzgK+ zioswk47zRX+q(42{Usv&U5N(^xV{C}vqdDM=9#`-nMVCI3-SFQDCFmj&$;rHijSB1 zbRt9=@W6BB>Jdw}7nJtB*F|*Nd-qTm13MOQ9V3v7aARfy=tAAQdW0-~Ju^-g|v zSi7=Q4BFT-knTd~0)<8hT{z|_j$SMEyGCty+^Bwb#2BZ0af<;98eAAo8~p55}BP5n7SJf?kP0+}O|%M4Dp}0Qs~} zt7V7y$krIs(A>KU%m^=tXo2d0NM&%5nda8H+m)M{gaonPCXC0#6+d$s0ZD z;H03s*e2xi)fLka;UU?cTL zd9x{RNy!89TRglK&gUtJ&tM((puEGRb=1Q&je3|K9`8=Uf(&+1kI18@yf+20?E886 zfGHnLG3)~mA2w+Z)h8b{8DM3l8qdOCp+8s6g-mI zG&-lVRnM92eja;2uTiy zl)~^uyK|(Af>Ydn-xLEm3jnr73|#2|bdum@|VyYHu(kEVRhf-2}6bYT0YA!}lOJt`6z zqRZPb{(vyPMHqh=BWz^a4h(zSjRQU1O?%X7$N9mb{aa6eV3rV7bAwQ-7+79KOuxaC4}!AvNL95&UpKFv?qOQYp;rFXm{|>2=mVg z^Dkgr7!zhTm>&?#UlPn;#h(Yhrg6~hHvN#m{+hu4CWaHCJyL2?*#Le-06!)G*dL7= z&C~{viNKgOFJX=Lb@>Uw!6a=(%rWxx=cg?HGnW5{_|1Vn_xBy`8t7qYc^$l6 z8M=L4{*mBd?H0Fp86=a{yMJcu{)OOv9$#|+&)*%r{e$Uld&K{hKw#!}H7(|H4(eF%?dN-XyXjqfgZz7pII#9?@GUD2G{X3g_#5u}=C|id z_JaEr>|kT|G;CS%g8I)GL3ZulMKtEu@eMnDeG9G!-d6or{5d)j7??W7oe{pKWj;f1 zn_h?!Pud$imNN}E!vS|aWMW6!~uw&x98V$UoEP`arZ~{Ep?tnJ+kjPYyzDl zYz#x5dp$>0s7B;)%2r zz4j=!rVy_^N7(tl_NWST8j&c@tPmrTBkcS~B&tF+B5@Tue&#ZA9CC0bj|CCE&r?C< zuO_EHX9jNMr9X#0$AXAHpNJiZF`V&pnEz-^_R-ii_!z9w0e8g)>_f~R3U>55u8Um< z7sQ}8VRZPNvEk>`>{GG9`i3S*#*N;7V9wZ-P1>i^7#()}sp;!P1~ZLbLq`s+cgUR4 zp~jz=QG~(xUxMb@L)?Zt6XJS%!12i1+ylotV(9Vr>%-v z6IE6n4BHHlg9)tt<%DJp9!vkoj3pmgH63%J=E!V?s?Szq%E@PtH zXiS4RdpaEEI1RK{;|zNmFm%2S#L3ikXkh{^6c`Rx)>M1{Dlu0e6%Wps#xHPN7Byp@ zlO_z+iYJh@Qe~AkJ|`=YwTfA*RaP0YRwHW-v(~CC3t4NCwT@W`aRpeF$XbuA4b0l8 zvZ|4_5m}p<)vU56A&YLQo0)Zm%9@HSI;C!5)>f4@16f;Jwy(}Njv6K2fK zm@#KDWBM>-`Y~f}!HgNej2XlnIgc500q^`)%$VEoj&H~NxC8Iw<(M(Az>|04$-8j} zR$hot_$vH+J&dT{jtxZLfgPRR14F8hAa>tJ5en;5FsAw(c7*yo_A~f04643`{R;jN zJB<7ZjH>gAp@^&X+Kn zwC7ILjDdsW4QRMtpgaza&&fWF*a2<*8oo-U^z;OTtCiO}mpBgue5HT@mtz|CjR*lB zE!`^<@D9C6oo|K@sbF9$G0aQhxKxJEHi2QAhEKOdomaz1@FOtTT??~7p4Y>gmgkN5 zDEM46+_!jrg-lWBt$3NtZ-EUe&pYsm@qQ~@u6Vs0FZm;=rwu*_xb_dgx4lw@Kfq zxN%IP8wJO|w!XgffZA^}jr|0(3^(Izf1_%ts>4j9bu|AJKEsuEcPR!Q#MUVIF}xi{ zTBIuJe-Bf>w_xa(c#6LRxS#KPu&oIKZ=FFYT;=kSLe6|s>P>8KBqg_; zPY>J{;SNytbh|_e1#yUTE9AA`h2 zR1#+~QH~%MzPR%Izion9Y{?|LOvaUGj@CU>mK4UO285s?Z;DOwkJhf(k=r5xtyH3) ze5?=)vF5`GhAxKWG_ZRW@FBMgq#p}oj?&M?>xi7tsJsH%+7WpPmhJ}S#^N~cRf2XR zuo|FW4PYw(bHO*c2d2@WV{^M8mI0qHS0J=sj0v1i5NwBb$boMfT(32-HOf^0)pKLf z1_N7*y6`j?wc691l!gWRWD=+V4Z&r>E=ftS@B&Za5>Mf&`4pZu*23^Ch6_!w*-wfw za1`Xo_CtkY9b(;H*)w@`C_Sv zjsSTTf^p6m#^zuL616r)w>0QzmvJ4;(?BGuC5lnVDXw@ShzYOzpi6-Hh%-at>uI7o zmsxW|y)NmN=f)T>BtZi|Z&&8wIC+Qp3bF}fb_)MNNF{NXf^d8F&G~+T`E#ze)GRyl z__94gfn~%;WcwK+&VVg0h-(lQHH#wZF=e*}ho9n_?*P)|E14$k6Ga z0yPm^4sz2~^v{HFi^SzDMZ}Ue9zAw5VFhD1&p{rd*)R%?4!K28cOZ-5ZKghrecqvr zD{ZVcae_5KStcBC5`CErYPJZ}Ey;8S;+O&|cN+3%;Cec)W~wG@%6TfT*klF`)&UEd zst1jn03}ViUE(M-Qj`K>O|u6i*pPBWnKe~}=!!r?*e|ysb!f;uG^8F?&PPKQL^PxU zI)18*bd$UdnZO{h3!z2SZ^omad`>Dc7FC*U34c8)-B&=Ug911U0yLbkkEo6T0i9Zo z<|V)_tWf8bD#BScaw&PBUL+I4Otw2xr8_I`cY)r_5|35Wt0id z<6(XZ!wn_Zf)r+rmeZNCSc%T9a|8WD{U`f-bp-v;DC&;1tq`PqB*_^+m@W&07MG+Q`HhUA z$+C54E2%s4skLe~$(iYxO<9&p%EBjw1H>8eJyyfVm_QmWEG^S=@HQQ>s|duLhm)|f z$}iB6gQmzi1_ERx)Xi`;OK^}?3L}m)12?$>B(Lts4)|D!hqUf!1(ukVP(-+xa3T;Y zv4&RnPy*%C9VKktkqcrCaMfA`!qFXR5X3=j7p(}U0C8w>mJgVth;o5#1h7pJz}Ptg zqsT}DTa5*s_@?YxDZUypdP}rUhTt@k-|FdF8CH074OnC-g>3%9*v5F|nrEwKCxO-}p$ita^-yk~{moW|F+=Lm-~{Fjwj_ioF@bke zjB^?0$`YRa`0nAVzj(!Spg{QBfdY9=^8lV(tFT)NRWBg21Ac=NjH$)cNG$=H(^@F6 z!YLr{$W{L|Caejco4mT65vh=28Iv+)(ioQQ!XYncp00>JM>D9|H!XeImGPw@oT?P@Fm^@D zm}7cm&?WgA7dd*hE7hDASnbHH)dK%PDut_~SS)EdF#k$gECMN zH7;2Iu>G%%%Ul49LjV+IkOCDgtY|*YP?exBBq_)~mVx1-k|ls3>K_<>+4u1zf5%ZE zuaz=FY#6{BH>A+hwN$IDGi5y)WnhvG)TC^XO-X4^;hjO9a)oS3%2r-%Q<{_wvYnaG zrBusK9;i)$M3MmZn9|1cUQ>x8eL9w-`7OQ?eZrbt2(~PvVHrpN>PY>{_&HwZq+Q7$ zJI2zjjK5=(t63==e!e_%t6wlzsE%;7-Kgm#_+^9H54w-NJf(5Ja9=CS<|q=}I%BF43|e3o@-MN6@h>kz*Ddovya9 zmS1B*Q+`d9qNPyr!j_Qr&X4wD@9W1$$r4wcl$kgY(}4jSmc+#)qXEN|1WV#mljZo$ zWol5W>}&!(!lZ1xR4E=cjtL(u#7fC|s4^dH=(v#a8j~vJHztxa;fFT%uyteLMwAkT zq8S?I?gfoVbpQzm`^_%nwnbG*;WYUwZW^vq6AcD8w43qrI*^Wjb0t6L)7osX-!K&|=yv z%?Cxz9x$~4+6qIZxT_O4g;F||E)y_u0}EJ0yDGF!1x}om$B@KUh4kn2s8`soiN1Et z1&KXhsaIyB*14#A9&`}l?OFy6hHe*I=xx_Tinf-h_r>w@saUu-VU6Zh!e3rWcO4Ms zLp7pD+yenB7WkV^iU9!~TZ!5eWUtkE4Rk`9qo@W^-hktI>IaA&2ov4=ixO&BllqGk=(p?6{dMW-lO_@6(lHeRUO_bxmfC^nR=HfHQmj2L0`p0eg7Yqe)co82l-SCuJ@HA35vu_`;ZcR%STLR22;H%u>f7VK zo#yyG2?9mq3#K_k*9-DBI|f-}QJ{^q1X&Qt`1EK6SrMpCTV_F|GNr%)ij}WOTnnn; zdv)eMwkb5^p#r~{!n|hfF*eq)?uHZybpXr8Q~zzjy3dtg8DnSZ|5ia>x!R*|E7f`~ zJ`V||*0*7f0DDmHEVf~CDz zVVJ9i(6N~bHK+-tC=i6R5JTWBc#0(-la_E-LD~U?8c-G%oR}G1XJ|oZ=%Ei@%3u#&)&1}9T9|#)l`A!Ax1lVIjwhKI?_hqCbJB0|CiZ~Y| zR}&>vu=*6V888i)ld>!Q&r%JyIV$?oruuQVSQ19EznKzHG5O zT_thB>9WhCEB?ugL{}U{zGG7v2ccxh_$6_&^}KK*FN8Txcte-;OxNP+89ht&$*LLZ9BV$x;oBu4WtJG zj0)tF6jVZg2Z5G$V1rplh=$EW6J`9|vRvOr6ZXvB-__SW(BIR2v8k)CuYag(2pgO> zwV&_puh=`JDS+AD-`|_=>I;W-d#NUPnWvc@(5VzcLkU?|e3(*uaF z5QH246e>G+KJ&I0MYgxMtM7D^^R}Uao`6yZ&i4-WI1{DXE~@%*pzE9x3pumL$@I3t z2IgtIor9*Mf>Km4!h6LNCY~7Sz2cE19vShy;+Yhl8Rfm=sdC*M`@JrR94GfRvJ;y| zf6hB3o|lKhHJC~*ecf$+CkNC%&AP7@I=7{}TVGEJsba61)YXCVYBWh(04R#9%dpaI z>Z`F!?@ZgZdrR+3_o_>CAdSyq5VN#sF}v%N1Xp=i8ctzDnnshl1Qw_eJIPYIAZU}2 zT5KmuK+>3PI+EIRJ4G&kRe+GrcHyrm&9ZDk$q=r;ljX9ELRO{@|zv~cyIr=-Uq1}f7|guoN-lOD}0+6NY=#x^+98!`DGZ=X(^29 z^b|CxGfbIj$}9}#(3u_tpvAt)qcTE#i{Z6-*yh^2j=$#?e07? zknZY^v5|B%L_;PH4s{J+k?!i=HPC-{XU`z4o1%=G9D%-yalCbOwM2Biz#sSsbzpF# zs)}QzJ|empm*JD5z_<-xJH^iBIG+nSX*mXGbUa|ptZA7+nVQRG(tlTD_I6KBd5z|$AHdh2gaM+H5v;BEZQr*(7)10-8n zCeQyJ*{0*;uo`veHQ};Xqc%^L-Rc*`0<{WDuy+wEN%svg9{tK#tDfQ;x?5QUaDuXR zANtRALEB=%V}F}+K(Y8iSwzIj(#wLDXTF8cX1fKqZblZgu!+jcNN2G9LVeI@p3BU95A!g;5l3aW-O>yLNizcaUOtJ$^~va^v; ziH6_T)8B-qolAFbzilYJ{rss@;IZ6_*07>H#}vz6quoK)=BdZ~LOm>45m=G{gKiH> z_L$g1thhM)An+X;<( zOZCabDe;UX6Y`vt?Qi^wG(9V2SL=HzeJbZ8Pl>f(U#b#~&qzg)9BXtREJ6}G^a*UB zcF<=!%DMd>CxNJJGmzVGEgeBi7P;u&d9bo`#>W8$LsJDwQ^)Td9C2zorW>y@MCw zI-!{XAbduoszVOZza~2Z=)das|Lup@gTLV~PxQY!fd03afwEc11L9khGrk3s6)7|u zHg5IOSPnzGWw4gzMV4{fD5yR~v_6m?gxmn^?KRoc^nU`nZL+mdQHvHVXhAH-5)t@t`WD%0$u?=p4~fGv^D-mx-DwAj`|z5x zz=wSt4$gfEg=^7q(_r__O+y1+CvR>VGYY4o&nBjpgrnaO+=<1x9LpjI6)6IyQ=%lC zDM}*dQy5XMWyE~gheYE%H|)h@%6d!Vyjn<_81fM%6 z;=O=LoVQZrsSI2R*iSv^C8=ps|PfAgdjm*lhonwc_VysdJZTxLuv5%mlj{;MD z48C-q@HAni-2?`7oQ?a2!c7R*^=m2&lE7X>%Cya%k8`oYa5U^9_98otKR@&O#^qht z{St%0U!K@&O#pi-Ppmog#H!TB6!$(=CUkvJpyN=mSrXJ$8xMB{?0P3mP=RWKl-vv ze-kYbd>TuzV|dGjovSC8WiJ0*mbJeSMgBXm3K}?1;zCTufj`ItOW)cBpb92Wt-3%1^}dnfondORSGom=U-Oe*8i3#mqQoAU!KeA+Q71k2u4^( zd4PTZVP1Japw}`6!mFd7E7TaC7A1tOkx=CSDh&`;Hj&b3%at0U}Ido7;AwOB91q>pWJj;U#Hy{@uRzmGNcYYMntjj4$tF{?#B+!K#+ z%8Sw2xgd-6YOHqk39iOhpM2=Ao1xR?FVEF@U0^ltEdz>J!vhpB@O>($?^8KsPn*i| zbv5&_8LF2QqFHPy$4lAD2ezrea;q3vZWRN|tx}?NN_Teg_HI77hnH=p8(K~g$!Z~n zsZFKkNgc5)mW6`Tw(WV*wt@D&D9F@xfz>k~g-eNg6Nn^Xvm=xI9j}22LPA5>tJkvGUp1uf(Fo?kG zE$&kn{(xRKdU16vN#JmYl^_l%3qv6Vk`xJD84c=$ zj9pMvQ55(%DU#>qqdYxB+b?La;NuI`6Rg}GlHWqYG$_J5pvji9rO!x;PnA=!XSFZ; ziX7K>Dk2})_^epAwrhfZs8U0DPO6@giS53E$XoV|R73BzOy^HX;^7)YE=lc&Ubu;* zQn4a`R*|+drlNV%u=CV(*=o#?9mY)AYs``xjM>s{%#nMII(gWbFCR1N<&(xjH99n= z*(ztD{aS?75{NGsL#)E#e%v0hMx;r2>3d6Yk7jVSQuJ$?46W0D$-Ut45zXx1nTM!h zd|6-_a4uG{yXGOqIKZU*_?HA}v!pZ-wd?19fAqUEpgH3&Ptt6CfHd1xhUPc&01Fz# zs^I|^G>GM$0woL!nkBTLL6kAI$zmA^xK%1Nuqhi%*=U=-XAgDh|Cn-85t~N^)O^ie7`(n1tpC;rX+4`>tJ3IFY2RtJOMKZe z1hoZB3KV5#N_C#J)Fw-sWwUKBqqu3c)34h7S4BdYDswG{nKm=(vI(w*3L9nNQ`lia zLEV}kZL>Z;ZL=xPn+`XO#0UaqwzfO10~k;l4^to>qA02jbi*tx(<1sRSz{?l5(x#ViQyd1>;71V<+Ks*)7|Qw5K^O zc5|wMO|&sdJ<4E1vKMff0IH#Z2F?qN7tnbpqlg*B$XJMnoyY{mSlt_l;+2e+)}#3I z%V_`Y@$I+H2HN2-Pt>#FMUHx4(3FjO9OKUaEvTmr-V!ec)UzQ1_0)J!4}3P1Z_tYZ z`%HAPkKL|9A)pPH2?51+rTO27f08|Yj?5eA2j6tYTPp(l;3Fvr_DKLcgkYaZ3j0hd ziNHQD_bm6LVIOTzkpuR*;b0K^EDymxcLDp{4eWCdu+P1o=H!8WAgst0`~3D#mk$>R z`)qs(`zRiSX26-4IG3C8d9h%hjW1yz+o!r7Ec|6rJKEUCam9>wRWeO)2ki%Ii>)PT%WFn|7}|zx-%K!)CeJ+`q(jS73OkU+6G+ zFyk~^=WY0&U=8eCb+o4kjBe@fZI^407?(6}=74B-z*s?Pa1?$1+v^K7RQD}O=yWnL z==M89Iw)k;>}@mSZjL)>66+#}4}s zi@<7Q*w7xir|0OAzP8qQwhT^7Vz$+Z1NI=k+-0N^k;W3=<6sk!p-ROTk+3;|h|uCW zv`(~MH=Jqc$XLbEXIvf>%;se8sUuN8>v$p2YX?k1m7=fi%F~F*AzC#Q=x+DDJHSk2 z-k57IUe+sTHO}bt{4}tq#hSCeqoeI$OGjhR!K0l}XB?MaJDt^gwC702QAW-12%Ph> zwk);=&pKrZbr?hCjrbfD9d2i9I}fvvlCzss#ZF7pab@;U6D6j=c9A^)q*~h!1k&jm z=qJaEPmc4Rx6TPrVL8|!Tmuz2yAeXq>Qx4BLBnG-XVWsqJ>Cz_i%=Zez&8!~FN$xW zpgnfJxBiE4X2duws4g_!H6ltIUmQYVq9%KMsb-~lIVwcw$Qsup5ikQ)@MiibDzRgf z5r0{%wXEuKSaRAFU!9gSeZ4(vgnPDCv>%54e?LbK1BLw@rQ;{ogFoxDC*DbWy5O6C zgClgOgpm^^$QGU|Iy$tEIm=tmq+wHPLHB*H@9TW9jfV0^Nx)9kcz$# zO+lRU44_t7FV+v>y}L3!kbYwq^F-^_UT2mq%ynagutpc3Ke z!pHI%?_Td+1teg;cVEUl;@zL|9uPnivp6lp*y2sAA3I#zp5=g5)O&sJeHrgT@BJC? zA@gD1dpP5L!22LZIJRion!lG~Nc9{OzCM)kKJ0zO_dc5O9`PPM4~0?hX=Ju*W0jCSzVU zU%jxFU?6|L_mzzIRqq8|PUvz{ms1(@FXmq_tnkZK_>0UW(Yhy`r2yQ zt6+Mcu66r}+GVvF=C-e8ywl$O#8l18)ZW$G*3%c{DXBfy-gl@rWB$YZuM2~bh%NVf zUuTmURYyNH#32+=-{_Yh22RGjexXnxy$=A`c647p0R6tj>K^1Y67z`|PRo`i@wV1| z%<1T7!U;@!_W}F}jk9SA?x%B9v025CyGTOSg*>6Y0Gl2+Zg*oP3Gjh0nSD=-#q{D~%Rc_#I@B^2?hq5@k9BUm{AnzpbTnOH1D&maqb?2xux4 zKY3BE z2S&e96u=PXr=sFcjiWU|E6$k#iCDJieB!0JoX{1)Sc;(}+w;OoDZY{058T$+f$(?o z%&V`yK2~-PUaT+;Sgk%rD5|sLBI~ir3RAPT4j1Y`s`bM3d~Nr|>3V$(3z1o&XD)U% z0}P)bntoiEb#MU~hHNk_@{mj@D`LMen1&3%0R0uy4(hL{nm)k3C8j)tOf0j{xz)}p zv~sd?(JcXeE9cpKK+e0@2ZJci4>B-E%nV1a{U9cZw^>%-8s?c^lxD}7qvjUwk8uPlQ`;(S z!MU%}zS{n>wdQxFcGory`+57z=GXqR@8!2jTYSp7$G*Flve^7{KaF?yn+LeQm#5<> zP$l@iN*c>`r;Xoy(xl{)r}zcqNx`|4muX&>5r;lgHH-`X-(J8F2$yx0N;RCERnCI= zeH1hUO<`ieQ|LUjtLI~)vIraDOS!v@{}vOC@N%=ty$|wGH6F&Q zxZe}r=EnfWDN*V=Tvrbx1^dDP#s`2|c%2R~!yiS7{e+ov{LFs{psHyOp6RE|j1%UP z<7fU{fcX}GQ*!{@&fA^5zaE6_qMNV7WULwBu7|pAhybgA1|Pv6pa7em3z*sHo2g(_ z!Y2oq*FZn$K+EL6~Fe96IN`V5)Un{XCXN z{8qkaq?y-#!Yq(qMI1{VH|b4P6YEczg>`614yV0~L@nut#Ozn96E8gnKE{E~t>8WZ zCJzEaIYzMhJ!lTwU)LwgX(*l*Nwt@zw~%T-IomMbIslCyq=ybs&S6SwH`}m}Z8jZP z<91?I+r@v~7A-RV+6L*VzvSCwo%s}w64f8wF$B#s$u~?nRCWexEOrFvo5w|S<4eu} zWG)fPSODop(7`JM8ez?6C`aWz74*xu zC{Ny^JG}<^45+RhohUwGu4pL1G*4T_l2^_4>XH+t;ken*;8#to_CfV=vm&}ziiJUn zy6Ek3GqEA1Pa}g*nTAp_my)?UMX&gfR4oRC8WE$R_wfwUl{hkR&({LbJZ2I7UWeiH zUfy2|X7%iPYajNEhB>5|>9dkqWi-=e4WiFlCdsKxnA4FJ&f$I@)9M1Gl~?e68Q)j( zu8Hq!`M!z!Yk7Yi@88D#Uf%EHeLE9u2h*$Oup2<;bf8s_5;-KgTN~Z&3+^;8c1Eb~ z3{b6ipJf^r4Ro5%ai_WMCi66|$V!YR9{gpW0pz5mgD)f!KK3D^glqWQ;eJ1=naPhQ zV0N;JQoo|)A_;l}^7tj?Yr>b#R3(4KFDg82>(b(7<7=KYOHP})GL^JeCnwEP*+v%E zpD@d?iCm6Nqz0NdoDuMX@vuJGbljBQUF01<^QvZw(fB7`&4lebpw3dweGHoW`SM;C zhWD`mJV=^{N%ek^{1D`SKS=(dMdYQ7{uy-9m&}(bp^3jou!t;X)?Q|wV+jy~HV5dd z04dM&&a()rFo|EA;hxunU03nz)&nZSu%Nv+fPJ;MlF4I5bKUIm^(Rf^NwYG4P<#YH zpR|CuRjD%SsY>$#bfihF5+hItNToSxaBU7y$nS!hTIesWN;L^)_2y!4o93%kREcBt z)zmnrc%h3us`aPbCVdLx`7D$Bv$kSQ4rGrit_dnGWgb0^dn9!vvb~_X(^RMVa*4W# zvDhSo*tr|$ylQ3$fK78664bw@xvsuCanh`1`Cg})k@=t=Knyiwe-*G^ zpt2KybduTWl(~co=kmM2e3Q<5IqIk}tVRM!b65@EzHD*4Bm#X&0D3v5t1t1j$aa)< zmvu=bu7+HZ(~yQlb>dYsi6&g#jAvYR+cnkro(&tp4DLE}gL=}n!a1Y; z4n4`VVqRm=d>FK8U*9uxu z>zmCh_&cLo?fkHrr;vX!zeQ1M&3E{HmA@cph#o!g_S;<5#214m*5x&Ei)&(;`sG?$ zIllgtFUDKxoFPsH+NqQhZ{?rSia)27zo3^1{{_(h zlMea!s2Nqvz$eY`owY-L7;lE_kZmhqm!l4WVTqdY`{qaIXvU6MGj8NbeJ#vh!u8Sm)dj2#2g)sM|jNUq`WQ+^B00J{c110?LG#LfU7Nb|5T^%5v7 zC((N?iM2s9z)x)(2ATl`%+CNyI|Dak{9%y(73PzEv%@ZChh4Fb;wkE?dnl%e3CJ@) zH-F5X+WK({6G4y9P6a8}x9jR1OU(=?mYOC&HS!cV5;jb~u_5D?(Le&Wc!d4%%1w<| zVH;RW8o8VZdC~FFKp9V?!-=g%yRebr=!nf_uhRwnrm#OTe;UC38KBn0SzFh<(GC66 zrkS<*q`6*8Gtj3t&3UChfPD~FktMgaRrV8=nBH`7e=W?9&UY|4&cuQh*W z_ulFVx0BSHKL?W<>|@miqITQ)_!l9%wV8An{|nOn()=Y?Vf+&T#wTSlo)~}1>@JA( zk=AP$A&cy99O?A{J{Q2}K~?k3L~lV1>60SBCq+n~1k%HKwpsv>36UO6u0pwI{>uDy z0O@1kSmR(yY<^9$zr6uanM;qGGUi$4)TG675)zg3uxeNd{l0J@J7wNBM6@mgt;<2{RiJeR zXl;x^IuKgJ*)kWc(G=;>`gg1-0}_A_0o*u5Nbv!O*OipF7D(3t>H0xH8cvA0kmeD< z-?Qeq$>B=@sZPj}YGp~`*`dmMCmBW0%S%aoglz=QO~AR?RC-&mCb%{R=Y**5Cq#We z!A^;8(sN@?Z^Eh0&KILXYKYBl!m=A~{|KJ{DPj`V@Lm%|s&00=_JrB<4EIHLb{4T4 zZ$}KAR0K{c0w-nu*^;8CBWu@#-aFU|X$$=f1Ea*wK8dfFHOO{9%eb={PQA*m?6lbn zzniM|M<;4enwIKRmgTLQ7>MGk_92Etew3-&&!yTg#@4?Tv78g%wCP24$t%y)%}$O^ zxZ#)0dLYbtkekskZn2D`6Ux?5%C)-mB}QbDE$LGL-i1^^zfZ9#)vp_?4KSow05Dc} zGgfaf<=)#cRJf5CTzfEmIKaMgk6G>=GzYvxrpvntMWVyzF|Wfs<#kykE(g`+>{Cy} zc`z56dySZFxmI{4LhLgUVxNIGaogl)EQ%ddKGhWcm+4{-_09ieV{yhT4mQf0wNZ|E zj(?AM*){a878?3DG5&-(^hNID{p$oHZZR6De>E?9I_?kp_jZ65*iT0jU*hZO200>+ z^>2xU9h>OicKWxZIyqtdNpq8AAK{KB+ee4PKC<1LEXJ6&Bi6mVU>f}*4Vd88fo#ss zFsFKVLQb{`%G2O$0`^_sb|B@E(BX0W`vf~tJ;(d@R{Hif`u2AE_73{??ey)PW|4Qd zY2bI6_a1!3{U(y3d(AQLKJy9he)EL)-dNv$kiPxjP|*YgChA+Q4o^pY`*hT|Pt&(T zs=od+cg1#{w0+xY`}RL=-!2OK){CYfdf-D!Ofx1XkOKSSSsmcBhs-+qq1 z{rp?nx0T}K-*EkaoDSu22t=+`l3C%6os`+0NfhU1|0GO5#|dQquY#EqgUft|%G~;! zt(ozhmEfGEX3v9(@1MD7aG8tY{QGD2@`$-mn@tfL2BtH%41N4=Wzp&8SO41GkJ%gi zHV3~~2fu5A-&Miyir`l+y7`A?-Xtg`&xhE9KYU@$ETS9e?=n)IOMOHEDuU>nI;qdK zU^GWDb2Io38*kS7^1@ndFxe|M>r>`x{a&Ll8+6&I%O+irWMHb7F_`M%E;Eg59t5I0 zS>>_uGnnYDGR=C4k=|Bawwv8$2yEYmk)mlPsJwo$)f;d2`liJ<`#6Oenq6GnjwMiH zK=-I~^U*%k3V4Bv2+CvbL_bjch<`)yI%LzfQfmt*^qs?|C}wOncPPeYRy}8xRT`<- z(B9Ry>1gM^wjS%EOU8xWaWe_0t(8z%)WMo1#rmJ-_cJilS-$jZTmvT#r~#wzGs~n{Wl+N$Lh=Kv!D%-IuPK+$G2dv*V)P#R&BV{45w0P6XK}(EXv!Q zU7-n(oZA5|1J|zjpvoYHza+z%;nWOs%=Cekq2vhmCf|^?!>~$sWteOhh8lfPd(60x z5WfBOl+M-{8FR!mDjFhRy#YFdZABr zOgDu6_Fi;a+M(D`T1wwLx8T*2>>2(X;1@hT(dXkyfzNV!OqX`kgq)5)+E zL>j#^o({8Gh{c}_t6+5)ir~77(P2n%2$HOVmH556UYopDZG~A z=kVU`112NCoy(IX&hwDnd-iTVPdbu;*4%MUo$KCN#m-GpR(7?M4g@K zTrE|6!VG(X*ZORgDav$PcEVJkZo!G#2Jcz^=T+&m6h{dU@B?2i`Jz|^4-3T=!XfWf0E zI&MbFdtHDKy_x;eakE%{UC)+BCy=o`^)JmqdQ^-IZ3ckp-&hXb4QdhB)9>272s>M? z%&`G%E#doBblD2hucDQ!$+e0%tIZnzTW8jidpWtU;_!F{Wvt?0e>Eko;)HxPN5gB_ zjjTn_W}U^L<1pVD!`2*AHg?z+yq4Mn`zv2J9%#Gr*X(Qg$$9@Pkk42Eq1%#@xuSKp9PAY7}U!9LWr0!cTB=Q7o?A;o9k59As8I6vN%< zpo%24x{|rf;f}O#n329&wL+Vo)s#_WnsS{e-no7M>OVquroX}R-Z2LSdA#?p(oo|= zrObB^r$NUDborofK9n*aP6P2r^!!oZd`#hP9#OJKb@{j+KcR1*)ZM3a_n0n^`{oJX zJgM7HrEY=#^!WKS#PS7H8pQlRRK^H-#y4Nm=P&EwS>HUD zW=?)6WuDjXS9JNRE-&cwi83@hPU><>)x4;DUrU+O`u(~t-@tK*`KD@pNp~-+%5UlJ z+rD{4RsWVAzN5>lzIjc*zwMjv!fb79yl&67_18Di0qf!E@rjZ3p8@S^6qoO8+_0l* z&-UgmO?%dF+D^)qd@&w;W9-X@&70QhbrHip_Ik~R&5hgjz8J1L_I~B&%^R8;H|a|P zb@SMlZQHl5cSR*p$c%lU?mdlLw>CB_QwiK?XI2|F(P>?}7u=|ncUo=JY~sj=*V$C3 z6K~$$AV#0?_UV0*(stxCB2Sl_e#-h>D8+X=DcY4%KqLSzn90s z4r_4Z@)kv24(DrF(qnrpobB=^Lo?m*CLBD>x?7x7!g>g`{s9`?``b|yIT5= z_Oy9sSJRUg_U6a>reHew_oALB>cRAsNPkQ>wTojh!5 z>A`mn9!qk(Jw#3-2ji?5I=)&Yel&EoFu=H6Bbv0H6-XK=VWXK>7&X`1#Ox=}H*A2U z09mtz$bCnS6>?>4C*g~SPWv^?>4`iL6zb625I~`ZaduefmWKBTDlZ=9l{Rm%98_xT~}@ZU=)rK4dsS{hLAVT4Q)s z3;s3(ugWHVAOmga($cB0*4N!#E1%hOguA0{b35!@Idhh59Z_NVAG-W~hQmBUy!@m2 zrwo$#f5~u$^=}!Z^R)dg^FKWaS%1U)yUfS`LvOZa5R}vS-!SjaAnB*6D4pjZG&CAD z|EbdMHP}GE-P{=>F&gX{X}#xkV(vaXcT9#Rfi`;>O~uMd7ziUHRePH$w20HpyX{bS z4`ydf)vOPr3hbcmHxjWsjY?ZJ=y|z)l$KqGrz0 zs^r4g0}l5xNrL*2iP`QstC2?5XA60mA_^OA#C)`nY|YTEIEq_ZTl4S~40na989Q@8 z1f93J3J2;t7FeiJg;ipu!>!vo-_O*TBpwK+-aw(6NYQ8WMw=W_KK%mbix$xV$+jYKH*cQbVB9A z&zNDSO|rSFvH%9`9eVebnj<8(N8P_z?lTtP)w zvZZJMkR|Nema$`7&enc~6>D4$=&M1(7Op$VBh_JmQj{!ybw{Wykfpob)FcO5&`^+0+x09}JmdnfphoA;7s9JSN+vvM z##SeTDql2{ip;LNOHg+IyXxe#o^X(`z340ruBQig(}Oo4Yj_)jcn>|dm;P*JK(*0p z?R>e3r!G+1jZFIpYi1uxVn;E>y2X3|bFWWeh446wL%Izg`CmX4EH1JodamomlE^m`{Yh;qLlQOfrNN-0NAyBiu&+}#(cU!&n> zQmWHk&K`2^<;@gR_G`1$^D@ISZW3DRr{O`zi!6;iZXghr{32=qo>6? z=xdbwZ2fm;`??Z|>*QTTQJTWyb_nYMQ2~ENt*xIiQ#DhZHdDd-w5sVR%nX+f`y(uV zG=I!?FFvR-#qBK2A~8m&Q*@{rZf1MtWm9#+%n1fw{jTF@eyQoib&gYPe}K+;FM^`? zL0S(o&>mt)Kg^lJ2ap`dyzE03!BYJ1p^e8Zf(_^oCzLy*!FDI+BwaK$M4VFHO0FV{ zIJElzk6HJIsq)QjubW!rhZ<#ffFO7t(5*T2On{)nSp=cu1AbmJB~|F}Gi4~Mm0x_^ zl+=Hd`D9cg4`q}4p^S+qhA=n(j7l&W&&GD6a#wbCwLz;;xhchJtpW^=z-nbQlvjfx z)=X@X8n8yX&O8D2J_)UU8j5|&qQ=dzha-d>ju3JfbZw!LHC*38zGBK*WvNT@H`z=i zN57!}BJW)3y1b*KFU;t!VDl+h(~r zY_ui0&(W->Y1ZeFWqtvGzGy+Ob>&2$bMzZ54&!Z4>!cp`HK zdxCmTQZF+iBf17t1kJ-|Z}2^~Uik2!!Pt7*2G6NXEzh1mvuufIVV-(((niS_hl+W#$9>Tg?EoGs0q2nTZ_9Lxa+_uEdMWoNWvOD&gZJ)Rz0 zk3E*qW^mg3T*hkJft~{<_c$Cv{!Ysjx zHj5J{RR0d+;5An8-{!#KdyI(RfkFFS_=z8|X8aJzuT$nx2mP1V;?cP@}$L>CNiLhAwuZx+IG}>@f~2Bz$Pg!ttpwgeY+Lwq`JG-v~Zfq z_JXYmsb((S-q2sFu)P1Pyp!)?5r`5=poB2wwiVcPmWaNJz$Ca#03p=n*g^FCdG@Wc zpUgsp>r$)QS!Bv`asIYUVTmn83UKQbo+)S+Z(NG?26X*-Xkr2`wxlsI9eHa`bdT44Ip-u2GH!7 zxR>q>{Qcngru{(s{uWgbsfp0G!2NWt`Uvk)zBwRS5^|DQd7*TP1SKsNSSAD-f`q6* zDm78wVCikHx#~o|*@V4HzX`F?O^7{h2St$7&Lp|P4ZfpY*|(Wqbe1~W9KLYe6Bk|u zLYe-PrDoPr6$d!F?^wE_z%1KX;N9|}_w(wYOWS%vEac1{qx;cWAQ&dbUIymzTHkEV z!2fN}4SCJCOU-2jL$xAjEATr<7Z1X#V@Ol;`oVYYXzvngc;QoBpo zY|liRH48~r9Srzvb^`S<;d3n}B(s1UH(N>DRQ3TFY9fvtZ-#={vbfmp>;=n|K|Zpv0; zX&~F6Fsw2xC9Si)!M0KQQ1qtk1}n3@i@FAH7eWe)Y`dIVOi2W9iEW{SH~Y=Orf*VQ zbM;A+jG9YnAd%LW2>i-icI(Fvo|$2834h2%@gy5UYl=Lpu?0hD^q5bZ&s(-*{xB!wYsdc zEAQG|Rv|ij&sE}JBXN#dFTQgLn1<-QDqP|&2Cug4Le^T?i~*Uol^0TQ*uqq}(hba` zmG|~_qo^FNA-U)em=mVOA#-Y}uC}(;-bNilDbUP08MGL$cy1OgpE<+JIVWj_nXgLD z=EByk>L!{4bY^`F&Me4KoqO9lCM+$xURlz5~W<(q3V91LvKN&05X zHR+U_97AD`E|9J054vYA9)rY*^7I%Fc1wW>kzCz?JrVUJ_ctYSi! zXvYejUq?8#cdM#RF6BD!m^_z)9w2^baYk|bq0^gDyQQUvLjPFZFsN?8r0$y-9 z-5}(N1Kb!PZ(|2bIq#aYu&?)kSsHe+wxvTP_Xo4eQ)A80>#lua4p4!Q3=*cMLId5l&hKOEEDwYsIRWmPwR_pM~ZWn;BX z;MWszLC*et%Gv91g1|Kw2jUBq2~Ifh-LXFhefnE7G&JtOvHSYg01@XxU`2OVPWn6! zDXuNJt@~)t{x(_OV79k8H(OMV!{Le{;4r89u$&9T-o_JO$|Ts(=&xZY*xTO2Q31Q= zwwBgBk{j)t5xyp@QF%mF8PDUIUmYk??riUE--p1f3vEA3)QBb~$F}DL0}MXQTiw0i zMlx$_g&U4XjEkk&iYg{ixNM-gP^olA;o_pCTe>^i_uodDCEGUd*t)8T9bDlY+gp0z z5J@SBt;Xi zl55sCZ6M*LgC>a>J`0cmtBsZ9ScyjrIawsn&^)J;NzJ#Kp|l9;s6&g!BHn5_$B&yS zZG$r?v!aXA-6q)v_ff7(g)Wu44A-S9cY=HD^OZNIDMEicDQVE{I@?Lbw5FX+sK5(- zd31-laKc=XX0>kc&2--^O|wuh(`9+eRHym4Tq&+f83LLn%nF>a>X+^1B>l3-tko|P z{>l19%s)lHO(`=~ziWKMUNgOU)1Fm0Ro~u3Po=Z4VBDJT2g4j-(fnq+xUc5M6{&XNMgZTeuUX)YAPO?OpAC%W?D>o1mJyj&=^=oM^B#sq>E< z&Y`C80b{V?#gdb;nSuFpJMb+Rdrru}J+f<_XXa;+UXIVeigB5!%OqWDb;0$rY@WHW z%L%oUop>HBF%uInql>jW*Q_IwZ8TjJpoza1@7q-!Exo-3LSMu^v8xZ4tPqbc>2v9d zYBkXqercusrJ?qhw%T8sYk%czTIp!C{k6Nh%5m&1rc+Xa{fh*zoXqniVXOY7pEuqK zQ}nX)gMrk6caHt-B7Gf9wo8Ah(Go)Xszlu>lYH4EoXTK4y@at&@STQT*RDpQ$N5}U z;{ui%EkQm`KJiSh?PyU>b=mClx=Rz-#c+A7J)OYB31eW>*9!*Kghm9h^b?8C@Pmu{4O8XY7YQA=l_=J7G5@X&RqE8ai%8b?)k0f6bNe`FS~f zF@M*9m5V~Oc}p(As>c~z6BAUsNS-=U^3;(sn|0Zui_ZB6dothA=D*9xNiQ4SU`^ zvUBCpuGWsWoU%Q$FgDwtrMT|ic`kcUmW*J!`tr)Ud~jv$4}yrhGHij-%5^yFha{&3 z7k_=#?sdS`zVOoP7SFX82Nt?DC2axe@kl^lu;V=Qj<*aU5d_)37LHQg@>4_nGvtK@ zl6Zz_Dg|M`IB1G-?n|P2@+e4Pmb6=nL~uD!DpuzGY&`qgkW$#+)+Q-2d8TKe287Lu zA>`sTioTCv0k=I0SMFfhGXN~JE`G&Z+j`j&hZTZY@M8^hzXdpCSJV-}` zAZ=+m+8Y5nF?_qN<(9Vf*5rCWv9{>f0N@yAy9w#muYs%Edi#31Z;L+2MmL<7WxO*h zYFW+m&TzJwSZWfJPIBXJ4`eQf)r+V>HVf{x%R>45Qiw% zzmJ^sa5uwD_RPiMLNizyOj0ci!GBX)%14i?0l_;Z+#~!s`l3FfRK3z@FtX$>i7r2AsW>g0c(F?(em6ervYd zE*4k84F}s5>1c>em;Qy05Q;<*Fn&|)J+2c)7t!-{o*|Lw`4WyP#At^ubM{h_*ljtn z@UYS>wHjq#35Y#NDBFVUd;uZs(P@f5kD?4d2`K4WMOLO}#yEi61 znyv$uF|0rtOA3~Os_X#GDyLZ$`G8zCK(mA;HEa2RAXOFsX;ndxvYu!1F+IExrq>Pt zQi$ml1A8!1QK*)#Gxxl)wXxez25#8A~~9sOCJ- zxR@i`5aDiTAn%NoIWibJt0{x?U>zT$R#Twx5?45uDHJ}sf8o0+{00iY@$7{&w+Dsa z!>>qx4j}ulnz8z!a954)UN&Q%l^Y{@)=8n!RvoX@1^n1grL9baZ4AbP48}u9Fxna5 zhb<6p#OPdCGKR^>tliY4wi41~{2PXYK!u`O&x<0gFQrwcEp!cY?D+gG^rvol;}>^T z7^A;SNqEjJG!au*qB`oLrsnOH4@v^v$jNCM{lQ3^Ne^pvajN7g)2N;9)hTI{r?jsg zV>U|dN!rBL|7E4!Q%lv{xxm!j&NQx_HONeWa)DNK20h#xq-Hu8(CuTY>$P6+nmG@g3=D@9weNQtQ|V z=6e5O0<;DWK8GFX?&)mFh6KK{K#K~M+`oQ$1QyyBuR|$C%teD8`BuEd7)A<}P}B=< zM!PRKHFQPn0{XTd$gAiGUxTjT>lVuIi#{f4r}lr#&Vj;BNs>dv=8-|40C5&2IUwUGIwax8I~7=$*?{Q86DGy52+z? zq2cuD&f9TB%ritT#~k!8bD*PaKtz<-& zal%u{$palndk-ZhGf_KbE5jZ3B5>?lRTR07e&tubtgY0y$O^C>pEuSnK#ns@2F(3k z#xeKGyc2#<%yYUfIoq2fX4$dl5`jBits2&~@wODl7^j0$^j-clRdpK1U7G~mNnfeh zs1C{dRYwbT$l9umI_B8;Vf`wpCF5jUiL(tZiz+FLDk-x&3{_HQtJ5B#%vLA7)Urgo z>NZoIz711#9y(r#=KEDRozsFDwnDer7#3Bi0GF;+!-5L$;v`~nP(qMRfz=YZdrfJ!pK@7r<9)0DP^3zSi54hc+JT3*aj? z5WY5ouT2+(ulfS`qPl_bwH17A8#KP=M)=wl%wJ`7l>-gPU9{!87{UtAE#U;_W)w|e zb0dW93ns9PDj8q~yMeigfF;a3ZU(dBG@f)U8IrNYV6*HrDLI>h;OeU;OwC|(BaCec zX0UQw-#{p9VY|LBhO+W#;FL!-mPb>VA=}m3WHI_L`}} z*9L)_Sw~A6630!00s)SwPJZ)KtVY#|>Le~m8WQ!d8k}yV(2XutcFd``*gX6amV;26 zHFK&HINKPe#kklm=gtp6A1FHt&D_fRe;e=bpjY1x1>Fe^y%T`%Lg=E1vv)H^yoWBl z4>9Wfw!u;?rU4O++#HB{^iK4tb@gUTEuzYsEq#hHxY^QY(r!;tfG#r?rKKaK_v=I| zTHp8cq@<;@=b%mf?55ezf;KtaF2S3GgWd@X9^}pY*zMDmpF7GA}`ok5}6 z62r!z7*)ZOlW?iRJE%?-;_aofdANtyis2hio2uqHoXC&ZBrhc+`5Q$?jdlVeaWhWj z_Yn~IQ8wrwgME60svotWxS4cn1jWxID3*CMkb z_f-7%cl`A`2o9kD5!UO_U|8B!E&C4@-qZIvnTGFpSj$n|q>^mm$32z`V$Yxqz8bowo95dq=Ab`~B!HM=>;z2(a7N zPM^yjtKO1Cn;L!cZ)bZ)QfUdfyyT z+>M?LI!EAa$YOzDi#;gmK!7Fy?QQF0D;kThq^a|W^k9G(GtN;~P-UquYkER^ zO52pm%}s&7YbO-yXVq7NU(&{oj;xF0yuy~K8ZIebn$qch6NR(B-o#4S@vqh(mK9ofxK&vY?@b#ryoHF1gAUMg?tXLz9gDNJ&aWAA;W&IF^3Ly*J@zZd8hMh81&z?3Tn%$d`ClS}m$ynk= zGa92k_jU~2r%tj}(KxCS3~Ashr|1b3K9d8^d2nXJush6I5L7+^TsRvod5aYG-5Z@8 zDmVy7S=wT$dx;_oUl>AqUjV7?4qQZ@6Ve!njw`(q5XImxqsrxgxFQB#i2e!eUGl-x zn}XmKQJer>!l9lh3j(H4Oc-;S)$e`qp$*UD>PUafNH~~*+K-8wwjl2ysbFj4K?v3+;q}TAZmy55xTzu{2 zmah$c;FNT(?`mzk6;fLdqtdydrMC~Oo1;BlZLPtFROkBMHSIlORAl>IBK5p!^Dl&N zjSj&$k~%N6Sr~W?(Idj(bCat5POBM=5tX+%?_Q=;#2L^`og4{9QF!YwRlmhzM0gUcPh?X%!y57`< zdc7PRZM4pGIMpPkpWFc)XZ+(-*!Z1VmiX9=P)~ijOa%3WYn@(xm$FB zHG>q-i;AWdIv5(j%KiM9Rk-{?xaUc?SjTt|ih&20LQbqGYgqBZZN*D*ArL(3r{?4vTcpOj~_72^xnx?5LzI$%WL%fgYsPNagydp9l& z?k2kYy=J0!pPAv^57HknbG!$Me*Av3!F!l!r1@Xor@f^2nJfi+B|(QR!nx~0hEa@yqi^auq2AE+dQ5&PbmD?Y&|}q7+|x_ zXUbTrpVH;CdUrg{no1n7J&FUiM{&TuV7};^XHts(Rimh4v(1-vQ3x;_{p%|V{Plu4 zk!BXMpHJ%Zsg%Ng%{E{274~bE`MPhu;hS&z<|W^}+|LBJ67&A{mX4imJ-w{)JJzpe zc(aKfmLn-mJ}?gdB9od}kV-oP9sI8(jamRFN|PTk;A>piO{U{QqGDC z*n=e-ju`8i2p;rE3gjmdaQWt2tlAN8NyMmc^DIXRAruqyp;+G)g^;%U4rz)NwRdqK z=$ao@7f(n~JKy}4-2p&)`GOao11hW}x^SNea#hQbmi<;4%>k&w(AGj zqlEjC#F4f(qF+bblhXDs%tqv3ff6W^rkA$1MXv_K4AkShiG#AVsi2$N?4Gf4UvD3ZihZ#;#ZD=)>Bb&`xfw1Rh1=|k z(tf){!EBmyEZ+y%@n*1qxGf{qcB$wr*5NRZDBow~XgZ?`H>T|KqmVcwo0kO-#`ziayBZSDsd^LzS=1ACoX^!euZGv-I;4-lee zVcf*LvZWj4%eI~veA65IkYpdh7>+<>wM;FwU8Jk+-ggtehxF=Z9WQ23;-Y=4To6hvtv$K})d1FSkIcC@Y)JV0H~)=ezG$7ySHuPoP+5&PJ+X=sCOU zSu%}lgvLm807-*Wdt_0`iP~Omt1PtCgxzE3cW8SH91JSb>u zY>CGGWDC+sig5=t7JloV5#}&)fKwTm{h0-11a7K`uZH3(hG$0%g(@T^@SQvKTl{b+ z(5Q|yTQ5Xe`}`$9JI!SH?>TH-bed4YrpIkWooj+cXty}U6G`MHi2n_%pkerxxsiTl zG9=M}Y<&zHe<^CL;M&O3TJmVe?blV-;(WnL5?1oU9lmU$!p&A>?l$nagthMH){r9m zW-U)DR8}V{^oxP~e-UzvQnzR>R`7J2g|U`Snart&!p#+{jtE+Lj@}W2 z)}`7Rr3&#@rm)%gcHZ?mop2pR=xD1MkWsmJ4e(^bsMo8ExK{V|d z38{&Aiekj0R3Q?k$i3lck2qZ;akVk~422Z)YlGBUiXxhna3cv(Ga_w{$&qQhadC=w zqOSGD5(7z62aeYJp1u+3`Q*)()<)D-Z@`G$&lZ=wC0Nx<`Cjc zwLVVqj4$K_lZZ4`XC7zdKMCkh#i(?B(BvZC=Z_M73maNyk~r}8#;)*+OMxGee9(&R z{A%0`6Ma6KBL~#@aEF;+wl%u3H!P?zJ89@mSP!Wj>Q}7Zs91&N3!%LZl&GxXf>vk@X)KF7SM+ycMTa9f3voDpHGcqc#R@@qAnMsh zo>Ysv%?AIA1O09NVu(y>T_7^FmFk@ds)ZzneVVkTOBnG*{smXu?ZiZQ>&g zzY}JX1cJqN&Curc3bdG82_F@_zw)& ze`a0#S7yS0V~zTEVEPZ{#{aaiyIFHn06TCo(<*qG@Rt3iR=96F`KG?Xc%!1Mwj!8a z5A(JIWgOSWm(aA-32{Yxisco*BoMfVEE=DXm1;0Y$^>fNhC1{>x zx_-@G_3IJ2*Dan9NSQ_JcGP)9u4LjX_mZZ@^AUKKn0gPVcwU)piBqLq5w&CmCjDBQ zlC00&cA~iwxQ=jF9HR=QSL@U!U-NFcH!*0`&1CM0I&LS8Q^(;n|Ad*IH(tDPF=UoR zAe8h&roK63>YI@1mM0;w7q3VTfxCGUm6wCS8IRCvnT}3!LU*CVE{)zKgzmMRKun1d zVR=w@mhAN=Y#@Sm*=vh`IZ6dLMHTJ`C#rC?s}Q=Rt1hvQYnd zEyIfLDk4#?Kv2HYtnn1|H5^{+f+p0!cIFQ~eN$uHw{YmCP4VzI%sd?aO1ADgvq*by zFsUi#CR!;R7L<_@nmGsSH&W!|;8U)~l2s3HJ}0)i2gB_@4~VRsny zAOZ;LJsi|qdAE0_<`dS(a8V*>@pKgvu+zH{4DDg8wZtl}j4H0|SFyguDlVr=cJ)-K z1~&!Ni$kpq6Z-^hRYnT;5rkA^_w&gEnPN+#muC`l8q( zIWJah(!5LS4Vmv^C*V3Fp1EXjncrQo=H%cq-wl)IY8G4NCaap0#;y+i;dZloiG!P# zmB8LHU($kmVDS34pjcS#UyQ?Qw)yuJY=K>?&z<@H!Y$w+uu#w;t1IntU!bxW5edPK<|V*}mcRCWcL|A+>LqN2aOaqXThTbtIb$7;WPW8-yu zHf&zIXG7DbwcGKA64<#*DAUz;4ELm* zJm`!gG6;@o%nwCdKKG=_1?Z!WbLr6Mh;1ultqyd@ZCH+ecHRS9Zx2#aJ5~`*-@dhR z)irZvhS-#W)tVbMgBm8E`!8_2q5gr$phsQ;_f_jvYy6)i6Yv$mr_;vK-TPg5nTqz`HuD6FlPJh_S-!``N z9b#7L)InNl?@>woty6;0O4=U?E03sTGAKr>6%VUX!ShHYR-6j+RUs9|v1uP2J&z$r zJj_ORMT;!HWftW&cB4?`4OiJD`lQlWbwR)sZOq`orPyvxzED%?f0{i($&*;Doi@d2 zbR?>9#Ud?YT&1KoN|Sha#p6$!aio0QWaiYLGU;7J_^Q!e*{uv_`#6*7q7^>(+-Oc2VS(ZDg86|*9_WGGJGpEG- z8pRQUSl4)sp7(3^QNEeupJ9zi`fN4yp#SU1Ak$EFIlafT+mjSm1-_569ltcz@x=jL zQsWUtEy!Kj{%psCH$D%9`Wx)Z*3{F}-BWO7nCtzim1w$C|00}7+(myP(Xzi#Q9>L&voOzA(q(_$dp~%uVD9>!5(SYy6}(rx z1-&l~-j@dN)4_YqWJ;e7-j@aMkrLbYW%fPFSJ1XGFxOm*Ohqn%&HddwpZYgG=x-5! zgCVfMeyyKPMVSrLU79y-WP;=aYQCpEQ$M6?`|G%64;I5C(4iRORn~$X*MAy~S_^gN z0~Wbz1=W-yl|+qZ_U*YCyzdXXuly#d^mirShtib5JTh;nvZG3yd5Xh5UzY{CEYxL@ zE|;<=OG8YHeRH{Qu8_uF)`rmO-&s?@xc*kaVM1G-kwss+Ga9LpPbteWMz2|@NR+j- zliuLP*oO^Z6^fp~pw!eu@(Yj$lo!p8GPa(+DJGR}fzC0k%vwahxh9sSeOc?xi)TSu z+LtAd^S7qV*^1`O7a6US87-=s#mX}*s$5u6Gc2Sd203#F1f#MV&exJTws&+>jBg;k z&cSWcVhdq*a9uqlVqajDG=?@$vQtn{S$jGb@Fhi-&#~)3upWpz(KG1eR}54(c%s~_ z=CM)YQk)*bGA766*3dL$rEDAyGHAQu^tZ2~U`?!u)KDa_!qy_o=(r*pnPXSlB0_1H zD?)HgvK*Rcoc)FPq-UljK%Yz>929iXr&mbvrTCQa@K8HFYK6?P4UZ5W`iX+dGK zIL&V+g(dqHwuQp39iXsFK);ae922`{hH*e9>CD}UQnP5^L18-wD6Eb?RAJIhageQr z0@Q;OP8(k)sXEoxnTbp^bS_xpp-z#l!tKtlx5c|&oq&mqHlI>;(RD5-3{qSvqF{DY zT(#!)TNI}*{=`2XWkJ;60=^GnQOsKmSIPw_5=f3`6ib6J$tiQCE)BY1x0$lOM+mp* zo2&9T10p9Q9f^Dp{6Di$F29gDLfr9rsmIq3Wp-gEi>-Y5vd8$!5ufknXgge#kgL!f@gHfvM;wTpSw!<88=z1lxgQr|6J{40s)7@FtLI*;$ZXYwi*CgTt?1T8 zx?HNuWo%uCf^TcqdbUMxj2W9@o@~r8)l@|59gE%wGcqHw+t60NP=7C`kT-Om`}{nB z$ijn3H^unXI#i_waaUz`#rmzX>yK6+NzJOPOi}Z{)`_vqUDdq(5InihZ>=@DV<{S-Ms`C)f zY57p|;+Yd}GR|hcq+sSt3T95`XP#X!vs-KOYc9#pJg;EpI#!nuY^I*!nUyRQ~r5trUTt@95kjF<1&aN^a(&fXx`G|f$T4p|G9+Bg@M|JtQ zH8iU*pHh~`ba`BtCvXDRcQGzYq0_00=u1jN{#U1CnA0)w-A&DT=qv~RvHPZfNcYfd`Dvox)FDGiQK zs9i7X?prG6+bQ#k9^R+RZ~5jsO88{TyqXRa@6}Y$^1U?Ta1yOCzvG+lr;%dw@^_I} zY*@dED2+{u4r;@N7TnSL92c%_+PV!E%CXs2NtyT>f&Z9!7`g?w>sopb;rE%OKbXLFYF{7-JF_H@{uPAOn!^EAj^ilm6gC09#1&4qH<>of_!Yh++jNIb|-~8bJ zh2H8VXx?DY?ne@pl3v`X5>6u_eF&QmH@~rWQi(h; zocFc0KoSRzc5Lj0y|3;EW>y-PR6!Vs#W|pfGdujwdZR*AXs<*F&sDr&|5o|O+lx5e|8T0$*M;YwVot;w#Mckhb zl4A~L7;6{ho1e=|{2yn`pO`<*m_IXr?weoGE(=iZ_{f;QFn@0S*Xz=$i}fdmZ@ZiI zc(WdRG7x@kLvNLftViq<%d7?%MU;F-}ZkC;iLa; z!2>V`iMgT`D^nx?C5ToM{1N&{?_~*H@h*j%E=LovC%m%qp>uw zii~VPj@dY08S_8P-)B&`{znGhFF*Wnt!n;J!F^h}hSS_$g~({>>+bQ*KV?wT{uj^8 z$kO52*quQhtHusuGcrCIWVV>AMb7aC&ms?Clou1ThI7zB25aX$@hirhT!dK=$nZT&Pb|(vo}qh? z2tWzo(m}uz&#aCYvg%Mv&$c!UN|~OQ#BiSTK90wz!1GqXO7#7nF-!7IcAYuM(wU9h4($+aJA5&PY1?As0Odx;Nh4b3+5 z20zVwljc5KUqykd!ZUQ@C7X5&S0^H$j>usK(&JEJq1+maDUOpf5K~FZf|4qpM;!7Z zGF^kRx*zyc%obIL6l8X+s0!rByU1ROag=P$98hjgUmg{OmQ}x!=CWCy7b~eeDybR6 zGgs1OG*-ZwrdRO*tYXV-{1y2~+l>$zWwy z{~}80=toWE1|^f1O@+Lgzld`i?$dR%F~-B(>-d?UKS$04Dubbm#l`IMFGsv|g{ehm zy$}WF%jhp*R;nAd$OCS~c1QxA#aLDe7TxXVPR&@Xm@zadgNoDvoR$~koYv{(yPWOh zbl+~`+hM+y%0wiC>)DyMQJq1fenMpk#Z5!3QLY8HS<}^)pk2x8WUO7Zw<<})ewN>` z<@Cl?h*nl0k7@+al{9aaZI~onQ;~bAVM1g>)UXEB=DTgf8f+6K>uRt~ET*)2+r&iF zL{~;TWw>uQ(b%Yo!)W4cS_{V$H1TU-KzLjfYoe?rZ3An$aqUxAhQ0Qp8Bt8z{xrXB zYe4E+I&B>ix%JT8)pXo7wr$c>lXp5+Tqf-liq#1iEmLm z)K$~zs+%vE{zew0zfDxSnWk)k)~=-)TcN*gZx#K)gkA*t8 c0v`qpug*&wPxt= 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 literal 0 HcmV?d00001 diff --git a/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/AppModuleGenerator.java b/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/AppModuleGenerator.java index 7c8af5ce..48e3e23b 100644 --- a/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/AppModuleGenerator.java +++ b/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/AppModuleGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.sdk.annotation.compiler; import com.squareup.javapoet.AnnotationSpec; @@ -30,8 +29,8 @@ import static com.growingio.sdk.annotation.compiler.ProcessUtils.GENERATED_APP_MODULE_IMPL_SIMPLE_NAME; import static com.growingio.sdk.annotation.compiler.ProcessUtils.GENERATED_ROOT_MODULE_SIMPLE_NAME; import static com.growingio.sdk.annotation.compiler.ProcessUtils.GIO_LOG_TAG; -import static com.growingio.sdk.annotation.compiler.ProcessUtils.GIO_TRACKER_REGISTRY_NAME; -import static com.growingio.sdk.annotation.compiler.ProcessUtils.GIO_TRACKER_REGISTRY_PACKAGE_NAME; +import static com.growingio.sdk.annotation.compiler.ProcessUtils.GIO_TRACKER_CONTEXT_NAME; +import static com.growingio.sdk.annotation.compiler.ProcessUtils.GIO_TRACKER_CONTEXT_PACKAGE; import static com.growingio.sdk.annotation.compiler.ProcessUtils.GROWINGIO_MODULE_PACKAGE_NAME; @@ -50,9 +49,9 @@ * } * * {@literal @Override} - * public void registerComponents(Context context, TrackerRegistry registry) { - * new OkhttpLibraryGioModule().registerComponents(context,registry); - * appModule.registerComponents(context,registry); + * public void registerComponents(TrackerContext context) { + * new OkhttpLibraryGioModule().registerComponents(context); + * appModule.registerComponents(context); * } * } * @@ -94,18 +93,15 @@ void generate(TypeElement appModule, Set gioModules) { .addModifiers(Modifier.PUBLIC) .addAnnotation(Override.class) .addParameter( - ParameterSpec.builder(ClassName.get("android.content", "Context"), "context") + ParameterSpec.builder(ClassName.get(GIO_TRACKER_CONTEXT_PACKAGE, GIO_TRACKER_CONTEXT_NAME), "context") //.addAnnotation(processorUtil.nonNull()) - .build()) - .addParameter( - ParameterSpec.builder(ClassName.get(GIO_TRACKER_REGISTRY_PACKAGE_NAME, GIO_TRACKER_REGISTRY_NAME), "registry") .build()); for (String module : gioModules) { ClassName moduleClassName = ClassName.bestGuess(module); registerComponents.addStatement( - "new $T().registerComponents(context,registry)", moduleClassName); + "new $T().registerComponents(context)", moduleClassName); } - registerComponents.addStatement("appModule.registerComponents(context,registry)"); + registerComponents.addStatement("appModule.registerComponents(context)"); MethodSpec registerMethod = registerComponents.build(); TypeSpec.Builder builder = diff --git a/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/ConfigurationGenerator.kt b/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/ConfigurationGenerator.kt index 6ae820a0..c1914efd 100644 --- a/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/ConfigurationGenerator.kt +++ b/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/ConfigurationGenerator.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,11 +19,23 @@ import com.growingio.sdk.annotation.GIOAppModule import com.growingio.sdk.annotation.compiler.ProcessUtils.Companion.GIO_DEFAULT_CONFIGURABLE import com.growingio.sdk.annotation.compiler.ProcessUtils.Companion.GIO_DEFAULT_CONFIGURATION import com.growingio.sdk.annotation.compiler.ProcessUtils.Companion.GIO_DEFAULT_LIBRARY_MODULE -import com.squareup.javapoet.* +import com.squareup.javapoet.AnnotationSpec +import com.squareup.javapoet.ClassName +import com.squareup.javapoet.FieldSpec +import com.squareup.javapoet.MethodSpec +import com.squareup.javapoet.ParameterSpec +import com.squareup.javapoet.ParameterizedTypeName +import com.squareup.javapoet.TypeName +import com.squareup.javapoet.TypeSpec +import com.squareup.javapoet.TypeVariableName +import com.squareup.javapoet.WildcardTypeName import java.lang.StringBuilder -import java.util.* import javax.annotation.processing.ProcessingEnvironment -import javax.lang.model.element.* +import javax.lang.model.element.ElementKind +import javax.lang.model.element.ExecutableElement +import javax.lang.model.element.Modifier +import javax.lang.model.element.TypeElement +import javax.lang.model.element.VariableElement import javax.lang.model.type.TypeVariable import javax.lang.model.util.ElementFilter import kotlin.collections.HashMap @@ -77,7 +89,8 @@ import kotlin.collections.HashMap * @author cpacm 4/29/21 */ internal class ConfigurationGenerator( - private val processEnv: ProcessingEnvironment, private val processUtils: ProcessUtils + private val processEnv: ProcessingEnvironment, + private val processUtils: ProcessUtils, ) { private val methodMap = HashMap() @@ -99,22 +112,22 @@ internal class ConfigurationGenerator(

This class is generated and should not be modified - """.trimIndent() + """.trimIndent(), ) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - //private final CoreConfiguration coreCoreConfiguration; + // private final CoreConfiguration coreCoreConfiguration; .addField( coreConfigurationClass, "coreConfiguration", Modifier.PRIVATE, - Modifier.FINAL + Modifier.FINAL, ) val libraryModuleClass = ClassName.get( processEnv.elementUtils.getTypeElement( - GIO_DEFAULT_LIBRARY_MODULE - ) + GIO_DEFAULT_LIBRARY_MODULE, + ), ) - //private final Map, Configurable> MODULE_CONFIGURATIONS = new HashMap<>(); + // private final Map, Configurable> MODULE_CONFIGURATIONS = new HashMap<>(); val configurable = processEnv.elementUtils.getTypeElement(GIO_DEFAULT_CONFIGURABLE) val configurableClass = ClassName.get(configurable) val wildcard: TypeName = WildcardTypeName.subtypeOf(configurableClass) @@ -122,15 +135,15 @@ internal class ConfigurationGenerator( val mapOfConfigAndClass = ParameterizedTypeName.get( ClassName.get(HashMap::class.java), classAny, - configurableClass + configurableClass, ) configBuilder.addField( FieldSpec.builder(mapOfConfigAndClass, "MODULE_CONFIGURATIONS") .addModifiers(Modifier.PRIVATE, Modifier.FINAL) .initializer("new \$T()", mapOfConfigAndClass) - .build() + .build(), ) - .addMethod(generateConstructor(coreConfigurationClass, gioConfigs))//构造方法 + .addMethod(generateConstructor(coreConfigurationClass, gioConfigs)) // 构造方法 .addMethod(generateCore(coreConfigurationClass)) .addMethod(generateGetConfigModules(mapOfConfigAndClass)) .addMethod(generateAddConfiguration(generateClass, configurableClass)) @@ -139,7 +152,7 @@ internal class ConfigurationGenerator( generateClass, libraryModuleClass, configurableClass, - ) + ), ) .addMethod(generateGetConfiguration()) @@ -175,13 +188,12 @@ internal class ConfigurationGenerator( break } } - } } private fun generateConstructor( coreConfigurationClass: ClassName, - gioConfigs: Set + gioConfigs: Set, ): MethodSpec { val methodSpec = MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) @@ -189,7 +201,7 @@ internal class ConfigurationGenerator( .addParameter(String::class.java, "urlScheme") .addStatement( "this.coreConfiguration = new \$T(projectId,urlScheme)", - coreConfigurationClass + coreConfigurationClass, ) for (config in gioConfigs) { @@ -199,7 +211,8 @@ internal class ConfigurationGenerator( throw IllegalStateException("Implement Error: $config should implement $GIO_DEFAULT_CONFIGURABLE?") } methodSpec.addStatement( - "addConfiguration(new \$T())", ClassName.get(configType) + "addConfiguration(new \$T())", + ClassName.get(configType), ) } @@ -226,7 +239,7 @@ internal class ConfigurationGenerator( val addMethod = MethodSpec.methodBuilder("addConfiguration") .addParameter(config, "config") .addModifiers(Modifier.PRIVATE) - //.addAnnotation(AnnotationSpec.builder(java.lang.Deprecated::class.java).build()) + // .addAnnotation(AnnotationSpec.builder(java.lang.Deprecated::class.java).build()) .beginControlFlow("if (config != null)") .addStatement("MODULE_CONFIGURATIONS.put(config.getClass(), config)") .endControlFlow() @@ -238,7 +251,7 @@ internal class ConfigurationGenerator( private fun generateAddModuleWithConfiguration( generateClass: ClassName, libraryModule: ClassName, - config: ClassName + config: ClassName, ): MethodSpec { val addMethod = MethodSpec.methodBuilder("addPreloadComponent") .addParameter(libraryModule, "module") @@ -253,7 +266,6 @@ internal class ConfigurationGenerator( return addMethod.build() } - private fun generateGetConfiguration(): MethodSpec { val wildcard: TypeName = TypeVariableName.get("T") val classAny = ParameterizedTypeName.get(ClassName.get(Class::class.java), wildcard) @@ -264,7 +276,7 @@ internal class ConfigurationGenerator( .addAnnotation( AnnotationSpec.builder(SuppressWarnings::class.java) .addMember("value", "\$S", "unchecked") - .build() + .build(), ) .returns(TypeVariableName.get("T")) .addParameter(classAny, "clazz") @@ -276,7 +288,7 @@ internal class ConfigurationGenerator( generateClass: ClassName, config: TypeElement, method: ExecutableElement, - isCore: Boolean = false + isCore: Boolean = false, ): MethodSpec? { val enclosingClass = method.enclosingElement require(!enclosingClass.modifiers.contains(Modifier.ABSTRACT)) { "Cannot transform method on abstract class $enclosingClass" } @@ -290,7 +302,7 @@ internal class ConfigurationGenerator( throw IllegalStateException( "Duplicate method name \"$methodName\" with ${ methodMap[methodName] - } and ${config.qualifiedName}" + } and ${config.qualifiedName}", ) } methodMap[methodName] = config.qualifiedName.toString() @@ -340,7 +352,7 @@ internal class ConfigurationGenerator( stateSb.append(")") } processUtils.debugLog( - "[config method]:$generateClass----${TypeName.get(method.returnType)}" + "[config method]:$generateClass----${TypeName.get(method.returnType)}", ) if (TypeName.get(method.returnType) != TypeName.VOID) { if (ClassName.get(config).canonicalName() == TypeName.get(method.returnType) @@ -393,4 +405,4 @@ internal class ConfigurationGenerator( private fun getAllMethods(type: TypeElement): List { return ElementFilter.methodsIn(type.enclosedElements) } -} \ No newline at end of file +} diff --git a/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/GioModuleProcessor.java b/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/GioModuleProcessor.java index d3cfbe95..a3b3988d 100644 --- a/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/GioModuleProcessor.java +++ b/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/GioModuleProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.sdk.annotation.compiler; import com.google.auto.service.AutoService; diff --git a/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/GioTrackerGenerator.kt b/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/GioTrackerGenerator.kt index 2d4753eb..11af585d 100644 --- a/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/GioTrackerGenerator.kt +++ b/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/GioTrackerGenerator.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.sdk.annotation.compiler import com.growingio.sdk.annotation.GIOAppModule import com.growingio.sdk.annotation.GIOTracker -import com.growingio.sdk.annotation.compiler.ProcessUtils.Companion.GIO_CONFIGURATION_PROVIDER import com.growingio.sdk.annotation.compiler.ProcessUtils.Companion.GIO_DEFAULT_LOGGER import com.growingio.sdk.annotation.compiler.ProcessUtils.Companion.GIO_DEFAULT_TRACKER +import com.growingio.sdk.annotation.compiler.ProcessUtils.Companion.GIO_PROVIDER_FACTORY import com.squareup.javapoet.ClassName import com.squareup.javapoet.FieldSpec import com.squareup.javapoet.MethodSpec @@ -71,18 +70,19 @@ import kotlin.check * Logger.e(TAG, "Tracker is running"); * return; * } - * ConfigurationProvider.initWithConfig(trackConfiguration.core(),trackConfiguration.getConfigModules()); + * TrackerLifecycleProviderFactory.create().createConfigurationProviderWithConfig(trackConfiguration.core(),trackConfiguration.getConfigModules()); * _gioTracker = new Tracker(context); - * initSuccess(); + * initSuccess(_gioTracker.getContext().getConfigurationProvider().printAllConfigurationInfo()); * } * * private static Tracker empty() { * return new Tracker(null); * } * - * private static void initSuccess() { + * private static void initSuccess(String message) { * Logger.i(TAG, "!!! Thank you very much for using GrowingIO. We will do our best to provide you with the best service. !!!"); * Logger.i(TAG, "!!! GrowingIO Tracker version: "+SDKConfig.SDK_VERSION+" !!!"); + * Logger.d(TAG, message); * } *} * @@ -90,7 +90,7 @@ import kotlin.check */ class GioTrackerGenerator( private val processEnv: ProcessingEnvironment, - private val processUtils: ProcessUtils + private val processUtils: ProcessUtils, ) { private lateinit var trackerClassName: String @@ -111,12 +111,15 @@ class GioTrackerGenerator( val configClass = ClassName.get(generatedCodePackageName, configPath) val trackerBuilder = TypeSpec.classBuilder(growingName) .apply { - if (isDeprecated) addJavadoc( - """

In version 4.0, we use GrowingAutotracker to fully replace GrowingTracker. + if (isDeprecated) { + addJavadoc( + """

In version 4.0, we use GrowingAutotracker to fully replace GrowingTracker. |

For details, please visit our version migration document: @see https://growingio.github.io/growingio-sdk-docs/docs/android/version#release-400 | - |""".trimMargin() - ) + | + """.trimMargin(), + ) + } } .addJavadoc( """ @@ -125,7 +128,7 @@ class GioTrackerGenerator(

This class is generated and should not be modified - """.trimIndent() + """.trimIndent(), ) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addField( @@ -135,17 +138,17 @@ class GioTrackerGenerator( "TAG", Modifier.STATIC, Modifier.FINAL, - Modifier.PRIVATE + Modifier.PRIVATE, ) .initializer("\$S", growingName) - .build() + .build(), ) .addField( trackerClass, "_gioTracker", Modifier.STATIC, Modifier.PRIVATE, - Modifier.VOLATILE + Modifier.VOLATILE, ) .apply { if (isDeprecated) this.addAnnotation(Deprecated::class.java) } .addMethod(generateGetMethod(trackerClass, growingName)) @@ -158,7 +161,6 @@ class GioTrackerGenerator( writeTracker(generatedCodePackageName, trackerBuilder) } - private fun generateGetMethod(trackerClass: ClassName, growingName: String): MethodSpec { val logger = processEnv.elementUtils.getTypeElement("com.growingio.android.sdk.track.log.Logger") @@ -168,7 +170,7 @@ class GioTrackerGenerator( .addStatement( "\$T.e(TAG, \$S)", ClassName.get(logger), - "${trackerClass.simpleName()} is UNINITIALIZED, please initialized before use API" + "${trackerClass.simpleName()} is UNINITIALIZED, please initialized before use API", ) .addStatement("return empty()") .endControlFlow() @@ -179,7 +181,7 @@ class GioTrackerGenerator( .addStatement( "\$T.e(TAG, \$S)", ClassName.get(logger), - "${trackerClass.simpleName()} is UNINITIALIZED, please initialized before use API" + "${trackerClass.simpleName()} is UNINITIALIZED, please initialized before use API", ) .addStatement("return empty()") .endControlFlow() @@ -190,49 +192,50 @@ class GioTrackerGenerator( private fun generateStartConfigurationMethod( appModule: TypeElement, trackerClass: ClassName, - configClass: ClassName + configClass: ClassName, ): MethodSpec { val logger = processEnv.elementUtils.getTypeElement(GIO_DEFAULT_LOGGER) val context = processEnv.elementUtils.getTypeElement("android.content.Context") val getMethod = MethodSpec.methodBuilder("startWithConfiguration") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - //.addAnnotation(java.lang.Deprecated::class.java) + // .addAnnotation(java.lang.Deprecated::class.java) .addParameter(ClassName.get(context), "context") .addParameter(configClass, "trackConfiguration") .beginControlFlow("if (_gioTracker != null)") .addStatement( "\$T.e(TAG, \$S)", ClassName.get(logger), - "${trackerClass.simpleName()} is running" + "${trackerClass.simpleName()} is running", ) .addStatement("return") .endControlFlow() if (trackerMethodName != null) { - //GrowingAppModule appModule = new GrowingAppModule(); + // GrowingAppModule appModule = new GrowingAppModule(); getMethod.addStatement("\$T appModule = new \$T()", appModule, appModule) - //appModule.config(trackConfiguration); - .addStatement("appModule.${trackerMethodName}(trackConfiguration)") + // appModule.config(trackConfiguration); + .addStatement("appModule.$trackerMethodName(trackConfiguration)") } - val configProvider = processEnv.elementUtils.getTypeElement(GIO_CONFIGURATION_PROVIDER) - //ConfigurationProvider.initWithConfig(trackConfiguration.core(),trackConfiguration.getConfigModules()); + val providerFactory = processEnv.elementUtils.getTypeElement(GIO_PROVIDER_FACTORY) + // TrackerLifecycleProviderFactory.create().createConfigurationProviderWithConfig(trackConfiguration.core(),trackConfiguration.getConfigModules()); getMethod.addStatement( - "\$T.initWithConfig(trackConfiguration.core(),trackConfiguration.getConfigModules())", - ClassName.get(configProvider) + "\$T.create().createConfigurationProviderWithConfig(trackConfiguration.core(),trackConfiguration.getConfigModules())", + ClassName.get(providerFactory), ) getMethod.addStatement( "_gioTracker = new \$T(context)", - trackerClass + trackerClass, ) - getMethod.addStatement("initSuccess()") + // _gioTracker.getContext().getConfigurationProvider().printAllConfigurationInfo() + getMethod.addStatement("initSuccess(_gioTracker.getContext().getConfigurationProvider().printAllConfigurationInfo())") return getMethod.build() } private fun generateStartMethod( appModule: TypeElement, trackerClass: ClassName, - configClass: ClassName + configClass: ClassName, ): MethodSpec { val logger = processEnv.elementUtils.getTypeElement(GIO_DEFAULT_LOGGER) @@ -244,18 +247,18 @@ class GioTrackerGenerator( .addStatement( "\$T.e(TAG, \$S)", ClassName.get(logger), - "${trackerClass.simpleName()} is running" + "${trackerClass.simpleName()} is running", ) .addStatement("return") .endControlFlow() if (trackerMethodName == null) { - getMethod.addStatement("throw new IllegalStateException(\"If you want use start() method, you must use @GIOTracker in GIOAppModule and rebuild project.\")"); + getMethod.addStatement("throw new IllegalStateException(\"If you want use start() method, you must use @GIOTracker in GIOAppModule and rebuild project.\")") return getMethod.build() } if (projectId.isNullOrEmpty() || urlScheme.isNullOrEmpty()) { - getMethod.addStatement("throw new IllegalStateException(\"If you want use start() method, you must define ProjectId and UrlScheme in @GIOTracker annotation\")"); + getMethod.addStatement("throw new IllegalStateException(\"If you want use start() method, you must define ProjectId and UrlScheme in @GIOTracker annotation\")") return getMethod.build() } @@ -264,26 +267,27 @@ class GioTrackerGenerator( configClass, configClass, projectId, - urlScheme + urlScheme, ) - //GrowingAppModule appModule = new GrowingAppModule(); + // GrowingAppModule appModule = new GrowingAppModule(); getMethod.addStatement("\$T appModule = new \$T()", appModule, appModule) - //appModule.config(trackConfiguration); - .addStatement("appModule.${trackerMethodName}(trackConfiguration)") + // appModule.config(trackConfiguration); + .addStatement("appModule.$trackerMethodName(trackConfiguration)") - val configProvider = processEnv.elementUtils.getTypeElement(GIO_CONFIGURATION_PROVIDER) - //ConfigurationProvider.initWithConfig(trackConfiguration.core(),trackConfiguration.getConfigModules()); + val providerFactory = processEnv.elementUtils.getTypeElement(GIO_PROVIDER_FACTORY) + // TrackerLifecycleProviderFactory.create().createConfigurationProviderWithConfig(trackConfiguration.core(),trackConfiguration.getConfigModules()); getMethod.addStatement( - "\$T.initWithConfig(trackConfiguration.core(),trackConfiguration.getConfigModules())", - ClassName.get(configProvider) + "\$T.create().createConfigurationProviderWithConfig(trackConfiguration.core(),trackConfiguration.getConfigModules())", + ClassName.get(providerFactory), ) getMethod.addStatement( "_gioTracker = new \$T(context)", - trackerClass + trackerClass, ) - getMethod.addStatement("initSuccess()") + // _gioTracker.getContext().getConfigurationProvider().printAllConfigurationInfo() + getMethod.addStatement("initSuccess(_gioTracker.getContext().getConfigurationProvider().printAllConfigurationInfo())") return getMethod.build() } @@ -300,28 +304,23 @@ class GioTrackerGenerator( processEnv.elementUtils.getTypeElement(GIO_DEFAULT_LOGGER) val sdk = processEnv.elementUtils.getTypeElement("com.growingio.android.sdk.track.SDKConfig") - val configProvider = processEnv.elementUtils.getTypeElement(GIO_CONFIGURATION_PROVIDER) val successMethod = MethodSpec.methodBuilder("initSuccess") .addModifiers(Modifier.PRIVATE, Modifier.STATIC) + .addParameter(String::class.java, "message") .addStatement( "\$T.i(TAG, \$S)", ClassName.get(logger), - "!!! Thank you very much for using GrowingIO. We will do our best to provide you with the best service. !!!" + "!!! Thank you very much for using GrowingIO. We will do our best to provide you with the best service. !!!", ) .addStatement( "\$T.i(TAG, \"!!! GrowingIO Tracker version: \"+\$T.SDK_VERSION+\" !!!\")", ClassName.get(logger), - ClassName.get(sdk) - ) - .addStatement( - "\$T.d(TAG, \$T.get().getAllConfigurationInfo())", - ClassName.get(logger), - ClassName.get(configProvider) + ClassName.get(sdk), ) + .addStatement("\$T.d(TAG, message)", ClassName.get(logger)) return successMethod.build() } - private fun initGIOConfig(appModule: TypeElement) { val tracker = processUtils.findAnnotatedElementsInClasses(appModule, GIOTracker::class.java) processUtils.debugLog(tracker.toString()) @@ -358,5 +357,4 @@ class GioTrackerGenerator( private fun writeTracker(packageName: String, gio: TypeSpec) { processUtils.writeClass(packageName, gio) } - -} \ No newline at end of file +} diff --git a/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/IndexerGenerator.java b/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/IndexerGenerator.java index 20cd5df0..95dac4d1 100644 --- a/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/IndexerGenerator.java +++ b/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/IndexerGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.sdk.annotation.compiler; import com.growingio.sdk.annotation.GIOLibraryModule; diff --git a/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/ProcessUtils.kt b/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/ProcessUtils.kt index 211b93c5..c7b38c48 100644 --- a/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/ProcessUtils.kt +++ b/growingio-annotation/compiler/src/main/java/com/growingio/sdk/annotation/compiler/ProcessUtils.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,20 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.sdk.annotation.compiler import com.growingio.sdk.annotation.GIOAppModule import com.squareup.javapoet.JavaFile import com.squareup.javapoet.TypeSpec -import java.util.* import javax.annotation.processing.ProcessingEnvironment import javax.annotation.processing.RoundEnvironment -import javax.lang.model.element.* +import javax.lang.model.element.Element +import javax.lang.model.element.ExecutableElement +import javax.lang.model.element.TypeElement import javax.lang.model.util.ElementFilter import javax.tools.Diagnostic - /** *

* @@ -78,7 +77,7 @@ class ProcessUtils(val processEnv: ProcessingEnvironment) { fun findAnnotatedElementsInClasses( inClass: TypeElement, - annotationClass: Class + annotationClass: Class, ): List { val result: MutableList = ArrayList() for (element in inClass.enclosedElements) { @@ -115,13 +114,11 @@ class ProcessUtils(val processEnv: ProcessingEnvironment) { .skipJavaLangImports(true) .build() .writeTo(processEnv.getFiler()) - } catch (e: Throwable) { throw RuntimeException(e) } } - companion object { const val GROWINGIO_MODULE_PACKAGE_NAME = "com.growingio.android.sdk" const val GROWINGIO_MODULE_NAME = "LibraryGioModule" @@ -144,17 +141,17 @@ class ProcessUtils(val processEnv: ProcessingEnvironment) { const val GENERATED_APP_MODULE_IMPL_SIMPLE_NAME = "GeneratedGioModuleImpl" const val GENERATED_ROOT_MODULE_SIMPLE_NAME = "GeneratedGioModule" - const val GIO_TRACKER_REGISTRY_PACKAGE_NAME = "com.growingio.android.sdk.track.modelloader" - const val GIO_TRACKER_REGISTRY_NAME = "TrackerRegistry" + const val GIO_TRACKER_CONTEXT_PACKAGE = "com.growingio.android.sdk" + const val GIO_TRACKER_CONTEXT_NAME = "TrackerContext" const val GIO_DEFAULT_TRACKER = "com.growingio.android.sdk.Tracker" const val GIO_DEFAULT_CONFIGURATION = "com.growingio.android.sdk.CoreConfiguration" const val GIO_DEFAULT_CONFIGURABLE = "com.growingio.android.sdk.Configurable" const val GIO_DEFAULT_LIBRARY_MODULE = "com.growingio.android.sdk.LibraryGioModule" const val GIO_DEFAULT_LOGGER = "com.growingio.android.sdk.track.log.Logger" - const val GIO_CONFIGURATION_PROVIDER="com.growingio.android.sdk.track.providers.ConfigurationProvider" + const val GIO_PROVIDER_FACTORY = "com.growingio.android.sdk.track.providers.TrackerLifecycleProviderFactory" const val GIO_INDEX_ANNOTATION_MODULES = "modules" const val GIO_INDEX_ANNOTATION_CONFIGS = "configs" } -} \ No newline at end of file +} diff --git a/growingio-annotation/compiler/src/test/java/android/app/Application.java b/growingio-annotation/compiler/src/test/java/android/app/Application.java index 9e1784a6..8bee8acc 100644 --- a/growingio-annotation/compiler/src/test/java/android/app/Application.java +++ b/growingio-annotation/compiler/src/test/java/android/app/Application.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/growingio-annotation/compiler/src/test/java/android/content/Context.java b/growingio-annotation/compiler/src/test/java/android/content/Context.java index 532cf282..75104455 100644 --- a/growingio-annotation/compiler/src/test/java/android/content/Context.java +++ b/growingio-annotation/compiler/src/test/java/android/content/Context.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package android.content; diff --git a/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/EmptyAppModuleTest.kt b/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/EmptyAppModuleTest.kt index 9026035f..1f2ea872 100644 --- a/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/EmptyAppModuleTest.kt +++ b/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/EmptyAppModuleTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ class EmptyAppModuleTest : CompilationProvider { @Before fun setUp() { compilation = Compiler.javac().withProcessors(GioModuleProcessor()).compile( - forResource(MODULE_NAME) + forResource(MODULE_NAME), ) assertThat(compilation).succeededWithoutWarnings() } @@ -51,7 +51,6 @@ class EmptyAppModuleTest : CompilationProvider { assertThat(compilation!!).generatedSourceFile(TestUtil.subpackage("TestTrackConfiguration")) .hasSourceEquivalentTo(forResource("TestTrackConfiguration.java")) - } private fun forResource(name: String): JavaFileObject { @@ -65,4 +64,4 @@ class EmptyAppModuleTest : CompilationProvider { override fun getCompilation(): Compilation { return compilation!! } -} \ No newline at end of file +} diff --git a/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/EmptyModuleTest.kt b/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/EmptyModuleTest.kt index 0679dae4..2c46d1ef 100644 --- a/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/EmptyModuleTest.kt +++ b/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/EmptyModuleTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ import com.google.testing.compile.CompilationSubject.assertThat import com.google.testing.compile.Compiler import com.growingio.sdk.annotation.compiler.rule.CompilationProvider import org.junit.Before -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 @@ -35,7 +34,7 @@ class EmptyModuleTest : CompilationProvider { @Before fun setUp() { compilation = Compiler.javac().withProcessors(GioModuleProcessor()).compile( - forResource(MODULE_NAME) + forResource(MODULE_NAME), ) assertThat(compilation).succeededWithoutWarnings() } @@ -51,7 +50,6 @@ class EmptyModuleTest : CompilationProvider { assertThat(compilation!!).generatedSourceFile(TestUtil.annotation(expectedClassName)) .hasSourceEquivalentTo(forResource(expectedClassName + ".java")) - } private fun forResource(name: String): JavaFileObject { @@ -65,4 +63,4 @@ class EmptyModuleTest : CompilationProvider { override fun getCompilation(): Compilation { return compilation!! } -} \ No newline at end of file +} diff --git a/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/TestUtil.java b/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/TestUtil.java index 58299c5c..cd4e5250 100644 --- a/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/TestUtil.java +++ b/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/TestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.sdk.annotation.compiler; import com.google.testing.compile.JavaFileObjects; diff --git a/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/rule/CompilationProvider.java b/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/rule/CompilationProvider.java index 778a3e6b..1f72d18b 100644 --- a/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/rule/CompilationProvider.java +++ b/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/rule/CompilationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.sdk.annotation.compiler.rule; import com.google.testing.compile.Compilation; diff --git a/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/sample/EmptyConfig.java b/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/sample/EmptyConfig.java index 44a3bef3..ad5b08ad 100644 --- a/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/sample/EmptyConfig.java +++ b/growingio-annotation/compiler/src/test/java/com/growingio/sdk/annotation/compiler/sample/EmptyConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/growingio-annotation/compiler/src/test/resources/EmptyAppModuleTest/EmptyAppGioModule.java b/growingio-annotation/compiler/src/test/resources/EmptyAppModuleTest/EmptyAppGioModule.java index 0de22bdb..a91c9bb6 100644 --- a/growingio-annotation/compiler/src/test/resources/EmptyAppModuleTest/EmptyAppGioModule.java +++ b/growingio-annotation/compiler/src/test/resources/EmptyAppModuleTest/EmptyAppGioModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package com.growingio.android.sdk.test; import com.growingio.android.sdk.AppGioModule; import com.growingio.android.sdk.Tracker; -import com.growingio.android.sdk.CoreConfiguration; import com.growingio.sdk.annotation.GIOAppModule; import com.growingio.sdk.annotation.GIOTracker; import com.growingio.sdk.annotation.compiler.sample.EmptyConfig; diff --git a/growingio-annotation/compiler/src/test/resources/EmptyAppModuleTest/GeneratedGioModuleImpl.java b/growingio-annotation/compiler/src/test/resources/EmptyAppModuleTest/GeneratedGioModuleImpl.java index ef5ab853..aade8b17 100644 --- a/growingio-annotation/compiler/src/test/resources/EmptyAppModuleTest/GeneratedGioModuleImpl.java +++ b/growingio-annotation/compiler/src/test/resources/EmptyAppModuleTest/GeneratedGioModuleImpl.java @@ -1,24 +1,22 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk; import android.content.Context; import com.growingio.android.sdk.test.EmptyAppGioModule; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; @SuppressWarnings("deprecation") final class GeneratedGioModuleImpl extends GeneratedGioModule { @@ -29,7 +27,7 @@ public GeneratedGioModuleImpl(Context context) { } @Override - public void registerComponents(Context context, TrackerRegistry registry) { - appModule.registerComponents(context,registry); + public void registerComponents(TrackerContext context) { + appModule.registerComponents(context); } } \ No newline at end of file diff --git a/growingio-annotation/compiler/src/test/resources/EmptyAppModuleTest/GrowingTracker.java b/growingio-annotation/compiler/src/test/resources/EmptyAppModuleTest/GrowingTracker.java index c2c5e694..82f828c9 100644 --- a/growingio-annotation/compiler/src/test/resources/EmptyAppModuleTest/GrowingTracker.java +++ b/growingio-annotation/compiler/src/test/resources/EmptyAppModuleTest/GrowingTracker.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.test; import android.content.Context; @@ -21,7 +20,7 @@ import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.track.SDKConfig; import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProviderFactory; /** * The entry point for interacting with GrowingIO Tracker for Applications @@ -64,18 +63,18 @@ public static void startWithConfiguration(Context context, } EmptyAppGioModule appModule = new EmptyAppGioModule(); appModule.config(trackConfiguration); - ConfigurationProvider.initWithConfig(trackConfiguration.core(),trackConfiguration.getConfigModules()); + TrackerLifecycleProviderFactory.create().createConfigurationProviderWithConfig(trackConfiguration.core(),trackConfiguration.getConfigModules()); _gioTracker = new Tracker(context); - initSuccess(); + initSuccess(_gioTracker.getContext().getConfigurationProvider().printAllConfigurationInfo()); } private static Tracker empty() { return new Tracker(null); } - private static void initSuccess() { + private static void initSuccess(String message) { Logger.i(TAG, "!!! Thank you very much for using GrowingIO. We will do our best to provide you with the best service. !!!"); Logger.i(TAG, "!!! GrowingIO Tracker version: "+SDKConfig.SDK_VERSION+" !!!"); - Logger.d(TAG, ConfigurationProvider.get().getAllConfigurationInfo()); + Logger.d(TAG, message); } } \ No newline at end of file diff --git a/growingio-annotation/compiler/src/test/resources/EmptyAppModuleTest/TestTrackConfiguration.java b/growingio-annotation/compiler/src/test/resources/EmptyAppModuleTest/TestTrackConfiguration.java index 0bc0f6bd..791717ae 100644 --- a/growingio-annotation/compiler/src/test/resources/EmptyAppModuleTest/TestTrackConfiguration.java +++ b/growingio-annotation/compiler/src/test/resources/EmptyAppModuleTest/TestTrackConfiguration.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.test; import com.growingio.android.sdk.Configurable; diff --git a/growingio-annotation/compiler/src/test/resources/EmptyModuleTest/EmptyLibraryGioModule.java b/growingio-annotation/compiler/src/test/resources/EmptyModuleTest/EmptyLibraryGioModule.java index 52fafa7d..08974f4e 100644 --- a/growingio-annotation/compiler/src/test/resources/EmptyModuleTest/EmptyLibraryGioModule.java +++ b/growingio-annotation/compiler/src/test/resources/EmptyModuleTest/EmptyLibraryGioModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,6 @@ * limitations under the License. */ package com.growingio.android.sdk; -import com.growingio.android.sdk.LibraryGioModule; import com.growingio.sdk.annotation.compiler.sample.EmptyConfig; import com.growingio.sdk.annotation.GIOLibraryModule; diff --git a/growingio-annotation/compiler/src/test/resources/EmptyModuleTest/GioIndexer_GIOLibraryModule_com_growingio_android_sdk_EmptyLibraryGioModule.java b/growingio-annotation/compiler/src/test/resources/EmptyModuleTest/GioIndexer_GIOLibraryModule_com_growingio_android_sdk_EmptyLibraryGioModule.java index 7986f64b..4c1e6cd4 100644 --- a/growingio-annotation/compiler/src/test/resources/EmptyModuleTest/GioIndexer_GIOLibraryModule_com_growingio_android_sdk_EmptyLibraryGioModule.java +++ b/growingio-annotation/compiler/src/test/resources/EmptyModuleTest/GioIndexer_GIOLibraryModule_com_growingio_android_sdk_EmptyLibraryGioModule.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.growingio.sdk.annotation.compiler; @Index( diff --git a/growingio-annotation/json-serializer/build.gradle b/growingio-annotation/json-serializer/build.gradle index 440c7fe3..30d4eb9a 100644 --- a/growingio-annotation/json-serializer/build.gradle +++ b/growingio-annotation/json-serializer/build.gradle @@ -1,25 +1,25 @@ plugins { - id 'java-library' - id 'kotlin' + id 'java-library' + id 'kotlin' } java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } dependencies { - testImplementation libs.junit + testImplementation libs.junit - implementation libs.kotlin.stdlib + implementation libs.kotlin.stdlib - annotationProcessor libs.google.auto.service - compileOnly libs.google.auto.service.annotations - implementation libs.square.javapoet + annotationProcessor libs.google.auto.service + compileOnly libs.google.auto.service.annotations + implementation libs.square.javapoet - implementation project(":growingio-annotation") - implementation "androidx.annotation:annotation:1.3.0" + implementation project(":growingio-annotation") + implementation "androidx.annotation:annotation:1.3.0" } \ No newline at end of file diff --git a/growingio-annotation/json-serializer/src/main/java/com/growingio/sdk/annotation/json/serializer/JsonFactoryGenerator.kt b/growingio-annotation/json-serializer/src/main/java/com/growingio/sdk/annotation/json/serializer/JsonFactoryGenerator.kt index 40ed24f9..9f775464 100644 --- a/growingio-annotation/json-serializer/src/main/java/com/growingio/sdk/annotation/json/serializer/JsonFactoryGenerator.kt +++ b/growingio-annotation/json-serializer/src/main/java/com/growingio/sdk/annotation/json/serializer/JsonFactoryGenerator.kt @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.sdk.annotation.json.serializer import com.growingio.sdk.annotation.json.JsonSerializer @@ -26,7 +25,6 @@ import com.squareup.javapoet.TypeSpec import javax.annotation.processing.ProcessingEnvironment import javax.lang.model.element.Modifier import javax.lang.model.element.TypeElement -import javax.lang.model.element.VariableElement /** *

@@ -35,7 +33,7 @@ import javax.lang.model.element.VariableElement */ internal class JsonFactoryGenerator( private val processEnv: ProcessingEnvironment, - private val processUtils: ProcessUtils + private val processUtils: ProcessUtils, ) { fun generate(typeElements: List) { val sortedList = arrayListOf() @@ -60,7 +58,7 @@ internal class JsonFactoryGenerator( val baseEventElement = sortedList.last() val annotation: JsonSerializer = baseEventElement.getAnnotation( - JsonSerializer::class.java + JsonSerializer::class.java, ) val builderName: String = annotation.builder @@ -78,29 +76,29 @@ internal class JsonFactoryGenerator( val superinterface = ParameterizedTypeName.get( ClassName.get( ProcessUtils.JSON_SERIALIZABLE_PACKAGE, - ProcessUtils.JSON_SERIALIZABLE_CLASS - ),//rawType - ClassName.get(eventType),//the value for T - builderType, //the value for R + ProcessUtils.JSON_SERIALIZABLE_CLASS, + ), // rawType + ClassName.get(eventType), // the value for T + builderType, // the value for R ) val jonSerialBuilder = TypeSpec.classBuilder(generateClass).addJavadoc( """

This class is generated and should not be modified - """.trimIndent() + """.trimIndent(), ).addModifiers(Modifier.PUBLIC, Modifier.FINAL).addSuperinterface(superinterface) .addMethod( generateToJsonMethod( ClassName.get(eventType), sortedList, - ) + ), ) .addMethod( generateParseFromMethod( builderType, - sortedList - ) + sortedList, + ), ) processUtils.writeClass(JSON_SERIALIZABLE_PACKAGE, jonSerialBuilder.build()) @@ -109,9 +107,9 @@ internal class JsonFactoryGenerator( processUtils.debugLog(it.simpleName.toString()) /** * if (event instanceof CustomEvent) { - CustomEventJsonSerializableFactory.create().toJson(jsonObject, (CustomEvent) event); - return; - } + CustomEventJsonSerializableFactory.create().toJson(jsonObject, (CustomEvent) event); + return; + } */ } } @@ -124,8 +122,9 @@ internal class JsonFactoryGenerator( .addParameter( ClassName.get( ProcessUtils.JSON_OBJECT_PACKAGE, - ProcessUtils.JSON_OBJECT_CLASS - ), "jsonObject" + ProcessUtils.JSON_OBJECT_CLASS, + ), + "jsonObject", ) .addParameter(eventType, "event").addModifiers(Modifier.PUBLIC) .addAnnotation(Override::class.java) @@ -139,7 +138,7 @@ internal class JsonFactoryGenerator( toJsonMethod.addStatement( "\$T.create().toJson(jsonObject, (\$T) event)", eventClass, - element + element, ) toJsonMethod.addStatement("return") toJsonMethod.endControlFlow() @@ -156,8 +155,9 @@ internal class JsonFactoryGenerator( .addParameter( ClassName.get( ProcessUtils.JSON_OBJECT_PACKAGE, - ProcessUtils.JSON_OBJECT_CLASS - ), "jsonObject" + ProcessUtils.JSON_OBJECT_CLASS, + ), + "jsonObject", ) .addModifiers(Modifier.PUBLIC).addAnnotation(Override::class.java) sortedList.forEach { element -> @@ -166,7 +166,7 @@ internal class JsonFactoryGenerator( val eventClass = ClassName.get(eventPackageName, eventName) val annotation: JsonSerializer = element.getAnnotation( - JsonSerializer::class.java + JsonSerializer::class.java, ) val builderName: String = annotation.builder val eventBuilderType = @@ -177,20 +177,19 @@ internal class JsonFactoryGenerator( parseFromMethod.addStatement( "\$T.create().parseFrom((\$T) builder, jsonObject)", eventClass, - elementBuilderType + elementBuilderType, ) parseFromMethod.addStatement("return") parseFromMethod.endControlFlow() } - return parseFromMethod.build() } private fun searchElementInSortedList( targetElement: TypeElement, sortedList: List, - typeElements: List + typeElements: List, ): Int { val index = sortedList.indexOf(targetElement) if (index == -1) { @@ -204,4 +203,4 @@ internal class JsonFactoryGenerator( } return index } -} \ No newline at end of file +} diff --git a/growingio-annotation/json-serializer/src/main/java/com/growingio/sdk/annotation/json/serializer/JsonSerializerGenerator.kt b/growingio-annotation/json-serializer/src/main/java/com/growingio/sdk/annotation/json/serializer/JsonSerializerGenerator.kt index 41ae0820..f1458faf 100644 --- a/growingio-annotation/json-serializer/src/main/java/com/growingio/sdk/annotation/json/serializer/JsonSerializerGenerator.kt +++ b/growingio-annotation/json-serializer/src/main/java/com/growingio/sdk/annotation/json/serializer/JsonSerializerGenerator.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,10 +30,18 @@ import com.growingio.sdk.annotation.json.serializer.ProcessUtils.Companion.JSON_ import com.growingio.sdk.annotation.json.serializer.ProcessUtils.Companion.JSON_SERIALIZABLE_PACKAGE import com.growingio.sdk.annotation.json.serializer.ProcessUtils.Companion.TEXT_UTILS_CLASS import com.growingio.sdk.annotation.json.serializer.ProcessUtils.Companion.TEXT_UTILS_PACKAGE -import com.squareup.javapoet.* +import com.squareup.javapoet.ClassName +import com.squareup.javapoet.FieldSpec +import com.squareup.javapoet.MethodSpec +import com.squareup.javapoet.ParameterizedTypeName +import com.squareup.javapoet.TypeName +import com.squareup.javapoet.TypeSpec import java.lang.Exception import javax.annotation.processing.ProcessingEnvironment -import javax.lang.model.element.* +import javax.lang.model.element.Element +import javax.lang.model.element.Modifier +import javax.lang.model.element.TypeElement +import javax.lang.model.element.VariableElement import javax.lang.model.type.TypeKind /** @@ -43,11 +51,12 @@ import javax.lang.model.type.TypeKind * @author cpacm 7/13/23 */ internal class JsonSerializerGenerator( - private val processEnv: ProcessingEnvironment, private val processUtils: ProcessUtils + private val processEnv: ProcessingEnvironment, + private val processUtils: ProcessUtils, ) { fun generate(jsonSerializer: TypeElement, containSuperElement: Boolean) { val annotation: JsonSerializer = jsonSerializer.getAnnotation( - JsonSerializer::class.java + JsonSerializer::class.java, ) val builderName: String = annotation.builder @@ -75,16 +84,16 @@ internal class JsonSerializerGenerator( val builderType = ClassName.get(eventBuilderType) val superinterface = ParameterizedTypeName.get( - ClassName.get(JSON_SERIALIZABLE_PACKAGE, JSON_SERIALIZABLE_CLASS),//rawType - ClassName.get(eventType),//the value for T - builderType, //the value for R + ClassName.get(JSON_SERIALIZABLE_PACKAGE, JSON_SERIALIZABLE_CLASS), // rawType + ClassName.get(eventType), // the value for T + builderType, // the value for R ) val jonSerialBuilder = TypeSpec.classBuilder(generateClass).addJavadoc( """

This class is generated and should not be modified - """.trimIndent() + """.trimIndent(), ).addModifiers(Modifier.PUBLIC, Modifier.FINAL).addSuperinterface(superinterface) .addField(generateInstanceField(generateClass)) .addMethod(generateStaticCreateMethod(generateClass, generatedName)) @@ -93,8 +102,8 @@ internal class JsonSerializerGenerator( ClassName.get(eventType), fields, jsonSerializer, - containSuperElement - ) + containSuperElement, + ), ) .addMethod( generateParseFromMethod( @@ -102,8 +111,8 @@ internal class JsonSerializerGenerator( builderName, fields, jsonSerializer, - containSuperElement - ) + containSuperElement, + ), ) processUtils.writeClass(generatedCodePackageName, jonSerialBuilder.build()) @@ -141,7 +150,7 @@ internal class JsonSerializerGenerator( eventType: TypeName, fields: MutableList, clazz: TypeElement, - containSuperElement: Boolean + containSuperElement: Boolean, ): MethodSpec { val toJsonMethod = MethodSpec.methodBuilder("toJson") .addParameter(ClassName.get(JSON_OBJECT_PACKAGE, JSON_OBJECT_CLASS), "jsonObject") @@ -175,12 +184,12 @@ internal class JsonSerializerGenerator( val annotation = field.getAnnotation(IntRange::class.java) if (annotation != null) { val state = if (annotation.from == annotation.to) { - "if (event.${fieldMethod} != ${annotation.from})" + "if (event.$fieldMethod != ${annotation.from})" } else { val hasFrom = - if (annotation.from != Long.MIN_VALUE) "event.${fieldMethod} > ${annotation.from}" else "" + if (annotation.from != Long.MIN_VALUE) "event.$fieldMethod > ${annotation.from}" else "" val hasTo = - if (annotation.to != Long.MAX_VALUE) "event.${fieldMethod} < ${annotation.to}" else "" + if (annotation.to != Long.MAX_VALUE) "event.$fieldMethod < ${annotation.to}" else "" if (hasFrom.isNotEmpty() && hasTo.isNotEmpty()) { "if ($hasFrom && $hasTo)" } else if (hasFrom.isEmpty() && hasTo.isEmpty()) { @@ -191,26 +200,26 @@ internal class JsonSerializerGenerator( } if (state != null) { toJsonMethod.beginControlFlow(state) - .addStatement("jsonObject.put(\"${fieldName}\", event.${fieldMethod})") + .addStatement("jsonObject.put(\"${fieldName}\", event.$fieldMethod)") .endControlFlow() } else { - toJsonMethod.addStatement("jsonObject.put(\"${fieldName}\", event.${fieldMethod})") + toJsonMethod.addStatement("jsonObject.put(\"${fieldName}\", event.$fieldMethod)") } } else { - toJsonMethod.addStatement("jsonObject.put(\"${fieldName}\", event.${fieldMethod})") + toJsonMethod.addStatement("jsonObject.put(\"${fieldName}\", event.$fieldMethod)") } } else if (field.asType().kind == TypeKind.DOUBLE || field.asType().kind == TypeKind.FLOAT) { val annotation = field.getAnnotation(FloatRange::class.java) if (annotation != null) { val state = if (annotation.from == annotation.to) { - "if (event.${fieldMethod} != ${annotation.from})" + "if (event.$fieldMethod != ${annotation.from})" } else { val fromEqual = if (annotation.fromInclusive) ">=" else ">" val toEqual = if (annotation.toInclusive) "<=" else "<" val hasFrom = - if (annotation.from != Double.NEGATIVE_INFINITY) "event.${fieldMethod} $fromEqual ${annotation.from}" else "" + if (annotation.from != Double.NEGATIVE_INFINITY) "event.$fieldMethod $fromEqual ${annotation.from}" else "" val hasTo = - if (annotation.to != Double.POSITIVE_INFINITY) "event.${fieldMethod} $toEqual ${annotation.to}" else "" + if (annotation.to != Double.POSITIVE_INFINITY) "event.$fieldMethod $toEqual ${annotation.to}" else "" if (hasFrom.isNotEmpty() && hasTo.isNotEmpty()) { "if ($hasFrom && $hasTo)" } else if (hasFrom.isEmpty() && hasTo.isEmpty()) { @@ -221,25 +230,25 @@ internal class JsonSerializerGenerator( } if (state != null) { toJsonMethod.beginControlFlow(state) - .addStatement("jsonObject.put(\"${fieldName}\", event.${fieldMethod})") + .addStatement("jsonObject.put(\"${fieldName}\", event.$fieldMethod)") .endControlFlow() } else { - toJsonMethod.addStatement("jsonObject.put(\"${fieldName}\", event.${fieldMethod})") + toJsonMethod.addStatement("jsonObject.put(\"${fieldName}\", event.$fieldMethod)") } } else { - toJsonMethod.addStatement("jsonObject.put(\"${fieldName}\", event.${fieldMethod})") + toJsonMethod.addStatement("jsonObject.put(\"${fieldName}\", event.$fieldMethod)") } } else if ("java.lang.String" == field.asType().toString()) { val annotation = field.getAnnotation(Nullable::class.java) if (annotation != null) { toJsonMethod.beginControlFlow( - "if(!\$T.isEmpty(event.${fieldMethod}))", - ClassName.get(TEXT_UTILS_PACKAGE, TEXT_UTILS_CLASS) + "if(!\$T.isEmpty(event.$fieldMethod))", + ClassName.get(TEXT_UTILS_PACKAGE, TEXT_UTILS_CLASS), ) - .addStatement("jsonObject.put(\"${fieldName}\", event.${fieldMethod})") + .addStatement("jsonObject.put(\"${fieldName}\", event.$fieldMethod)") .endControlFlow() } else { - toJsonMethod.addStatement("jsonObject.put(\"${fieldName}\", event.${fieldMethod})") + toJsonMethod.addStatement("jsonObject.put(\"${fieldName}\", event.$fieldMethod)") } } else if ("java.util.Map" == field.asType() .toString() @@ -247,25 +256,25 @@ internal class JsonSerializerGenerator( val annotation = field.getAnnotation(Nullable::class.java) if (annotation != null) { toJsonMethod.beginControlFlow( - "if(event.${fieldMethod} != null && !event.${fieldMethod}.isEmpty())" + "if(event.$fieldMethod != null && !event.$fieldMethod.isEmpty())", ).addStatement( - "jsonObject.put(\"${fieldName}\", new \$T(event.${fieldMethod}))", - ClassName.get(JSON_OBJECT_PACKAGE, JSON_OBJECT_CLASS) + "jsonObject.put(\"${fieldName}\", new \$T(event.$fieldMethod))", + ClassName.get(JSON_OBJECT_PACKAGE, JSON_OBJECT_CLASS), ).endControlFlow() } else { toJsonMethod.addStatement( - "jsonObject.put(\"${fieldName}\", new \$T(event.${fieldMethod}))", - ClassName.get(JSON_OBJECT_PACKAGE, JSON_OBJECT_CLASS) + "jsonObject.put(\"${fieldName}\", new \$T(event.$fieldMethod))", + ClassName.get(JSON_OBJECT_PACKAGE, JSON_OBJECT_CLASS), ) } } else { - toJsonMethod.addStatement("jsonObject.put(\"${fieldName}\", event.${fieldMethod})") + toJsonMethod.addStatement("jsonObject.put(\"${fieldName}\", event.$fieldMethod)") } } toJsonMethod.nextControlFlow( "catch (\$T ignored)", - ClassName.get(JSON_OBJECT_PACKAGE, JSON_OBJECT_EXCEPTION) + ClassName.get(JSON_OBJECT_PACKAGE, JSON_OBJECT_EXCEPTION), ).endControlFlow() } @@ -281,7 +290,7 @@ internal class JsonSerializerGenerator( builderName: String, fields: MutableList, clazz: TypeElement, - containSuperElement: Boolean + containSuperElement: Boolean, ): MethodSpec { val parseFromMethod = MethodSpec.methodBuilder("parseFrom").addParameter(parameterizedBuilderType, "builder") @@ -289,7 +298,7 @@ internal class JsonSerializerGenerator( .addModifiers(Modifier.PUBLIC).addAnnotation(Override::class.java) if (containSuperElement) { - //BaseEventJsonSerializableFactory.create().parseFrom(builder, jsonObject); + // BaseEventJsonSerializableFactory.create().parseFrom(builder, jsonObject); val superElement = clazz.superclass val superClassName = superElement.toString().substringAfterLast(".") val superPackageName = superElement.toString().substringBeforeLast(".") @@ -317,82 +326,82 @@ internal class JsonSerializerGenerator( } if (field.asType().kind == TypeKind.INT) { parseFromMethod.addStatement( - "builder.${fieldMethod}".replace( + "builder.$fieldMethod".replace( FIELD_REPLACE_REG, - "jsonObject.optInt(\"$fieldName\")" - ) + "jsonObject.optInt(\"$fieldName\")", + ), ) } else if (field.asType().kind == TypeKind.LONG) { parseFromMethod.addStatement( - "builder.${fieldMethod}".replace( + "builder.$fieldMethod".replace( FIELD_REPLACE_REG, - "jsonObject.optLong(\"$fieldName\")" - ) + "jsonObject.optLong(\"$fieldName\")", + ), ) } else if (field.asType().kind == TypeKind.FLOAT) { parseFromMethod.addStatement( - "builder.${fieldMethod}".replace( + "builder.$fieldMethod".replace( FIELD_REPLACE_REG, - "jsonObject.optFloat(\"$fieldName\")" - ) + "jsonObject.optFloat(\"$fieldName\")", + ), ) } else if (field.asType().kind == TypeKind.DOUBLE) { parseFromMethod.addStatement( - "builder.${fieldMethod}".replace( + "builder.$fieldMethod".replace( FIELD_REPLACE_REG, - "jsonObject.optDouble(\"$fieldName\")" - ) + "jsonObject.optDouble(\"$fieldName\")", + ), ) } else if (field.asType().kind == TypeKind.BOOLEAN) { parseFromMethod.addStatement( - "builder.${fieldMethod}".replace( + "builder.$fieldMethod".replace( FIELD_REPLACE_REG, - "jsonObject.optBoolean(\"$fieldName\")" - ) + "jsonObject.optBoolean(\"$fieldName\")", + ), ) } else if ("java.lang.String" == field.asType().toString()) { parseFromMethod.addStatement( - "builder.${fieldMethod}".replace( + "builder.$fieldMethod".replace( FIELD_REPLACE_REG, - "jsonObject.optString(\"$fieldName\")" - ) + "jsonObject.optString(\"$fieldName\")", + ), ) } else if ("java.util.Map" == field.asType() .toString() ) { parseFromMethod.addStatement( "\$T json = jsonObject.optJSONObject(\"$fieldName\")", - ClassName.get(JSON_OBJECT_PACKAGE, JSON_OBJECT_CLASS) + ClassName.get(JSON_OBJECT_PACKAGE, JSON_OBJECT_CLASS), ) parseFromMethod.addStatement( "\$T map = new \$T()", ParameterizedTypeName.get( ClassName.get(Map::class.java), ClassName.get(String::class.java), - ClassName.get(String::class.java) + ClassName.get(String::class.java), ), ParameterizedTypeName.get( ClassName.get(HashMap::class.java), ClassName.get(String::class.java), - ClassName.get(String::class.java) - ) + ClassName.get(String::class.java), + ), ) parseFromMethod.addStatement( "\$T keys = json.keys()", ParameterizedTypeName.get( ClassName.get(Iterator::class.java), - ClassName.get(String::class.java) - ) + ClassName.get(String::class.java), + ), ) parseFromMethod.beginControlFlow("while (keys.hasNext())") parseFromMethod.addStatement("\$T key = keys.next()", String::class.java) parseFromMethod.addStatement("map.put(key, json.getString(key))") parseFromMethod.endControlFlow() parseFromMethod.addStatement( - "builder.${fieldMethod}".replace( + "builder.$fieldMethod".replace( FIELD_REPLACE_REG, - "map" - ) + "map", + ), ) } else { processUtils.debugLog("unsupported data type.") @@ -401,12 +410,10 @@ internal class JsonSerializerGenerator( parseFromMethod.nextControlFlow( "catch (\$T ignored)", - Exception::class.java + Exception::class.java, ).endControlFlow() } return parseFromMethod.build() } - - -} \ No newline at end of file +} diff --git a/growingio-annotation/json-serializer/src/main/java/com/growingio/sdk/annotation/json/serializer/JsonSerializerProcessor.java b/growingio-annotation/json-serializer/src/main/java/com/growingio/sdk/annotation/json/serializer/JsonSerializerProcessor.java index dbbc8e3a..e5ca4e3c 100644 --- a/growingio-annotation/json-serializer/src/main/java/com/growingio/sdk/annotation/json/serializer/JsonSerializerProcessor.java +++ b/growingio-annotation/json-serializer/src/main/java/com/growingio/sdk/annotation/json/serializer/JsonSerializerProcessor.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.sdk.annotation.json.serializer; import com.google.auto.service.AutoService; diff --git a/growingio-annotation/json-serializer/src/main/java/com/growingio/sdk/annotation/json/serializer/ProcessUtils.kt b/growingio-annotation/json-serializer/src/main/java/com/growingio/sdk/annotation/json/serializer/ProcessUtils.kt index 77470f85..7a8459d5 100644 --- a/growingio-annotation/json-serializer/src/main/java/com/growingio/sdk/annotation/json/serializer/ProcessUtils.kt +++ b/growingio-annotation/json-serializer/src/main/java/com/growingio/sdk/annotation/json/serializer/ProcessUtils.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.sdk.annotation.json.serializer import com.squareup.javapoet.JavaFile @@ -54,7 +53,7 @@ class ProcessUtils(val processEnv: ProcessingEnvironment) { if (findFieldResult != null) { return "$findFieldResult = $FIELD_REPLACE_REG" } - return null; + return null } fun findGetFieldMethod(field: VariableElement, clazz: TypeElement): String? { @@ -76,15 +75,15 @@ class ProcessUtils(val processEnv: ProcessingEnvironment) { private fun findField( field: VariableElement, - clazz: TypeElement + clazz: TypeElement, ): String? { val findFieldResult = clazz.enclosedElements.filterIsInstance().find { it.simpleName.toString() == field.simpleName.toString() && field.asType().kind == it.asType().kind } - if (findFieldResult != null - && !findFieldResult.modifiers.contains(Modifier.PRIVATE) - && !findFieldResult.modifiers.contains(Modifier.FINAL) - && !findFieldResult.modifiers.contains(Modifier.STATIC) + if (findFieldResult != null && + !findFieldResult.modifiers.contains(Modifier.PRIVATE) && + !findFieldResult.modifiers.contains(Modifier.FINAL) && + !findFieldResult.modifiers.contains(Modifier.STATIC) ) { return findFieldResult.simpleName.toString() } @@ -105,19 +104,16 @@ class ProcessUtils(val processEnv: ProcessingEnvironment) { return null } - fun writeClass(packageName: String?, clazz: TypeSpec) { try { debugLog("Writing class:\n$clazz") JavaFile.builder(packageName, clazz).skipJavaLangImports(true).build() .writeTo(processEnv.getFiler()) - } catch (e: Throwable) { throw RuntimeException(e) } } - companion object { const val DEBUG = false @@ -135,6 +131,5 @@ class ProcessUtils(val processEnv: ProcessingEnvironment) { const val TEXT_UTILS_CLASS = "TextUtils" const val FIELD_REPLACE_REG = "#_FIELD_#" - } -} \ No newline at end of file +} diff --git a/growingio-annotation/src/main/java/com/growingio/sdk/annotation/GIOAppModule.java b/growingio-annotation/src/main/java/com/growingio/sdk/annotation/GIOAppModule.java index a41918d0..c2f0d83e 100644 --- a/growingio-annotation/src/main/java/com/growingio/sdk/annotation/GIOAppModule.java +++ b/growingio-annotation/src/main/java/com/growingio/sdk/annotation/GIOAppModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.sdk.annotation; import java.lang.annotation.ElementType; diff --git a/growingio-annotation/src/main/java/com/growingio/sdk/annotation/GIOConfig.java b/growingio-annotation/src/main/java/com/growingio/sdk/annotation/GIOConfig.java index c04305e9..c5921d18 100644 --- a/growingio-annotation/src/main/java/com/growingio/sdk/annotation/GIOConfig.java +++ b/growingio-annotation/src/main/java/com/growingio/sdk/annotation/GIOConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.sdk.annotation; import java.lang.annotation.ElementType; diff --git a/growingio-annotation/src/main/java/com/growingio/sdk/annotation/GIOLibraryModule.java b/growingio-annotation/src/main/java/com/growingio/sdk/annotation/GIOLibraryModule.java index be9c6ae4..6912532f 100644 --- a/growingio-annotation/src/main/java/com/growingio/sdk/annotation/GIOLibraryModule.java +++ b/growingio-annotation/src/main/java/com/growingio/sdk/annotation/GIOLibraryModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.sdk.annotation; import java.lang.annotation.ElementType; diff --git a/growingio-annotation/src/main/java/com/growingio/sdk/annotation/GIOTracker.java b/growingio-annotation/src/main/java/com/growingio/sdk/annotation/GIOTracker.java index 68a4a06f..2e506fc1 100644 --- a/growingio-annotation/src/main/java/com/growingio/sdk/annotation/GIOTracker.java +++ b/growingio-annotation/src/main/java/com/growingio/sdk/annotation/GIOTracker.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/growingio-annotation/src/main/java/com/growingio/sdk/annotation/compiler/Index.java b/growingio-annotation/src/main/java/com/growingio/sdk/annotation/compiler/Index.java index 4613482b..522ebac7 100644 --- a/growingio-annotation/src/main/java/com/growingio/sdk/annotation/compiler/Index.java +++ b/growingio-annotation/src/main/java/com/growingio/sdk/annotation/compiler/Index.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.sdk.annotation.compiler; import java.lang.annotation.ElementType; diff --git a/growingio-annotation/src/main/java/com/growingio/sdk/annotation/json/JsonAlias.java b/growingio-annotation/src/main/java/com/growingio/sdk/annotation/json/JsonAlias.java index 2b612152..1b75671c 100644 --- a/growingio-annotation/src/main/java/com/growingio/sdk/annotation/json/JsonAlias.java +++ b/growingio-annotation/src/main/java/com/growingio/sdk/annotation/json/JsonAlias.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.sdk.annotation.json; import java.lang.annotation.ElementType; diff --git a/growingio-annotation/src/main/java/com/growingio/sdk/annotation/json/JsonIgnore.java b/growingio-annotation/src/main/java/com/growingio/sdk/annotation/json/JsonIgnore.java index 7afc55ce..4d49c708 100644 --- a/growingio-annotation/src/main/java/com/growingio/sdk/annotation/json/JsonIgnore.java +++ b/growingio-annotation/src/main/java/com/growingio/sdk/annotation/json/JsonIgnore.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.sdk.annotation.json; import java.lang.annotation.ElementType; diff --git a/growingio-annotation/src/main/java/com/growingio/sdk/annotation/json/JsonSerializer.java b/growingio-annotation/src/main/java/com/growingio/sdk/annotation/json/JsonSerializer.java index 81fd0671..36b59583 100644 --- a/growingio-annotation/src/main/java/com/growingio/sdk/annotation/json/JsonSerializer.java +++ b/growingio-annotation/src/main/java/com/growingio/sdk/annotation/json/JsonSerializer.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.sdk.annotation.json; import java.lang.annotation.ElementType; diff --git a/growingio-apm/build.gradle b/growingio-apm/build.gradle index 9f1bafe2..e5d14708 100644 --- a/growingio-apm/build.gradle +++ b/growingio-apm/build.gradle @@ -1,59 +1,58 @@ plugins { - id 'com.android.library' + id 'com.android.library' } android { - namespace 'com.growingio.android.apm' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - consumerProguardFiles 'consumer-rules.pro' - } - } - - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } - - testOptions { - unitTests.all { - jacoco { - jvmArgs '-noverify' - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - } - } + namespace 'com.growingio.android.apm' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + consumerProguardFiles 'consumer-rules.pro' + } + } + + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } + + testOptions { + unitTests.all { + jacoco { + jvmArgs '-noverify' + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } } dependencies { - implementation libs.growingio.gmonitor + implementation libs.growingio.gmonitor - testImplementation libs.bundles.test - testImplementation libs.androidx.test.core + testImplementation libs.bundles.test + testImplementation libs.androidx.test.core - testImplementation project(path: ':growingio-tracker-core') + testImplementation project(path: ':growingio-tracker-core') - compileOnly project(':growingio-tracker-core') - implementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + compileOnly project(':growingio-tracker-core') + implementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" diff --git a/growingio-apm/src/main/java/com/growingio/android/apm/ApmConfig.java b/growingio-apm/src/main/java/com/growingio/android/apm/ApmConfig.java index 3acdc1d2..a628e179 100644 --- a/growingio-apm/src/main/java/com/growingio/android/apm/ApmConfig.java +++ b/growingio-apm/src/main/java/com/growingio/android/apm/ApmConfig.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.apm; import com.growingio.android.sdk.Configurable; diff --git a/growingio-apm/src/main/java/com/growingio/android/apm/ApmDataLoader.java b/growingio-apm/src/main/java/com/growingio/android/apm/ApmDataLoader.java index b0b37d7d..678195a9 100644 --- a/growingio-apm/src/main/java/com/growingio/android/apm/ApmDataLoader.java +++ b/growingio-apm/src/main/java/com/growingio/android/apm/ApmDataLoader.java @@ -1,31 +1,29 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.apm; -import android.content.Context; import com.growingio.android.gmonitor.GMonitor; import com.growingio.android.gmonitor.GMonitorOption; import com.growingio.android.sdk.CoreConfiguration; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.middleware.apm.EventApm; import com.growingio.android.sdk.track.modelloader.ModelLoader; import com.growingio.android.sdk.track.modelloader.ModelLoaderFactory; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; /** *

@@ -42,10 +40,10 @@ public LoadData buildLoadData(EventApm eventApm) { public static class Factory implements ModelLoaderFactory { - protected Factory(Context context) { + protected Factory(TrackerContext context) { if (GMonitor.getInstance() == null) { - CoreConfiguration core = ConfigurationProvider.core(); - ApmConfig apmConfig = ConfigurationProvider.get().getConfiguration(ApmConfig.class); + CoreConfiguration core = context.getConfigurationProvider().core(); + ApmConfig apmConfig = context.getConfigurationProvider().getConfiguration(ApmConfig.class); if (apmConfig == null) apmConfig = new ApmConfig(); GMonitorOption option = new GMonitorOption(); @@ -65,8 +63,6 @@ protected Factory(Context context) { option.setAnrTimeoutIntervalMillis(apmConfig.getAnrTimeoutIntervalMillis()); ApmTracker apmTracker = new ApmTracker(); - ConfigurationProvider.get().addConfigurationListener(apmTracker); - Logger.d("Apm", "init gmonitor success"); GMonitor.init(context, new ApmLogger(), option, apmTracker); } diff --git a/growingio-apm/src/main/java/com/growingio/android/apm/ApmEventBuilder.java b/growingio-apm/src/main/java/com/growingio/android/apm/ApmEventBuilder.java index d0c4bdf4..51cac173 100644 --- a/growingio-apm/src/main/java/com/growingio/android/apm/ApmEventBuilder.java +++ b/growingio-apm/src/main/java/com/growingio/android/apm/ApmEventBuilder.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.apm; import com.growingio.android.gmonitor.event.Breadcrumb; diff --git a/growingio-apm/src/main/java/com/growingio/android/apm/ApmLibraryGioModule.java b/growingio-apm/src/main/java/com/growingio/android/apm/ApmLibraryGioModule.java index 1f679f81..1ce09208 100644 --- a/growingio-apm/src/main/java/com/growingio/android/apm/ApmLibraryGioModule.java +++ b/growingio-apm/src/main/java/com/growingio/android/apm/ApmLibraryGioModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,14 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.apm; -import android.content.Context; import com.growingio.android.sdk.LibraryGioModule; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.middleware.apm.EventApm; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import com.growingio.sdk.annotation.GIOLibraryModule; /** @@ -31,7 +29,7 @@ @GIOLibraryModule public class ApmLibraryGioModule extends LibraryGioModule { @Override - public void registerComponents(Context context, TrackerRegistry registry) { - registry.register(EventApm.class, Void.class, new ApmDataLoader.Factory(context)); + public void registerComponents(TrackerContext context) { + context.getRegistry().register(EventApm.class, Void.class, new ApmDataLoader.Factory(context)); } } diff --git a/growingio-apm/src/main/java/com/growingio/android/apm/ApmLogger.java b/growingio-apm/src/main/java/com/growingio/android/apm/ApmLogger.java index 106e450f..4d97a7e3 100644 --- a/growingio-apm/src/main/java/com/growingio/android/apm/ApmLogger.java +++ b/growingio-apm/src/main/java/com/growingio/android/apm/ApmLogger.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.apm; import android.util.Log; diff --git a/growingio-apm/src/main/java/com/growingio/android/apm/ApmTracker.java b/growingio-apm/src/main/java/com/growingio/android/apm/ApmTracker.java index 28e944f2..23464467 100644 --- a/growingio-apm/src/main/java/com/growingio/android/apm/ApmTracker.java +++ b/growingio-apm/src/main/java/com/growingio/android/apm/ApmTracker.java @@ -1,29 +1,24 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.apm; import com.growingio.android.gmonitor.ITracker; import com.growingio.android.gmonitor.event.Breadcrumb; -import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.events.CustomEvent; -import com.growingio.android.sdk.track.listener.OnConfigurationChangeListener; -import com.growingio.android.sdk.track.log.CircularFifoQueue; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; /** *

@@ -31,31 +26,18 @@ * * @author cpacm 2022/9/27 */ -class ApmTracker implements ITracker, OnConfigurationChangeListener { - - private final CircularFifoQueue caches = new CircularFifoQueue<>(100); +class ApmTracker implements ITracker { @Override public void trackBreadcrumb(Breadcrumb breadcrumb) { - caches.add(breadcrumb); - if (!TrackerContext.initializedSuccessfully() || !ConfigurationProvider.core().isDataCollectionEnabled()) { - return; - } - releaseCaches(); - } - - public void releaseCaches() { - if (caches.size() > 0 && TrackerContext.initializedSuccessfully() && ConfigurationProvider.core().isDataCollectionEnabled()) { - for (Breadcrumb bc : caches) { - CustomEvent.Builder builder = ApmEventBuilder.filterWithApmBreadcrumb(bc); - if (bc.getType().equals(Breadcrumb.TYPE_ERROR)) { - // 遇到异常时,不能通过Handler保存事件. - TrackMainThread.trackMain().buildEventSendRunnable(builder).run(); - } else { - TrackMainThread.trackMain().postEventToTrackMain(builder); - } - } - caches.clear(); + if (breadcrumb == null) return; + + CustomEvent.Builder builder = ApmEventBuilder.filterWithApmBreadcrumb(breadcrumb); + if (breadcrumb.getType().equals(Breadcrumb.TYPE_ERROR)) { + // 遇到异常时,不能通过Handler保存事件. + TrackMainThread.trackMain().cacheEventSync(builder); + } else { + TrackMainThread.trackMain().cacheEventToTrackMain(builder); } } @@ -63,11 +45,4 @@ public void releaseCaches() { public ITracker clone() { return new ApmTracker(); } - - @Override - public void onDataCollectionChanged(boolean isEnable) { - if (isEnable) { - releaseCaches(); - } - } } diff --git a/growingio-apm/src/main/java/com/growingio/android/apm/GrowingApm.java b/growingio-apm/src/main/java/com/growingio/android/apm/GrowingApm.java deleted file mode 100644 index 564f3e4f..00000000 --- a/growingio-apm/src/main/java/com/growingio/android/apm/GrowingApm.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.growingio.android.apm; - -import android.content.Context; - -import com.growingio.android.gmonitor.GMonitor; -import com.growingio.android.gmonitor.GMonitorOption; - -/** - *

- * - * @author cpacm 2022/9/29 - */ -public class GrowingApm { - - private GrowingApm() { - } - - public static void startWithConfiguration(Context context, ApmConfig apmConfig) { - if (GMonitor.getInstance() == null) { - GMonitorOption option = new GMonitorOption(); - option.setDebug(false); - option.setAvoidRunningAppProcesses(false); - option.setEnableActivityLifecycleTracing(apmConfig.isActivityLifecycleTracing()); - - option.setEnableFragmentXLifecycleTracing(apmConfig.isFragmentXLifecycleTracing()); - option.setEnableFragmentSupportLifecycleTracing(apmConfig.isFragmentSupportLifecycleTracing()); - option.setEnableFragmentSystemLifecycleTracing(apmConfig.isFragmentSystemLifecycleTracing()); - - option.setEnableUncaughtExceptionHandler(apmConfig.isUncaughtException()); - option.setPrintUncaughtStackTrace(apmConfig.isPrintUncaughtException()); - - option.setAnrInDebug(apmConfig.isAnrInDebug()); - option.setEnableAnr(apmConfig.isAnrTracing()); - option.setAnrTimeoutIntervalMillis(apmConfig.getAnrTimeoutIntervalMillis()); - - GMonitor.init(context, new ApmLogger(), option, new ApmTracker()); - } - } -} diff --git a/growingio-apm/src/test/java/com/growingio/android/apm/ApmTest.java b/growingio-apm/src/test/java/com/growingio/android/apm/ApmTest.java index bb139a7b..34c8d418 100644 --- a/growingio-apm/src/test/java/com/growingio/android/apm/ApmTest.java +++ b/growingio-apm/src/test/java/com/growingio/android/apm/ApmTest.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.apm; import static com.growingio.android.apm.ApmEventBuilder.EVENT_APP_LAUNCHTIME_NAME; @@ -30,10 +29,11 @@ import com.growingio.android.gmonitor.utils.ExceptionHelper; import com.growingio.android.sdk.Configurable; import com.growingio.android.sdk.CoreConfiguration; +import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.events.helper.DefaultEventFilterInterceptor; -import com.growingio.android.sdk.track.middleware.apm.EventApm; import com.growingio.android.sdk.track.providers.ConfigurationProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProviderFactory; import org.junit.Before; import org.junit.Test; @@ -53,10 +53,11 @@ public class ApmTest { private final Application application = ApplicationProvider.getApplicationContext(); + private TrackerContext context; + @Before public void setup() { - TrackerContext.init(application); ApmConfig config = new ApmConfig(); config.setActivityLifecycleTracing(true) .setFragmentXLifecycleTracing(true) @@ -66,19 +67,22 @@ public void setup() { .setPrintUncaughtException(false); HashMap, Configurable> configs = new HashMap<>(); configs.put(config.getClass(), config); - ConfigurationProvider.initWithConfig(new CoreConfiguration("ApmTest", "growingio://apm"), configs); - TrackerContext.initSuccess(); + + TrackerLifecycleProviderFactory.create().createConfigurationProviderWithConfig(new CoreConfiguration("ApmTest", "growingio://apm"), configs); + Tracker tracker = new Tracker(application); + ApmLibraryGioModule module = new ApmLibraryGioModule(); + tracker.registerComponent(module); + context = tracker.getContext(); } @Test public void apmModuleTest() { - ApmLibraryGioModule module = new ApmLibraryGioModule(); - module.registerComponents(application, TrackerContext.get().getRegistry()); Truth.assertThat(GMonitor.getInstance()).isNotNull(); + ConfigurationProvider configurationProvider = context.getConfigurationProvider(); - ApmConfig config = ConfigurationProvider.get().getConfiguration(ApmConfig.class); + ApmConfig config = configurationProvider.getConfiguration(ApmConfig.class); GMonitorOption options = GMonitor.getInstance().getOption(); - Truth.assertThat(options.getAvoidRunningAppProcesses()).isEqualTo(!ConfigurationProvider.core().isRequireAppProcessesEnabled()); + Truth.assertThat(options.getAvoidRunningAppProcesses()).isEqualTo(!configurationProvider.core().isRequireAppProcessesEnabled()); Truth.assertThat(options.getEnableActivityLifecycleTracing()).isEqualTo(config.isActivityLifecycleTracing()); Truth.assertThat(options.getEnableFragmentXLifecycleTracing()).isEqualTo(config.isFragmentXLifecycleTracing()); Truth.assertThat(options.getEnableFragmentSupportLifecycleTracing()).isEqualTo(config.isFragmentSupportLifecycleTracing()); @@ -101,8 +105,8 @@ public boolean filterEventName(String eventName) { } } - ConfigurationProvider.core().setEventFilterInterceptor(new ActivityFilter()); - TrackerContext.get().getRegistry().register(EventApm.class, Void.class, new ApmDataLoader.Factory(application)); + ConfigurationProvider configurationProvider = context.getConfigurationProvider(); + configurationProvider.core().setEventFilterInterceptor(new ActivityFilter()); Breadcrumb breadcrumb = new Breadcrumb(Breadcrumb.TYPE_PERFORMANCE, Breadcrumb.CATEGORY_PERFORMANCE_ACTIVITY, null); breadcrumb.putData(Breadcrumb.ATTR_PERFORMANCE_PAGE_NAME, "TestActivity"); @@ -135,9 +139,8 @@ public boolean filterEventName(String eventName) { return super.filterEventName(eventName); } } - - ConfigurationProvider.core().setEventFilterInterceptor(new ActivityFilter()); - TrackerContext.get().getRegistry().register(EventApm.class, Void.class, new ApmDataLoader.Factory(application)); + ConfigurationProvider configurationProvider = context.getConfigurationProvider(); + configurationProvider.core().setEventFilterInterceptor(new ActivityFilter()); NullPointerException e = new NullPointerException("NPE FOR TEST"); Breadcrumb breadcrumb = new Breadcrumb(Breadcrumb.TYPE_ERROR, Breadcrumb.CATEGORY_ERROR_EXCEPTION, e.getMessage()); @@ -145,16 +148,4 @@ public boolean filterEventName(String eventName) { breadcrumb.putData(Breadcrumb.ATTR_ERROR_MESSAGE, ExceptionHelper.INSTANCE.getThrowableMessage(e)); GMonitor.getInstance().trackBreadcrumb(breadcrumb); } - - @Test - public void growingioApmTest() { - if (GMonitor.getInstance() != null) { - GMonitor.getInstance().close(); - } - ApmConfig config = ConfigurationProvider.get().getConfiguration(ApmConfig.class); - GrowingApm.startWithConfiguration(application, config); - Truth.assertThat(GMonitor.getInstance()).isNotNull(); - - Truth.assertThat(GMonitor.getInstance().getOption().getLogger().isEnabled(0)).isTrue(); - } } diff --git a/growingio-autotracker-core/build.gradle b/growingio-autotracker-core/build.gradle index 27eca5b7..61de9309 100644 --- a/growingio-autotracker-core/build.gradle +++ b/growingio-autotracker-core/build.gradle @@ -1,76 +1,76 @@ plugins { - id 'com.android.library' - // id 'org.jetbrains.kotlin.android' + id 'com.android.library' + // id 'org.jetbrains.kotlin.android' } android { - namespace 'com.growingio.android.sdk.autotrack' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } + namespace 'com.growingio.android.sdk.autotrack' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } - buildTypes { -// debug { -// testCoverageEnabled = true -// } + buildTypes { + // debug { + // testCoverageEnabled = true + // } - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - consumerProguardFiles 'consumer-proguard-rules.pro' - } - } + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + consumerProguardFiles 'consumer-proguard-rules.pro' + } + } - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } - testOptions { - unitTests.all { - jacoco { - jvmArgs '-noverify' - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - } - } + testOptions { + unitTests.all { + jacoco { + jvmArgs '-noverify' + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } - lintOptions { - abortOnError false - } + lintOptions { + abortOnError false + } } dependencies { -// implementation libs.kotlin.stdlib + // implementation libs.kotlin.stdlib - compileOnly fileTree(dir: '../thirdLibs', include: ['*.jar']) - compileOnly libs.bundles.androidx - compileOnly libs.google.material - compileOnly libs.bundles.android.support + compileOnly fileTree(dir: '../thirdLibs', include: ['*.jar']) + compileOnly libs.bundles.androidx + compileOnly libs.google.material + compileOnly libs.bundles.android.support - testImplementation fileTree(dir: '../thirdLibs', include: ['*.jar']) - testImplementation libs.bundles.test - testImplementation libs.bundles.androidx - testImplementation libs.androidx.test.core - testImplementation libs.androidx.fragment.test - testImplementation libs.protobuf.javalite + testImplementation fileTree(dir: '../thirdLibs', include: ['*.jar']) + testImplementation libs.bundles.test + testImplementation libs.bundles.androidx + testImplementation libs.androidx.test.core + testImplementation libs.androidx.fragment.test + testImplementation libs.protobuf.javalite - testCompileOnly libs.support.fragment + testCompileOnly libs.support.fragment - debugApi project(':growingio-tracker-core') - releaseApi libs.growingio.tracker.core - - implementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + debugApi project(':growingio-tracker-core') + releaseApi libs.growingio.tracker.core + + implementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/AutotrackConfig.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/AutotrackConfig.java index b12aaf66..d774d03d 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/AutotrackConfig.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/AutotrackConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack; import com.growingio.android.sdk.Configurable; diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/AutotrackOptions.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/AutotrackOptions.java index dbbade68..329f9376 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/AutotrackOptions.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/AutotrackOptions.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack; /** diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/Autotracker.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/Autotracker.java index ad22300f..76e697dc 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/Autotracker.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/Autotracker.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack; import android.app.Activity; @@ -22,14 +21,16 @@ import android.view.View; import android.widget.EditText; -import com.growingio.android.sdk.autotrack.inject.ViewChangeProvider; +import com.growingio.android.sdk.autotrack.inject.InjectorProvider; import com.growingio.android.sdk.autotrack.impression.ImpressionProvider; import com.growingio.android.sdk.autotrack.page.PageProvider; import com.growingio.android.sdk.autotrack.page.SuperFragment; import com.growingio.android.sdk.autotrack.view.ViewAttributeUtil; import com.growingio.android.sdk.Tracker; +import com.growingio.android.sdk.autotrack.view.ViewNodeProvider; +import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.utils.ThreadUtils; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; import java.util.HashMap; import java.util.Map; @@ -43,11 +44,15 @@ public Autotracker(Context context) { } @Override - protected void setup(Context context) { - super.setup(context); - PageProvider.get().setup(); - ViewChangeProvider viewChangeProvider = new ViewChangeProvider(); - viewChangeProvider.setup(); + protected Map, TrackerLifecycleProvider> extraProviders() { + + Map, TrackerLifecycleProvider> providerMap = super.extraProviders(); + InjectorProvider.injectProviders(providerMap); + providerMap.put(PageProvider.class, PageProvider.get()); + providerMap.put(ViewNodeProvider.class, new ViewNodeProvider()); + providerMap.put(ImpressionProvider.class, new ImpressionProvider()); + + return providerMap; } /** @@ -75,14 +80,15 @@ public void autotrackPage(Activity activity, String alias) { * @param alias page's name * @param attributes page's attributes. Will be carried to the autotracker event. */ - public void autotrackPage(Activity activity, String alias, final Map attributes) { + public void autotrackPage(Activity activity, String alias, + final Map attributes) { if (!isInited) return; if (activity == null || TextUtils.isEmpty(alias)) { Logger.e(TAG, "fragment or alias is NULL"); return; } Logger.d(TAG, "make activity page: " + activity.getClass() + " active."); - ThreadUtils.runOnUiThread(() -> PageProvider.get().autotrackActivity(activity, alias, attributes)); + TrackMainThread.trackMain().runOnUiThread(() -> PageProvider.get().autotrackActivity(activity, alias, attributes)); } private void autotrackPage(androidx.fragment.app.Fragment fragment) { @@ -106,14 +112,15 @@ public void autotrackPage(androidx.fragment.app.Fragment fragment, String alias) * @param alias page's name * @param attributes page's attributes. Will be carried to the autotracker event. */ - public void autotrackPage(androidx.fragment.app.Fragment fragment, String alias, final Map attributes) { + public void autotrackPage(androidx.fragment.app.Fragment fragment, String alias, + final Map attributes) { if (!isInited) return; if (fragment == null || TextUtils.isEmpty(alias)) { Logger.e(TAG, "fragment or alias is NULL"); return; } Logger.d(TAG, "make fragment page: " + fragment.getClass() + " active."); - ThreadUtils.runOnUiThread(() -> PageProvider.get().autotrackFragment(SuperFragment.makeX(fragment), alias, attributes)); + TrackMainThread.trackMain().runOnUiThread(() -> PageProvider.get().autotrackFragment(SuperFragment.makeX(fragment), alias, attributes)); } private void autotrackSystemPage(android.app.Fragment fragment) { @@ -137,14 +144,15 @@ public void autotrackSystemPage(android.app.Fragment fragment, String alias) { * @param alias page's name * @param attributes page's attributes. Will be carried to the autotracker event. */ - public void autotrackSystemPage(android.app.Fragment fragment, String alias, final Map attributes) { + public void autotrackSystemPage(android.app.Fragment fragment, String alias, + final Map attributes) { if (!isInited) return; if (fragment == null || TextUtils.isEmpty(alias)) { Logger.e(TAG, "fragment or alias is NULL"); return; } Logger.d(TAG, "make fragment page: " + fragment.getClass() + " active."); - ThreadUtils.runOnUiThread(() -> PageProvider.get().autotrackFragment(SuperFragment.make(fragment), alias, attributes)); + TrackMainThread.trackMain().runOnUiThread(() -> PageProvider.get().autotrackFragment(SuperFragment.make(fragment), alias, attributes)); } private void autotrackSupportPage(android.support.v4.app.Fragment fragment) { @@ -168,14 +176,15 @@ public void autotrackSupportPage(android.support.v4.app.Fragment fragment, Strin * @param alias page's name * @param attributes page's attributes. Will be carried to the autotracker event. */ - public void autotrackSupportPage(android.support.v4.app.Fragment fragment, String alias, final Map attributes) { + public void autotrackSupportPage(android.support.v4.app.Fragment fragment, String alias, + final Map attributes) { if (!isInited) return; if (fragment == null || TextUtils.isEmpty(alias)) { Logger.e(TAG, "fragment or alias is NULL"); return; } Logger.d(TAG, "make fragment page: " + fragment.getClass() + " active."); - ThreadUtils.runOnUiThread(() -> PageProvider.get().autotrackFragment(SuperFragment.makeSupport(fragment), alias, attributes)); + TrackMainThread.trackMain().runOnUiThread(() -> PageProvider.get().autotrackFragment(SuperFragment.makeSupport(fragment), alias, attributes)); } /** @@ -189,7 +198,7 @@ public void setUniqueTag(final View view, final String tag) { return; } Logger.d(TAG, "setUniqueTag: " + tag + " for " + view.getClass().getSimpleName()); - ThreadUtils.runOnUiThread(new Runnable() { + TrackMainThread.trackMain().runOnUiThread(new Runnable() { @Override public void run() { ViewAttributeUtil.setCustomId(view, tag); @@ -201,13 +210,14 @@ public void run() { * will be carried to the autotracker event's attribute. * note: please use autotrackPage generate page first. @see {@link #autotrackPage(Activity, String, Map)} */ - public void setPageAttributes(final Activity activity, final Map attributes) { + public void setPageAttributes(final Activity activity, + final Map attributes) { if (!isInited) return; if (activity == null) { Logger.e(TAG, "page or attributes is NULL"); return; } - ThreadUtils.runOnUiThread(() -> { + TrackMainThread.trackMain().runOnUiThread(() -> { PageProvider.get().setPageAttributes(activity, attributes); }); } @@ -216,43 +226,47 @@ public void setPageAttributes(final Activity activity, final Map * will be carried to the autotracker event's attribute. * note: please use autotrackSystemPage generate page first. @see {@link #autotrackSystemPage(android.app.Fragment, String, Map)} */ - public void setPageAttributesSystem(final android.app.Fragment fragment, final Map attributes) { + public void setPageAttributesSystem(final android.app.Fragment fragment, + final Map attributes) { if (!isInited) return; if (fragment == null) { Logger.e(TAG, "page or attributes is NULL"); return; } - ThreadUtils.runOnUiThread(() -> PageProvider.get().setPageAttributes(SuperFragment.make(fragment), attributes == null ? new HashMap<>() : new HashMap<>(attributes))); + TrackMainThread.trackMain().runOnUiThread(() -> PageProvider.get().setPageAttributes(SuperFragment.make(fragment), attributes == null ? new HashMap<>() : new HashMap<>(attributes))); } /** * will be carried to the autotracker event's attribute. * note: please use autotrackSupportPage generate page first. @see {@link #autotrackSupportPage(android.support.v4.app.Fragment, String, Map)} */ - public void setPageAttributesSupport(final android.support.v4.app.Fragment page, final Map attributes) { + public void setPageAttributesSupport(final android.support.v4.app.Fragment page, + final Map attributes) { if (!isInited) return; if (page == null) { Logger.e(TAG, "page or attributes is NULL"); return; } - ThreadUtils.runOnUiThread(() -> PageProvider.get().setPageAttributes(SuperFragment.makeSupport(page), attributes == null ? new HashMap<>() : new HashMap<>(attributes))); + TrackMainThread.trackMain().runOnUiThread(() -> PageProvider.get().setPageAttributes(SuperFragment.makeSupport(page), attributes == null ? new HashMap<>() : new HashMap<>(attributes))); } /** * will be carried to the autotracker event's attribute. * note: please use autotrackPage generate page first. @see {@link #autotrackPage(androidx.fragment.app.Fragment, String, Map)} */ - public void setPageAttributes(final androidx.fragment.app.Fragment page, final Map attributes) { + public void setPageAttributes(final androidx.fragment.app.Fragment page, + final Map attributes) { if (!isInited) return; if (page == null) { Logger.e(TAG, "page or attributes is NULL"); return; } - ThreadUtils.runOnUiThread(() -> PageProvider.get().setPageAttributes(SuperFragment.makeX(page), attributes == null ? new HashMap<>() : new HashMap<>(attributes))); + TrackMainThread.trackMain().runOnUiThread(() -> PageProvider.get().setPageAttributes(SuperFragment.makeX(page), attributes == null ? new HashMap<>() : new HashMap<>(attributes))); } @Deprecated - public void setPageAttributesX(final androidx.fragment.app.Fragment page, final Map attributes) { + public void setPageAttributesX(final androidx.fragment.app.Fragment page, + final Map attributes) { setPageAttributes(page, attributes); } @@ -261,7 +275,8 @@ public void trackViewImpression(View view, String impressionEventName) { trackViewImpression(view, impressionEventName, null); } - public void trackViewImpression(final View view, final String impressionEventName, final Map attributes) { + public void trackViewImpression(final View view, final String impressionEventName, + final Map attributes) { if (!isInited) return; if (view == null || TextUtils.isEmpty(impressionEventName)) { Logger.e(TAG, "view or impressionEventName is NULL"); @@ -273,7 +288,10 @@ public void trackViewImpression(final View view, final String impressionEventNam } else { attributesCopy = new HashMap<>(attributes); } - ThreadUtils.runOnUiThread(() -> ImpressionProvider.get().trackViewImpression(view, impressionEventName, attributesCopy)); + TrackMainThread.trackMain().runOnUiThread(() -> { + ImpressionProvider impressionProvider = getContext().getProvider(ImpressionProvider.class); + impressionProvider.trackViewImpression(view, impressionEventName, attributesCopy); + }); } public void stopTrackViewImpression(final View trackedView) { @@ -283,7 +301,10 @@ public void stopTrackViewImpression(final View trackedView) { return; } - ThreadUtils.runOnUiThread(() -> ImpressionProvider.get().stopTrackViewImpression(trackedView)); + TrackMainThread.trackMain().runOnUiThread(() -> { + ImpressionProvider impressionProvider = getContext().getProvider(ImpressionProvider.class); + impressionProvider.stopTrackViewImpression(trackedView); + }); } /** @@ -306,7 +327,8 @@ public void setPageAlias(final android.app.Fragment page, final String alias) { * please use autotrackSupportPage replace. @see {@link #autotrackSupportPage(android.support.v4.app.Fragment, String)} */ @Deprecated - public void setPageAliasSupport(final android.support.v4.app.Fragment page, final String alias) { + public void setPageAliasSupport(final android.support.v4.app.Fragment page, + final String alias) { autotrackSupportPage(page, alias); } @@ -329,12 +351,14 @@ public void ignorePage(final android.app.Fragment page, final IgnorePolicy polic } @Deprecated - public void ignorePageSupport(final android.support.v4.app.Fragment page, final IgnorePolicy policy) { + public void ignorePageSupport(final android.support.v4.app.Fragment page, + final IgnorePolicy policy) { Logger.w(TAG, "Ignore page is no longer supported"); } @Deprecated - public void ignorePageX(final androidx.fragment.app.Fragment page, final IgnorePolicy policy) { + public void ignorePageX(final androidx.fragment.app.Fragment page, + final IgnorePolicy policy) { Logger.w(TAG, "Ignore page is no longer supported"); } @@ -344,7 +368,7 @@ public void ignoreView(final View view, final IgnorePolicy policy) { Logger.e(TAG, "view or policy is NULL"); return; } - ThreadUtils.runOnUiThread(() -> ViewAttributeUtil.setIgnorePolicy(view, policy)); + TrackMainThread.trackMain().runOnUiThread(() -> ViewAttributeUtil.setIgnorePolicy(view, policy)); } public void ignoreViewClick(final View view, boolean isIgnore) { @@ -353,7 +377,7 @@ public void ignoreViewClick(final View view, boolean isIgnore) { Logger.e(TAG, "ignoreViewClick failed: view is NULL"); return; } - ThreadUtils.runOnUiThread(() -> ViewAttributeUtil.setIgnoreViewClick(view, isIgnore)); + TrackMainThread.trackMain().runOnUiThread(() -> ViewAttributeUtil.setIgnoreViewClick(view, isIgnore)); } /** diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/AutotrackerLibraryModule.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/AutotrackerLibraryModule.java index f8e913ab..cdd5d0c8 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/AutotrackerLibraryModule.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/AutotrackerLibraryModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack; import com.growingio.android.sdk.LibraryGioModule; diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/IgnorePolicy.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/IgnorePolicy.java index bde500c2..69d63944 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/IgnorePolicy.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/IgnorePolicy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack; public enum IgnorePolicy { diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/impression/ImpressionProvider.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/impression/ImpressionProvider.java index 97b807f4..94f6a2f5 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/impression/ImpressionProvider.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/impression/ImpressionProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,18 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.impression; import android.app.Activity; import android.graphics.Rect; -import android.os.Handler; -import android.os.Looper; import android.text.TextUtils; import android.view.View; import androidx.annotation.Nullable; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.autotrack.AutotrackConfig; import com.growingio.android.sdk.autotrack.view.ViewAttributeUtil; import com.growingio.android.sdk.track.events.PageLevelCustomEvent; @@ -32,12 +30,12 @@ import com.growingio.android.sdk.autotrack.page.PageProvider; import com.growingio.android.sdk.track.listener.IActivityLifecycle; import com.growingio.android.sdk.track.listener.event.ActivityLifecycleEvent; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; import com.growingio.android.sdk.track.view.OnViewStateChangedListener; import com.growingio.android.sdk.track.view.ViewStateChangedEvent; import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.providers.ActivityStateProvider; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; import com.growingio.android.sdk.track.utils.ActivityUtil; import com.growingio.android.sdk.track.view.ViewTreeStatusObserver; @@ -46,38 +44,40 @@ import java.util.Map; import java.util.WeakHashMap; -public class ImpressionProvider implements IActivityLifecycle, OnViewStateChangedListener { +public class ImpressionProvider implements IActivityLifecycle, OnViewStateChangedListener, TrackerLifecycleProvider { private static final String TAG = "ImpressionProvider"; private static final int CHECK_IMPRESSION_ANTI_SHAKE_TIME = 500; private static final Map> ACTIVITY_SCOPE = new WeakHashMap<>(); - private final float mImpressionScale; - private final Handler mUiHandler; - private final Runnable mCheckImpressionRunnable = new Runnable() { + private float impressionScale; + private final Runnable checkImpressionRunnable = new Runnable() { @Override public void run() { checkImpression(); } }; - private final ViewTreeStatusObserver mViewTreeStatusObserver; + private final ViewTreeStatusObserver viewTreeStatusObserver; + private ActivityStateProvider activityStateProvider; - private static class SingleInstance { - private static final ImpressionProvider INSTANCE = new ImpressionProvider(); + public ImpressionProvider() { + viewTreeStatusObserver = new ViewTreeStatusObserver(this); } - private ImpressionProvider() { - AutotrackConfig configuration = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - mImpressionScale = configuration.getImpressionScale(); + @Override + public void setup(TrackerContext context) { + + AutotrackConfig configuration = context.getConfigurationProvider().getConfiguration(AutotrackConfig.class); + impressionScale = configuration.getImpressionScale(); - ActivityStateProvider.get().registerActivityLifecycleListener(this); - mViewTreeStatusObserver = new ViewTreeStatusObserver(this); - mUiHandler = new Handler(Looper.getMainLooper()); + activityStateProvider = context.getActivityStateProvider(); + activityStateProvider.registerActivityLifecycleListener(this); } - public static ImpressionProvider get() { - return SingleInstance.INSTANCE; + @Override + public void shutdown() { + activityStateProvider.unregisterActivityLifecycleListener(this); } @Override @@ -85,9 +85,9 @@ public void onActivityLifecycle(ActivityLifecycleEvent event) { Activity activity = event.getActivity(); if (!ACTIVITY_SCOPE.containsKey(activity)) return; if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_RESUMED) { - mViewTreeStatusObserver.onActivityResumed(activity); + viewTreeStatusObserver.onActivityResumed(activity); } else if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_PAUSED) { - mViewTreeStatusObserver.onActivityPaused(activity); + viewTreeStatusObserver.onActivityPaused(activity); } else if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_DESTROYED) { ACTIVITY_SCOPE.remove(activity); } @@ -99,12 +99,13 @@ public void onViewStateChanged(ViewStateChangedEvent changedEvent) { } private void delayCheckImpression() { - mUiHandler.removeCallbacks(mCheckImpressionRunnable); - mUiHandler.postDelayed(mCheckImpressionRunnable, CHECK_IMPRESSION_ANTI_SHAKE_TIME); + TrackMainThread.trackMain().removeOnUiThreadCallbacks(checkImpressionRunnable); + TrackMainThread.trackMain().postOnUiThreadDelayed(checkImpressionRunnable, CHECK_IMPRESSION_ANTI_SHAKE_TIME); } private void checkImpression() { - Activity activity = ActivityStateProvider.get().getResumedActivity(); + if (activityStateProvider == null) return; + Activity activity = activityStateProvider.getResumedActivity(); List viewImpressions = ACTIVITY_SCOPE.get(activity); if (activity == null || viewImpressions == null || viewImpressions.isEmpty()) { Logger.w(TAG, "ResumedActivity is NULL or This activity has nothing impression"); @@ -128,13 +129,13 @@ private void checkImpression() { private boolean isVisibility(View view) { if (ViewAttributeUtil.viewVisibilityInParents(view)) { - if (mImpressionScale <= 0) { + if (impressionScale <= 0) { return true; } Rect rect = new Rect(); view.getLocalVisibleRect(rect); - return rect.right * rect.bottom >= view.getMeasuredHeight() * view.getMeasuredWidth() * mImpressionScale; + return rect.right * rect.bottom >= view.getMeasuredHeight() * view.getMeasuredWidth() * impressionScale; } return false; } @@ -188,7 +189,7 @@ public void trackViewImpression(View view, String impressionEventName, Map + * + * @author cpacm 2023/7/30 + */ +public class InjectorProvider implements TrackerLifecycleProvider { + + private final static String TAG = "InjectorProvider"; + + private static class SingleInstance { + private static final InjectorProvider INSTANCE = new InjectorProvider(); + } + + public static InjectorProvider get() { + return InjectorProvider.SingleInstance.INSTANCE; + } + + public InjectorProvider() { + + } + + private ActivityStateProvider activityStateProvider; + private ViewClickProvider viewClickProvider; + private ViewChangeProvider viewChangeProvider; + private DialogClickProvider dialogClickProvider; + + private TrackerRegistry registry; + + @Override + public void setup(TrackerContext context) { + activityStateProvider = context.getActivityStateProvider(); + viewClickProvider = context.getProvider(ViewClickProvider.class); + viewChangeProvider = context.getProvider(ViewChangeProvider.class); + dialogClickProvider = context.getProvider(DialogClickProvider.class); + + registry = context.getRegistry(); + } + + public void onActivityNewIntent(Activity activity, Intent intent) { + if (activityStateProvider != null) { + activityStateProvider.onActivityNewIntent(activity, intent); + } + } + + public void activityOptionsItemOnClick(Activity activity, MenuItem menuItem) { + if (viewClickProvider != null) { + viewClickProvider.activityOptionsItemOnClick(activity, menuItem); + } + } + + public void bridgeForWebView(View view) { + boolean result = false; + if (registry != null) { + ModelLoader modelLoader = registry.getModelLoader(HybridBridge.class, Boolean.class); + if (modelLoader != null) { + result = modelLoader.buildLoadData(new HybridBridge(view)).fetcher.executeData(); + } + } + Logger.d(TAG, "bridgeForWebView: webView = " + view.getClass().getName() + ", result = " + result); + } + + public void alertDialogShow(AlertDialog dialog) { + if (dialogClickProvider != null) { + dialogClickProvider.alertDialogShow(dialog); + } + } + + public void alertDialogXOnClick(androidx.appcompat.app.AlertDialog dialog, int which) { + if (dialogClickProvider != null) { + dialogClickProvider.alertDialogXOnClick(dialog, which); + } + } + + public void alertDialogSupportOnClick(android.support.v7.app.AlertDialog dialog, int which) { + if (dialogClickProvider != null) { + dialogClickProvider.alertDialogSupportOnClick(dialog, which); + } + } + + public void alertDialogOnClick(AlertDialog dialog, int which) { + if (dialogClickProvider != null) { + dialogClickProvider.alertDialogOnClick(dialog, which); + } + } + + public void toolbarMenuItemOnClick(MenuItem menuItem) { + if (viewClickProvider != null) { + viewClickProvider.toolbarMenuItemOnClick(menuItem); + } + } + + public void actionMenuItemOnClick(MenuItem menuItem) { + if (viewClickProvider != null) { + viewClickProvider.actionMenuItemOnClick(menuItem); + } + } + + public void popupMenuItemOnClick(MenuItem menuItem) { + if (viewClickProvider != null) { + viewClickProvider.popupMenuItemOnClick(menuItem); + } + } + + public void contextMenuItemOnClick(MenuItem menuItem) { + if (viewClickProvider != null) { + viewClickProvider.contextMenuItemOnClick(menuItem); + } + } + + public void viewOnClick(View view) { + if (viewClickProvider != null) { + viewClickProvider.viewOnClick(view); + } + } + + public void viewOnClickListener(View view) { + if (viewClickProvider != null) { + viewClickProvider.viewOnClickListener(view); + } + } + + public void adapterViewItemClick(View view) { + if (viewClickProvider != null) { + viewClickProvider.adapterViewItemClick(view); + } + } + + public void spinnerViewOnClick(View view) { + if (viewClickProvider != null) { + viewClickProvider.spinnerViewOnClick(view); + } + } + + public void expandableListViewOnGroupClick(View view) { + if (viewClickProvider != null) { + viewClickProvider.expandableListViewOnGroupClick(view); + } + } + + public void expandableListViewOnChildClick(View view) { + if (viewClickProvider != null) { + viewClickProvider.expandableListViewOnChildClick(view); + } + } + + public void radioGroupOnCheck(View view) { + if (viewClickProvider != null) { + viewClickProvider.radioGroupOnCheck(view); + } + } + + public void materialButtonToggleGroupOnButtonCheck(View view) { + if (viewClickProvider != null) { + viewClickProvider.materialButtonToggleGroupOnButtonCheck(view); + } + } + + public void tabLayoutOnTabSelected(TabLayout.Tab tab) { + if (viewClickProvider != null) { + viewClickProvider.tabLayoutOnTabSelected(tab); + } + } + + public void compoundButtonOnCheck(CompoundButton button) { + if (viewClickProvider != null) { + viewClickProvider.compoundButtonOnCheck(button); + } + } + + public void editTextOnFocusChange(View view) { + if (viewChangeProvider != null) { + viewChangeProvider.editTextOnFocusChange(view); + } + } + + public void seekBarOnProgressChange(SeekBar seekBar) { + if (viewChangeProvider != null) { + viewChangeProvider.seekBarOnProgressChange(seekBar); + } + } + + public void ratingBarOnRatingChange(View view, float rating) { + if (viewChangeProvider != null) { + viewChangeProvider.ratingBarOnRatingChange(view, rating); + } + } + + public void sliderOnStopTrackingTouch(Slider slider) { + if (viewChangeProvider != null) { + viewChangeProvider.sliderOnStopTrackingTouch(slider); + } + } + + public void rangeSliderOnStopTrackingTouch(RangeSlider slider) { + if (viewChangeProvider != null) { + viewChangeProvider.rangeSliderOnStopTrackingTouch(slider); + } + } + + @Override + public void shutdown() { + + } + + public static void injectProviders(Map, TrackerLifecycleProvider> provideStore) { + provideStore.put(DialogClickProvider.class, new DialogClickProvider()); + provideStore.put(ViewClickProvider.class, new ViewClickProvider()); + provideStore.put(ViewChangeProvider.class, new ViewChangeProvider()); + provideStore.put(InjectorProvider.class, InjectorProvider.get()); + } +} diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/MenuItemInjector.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/MenuItemInjector.java index 6ed17352..964d1907 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/MenuItemInjector.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/MenuItemInjector.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.inject; import android.view.MenuItem; @@ -29,7 +28,7 @@ private MenuItemInjector() { public static void toolbarOnMenuItemClick(Toolbar.OnMenuItemClickListener listener, MenuItem item) { - ViewClickProvider.toolbarMenuItemOnClick(item); + InjectorProvider.get().toolbarMenuItemOnClick(item); } /* public static void toolbarXOnMenuItemClick(androidx.appcompat.widget.Toolbar.OnMenuItemClickListener listener, MenuItem item) { @@ -42,15 +41,15 @@ public static void toolbarSupportOnMenuItemClick(android.support.v7.widget.Toolb */ public static void actionMenuViewOnMenuItemClick(ActionMenuView.OnMenuItemClickListener listener, MenuItem item) { - ViewClickProvider.actionMenuItemOnClick(item); + InjectorProvider.get().actionMenuItemOnClick(item); } public static void popupMenuOnMenuItemClick(PopupMenu.OnMenuItemClickListener listener, MenuItem item) { - ViewClickProvider.popupMenuItemOnClick(item); + InjectorProvider.get().popupMenuItemOnClick(item); } public static void contextMenuOnMenuItemClick(MenuItem.OnMenuItemClickListener listener, MenuItem item) { - ViewClickProvider.contextMenuItemOnClick(item); + InjectorProvider.get().contextMenuItemOnClick(item); } /* diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/UcWebViewInjector.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/UcWebViewInjector.java index 22d8a2cc..ed756463 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/UcWebViewInjector.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/UcWebViewInjector.java @@ -1,25 +1,22 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.inject; import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.modelloader.ModelLoader; -import com.growingio.android.sdk.track.middleware.hybrid.HybridBridge; import com.uc.webview.export.WebView; import java.util.Map; @@ -33,12 +30,7 @@ private static void bridgeForWebView(WebView view) { return; } - boolean result = false; - ModelLoader modelLoader = TrackerContext.get().getRegistry().getModelLoader(HybridBridge.class, Boolean.class); - if (modelLoader != null) { - result = modelLoader.buildLoadData(new HybridBridge(view)).fetcher.executeData(); - } - Logger.d(TAG, "bridgeForWebView: webView = " + view.getClass().getName() + ", result = " + result); + InjectorProvider.get().bridgeForWebView(view); } private UcWebViewInjector() { diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/ViewChangeInjector.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/ViewChangeInjector.java index 055efb62..0c3c226c 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/ViewChangeInjector.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/ViewChangeInjector.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.inject; @@ -30,20 +29,20 @@ private ViewChangeInjector() { } public static void seekBarOnStopTrackingTouch(SeekBar.OnSeekBarChangeListener listener, SeekBar seekBar) { - ViewChangeProvider.seekBarOnProgressChange(seekBar); + InjectorProvider.get().seekBarOnProgressChange(seekBar); } public static void ratingBarOnRatingChange(RatingBar.OnRatingBarChangeListener listener, RatingBar ratingBar, float rating, boolean fromUser) { if (fromUser) { - ViewChangeProvider.ratingBarOnRatingChange(ratingBar, rating); + InjectorProvider.get().ratingBarOnRatingChange(ratingBar, rating); } } public static void sliderOnStopTrackingTouch(Slider.OnSliderTouchListener listener, Slider slider) { - ViewChangeProvider.sliderOnStopTrackingTouch(slider); + InjectorProvider.get().sliderOnStopTrackingTouch(slider); } public static void rangeSliderOnStopTrackingTouch(RangeSlider.OnSliderTouchListener listener, RangeSlider slider) { - ViewChangeProvider.rangeSliderOnStopTrackingTouch(slider); + InjectorProvider.get().rangeSliderOnStopTrackingTouch(slider); } } diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/ViewChangeProvider.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/ViewChangeProvider.java index 084fca04..4e9eacb4 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/ViewChangeProvider.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/ViewChangeProvider.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.inject; import android.app.Activity; @@ -27,7 +26,7 @@ import com.growingio.android.sdk.autotrack.AutotrackConfig; import com.growingio.android.sdk.autotrack.R; import com.growingio.android.sdk.autotrack.view.ViewNodeProvider; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; import com.growingio.android.sdk.track.view.OnViewStateChangedListener; import com.growingio.android.sdk.track.view.ViewStateChangedEvent; import com.growingio.android.sdk.track.view.ViewTreeStatusObserver; @@ -36,20 +35,35 @@ import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.providers.ActivityStateProvider; -public class ViewChangeProvider implements IActivityLifecycle, OnViewStateChangedListener { +public class ViewChangeProvider implements IActivityLifecycle, OnViewStateChangedListener, TrackerLifecycleProvider { private static final String TAG = "ViewChangeProvider"; - public ViewChangeProvider() { + ViewChangeProvider() { } - private ViewTreeStatusObserver mViewTreeStatusObserver; + private ViewTreeStatusObserver viewTreeStatusObserver; + private ActivityStateProvider activityStateProvider; + private ViewNodeProvider viewNodeProvider; + + private AutotrackConfig autotrackConfig; + + + @Override + public void setup(TrackerContext context) { + activityStateProvider = context.getActivityStateProvider(); + autotrackConfig = context.getConfigurationProvider().getConfiguration(AutotrackConfig.class); + activityStateProvider.registerActivityLifecycleListener(this); + viewNodeProvider = context.getProvider(ViewNodeProvider.class); - public void setup() { - ActivityStateProvider.get().registerActivityLifecycleListener(this); - mViewTreeStatusObserver = new ViewTreeStatusObserver(false, false, true, false, this, + viewTreeStatusObserver = new ViewTreeStatusObserver(false, false, true, false, this, R.id.growing_tracker_monitoring_focus_change); } + @Override + public void shutdown() { + activityStateProvider.unregisterActivityLifecycleListener(this); + } + @Override public void onActivityLifecycle(ActivityLifecycleEvent event) { Activity activity = event.getActivity(); @@ -57,9 +71,9 @@ public void onActivityLifecycle(ActivityLifecycleEvent event) { return; } if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_RESUMED) { - mViewTreeStatusObserver.onActivityResumed(activity); + viewTreeStatusObserver.onActivityResumed(activity); } else if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_PAUSED) { - mViewTreeStatusObserver.onActivityPaused(activity); + viewTreeStatusObserver.onActivityPaused(activity); View focusView = activity.getWindow().getDecorView().findFocus(); if (focusView instanceof EditText) { Logger.d(TAG, "onActivityPaused, and focus view is EditText"); @@ -79,56 +93,51 @@ public void onViewStateChanged(ViewStateChangedEvent changedEvent) { } } - public static void editTextOnFocusChange(View view) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isEditTextChangeEnabled()) { + void editTextOnFocusChange(View view) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isEditTextChangeEnabled()) { Logger.i(TAG, "AutotrackOptions: edittext change enable is false"); return; } sendChangeEvent(view); } - public static void seekBarOnProgressChange(SeekBar seekBar) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isSeekbarChangeEnabled()) { + public void seekBarOnProgressChange(SeekBar seekBar) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isSeekbarChangeEnabled()) { Logger.i(TAG, "AutotrackOptions: seekbar change enable is false"); return; } sendChangeEvent(seekBar); } - public static void ratingBarOnRatingChange(View view, float rating) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isRatingBarChangeEnabled()) { + public void ratingBarOnRatingChange(View view, float rating) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isRatingBarChangeEnabled()) { Logger.i(TAG, "AutotrackOptions: ratingbar change enable is false"); return; } sendChangeEvent(view); } - public static void sliderOnStopTrackingTouch(Slider slider) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isSliderChangeEnabled()) { + public void sliderOnStopTrackingTouch(Slider slider) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isSliderChangeEnabled()) { Logger.i(TAG, "AutotrackOptions: slider value change enable is false"); return; } sendChangeEvent(slider); } - public static void rangeSliderOnStopTrackingTouch(RangeSlider slider) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isSliderChangeEnabled()) { + public void rangeSliderOnStopTrackingTouch(RangeSlider slider) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isSliderChangeEnabled()) { Logger.i(TAG, "AutotrackOptions: rangeSlider value change enable is false"); return; } sendChangeEvent(slider); } - private static void sendChangeEvent(View view) { + private void sendChangeEvent(View view) { if (!TrackerContext.initializedSuccessfully()) { Logger.e(TAG, "Autotracker do not initialized successfully"); } - ViewNodeProvider.get().generateViewChangeEvent(view); + viewNodeProvider.generateViewChangeEvent(view); } } diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/ViewClickInjector.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/ViewClickInjector.java index 8e335171..e0de21e8 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/ViewClickInjector.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/ViewClickInjector.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.inject; import android.view.View; @@ -34,43 +33,43 @@ private ViewClickInjector() { } public static void viewOnClick(View.OnClickListener listener, View view) { - ViewClickProvider.viewOnClickListener(view); + InjectorProvider.get().viewOnClickListener(view); } public static void adapterViewOnItemClick(AdapterView.OnItemClickListener listener, AdapterView adapterView, View view, int position, long id) { - ViewClickProvider.adapterViewItemClick(view); + InjectorProvider.get().adapterViewItemClick(view); } public static void adapterViewOnItemSelected(AdapterView.OnItemSelectedListener listener, AdapterView adapterView, View view, int position, long id) { if (adapterView instanceof Spinner) { // 目前只需要将Spinner的onItemSelected回调触发点击事件,因为Spinner的元素点击只会触发onItemSelected回调 - ViewClickProvider.spinnerViewOnClick(view); + InjectorProvider.get().spinnerViewOnClick(view); } } public static void expandableListViewOnGroupClick(ExpandableListView.OnGroupClickListener listener, ExpandableListView parent, View v, int groupPosition, long id) { - ViewClickProvider.expandableListViewOnGroupClick(v); + InjectorProvider.get().expandableListViewOnGroupClick(v); } public static void expandableListViewOnChildClick(ExpandableListView.OnChildClickListener listener, ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { - ViewClickProvider.expandableListViewOnChildClick(v); + InjectorProvider.get().expandableListViewOnChildClick(v); } public static void radioGroupOnChecked(RadioGroup.OnCheckedChangeListener listener, RadioGroup radioGroup, int i) { - ViewClickProvider.radioGroupOnCheck(radioGroup.findViewById(radioGroup.getCheckedRadioButtonId())); + InjectorProvider.get().radioGroupOnCheck(radioGroup.findViewById(radioGroup.getCheckedRadioButtonId())); } public static void materialButtonToggleGroupOnButtonChecked(MaterialButtonToggleGroup.OnButtonCheckedListener listener, MaterialButtonToggleGroup group, int checkedId, boolean isChecked) { if (isChecked) { - ViewClickProvider.materialButtonToggleGroupOnButtonCheck(group.findViewById(checkedId)); + InjectorProvider.get().materialButtonToggleGroupOnButtonCheck(group.findViewById(checkedId)); } } public static void tabLayoutOnTabSelected(TabLayout.OnTabSelectedListener listener, TabLayout.Tab tab) { - ViewClickProvider.tabLayoutOnTabSelected(tab); + InjectorProvider.get().tabLayoutOnTabSelected(tab); } public static void compoundButtonOnChecked(CompoundButton.OnCheckedChangeListener listener, CompoundButton button, boolean checked) { - ViewClickProvider.compoundButtonOnCheck(button); + InjectorProvider.get().compoundButtonOnCheck(button); } } diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/ViewClickProvider.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/ViewClickProvider.java index 133a083c..dd036f23 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/ViewClickProvider.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/ViewClickProvider.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.inject; import android.app.Activity; @@ -27,91 +26,96 @@ import com.growingio.android.sdk.autotrack.util.ClassUtil; import com.growingio.android.sdk.autotrack.view.ViewAttributeUtil; import com.growingio.android.sdk.autotrack.view.ViewNodeProvider; +import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; -class ViewClickProvider { +class ViewClickProvider implements TrackerLifecycleProvider { private static final String TAG = "ViewClickProvider"; - private ViewClickProvider() { + ViewClickProvider() { + } + + private AutotrackConfig autotrackConfig; + private ViewNodeProvider viewNodeProvider; + + @Override + public void setup(TrackerContext context) { + autotrackConfig = context.getConfigurationProvider().getConfiguration(AutotrackConfig.class); + viewNodeProvider = context.getProvider(ViewNodeProvider.class); } - public static void adapterViewItemClick(View view) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isAdapterViewItemClickEnabled()) { + @Override + public void shutdown() { + + } + + public void adapterViewItemClick(View view) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isAdapterViewItemClickEnabled()) { Logger.i(TAG, "AutotrackOptions: adapter view item click enable is false"); return; } viewOnClick(view); } - public static void spinnerViewOnClick(View view) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isSpinnerItemClickSelectEnabled()) { + public void spinnerViewOnClick(View view) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isSpinnerItemClickSelectEnabled()) { Logger.i(TAG, "AutotrackOptions: spinner item click select is false"); return; } viewOnClick(view); } - public static void expandableListViewOnGroupClick(View view) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isExpandableListGroupClickEnabled()) { + public void expandableListViewOnGroupClick(View view) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isExpandableListGroupClickEnabled()) { Logger.i(TAG, "AutotrackOptions: expandable list group click enable is false"); return; } viewOnClick(view); } - public static void expandableListViewOnChildClick(View view) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isExpandableListChildClickEnabled()) { + public void expandableListViewOnChildClick(View view) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isExpandableListChildClickEnabled()) { Logger.i(TAG, "AutotrackOptions: expandable list child click enable is false"); return; } viewOnClick(view); } - public static void radioGroupOnCheck(View view) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isRadioGroupCheckEnabled()) { + public void radioGroupOnCheck(View view) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isRadioGroupCheckEnabled()) { Logger.i(TAG, "AutotrackOptions: radio group check enable is false"); return; } viewOnClick(view); } - public static void materialButtonToggleGroupOnButtonCheck(View view) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isMaterialToggleGroupButtonCheckEnabled()) { + public void materialButtonToggleGroupOnButtonCheck(View view) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isMaterialToggleGroupButtonCheckEnabled()) { Logger.i(TAG, "AutotrackOptions: material toggle group check enable is false"); return; } viewOnClick(view); } - public static void tabLayoutOnTabSelected(TabLayout.Tab tab) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isTabLayoutTabSelectedEnabled()) { + public void tabLayoutOnTabSelected(TabLayout.Tab tab) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isTabLayoutTabSelectedEnabled()) { Logger.i(TAG, "AutotrackOptions: tablayout tab select enable is false"); return; } viewOnClick(tab.view); } - public static void compoundButtonOnCheck(CompoundButton button) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isCompoundButtonCheckEnabled()) { + public void compoundButtonOnCheck(CompoundButton button) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isCompoundButtonCheckEnabled()) { Logger.i(TAG, "AutotrackOptions: compound button check enable is false"); return; } viewOnClick(button); } - public static void viewOnClickListener(View view) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isViewClickEnabled()) { + public void viewOnClickListener(View view) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isViewClickEnabled()) { Logger.i(TAG, "AutotrackOptions: view click enable is false"); return; } @@ -119,9 +123,8 @@ public static void viewOnClickListener(View view) { } - public static void activityOptionsItemOnClick(Activity activity, MenuItem menuItem) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isActivityMenuItemClickEnabled()) { + public void activityOptionsItemOnClick(Activity activity, MenuItem menuItem) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isActivityMenuItemClickEnabled()) { Logger.i(TAG, "AutotrackOptions: activityMenuItemClickEnabled is false"); return; } @@ -129,53 +132,49 @@ public static void activityOptionsItemOnClick(Activity activity, MenuItem menuIt } - public static void toolbarMenuItemOnClick(MenuItem menuItem) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isToolbarMenuItemClickEnabled()) { + public void toolbarMenuItemOnClick(MenuItem menuItem) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isToolbarMenuItemClickEnabled()) { Logger.i(TAG, "AutotrackOptions: toolbarMenuItemClickEnabled is false"); return; } menuItemOnClick(menuItem); } - public static void actionMenuItemOnClick(MenuItem menuItem) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isActionMenuItemClickEnabled()) { + public void actionMenuItemOnClick(MenuItem menuItem) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isActionMenuItemClickEnabled()) { Logger.i(TAG, "AutotrackOptions: actionMenuItemClickEnabled is false"); return; } menuItemOnClick(menuItem); } - public static void popupMenuItemOnClick(MenuItem menuItem) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isPopupMenuItemClickEnabled()) { + public void popupMenuItemOnClick(MenuItem menuItem) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isPopupMenuItemClickEnabled()) { Logger.i(TAG, "AutotrackOptions: popupMenuItemClickEnabled is false"); return; } menuItemOnClick(menuItem); } - public static void contextMenuItemOnClick(MenuItem menuItem) { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && !config.getAutotrackOptions().isContextMenuItemClickEnabled()) { + public void contextMenuItemOnClick(MenuItem menuItem) { + if (autotrackConfig != null && !autotrackConfig.getAutotrackOptions().isContextMenuItemClickEnabled()) { Logger.i(TAG, "AutotrackOptions: contextMenuItemClickEnabled is false"); return; } menuItemOnClick(menuItem); } - private static void menuItemOnClick(MenuItem menuItem) { + private void menuItemOnClick(MenuItem menuItem) { if (!TrackerContext.initializedSuccessfully()) { Logger.e(TAG, "Autotracker do not initialized successfully"); return; } - Activity activity = ActivityStateProvider.get().getForegroundActivity(); + Activity activity = TrackMainThread.trackMain().getForegroundActivity(); menuItemOnClick(activity, menuItem); } - public static void viewOnClick(View view) { + public void viewOnClick(View view) { if (!TrackerContext.initializedSuccessfully()) { Logger.e(TAG, "Autotracker do not initialized successfully"); return; @@ -190,11 +189,12 @@ public static void viewOnClick(View view) { Logger.w(TAG, "Duplicate Click"); return; } - - ViewNodeProvider.get().generateViewClickEvent(view); + if (viewNodeProvider != null) { + viewNodeProvider.generateViewClickEvent(view); + } } - public static void menuItemOnClick(Activity activity, MenuItem menuItem) { + public void menuItemOnClick(Activity activity, MenuItem menuItem) { if (!TrackerContext.initializedSuccessfully()) { Logger.e(TAG, "Autotracker do not initialized successfully"); return; @@ -205,6 +205,9 @@ public static void menuItemOnClick(Activity activity, MenuItem menuItem) { return; } - ViewNodeProvider.get().generateMenuItemEvent(activity, menuItem); + if (viewNodeProvider != null) { + viewNodeProvider.generateMenuItemEvent(activity, menuItem); + } } + } diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/WebViewInjector.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/WebViewInjector.java index 4bb0ab28..a2fc3553 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/WebViewInjector.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/WebViewInjector.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.inject; import android.view.View; @@ -21,8 +20,6 @@ import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.modelloader.ModelLoader; -import com.growingio.android.sdk.track.middleware.hybrid.HybridBridge; import java.util.Map; @@ -35,12 +32,7 @@ private static void bridgeForWebView(View view) { return; } - boolean result = false; - ModelLoader modelLoader = TrackerContext.get().getRegistry().getModelLoader(HybridBridge.class, Boolean.class); - if (modelLoader != null) { - result = modelLoader.buildLoadData(new HybridBridge(view)).fetcher.executeData(); - } - Logger.d(TAG, "bridgeForWebView: webView = " + view.getClass().getName() + ", result = " + result); + InjectorProvider.get().bridgeForWebView(view); } private WebViewInjector() { diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/X5WebViewInjector.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/X5WebViewInjector.java index 4bb0a492..76a83632 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/X5WebViewInjector.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/inject/X5WebViewInjector.java @@ -1,26 +1,23 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.inject; import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.modelloader.ModelLoader; -import com.growingio.android.sdk.track.middleware.hybrid.HybridBridge; import com.tencent.smtt.sdk.WebView; import java.util.Map; @@ -34,12 +31,7 @@ private static void bridgeForWebView(WebView view) { return; } - boolean result = false; - ModelLoader modelLoader = TrackerContext.get().getRegistry().getModelLoader(HybridBridge.class, Boolean.class); - if (modelLoader != null) { - result = modelLoader.buildLoadData(new HybridBridge(view)).fetcher.executeData(); - } - Logger.d(TAG, "bridgeForWebView: webView = " + view.getClass().getName() + ", result = " + result); + InjectorProvider.get().bridgeForWebView(view); } private X5WebViewInjector() { diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/ActivityPage.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/ActivityPage.java index 93792889..12f43fc8 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/ActivityPage.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/ActivityPage.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.page; import android.app.Activity; @@ -21,13 +20,20 @@ import android.view.View; import com.growingio.android.sdk.autotrack.AutotrackConfig; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; public class ActivityPage extends Page { + + private AutotrackConfig autotrackConfig; + public ActivityPage(Activity carrier) { super(carrier); } + public ActivityPage(Activity carrier, AutotrackConfig autotrackConfig) { + super(carrier); + this.autotrackConfig = autotrackConfig; + } + @Override public String getName() { if (!TextUtils.isEmpty(getAlias())) { @@ -39,8 +45,7 @@ public String getName() { @Override public boolean isAutotrack() { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && config.getAutotrackOptions().isActivityPageEnabled()) { + if (autotrackConfig != null && autotrackConfig.getAutotrackOptions().isActivityPageEnabled()) { return true; } return super.isAutotrack(); diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/FragmentPage.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/FragmentPage.java index 0ec8373e..84d1bef4 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/FragmentPage.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/FragmentPage.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.page; import android.content.res.Resources; @@ -21,13 +20,20 @@ import android.view.View; import com.growingio.android.sdk.autotrack.AutotrackConfig; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; public class FragmentPage extends Page> { + + private AutotrackConfig autotrackConfig; + public FragmentPage(SuperFragment carrier) { super(carrier); } + public FragmentPage(SuperFragment carrier, AutotrackConfig autotrackConfig) { + super(carrier); + this.autotrackConfig = autotrackConfig; + } + @Override public String getName() { if (!TextUtils.isEmpty(getAlias())) { @@ -82,8 +88,7 @@ private String transformSwitcherTag(String tag) { @Override public boolean isAutotrack() { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && config.getAutotrackOptions().isFragmentPageEnabled()) { + if (autotrackConfig != null && autotrackConfig.getAutotrackOptions().isFragmentPageEnabled()) { return true; } return super.isAutotrack(); diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/Page.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/Page.java index b98cdd92..503301d4 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/Page.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/Page.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.page; import android.text.TextUtils; @@ -180,7 +179,7 @@ public String originPath(boolean omitted) { path.append("/"); xIndex.append("/"); } - path.append(page.getName()); + path.append(omitted ? page.getName() : getCarrier().getClass().getSimpleName()); String tag = page.getTag(); if (tag != null) { if (omitted) path.append("[").append(tag.isEmpty() ? "-" : tag).append("]"); diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/PageProvider.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/PageProvider.java index af7347a8..49679e5c 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/PageProvider.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/PageProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.page; import android.app.Activity; @@ -25,6 +24,7 @@ import androidx.annotation.UiThread; import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.autotrack.AutotrackConfig; import com.growingio.android.sdk.track.events.PageEvent; import com.growingio.android.sdk.autotrack.view.ViewAttributeUtil; import com.growingio.android.sdk.track.TrackMainThread; @@ -32,6 +32,7 @@ import com.growingio.android.sdk.track.listener.event.ActivityLifecycleEvent; import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.providers.ActivityStateProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; import com.growingio.android.sdk.track.utils.ActivityUtil; import java.util.HashMap; @@ -39,7 +40,7 @@ import java.util.Map; import java.util.WeakHashMap; -public class PageProvider implements IActivityLifecycle { +public class PageProvider implements IActivityLifecycle, TrackerLifecycleProvider { private static final String TAG = "PageProvider"; //CHECKSTYLE:OFF @@ -51,25 +52,38 @@ public class PageProvider implements IActivityLifecycle { * Cache Pages that not in the Lifecycle but has set properties. */ private final Map> CACHE_PAGES = new WeakHashMap<>(); + //CHECKSTYLE:ON private static class SingleInstance { private static final PageProvider INSTANCE = new PageProvider(); } + private ActivityStateProvider activityStateProvider; + private AutotrackConfig autotrackConfig; + private PageProvider() { + ALL_PAGE_TREE.clear(); + CACHE_PAGES.clear(); } - public static PageProvider get() { - return SingleInstance.INSTANCE; + @Override + public void setup(TrackerContext context) { + activityStateProvider = context.getActivityStateProvider(); + autotrackConfig = context.getConfigurationProvider().getConfiguration(AutotrackConfig.class); + activityStateProvider.registerActivityLifecycleListener(this); } - public void setup() { - ActivityStateProvider.get().registerActivityLifecycleListener(this); - ALL_PAGE_TREE.clear(); - CACHE_PAGES.clear(); + @Override + public void shutdown() { + activityStateProvider.unregisterActivityLifecycleListener(this); } + public static PageProvider get() { + return SingleInstance.INSTANCE; + } + + @Override public void onActivityLifecycle(ActivityLifecycleEvent event) { Activity activity = event.getActivity(); if (activity == null) { @@ -101,7 +115,7 @@ private ActivityPage generateActivityPageInTree(Activity activity) { CACHE_PAGES.remove(activity); } if (page == null) { - page = new ActivityPage(activity); + page = new ActivityPage(activity, autotrackConfig); } ALL_PAGE_TREE.put(activity, page); return page; @@ -117,7 +131,7 @@ protected ActivityPage findOrCreateActivityPage(Activity activity) { return page; } if (page == null) { - page = new ActivityPage(activity); + page = new ActivityPage(activity, autotrackConfig); CACHE_PAGES.put(activity, page); } return page; @@ -137,7 +151,7 @@ public void autotrackActivity(Activity activity, String alias, Map fragment) { if (CACHE_PAGES.containsKey(fragment)) { return (FragmentPage) CACHE_PAGES.get(fragment); } - FragmentPage fragmentPage = new FragmentPage(fragment); + FragmentPage fragmentPage = new FragmentPage(fragment, autotrackConfig); CACHE_PAGES.put(fragment, fragmentPage); return fragmentPage; } @@ -317,7 +331,7 @@ private FragmentPage generateFragmentPage(SuperFragment fragment) { CACHE_PAGES.remove(fragment); return fragmentPage; } - return new FragmentPage(fragment); + return new FragmentPage(fragment, autotrackConfig); } public void autotrackFragment(SuperFragment fragment, String alias, Map attributes) { @@ -339,7 +353,7 @@ public void autotrackFragment(SuperFragment fragment, String alias, Map findPage(View view) { //需要考虑其他Window的上面的View Context viewContext = view.getContext(); Activity activity = ActivityUtil.findActivity(viewContext); - if (activity == null) { - activity = ActivityStateProvider.get().getForegroundActivity(); + if (activity == null && activityStateProvider != null) { + activity = activityStateProvider.getForegroundActivity(); } if (activity != null) { ActivityPage activityPage = findOrCreateActivityPage(activity); diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/SuperFragment.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/SuperFragment.java index e6737d46..57695b91 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/SuperFragment.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/SuperFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.page; import android.app.Activity; diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/WindowPage.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/WindowPage.java index c2ceaac6..1022bf28 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/WindowPage.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/page/WindowPage.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.page; import android.view.View; diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/shadow/AlertControllerShadow.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/shadow/AlertControllerShadow.java index 99a57004..e4bd5fd8 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/shadow/AlertControllerShadow.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/shadow/AlertControllerShadow.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.shadow; import android.app.AlertDialog; diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/shadow/ListMenuItemViewShadow.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/shadow/ListMenuItemViewShadow.java index deeaac22..fca6d4d9 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/shadow/ListMenuItemViewShadow.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/shadow/ListMenuItemViewShadow.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.shadow; import android.annotation.SuppressLint; diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/util/ClassUtil.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/util/ClassUtil.java index b2fa7bbf..f0663f54 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/util/ClassUtil.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/util/ClassUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.util; import android.annotation.TargetApi; diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/util/HurtLocker.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/util/HurtLocker.java index da9c6256..ca48500c 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/util/HurtLocker.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/util/HurtLocker.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.util; import java.lang.reflect.Field; diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/PageHelper.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/PageHelper.java index eb2ead84..a7753f7f 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/PageHelper.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/PageHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.view; import android.view.View; @@ -21,7 +20,6 @@ import android.view.WindowManager; import com.growingio.android.sdk.autotrack.page.Page; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; /** *

@@ -50,7 +48,7 @@ static String getWindowPrefix(View root) { Page page = ViewAttributeUtil.getViewPage(root); if (page != null) { windowPrefix = PAGE_PREFIX; - } else if (root.hashCode() == ActivityStateProvider.get().getCurrentRootWindowsHashCode()) { + } else if (root.hashCode() == ViewUtil.getCurrentRootWindowsHashCode()) { windowPrefix = getMainWindowPrefix(); } else { windowPrefix = getSubWindowPrefix(root); diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ScreenElementHelper.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ScreenElementHelper.java index cf7bf6a4..042f644c 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ScreenElementHelper.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ScreenElementHelper.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.view; import android.view.View; diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewAttributeUtil.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewAttributeUtil.java index 0d2f339b..153d750b 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewAttributeUtil.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewAttributeUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.view; import android.content.Context; diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeProvider.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeProvider.java index d9f83a76..5e834437 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeProvider.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeProvider.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.view; import android.app.Activity; @@ -25,7 +24,8 @@ import com.growingio.android.sdk.track.middleware.hybrid.HybridDom; import com.growingio.android.sdk.track.middleware.hybrid.HybridJson; import com.growingio.android.sdk.track.modelloader.ModelLoader; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; +import com.growingio.android.sdk.track.modelloader.TrackerRegistry; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; import com.growingio.android.sdk.track.view.DecorView; import org.json.JSONArray; @@ -38,34 +38,32 @@ * * @author cpacm 2023/7/7 */ -public class ViewNodeProvider implements ViewNodeRenderer { +public class ViewNodeProvider implements ViewNodeRenderer, TrackerLifecycleProvider { private final static String TAG = "ViewNodeProvider"; - private static ViewNodeProvider INSTANCE = null; + private ViewNodeRenderer renderer; + private TrackerRegistry registry; + + public ViewNodeProvider() { - private final ViewNodeRenderer renderer; + } - private ViewNodeProvider() { - AutotrackConfig config = ConfigurationProvider.get().getConfiguration(AutotrackConfig.class); - if (config != null && config.isDowngrade()) { - renderer = new ViewNodeV3Renderer(); + @Override + public void setup(TrackerContext context) { + registry = context.getRegistry(); + AutotrackConfig config = context.getConfigurationProvider().getConfiguration(AutotrackConfig.class); + boolean isV4 = config == null || !config.isDowngrade(); + if (isV4) { + renderer = new ViewNodeV4Renderer(this); } else { - renderer = new ViewNodeV4Renderer(); + renderer = new ViewNodeV3Renderer(this); } } - public static ViewNodeProvider get() { - if (INSTANCE != null) { - return INSTANCE; - } - synchronized (ViewNodeProvider.class) { - if (INSTANCE != null) { - return INSTANCE; - } - INSTANCE = new ViewNodeProvider(); - return INSTANCE; - } + @Override + public void shutdown() { + } @Override @@ -94,7 +92,6 @@ public JSONArray buildScreenViews(List decorViews) { } ModelLoader getHybridModelLoader() { - return TrackerContext.get().getRegistry().getModelLoader(HybridDom.class, HybridJson.class); + return registry.getModelLoader(HybridDom.class, HybridJson.class); } - } diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeRenderer.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeRenderer.java index a8b4141c..dc7b8d00 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeRenderer.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeRenderer.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.view; import android.app.Activity; diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeV3.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeV3.java index 779a31bb..c783b256 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeV3.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeV3.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.view; import android.content.Context; @@ -281,7 +280,7 @@ public ViewNodeV3 append(View view, int index, boolean ignorePage) { public static ViewNodeV3 generateMenuItemViewNode(Context context, MenuItem menuItem) { StringBuilder xpath = new StringBuilder(); - xpath.append(PageHelper.PAGE_PREFIX); + xpath.append("/").append(PageHelper.PAGE_PREFIX); xpath.append("/MenuView/MenuItem#").append(ViewAttributeUtil.getPackageId(context, menuItem.getItemId())); return new ViewNodeV3() diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeV3Renderer.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeV3Renderer.java index f851ae66..30d42d41 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeV3Renderer.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeV3Renderer.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.view; import static com.growingio.android.sdk.autotrack.view.PageHelper.POPUP_DECOR_VIEW_CLASS_NAME; @@ -37,10 +36,9 @@ import com.growingio.android.sdk.track.middleware.hybrid.HybridJson; import com.growingio.android.sdk.track.modelloader.LoadDataFetcher; import com.growingio.android.sdk.track.modelloader.ModelLoader; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; import com.growingio.android.sdk.track.utils.ClassExistHelper; import com.growingio.android.sdk.track.view.DecorView; -import com.growingio.android.sdk.track.webservices.widget.TipView; +import com.growingio.android.sdk.track.view.TipView; import org.json.JSONArray; import org.json.JSONObject; @@ -56,6 +54,13 @@ * @author cpacm 2023/7/10 */ class ViewNodeV3Renderer implements ViewNodeRenderer { + + private final ViewNodeProvider viewNodeProvider; + + ViewNodeV3Renderer(ViewNodeProvider viewNodeProvider) { + this.viewNodeProvider = viewNodeProvider; + } + @Override public void generateMenuItemEvent(Activity activity, MenuItem menuItem) { Page page = PageProvider.get().searchActivityPage(activity); @@ -73,7 +78,7 @@ public void generateViewClickEvent(View view) { Logger.e(TAG, "View is NULL"); return; } - Activity activity = ActivityStateProvider.get().getForegroundActivity(); + Activity activity = TrackMainThread.trackMain().getForegroundActivity(); if (activity == null || ViewAttributeUtil.isIgnoredView(view)) { Logger.e(TAG, "View is ignored"); return; @@ -98,7 +103,7 @@ public void generateViewChangeEvent(View view) { Logger.e(TAG, "View is NULL"); return; } - Activity activity = ActivityStateProvider.get().getForegroundActivity(); + Activity activity = TrackMainThread.trackMain().getForegroundActivity(); if (activity == null || ViewAttributeUtil.isIgnoredView(view)) { Logger.e(TAG, "View is ignored"); return; @@ -189,7 +194,7 @@ private void traverseViewNode(ViewNodeV3 viewNode, JSONArray container) { if (ViewAttributeUtil.isViewInvisible(viewNode.getView())) return; // deal with WebView dom. - if (ClassExistHelper.isWebView(viewNode.getView()) && ViewNodeProvider.get().getHybridModelLoader() != null) { + if (ClassExistHelper.isWebView(viewNode.getView()) && viewNodeProvider.getHybridModelLoader() != null) { final CountDownLatch latch = new CountDownLatch(1); disposeWebView(viewNode, container, latch); try { @@ -277,7 +282,7 @@ private ViewNodeV3 findRootViewNode(View childView, LinkedList linkedViews originalXpath = "/" + ViewAttributeUtil.getCustomId(rootView); xpath = originalXpath; } else if (ViewAttributeUtil.getViewPage(rootView) != null) { - originalXpath = PageHelper.PAGE_PREFIX; + originalXpath = "/" + PageHelper.PAGE_PREFIX + "/" + ClassUtil.getSimpleClassName(rootView.getClass()); xpath = originalXpath; prefixPage = originalXpath; } else { @@ -303,7 +308,7 @@ private ViewNodeV3 findRootViewNode(View childView, LinkedList linkedViews } private void disposeWebView(ViewNodeV3 viewNode, JSONArray container, final CountDownLatch latch) { - ModelLoader modelLoader = ViewNodeProvider.get().getHybridModelLoader(); + ModelLoader modelLoader = viewNodeProvider.getHybridModelLoader(); if (modelLoader == null) { latch.countDown(); return; diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeV4.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeV4.java index 7afa50bf..38349151 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeV4.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeV4.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.view; import android.content.Context; diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeV4Renderer.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeV4Renderer.java index 90e1528f..6ea417f6 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeV4Renderer.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewNodeV4Renderer.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.view; import static com.growingio.android.sdk.autotrack.view.PageHelper.POPUP_DECOR_VIEW_CLASS_NAME; @@ -38,10 +37,9 @@ import com.growingio.android.sdk.track.middleware.hybrid.HybridJson; import com.growingio.android.sdk.track.modelloader.LoadDataFetcher; import com.growingio.android.sdk.track.modelloader.ModelLoader; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; import com.growingio.android.sdk.track.utils.ClassExistHelper; import com.growingio.android.sdk.track.view.DecorView; -import com.growingio.android.sdk.track.webservices.widget.TipView; +import com.growingio.android.sdk.track.view.TipView; import org.json.JSONArray; import org.json.JSONObject; @@ -57,6 +55,14 @@ * @author cpacm 2023/7/10 */ class ViewNodeV4Renderer implements ViewNodeRenderer { + + private final ViewNodeProvider viewNodeProvider; + + ViewNodeV4Renderer(ViewNodeProvider viewNodeProvider) { + this.viewNodeProvider = viewNodeProvider; + } + + @Override public void generateMenuItemEvent(Activity activity, MenuItem menuItem) { Page page = PageProvider.get().searchActivityPage(activity); @@ -92,7 +98,7 @@ public void generateViewClickEvent(View view) { Logger.e(TAG, "View is NULL"); return; } - Activity activity = ActivityStateProvider.get().getForegroundActivity(); + Activity activity = TrackMainThread.trackMain().getForegroundActivity(); if (activity == null || ViewAttributeUtil.isIgnoredView(view)) { Logger.e(TAG, "View is ignored"); return; @@ -137,7 +143,7 @@ public void generateViewChangeEvent(View view) { Logger.e(TAG, "View is NULL"); return; } - Activity activity = ActivityStateProvider.get().getForegroundActivity(); + Activity activity = TrackMainThread.trackMain().getForegroundActivity(); if (activity == null || ViewAttributeUtil.isIgnoredView(view)) { Logger.e(TAG, "View is ignored"); return; @@ -246,6 +252,9 @@ private ViewNodeV4 findRootViewNode(View childView, LinkedList linkedViews if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M && prefix.equals(PageHelper.POPUP_WINDOW_PREFIX) && !POPUP_DECOR_VIEW_CLASS_NAME.equals(ClassUtil.getSimpleClassName(rootView.getClass()))) { xpath.append("/").append(POPUP_DECOR_VIEW_CLASS_NAME).append("/").append(ClassUtil.getSimpleClassName(rootView.getClass())); xIndex.append("/0/0"); + } else { + xpath.append("/").append(ClassUtil.getSimpleClassName(rootView.getClass())); + xIndex.append("/0"); } } // construct root viewNode @@ -328,7 +337,7 @@ private void traverseViewNode(ViewNodeV4 viewNode, JSONArray container) { if (ViewAttributeUtil.isViewInvisible(viewNode.getView())) return; // deal with WebView dom. - if (ClassExistHelper.isWebView(viewNode.getView()) && ViewNodeProvider.get().getHybridModelLoader() != null) { + if (ClassExistHelper.isWebView(viewNode.getView()) && viewNodeProvider.getHybridModelLoader() != null) { final CountDownLatch latch = new CountDownLatch(1); disposeWebView(viewNode, container, latch); try { @@ -356,7 +365,7 @@ private void traverseViewNode(ViewNodeV4 viewNode, JSONArray container) { } private void disposeWebView(ViewNodeV4 viewNode, JSONArray container, final CountDownLatch latch) { - ModelLoader modelLoader = ViewNodeProvider.get().getHybridModelLoader(); + ModelLoader modelLoader = viewNodeProvider.getHybridModelLoader(); if (modelLoader == null) { latch.countDown(); return; diff --git a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewUtil.java b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewUtil.java index 86e0f3d6..a500836d 100644 --- a/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewUtil.java +++ b/growingio-autotracker-core/src/main/java/com/growingio/android/sdk/autotrack/view/ViewUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.view; +import android.app.Activity; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; @@ -35,10 +35,13 @@ import com.google.android.material.slider.Slider; import com.google.android.material.tabs.TabLayout; import com.growingio.android.sdk.autotrack.shadow.ListMenuItemViewShadow; +import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.utils.ClassExistHelper; public class ViewUtil { + private static int mCurrentRootWindowsHashCode = -1; + private ViewUtil() { } @@ -212,4 +215,15 @@ private static boolean isPasswordInputType(int inputType) { || variation == (EditorInfo.TYPE_CLASS_NUMBER | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD) || variation == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); } + + + public static int getCurrentRootWindowsHashCode() { + Activity mForegroundActivity = TrackMainThread.trackMain().getForegroundActivity(); + if (mCurrentRootWindowsHashCode == -1 + && mForegroundActivity != null) { + //该时间点, 用户理论上setContentView已经结束 + mCurrentRootWindowsHashCode = mForegroundActivity.getWindow().getDecorView().hashCode(); + } + return mCurrentRootWindowsHashCode; + } } diff --git a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/AutotrackTest.java b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/AutotrackTest.java index 550f428b..8055f0b8 100644 --- a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/AutotrackTest.java +++ b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/AutotrackTest.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack; @@ -26,7 +25,7 @@ import com.growingio.android.sdk.Configurable; import com.growingio.android.sdk.CoreConfiguration; import com.growingio.android.sdk.autotrack.impression.ImpressionProvider; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProviderFactory; import org.junit.Before; import org.junit.Test; @@ -46,10 +45,11 @@ public class AutotrackTest { @Before public void setup() { Application application = ApplicationProvider.getApplicationContext(); - autotracker = new Autotracker(application); + Map, Configurable> modules = new HashMap<>(); modules.put(AutotrackConfig.class, new AutotrackConfig().setImpressionScale(0.5f)); - ConfigurationProvider.initWithConfig(new CoreConfiguration("AutotrackTest", "growingio://autotrack"), modules); + TrackerLifecycleProviderFactory.create().createConfigurationProviderWithConfig(new CoreConfiguration("AutotrackTest", "growingio://autotrack"), modules); + autotracker = new Autotracker(application); } @Test @@ -67,10 +67,11 @@ public void apiTest() { autotracker.setPageAttributesSystem(new Fragment(), testMap); autotracker.setPageAttributes(new androidx.fragment.app.Fragment(), testMap); + ImpressionProvider impressionProvider = autotracker.getContext().getProvider(ImpressionProvider.class); autotracker.trackViewImpression(activity.getImageView(), "testImp"); - Truth.assertThat(ImpressionProvider.get().hasTrackViewImpression(activity.getImageView())).isTrue(); + Truth.assertThat(impressionProvider.hasTrackViewImpression(activity.getImageView())).isTrue(); autotracker.stopTrackViewImpression(activity.getImageView()); - Truth.assertThat(ImpressionProvider.get().hasTrackViewImpression(activity.getImageView())).isFalse(); + Truth.assertThat(impressionProvider.hasTrackViewImpression(activity.getImageView())).isFalse(); autotracker.setPageAlias(activity, "TestActivity"); autotracker.setPageAlias(new Fragment(), "TestFragment"); diff --git a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/RobolectricActivity.java b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/RobolectricActivity.java index ae929e5d..846d3b25 100644 --- a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/RobolectricActivity.java +++ b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/RobolectricActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack; import android.content.Context; diff --git a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/ShadowThreadUtils.java b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/ShadowThreadUtils.java index 362500d1..e225e7cb 100644 --- a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/ShadowThreadUtils.java +++ b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/ShadowThreadUtils.java @@ -1,36 +1,32 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack; -import com.growingio.android.sdk.track.utils.ThreadUtils; +import com.growingio.android.sdk.track.TrackMainThread; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; -@Implements(ThreadUtils.class) +@Implements(TrackMainThread.class) public class ShadowThreadUtils { public ShadowThreadUtils() { } - public void doSomeThing() { - } - @Implementation public static void runOnUiThread(Runnable r) { r.run(); diff --git a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/UtilTest.java b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/UtilTest.java index 313203ee..b4720ae6 100644 --- a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/UtilTest.java +++ b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/UtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,27 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack; import android.app.AlertDialog; import android.app.Application; -import android.content.Intent; import androidx.appcompat.view.menu.ListMenuItemView; import androidx.test.core.app.ApplicationProvider; import com.google.common.truth.Truth; -import com.growingio.android.sdk.autotrack.inject.ViewChangeProvider; -import com.growingio.android.sdk.autotrack.inject.ActivityInjector; import com.growingio.android.sdk.autotrack.shadow.AlertControllerShadow; import com.growingio.android.sdk.autotrack.shadow.ListMenuItemViewShadow; import com.growingio.android.sdk.autotrack.util.HurtLocker; import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import java.lang.reflect.Field; @@ -78,11 +73,4 @@ public void shadowTest() throws Exception { Truth.assertThat(lmivShadow.getMenuItem()).isNull(); } - @Test - public void activityInjectTest() { - RobolectricActivity activity = Robolectric.buildActivity(RobolectricActivity.class).create().get(); - ActivityInjector.onActivityNewIntent(activity, new Intent()); - ViewChangeProvider.editTextOnFocusChange(activity.getTextView()); - } - } diff --git a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/click/TrackMainThreadShadow.java b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/click/TrackMainThreadShadow.java index 255d74d3..a218f4b4 100644 --- a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/click/TrackMainThreadShadow.java +++ b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/click/TrackMainThreadShadow.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.click; import com.growingio.android.sdk.track.TrackMainThread; diff --git a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/click/ViewClickTest.java b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/click/ViewClickTest.java index 58e24705..d8903b3b 100644 --- a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/click/ViewClickTest.java +++ b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/click/ViewClickTest.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.autotrack.click; import android.app.AlertDialog; @@ -23,12 +22,11 @@ import androidx.test.core.app.ApplicationProvider; import com.google.common.truth.Truth; -import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.autotrack.Autotracker; import com.growingio.android.sdk.autotrack.RobolectricActivity; import com.growingio.android.sdk.autotrack.inject.DialogInjector; import com.growingio.android.sdk.autotrack.inject.ViewClickInjector; import com.growingio.android.sdk.track.events.ViewElementEvent; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; import org.junit.Before; import org.junit.Test; @@ -41,16 +39,13 @@ @Config(shadows = {TrackMainThreadShadow.class}) public class ViewClickTest { - private RobolectricActivity activity; private Application application = ApplicationProvider.getApplicationContext(); @Before public void setup() { - TrackerContext.init(application); - TrackerContext.initSuccess(); + Autotracker tracker = new Autotracker(application); activity = Robolectric.buildActivity(RobolectricActivity.class).create().resume().get(); - ActivityStateProvider.get().onActivityResumed(activity); } @Test diff --git a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/hybrid/HybridTest.java b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/hybrid/HybridTest.java index f87abc80..b9c93faa 100644 --- a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/hybrid/HybridTest.java +++ b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/hybrid/HybridTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.hybrid; @@ -22,7 +21,7 @@ import androidx.test.core.app.ApplicationProvider; -import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.autotrack.Autotracker; import com.growingio.android.sdk.autotrack.RobolectricActivity; import com.growingio.android.sdk.autotrack.inject.UcWebViewInjector; import com.growingio.android.sdk.autotrack.inject.WebViewInjector; @@ -45,8 +44,7 @@ public class HybridTest { @Before public void setup() { - TrackerContext.init(application); - TrackerContext.initSuccess(); + Autotracker tracker = new Autotracker(application); } @Test diff --git a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/impression/ImpressionTest.java b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/impression/ImpressionTest.java index d38982bb..081cbbd8 100644 --- a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/impression/ImpressionTest.java +++ b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/impression/ImpressionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.impression; import android.app.Application; @@ -26,9 +25,9 @@ import com.growingio.android.sdk.CoreConfiguration; import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.autotrack.AutotrackConfig; +import com.growingio.android.sdk.autotrack.Autotracker; import com.growingio.android.sdk.autotrack.RobolectricActivity; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProviderFactory; import com.growingio.android.sdk.track.view.ViewStateChangedEvent; import org.junit.Before; @@ -45,33 +44,38 @@ public class ImpressionTest { Application application = ApplicationProvider.getApplicationContext(); + TrackerContext context; @Before public void setup() { - TrackerContext.init(application); + Map, Configurable> map = new HashMap<>(); map.put(AutotrackConfig.class, new AutotrackConfig()); - ConfigurationProvider.initWithConfig(new CoreConfiguration("ImpressionTest", "growingio://impression"), map); + TrackerLifecycleProviderFactory.create().createConfigurationProviderWithConfig(new CoreConfiguration("ImpressionTest", "growingio://impression"), map); + + Autotracker tracker = new Autotracker(application); + context = tracker.getContext(); + } @Test public void impressionTest() { RobolectricActivity activity = Robolectric.buildActivity(RobolectricActivity.class).create().resume().get(); - ActivityStateProvider.get().onActivityResumed(activity); Map attrMap = new HashMap<>(); attrMap.put("username", "cpacm"); - ImpressionProvider.get().trackViewImpression(activity.getTextView(), "cpacm", attrMap); - boolean result = ImpressionProvider.get().hasTrackViewImpression(activity.getTextView()); + ImpressionProvider impressionProvider = context.getProvider(ImpressionProvider.class); + impressionProvider.trackViewImpression(activity.getTextView(), "cpacm", attrMap); + boolean result = impressionProvider.hasTrackViewImpression(activity.getTextView()); Truth.assertThat(result).isTrue(); ViewStateChangedEvent vscEvent = new ViewStateChangedEvent(ViewStateChangedEvent.StateType.LAYOUT_CHANGED); Truth.assertThat(vscEvent.getNewFocus()).isNull(); - ImpressionProvider.get().onViewStateChanged(vscEvent); + impressionProvider.onViewStateChanged(vscEvent); Robolectric.flushForegroundThreadScheduler(); Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - ImpressionProvider.get().stopTrackViewImpression(activity.getTextView()); - boolean result2 = ImpressionProvider.get().hasTrackViewImpression(activity.getTextView()); + impressionProvider.stopTrackViewImpression(activity.getTextView()); + boolean result2 = impressionProvider.hasTrackViewImpression(activity.getTextView()); Truth.assertThat(result2).isFalse(); } } diff --git a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/page/PageTest.java b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/page/PageTest.java index 562b5e57..e1010d5c 100644 --- a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/page/PageTest.java +++ b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/page/PageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.page; import android.app.Application; @@ -30,11 +29,13 @@ import androidx.test.core.app.ApplicationProvider; import com.google.common.truth.Truth; -import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.Configurable; +import com.growingio.android.sdk.CoreConfiguration; +import com.growingio.android.sdk.autotrack.AutotrackConfig; +import com.growingio.android.sdk.autotrack.Autotracker; import com.growingio.android.sdk.autotrack.RobolectricActivity; import com.growingio.android.sdk.autotrack.inject.FragmentInjector; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; -import com.growingio.android.sdk.track.providers.SessionProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProviderFactory; import org.junit.Before; import org.junit.Test; @@ -57,11 +58,11 @@ public class PageTest { @Before public void setup() { - application.registerActivityLifecycleCallbacks(ActivityStateProvider.get()); - TrackerContext.init(application); - TrackerContext.initSuccess(); - PageProvider.get().setup(); - SessionProvider.get(); + Map, Configurable> map = new HashMap<>(); + map.put(AutotrackConfig.class, new AutotrackConfig()); + TrackerLifecycleProviderFactory.create().createConfigurationProviderWithConfig(new CoreConfiguration("PageTest", "growingio://impression"), map); + + Autotracker autotracker = new Autotracker(application); activityController = Robolectric.buildActivity(RobolectricActivity.class); } @@ -101,7 +102,7 @@ public void fragmentInjectTest() { FragmentInjector.systemFragmentOnResume(appFragment); Page page = PageProvider.get().findOrCreateFragmentPage(SuperFragment.make(appFragment)); String name = page.getName(); - Truth.assertThat(name).isEqualTo("TestFragment[app]"); + Truth.assertThat(name).isEqualTo("TestFragment"); Truth.assertThat(page.getTag()).isEqualTo("app"); FragmentInjector.systemFragmentSetUserVisibleHint(appFragment, false); diff --git a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/view/ViewNodeV3Test.java b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/view/ViewNodeV3Test.java index 0219ca92..7cdb7d5e 100644 --- a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/view/ViewNodeV3Test.java +++ b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/view/ViewNodeV3Test.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.autotrack.view; @@ -30,12 +29,17 @@ import androidx.test.core.app.ApplicationProvider; import com.google.common.truth.Truth; +import com.growingio.android.sdk.Configurable; +import com.growingio.android.sdk.CoreConfiguration; import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.autotrack.AutotrackConfig; +import com.growingio.android.sdk.autotrack.Autotracker; import com.growingio.android.sdk.autotrack.IgnorePolicy; import com.growingio.android.sdk.autotrack.RobolectricActivity; import com.growingio.android.sdk.autotrack.page.ActivityPage; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProviderFactory; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; @@ -43,6 +47,8 @@ import org.robolectric.annotation.Config; import org.robolectric.fakes.RoboMenuItem; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; @Config(manifest = Config.NONE) @@ -50,6 +56,22 @@ public class ViewNodeV3Test { Application application = ApplicationProvider.getApplicationContext(); + private TrackerContext context; + + @Before + public void setup() { + CoreConfiguration coreConfiguration = new CoreConfiguration("ViewNodeV3Test", "growingio://apm"); + AutotrackConfig autotrackConfig = new AutotrackConfig(); + autotrackConfig.downgrade(); + Map, Configurable> map = new HashMap<>(); + map.put(AutotrackConfig.class, autotrackConfig); + TrackerLifecycleProviderFactory.create() + .createConfigurationProviderWithConfig( + coreConfiguration, + map); + Autotracker autotracker = new Autotracker(application); + context = autotracker.getContext(); + } @Test public void utilTest() { @@ -68,7 +90,9 @@ public void utilTest() { @Test public void viewNodeTest() { RobolectricActivity activity = Robolectric.buildActivity(RobolectricActivity.class).create().start().resume().get(); - ViewNodeV3Renderer renderer = new ViewNodeV3Renderer(); + + ViewNodeProvider viewNodeProvider = context.getProvider(ViewNodeProvider.class); + ViewNodeV3Renderer renderer = new ViewNodeV3Renderer(viewNodeProvider); ViewNodeV3 viewNode = renderer.renderViewNode(activity.getTextView()); Truth.assertThat(viewNode.getXPath()).endsWith("/DecorView/ActionBarOverlayLayout[0]/FrameLayout[0]/LinearLayout[0]/TextView[0]"); @@ -87,10 +111,9 @@ public void viewNodeTest() { @Test public void viewRenderTest() { - TrackerContext.init(application); RobolectricActivity activity = Robolectric.buildActivity(RobolectricActivity.class).create().start().resume().get(); - ActivityStateProvider.get().onActivityResumed(activity); - ViewNodeV3Renderer renderer = new ViewNodeV3Renderer(); + ViewNodeProvider viewNodeProvider = context.getProvider(ViewNodeProvider.class); + ViewNodeV3Renderer renderer = new ViewNodeV3Renderer(viewNodeProvider); RecyclerView recyclerView = activity.getRecyclerView(); recyclerView.getWindowVisibility(); diff --git a/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/view/ViewNodeV4Test.java b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/view/ViewNodeV4Test.java new file mode 100644 index 00000000..3120e4a9 --- /dev/null +++ b/growingio-autotracker-core/src/test/java/com/growingio/android/sdk/autotrack/view/ViewNodeV4Test.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.growingio.android.sdk.autotrack.view; + + +import android.app.Application; +import android.view.MenuItem; +import android.view.View; +import android.widget.EditText; +import android.widget.RadioButton; +import android.widget.RadioGroup; +import android.widget.RatingBar; +import android.widget.SeekBar; + +import androidx.recyclerview.widget.RecyclerView; +import androidx.test.core.app.ApplicationProvider; + +import com.google.common.truth.Truth; +import com.growingio.android.sdk.Configurable; +import com.growingio.android.sdk.CoreConfiguration; +import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.autotrack.AutotrackConfig; +import com.growingio.android.sdk.autotrack.Autotracker; +import com.growingio.android.sdk.autotrack.IgnorePolicy; +import com.growingio.android.sdk.autotrack.RobolectricActivity; +import com.growingio.android.sdk.autotrack.page.ActivityPage; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProviderFactory; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.fakes.RoboMenuItem; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +@Config(manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class ViewNodeV4Test { + + Application application = ApplicationProvider.getApplicationContext(); + private TrackerContext context; + + @Before + public void setup() { + CoreConfiguration coreConfiguration = new CoreConfiguration("ViewNodeV4Test", "growingio://apm"); + AutotrackConfig autotrackConfig = new AutotrackConfig(); + Map, Configurable> map = new HashMap<>(); + map.put(AutotrackConfig.class, autotrackConfig); + TrackerLifecycleProviderFactory.create() + .createConfigurationProviderWithConfig( + coreConfiguration, + map); + Autotracker autotracker = new Autotracker(application); + context = autotracker.getContext(); + } + + @Test + public void utilTest() { + RobolectricActivity activity = Robolectric.buildActivity(RobolectricActivity.class).create().start().resume().get(); + Truth.assertThat(ViewUtil.canCircle(activity.getTextView())).isFalse(); + activity.getTextView().setOnClickListener(v -> { + }); + Truth.assertThat(ViewUtil.canCircle(activity.getTextView())).isTrue(); + Truth.assertThat(ViewUtil.canCircle(activity.getRecyclerView())).isFalse(); + + RecyclerView recyclerView = activity.getRecyclerView(); + View itemView = Objects.requireNonNull(recyclerView.findViewHolderForAdapterPosition(0)).itemView; + Truth.assertThat(ViewUtil.canCircle(itemView)).isTrue(); + } + + @Test + public void viewNodeTest() { + RobolectricActivity activity = Robolectric.buildActivity(RobolectricActivity.class).create().start().resume().get(); + + ViewNodeProvider viewNodeProvider = context.getProvider(ViewNodeProvider.class); + ViewNodeV4Renderer renderer = new ViewNodeV4Renderer(viewNodeProvider); + + ViewNodeV4 viewNode = renderer.renderViewNode(activity.getTextView()); + Truth.assertThat(viewNode.getXPath()).isEqualTo("/DecorView/ActionBarOverlayLayout/FrameLayout/LinearLayout/TextView"); + Truth.assertThat(viewNode.getXIndex()).isEqualTo("/0/0/0/0/0"); + Truth.assertThat(viewNode.getViewContent()).isEqualTo("this is cpacm"); + Truth.assertThat(viewNode.getNodeType()).isEqualTo("TEXT"); + Truth.assertThat(viewNode.getClickableParentXPath()).isNull(); + Truth.assertThat(viewNode.getIndex()).isEqualTo(-1); + ViewNodeV4 newNode = viewNode.append(activity.getImageView(), 0); + Truth.assertThat(newNode.getXPath()).isEqualTo("/DecorView/ActionBarOverlayLayout/FrameLayout/LinearLayout/TextView/ImageView"); + Truth.assertThat(newNode.getXIndex()).isEqualTo("/0/0/0/0/0/0"); + + RecyclerView recyclerView = activity.getRecyclerView(); + View itemView = Objects.requireNonNull(recyclerView.findViewHolderForAdapterPosition(2)).itemView; + ViewNodeV4 listItemNode = renderer.renderViewNode(itemView); + Truth.assertThat(listItemNode.getXPath()).isEqualTo("/DecorView/ActionBarOverlayLayout/FrameLayout/LinearLayout/RecyclerView/TextView"); + Truth.assertThat(listItemNode.getXIndex()).isEqualTo("/0/0/0/0/0/-"); + } + + @Test + public void viewRenderTest() { + RobolectricActivity activity = Robolectric.buildActivity(RobolectricActivity.class).create().start().resume().get(); + ViewNodeProvider viewNodeProvider = context.getProvider(ViewNodeProvider.class); + ViewNodeV4Renderer renderer = new ViewNodeV4Renderer(viewNodeProvider); + + RecyclerView recyclerView = activity.getRecyclerView(); + recyclerView.getWindowVisibility(); + Truth.assertThat(ViewAttributeUtil.isViewSelfVisible(activity.getWindow().getDecorView())).isFalse(); + Truth.assertThat(ViewAttributeUtil.viewVisibilityInParents(recyclerView)).isFalse(); + + View itemView = Objects.requireNonNull(recyclerView.findViewHolderForAdapterPosition(0)).itemView; + String content = ViewAttributeUtil.getViewContent(itemView); + Truth.assertThat(content).isEqualTo("position:0"); + + EditText testEt = new EditText(activity); + testEt.setText("test edittext"); + MenuItem testItem = new RoboMenuItem().setTitle("test menu item") + .setActionView(testEt); + activity.onContextItemSelected(testItem); + ViewNodeV4 pageNode = ViewNodeV4.generateMenuItemViewNode(activity, testItem); + Truth.assertThat(pageNode.getXPath()).isEqualTo("/MenuView/MenuItem"); + Truth.assertThat(pageNode.getXIndex()).isEqualTo("/0/0"); + + ViewNodeV4 menuNode = renderer.renderViewNode(testItem.getActionView()); + Truth.assertThat(menuNode.getXPath()).isEqualTo("/EditText"); + Truth.assertThat(menuNode.getXIndex()).isEqualTo("/0"); + + RatingBar ratingBar = new RatingBar(activity); + ratingBar.setRating(10f); + ratingBar.setProgress(1); + Truth.assertThat(ViewAttributeUtil.getViewContent(ratingBar)).isEqualTo("0.5"); + + SeekBar seekBar = new SeekBar(activity); + seekBar.setProgress(100); + Truth.assertThat(ViewAttributeUtil.getViewContent(seekBar)).isEqualTo("100"); + + RadioGroup radioGroup = new RadioGroup(activity); + RadioButton radio1 = new RadioButton(activity); + radio1.setText("radio1"); + radio1.setId(1); + RadioButton radio2 = new RadioButton(activity); + radio2.setText("radio2"); + radio2.setId(2); + radio2.setChecked(true); + radio2.setSelected(true); + radioGroup.addView(radio1); + radioGroup.addView(radio2); + radioGroup.setSelected(true); + Truth.assertThat(ViewAttributeUtil.getViewContent(radioGroup)).isEqualTo("radio2"); + } + + @Test + public void viewAttrTest() { + RobolectricActivity activity = Robolectric.buildActivity(RobolectricActivity.class).create().start().resume().get(); + + ViewAttributeUtil.setCustomId(activity.getTextView(), "test id"); + Truth.assertThat(ViewAttributeUtil.getCustomId(activity.getTextView())).isEqualTo("test id"); + + ViewAttributeUtil.setTrackText(activity.getImageView(), true); + Truth.assertThat(ViewAttributeUtil.getTrackText(activity.getImageView())).isTrue(); + + ViewAttributeUtil.setContent(activity.getTextView(), "test content"); + Truth.assertThat(ViewAttributeUtil.getContent(activity.getTextView())).isEqualTo("test content"); + + ViewAttributeUtil.setIgnorePolicy(activity.getRecyclerView(), IgnorePolicy.IGNORE_ALL); + Truth.assertThat(ViewAttributeUtil.getIgnorePolicy(activity.getRecyclerView())).isEqualTo(IgnorePolicy.IGNORE_ALL); + + ViewAttributeUtil.setMonitoringFocusContent(activity.getImageView(), "test focus"); + Truth.assertThat(ViewAttributeUtil.getMonitoringFocusContent(activity.getImageView())).isEqualTo("test focus"); + + ViewAttributeUtil.setViewPage(activity.getTextView(), new ActivityPage(activity)); + Truth.assertThat(ViewAttributeUtil.getViewPage(activity.getTextView()).path()).isEqualTo("/RobolectricActivity"); + + Truth.assertThat(PageHelper.getWindowPrefix(activity.getTextView())).isEqualTo("Page"); + Truth.assertThat(PageHelper.getMainWindowPrefix()).isEqualTo("MainWindow"); + } + +} diff --git a/growingio-data/database/build.gradle b/growingio-data/database/build.gradle index 9e6f5a00..e0e72f77 100644 --- a/growingio-data/database/build.gradle +++ b/growingio-data/database/build.gradle @@ -1,55 +1,55 @@ plugins { - id 'com.android.library' + id 'com.android.library' } android { - namespace 'com.growingio.android.database' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } - - testOptions { - unitTests.all { - jacoco { - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - } - } + namespace 'com.growingio.android.database' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } + + testOptions { + unitTests.all { + jacoco { + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } } dependencies { - testImplementation libs.bundles.test - testImplementation libs.androidx.test.core - testImplementation project(path: ':growingio-data:protobuf') - testImplementation project(path: ':growingio-data:json') + testImplementation libs.bundles.test + testImplementation libs.androidx.test.core + testImplementation project(path: ':growingio-data:protobuf') + testImplementation project(path: ':growingio-data:json') - implementation project(':growingio-tracker-core') - compileOnly libs.growingio.gmonitor + implementation project(':growingio-tracker-core') + compileOnly libs.growingio.gmonitor - implementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + implementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" apply from: "${rootProject.projectDir}/gradle/jacocoModule.gradle" \ No newline at end of file diff --git a/growingio-data/database/src/main/java/com/growingio/android/database/DatabaseDataFetcher.java b/growingio-data/database/src/main/java/com/growingio/android/database/DatabaseDataFetcher.java index 8f125cdd..cf6e20fc 100644 --- a/growingio-data/database/src/main/java/com/growingio/android/database/DatabaseDataFetcher.java +++ b/growingio-data/database/src/main/java/com/growingio/android/database/DatabaseDataFetcher.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.database; import android.text.TextUtils; diff --git a/growingio-data/database/src/main/java/com/growingio/android/database/DatabaseDataLoader.java b/growingio-data/database/src/main/java/com/growingio/android/database/DatabaseDataLoader.java index 008b4ffa..5aa8be31 100644 --- a/growingio-data/database/src/main/java/com/growingio/android/database/DatabaseDataLoader.java +++ b/growingio-data/database/src/main/java/com/growingio/android/database/DatabaseDataLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.database; -import android.content.Context; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.middleware.EventDatabase; import com.growingio.android.sdk.track.middleware.EventDbResult; import com.growingio.android.sdk.track.modelloader.ModelLoader; @@ -44,9 +43,9 @@ public LoadData buildLoadData(EventDatabase database) { public static class Factory implements ModelLoaderFactory { - private final Context context; + private final TrackerContext context; - public Factory(Context context) { + public Factory(TrackerContext context) { this.context = context; } diff --git a/growingio-data/database/src/main/java/com/growingio/android/database/DatabaseLibraryModule.java b/growingio-data/database/src/main/java/com/growingio/android/database/DatabaseLibraryModule.java index 5f83e616..048c4fb3 100644 --- a/growingio-data/database/src/main/java/com/growingio/android/database/DatabaseLibraryModule.java +++ b/growingio-data/database/src/main/java/com/growingio/android/database/DatabaseLibraryModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,15 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.database; -import android.content.Context; - import com.growingio.android.sdk.LibraryGioModule; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.middleware.EventDatabase; import com.growingio.android.sdk.track.middleware.EventDbResult; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import com.growingio.sdk.annotation.GIOLibraryModule; /** @@ -32,7 +29,7 @@ @GIOLibraryModule() public class DatabaseLibraryModule extends LibraryGioModule { @Override - public void registerComponents(Context context, TrackerRegistry registry) { - registry.register(EventDatabase.class, EventDbResult.class, new DatabaseDataLoader.Factory(context)); + public void registerComponents(TrackerContext context) { + context.getRegistry().register(EventDatabase.class, EventDbResult.class, new DatabaseDataLoader.Factory(context)); } } diff --git a/growingio-data/database/src/main/java/com/growingio/android/database/DeprecatedEventSQLite.java b/growingio-data/database/src/main/java/com/growingio/android/database/DeprecatedEventSQLite.java index d2476ec8..707d65e4 100644 --- a/growingio-data/database/src/main/java/com/growingio/android/database/DeprecatedEventSQLite.java +++ b/growingio-data/database/src/main/java/com/growingio/android/database/DeprecatedEventSQLite.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.database; import android.annotation.SuppressLint; @@ -28,7 +27,7 @@ import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.middleware.GEvent; import com.growingio.android.sdk.track.middleware.Serializer; -import com.growingio.android.sdk.track.providers.EventStateProvider; +import com.growingio.android.sdk.track.providers.EventBuilderProvider; import java.io.File; import java.util.ArrayList; @@ -112,7 +111,7 @@ private boolean migrateEvent() { if (event != null) { events.add(event); if (event instanceof BaseEvent) { - Logger.printJson(TAG, "migrate data:", EventStateProvider.get().toJson((BaseEvent) event).toString()); + Logger.printJson(TAG, "migrate data:", EventBuilderProvider.toJson((BaseEvent) event).toString()); } } } diff --git a/growingio-data/database/src/main/java/com/growingio/android/database/EventDataContentProvider.java b/growingio-data/database/src/main/java/com/growingio/android/database/EventDataContentProvider.java index 18d877a7..6bfe0790 100644 --- a/growingio-data/database/src/main/java/com/growingio/android/database/EventDataContentProvider.java +++ b/growingio-data/database/src/main/java/com/growingio/android/database/EventDataContentProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.database; import static com.growingio.android.database.EventDataTable.TABLE_EVENTS; diff --git a/growingio-data/database/src/main/java/com/growingio/android/database/EventDataManager.java b/growingio-data/database/src/main/java/com/growingio/android/database/EventDataManager.java index da85dd15..2091a640 100644 --- a/growingio-data/database/src/main/java/com/growingio/android/database/EventDataManager.java +++ b/growingio-data/database/src/main/java/com/growingio/android/database/EventDataManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,14 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.database; import android.annotation.SuppressLint; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.ContentValues; -import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteFullException; import android.net.Uri; @@ -34,6 +32,7 @@ import com.growingio.android.sdk.track.middleware.GEvent; import com.growingio.android.sdk.track.modelloader.ModelLoader; +import java.io.File; import java.util.ArrayList; import java.util.List; @@ -42,18 +41,17 @@ import static com.growingio.android.database.EventDataTable.COLUMN_ID; import static com.growingio.android.database.EventDataTable.COLUMN_POLICY; import static com.growingio.android.database.EventDataTable.TABLE_EVENTS; -import static com.growingio.android.database.EventDataTable.getContentUri; public class EventDataManager { private static final String TAG = "EventDataManager"; private static final long EVENT_VALID_PERIOD_MILLS = 7L * 24 * 60 * 60_000; - private final Context context; + private final TrackerContext context; private final String eventsInfoAuthority; private boolean ignoreOperations = false; - EventDataManager(Context context) { + EventDataManager(TrackerContext context) { this.context = context; eventsInfoAuthority = context.getPackageName() + "." + EventDataContentProvider.CONTENT_PROVIDER_NAME; @@ -65,7 +63,7 @@ public class EventDataManager { } private EventByteArray formatData(EventFormatData data) { - ModelLoader modelLoader = TrackerContext.get().getRegistry().getModelLoader(EventFormatData.class, EventByteArray.class); + ModelLoader modelLoader = context.getRegistry().getModelLoader(EventFormatData.class, EventByteArray.class); if (modelLoader == null) { Logger.e(TAG, "please register eventformat component first"); return null; @@ -283,6 +281,10 @@ void removeAllEvents() { } } + public Uri getContentUri() { + return Uri.parse("content://" + context.getPackageName() + "." + EventDataContentProvider.CONTENT_PROVIDER_NAME + File.separator + TABLE_EVENTS); + } + private void onDiskFull(SQLiteFullException e) { Logger.w(TAG, e, "Disk full, all operations will be ignored"); ignoreOperations = true; diff --git a/growingio-data/database/src/main/java/com/growingio/android/database/EventDataSQLiteOpenHelper.java b/growingio-data/database/src/main/java/com/growingio/android/database/EventDataSQLiteOpenHelper.java index c75214de..9e1e3549 100644 --- a/growingio-data/database/src/main/java/com/growingio/android/database/EventDataSQLiteOpenHelper.java +++ b/growingio-data/database/src/main/java/com/growingio/android/database/EventDataSQLiteOpenHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.database; import android.content.Context; diff --git a/growingio-data/database/src/main/java/com/growingio/android/database/EventDataTable.java b/growingio-data/database/src/main/java/com/growingio/android/database/EventDataTable.java index 3e034bcc..107ab26a 100644 --- a/growingio-data/database/src/main/java/com/growingio/android/database/EventDataTable.java +++ b/growingio-data/database/src/main/java/com/growingio/android/database/EventDataTable.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,15 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.database; import android.content.ContentValues; -import android.net.Uri; -import com.growingio.android.sdk.TrackerContext; -import java.io.File; public class EventDataTable { @@ -49,10 +45,6 @@ private EventDataTable() { public static final String DROP_TABLE_EVENTS = "DROP TABLE IF EXISTS " + TABLE_EVENTS + ";"; - public static Uri getContentUri() { - return Uri.parse("content://" + TrackerContext.get().getPackageName() + "." + EventDataContentProvider.CONTENT_PROVIDER_NAME + File.separator + TABLE_EVENTS); - } - public static ContentValues putValues(byte[] data, String eventType, Integer policy) { long current = System.currentTimeMillis(); ContentValues contentValues = new ContentValues(); diff --git a/growingio-data/database/src/test/java/com/growingio/android/database/DbTest.java b/growingio-data/database/src/test/java/com/growingio/android/database/DbTest.java index cabdb8b1..8017e38b 100644 --- a/growingio-data/database/src/test/java/com/growingio/android/database/DbTest.java +++ b/growingio-data/database/src/test/java/com/growingio/android/database/DbTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.database; import android.app.Application; @@ -23,6 +22,7 @@ import com.google.common.truth.Truth; import com.growingio.android.json.JsonDataLoader; +import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.events.CustomEvent; import com.growingio.android.sdk.track.events.PageEvent; @@ -32,7 +32,6 @@ import com.growingio.android.sdk.track.middleware.format.EventFormatData; import com.growingio.android.sdk.track.modelloader.DataFetcher; import com.growingio.android.sdk.track.modelloader.ModelLoader; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import com.growingio.android.protobuf.ProtobufDataLoader; import org.json.JSONArray; @@ -60,11 +59,14 @@ public class DbTest { private ProviderInfo providerInfo; private EventDataManager sqLite; + private TrackerContext trackerContext; + @Before public void setup() { - TrackerContext.init(application); + Tracker tracker = new Tracker(application); + trackerContext = tracker.getContext(); providerInfo = new ProviderInfo(); - sqLite = new EventDataManager(application); + sqLite = new EventDataManager(trackerContext); providerInfo.authority = application.getPackageName() + "." + EventDataContentProvider.class.getSimpleName(); } @@ -85,16 +87,15 @@ public void shouldInitializeFromInitializeProviderInfo() throws Exception { @Test public void dataModuleTest() { - TrackerContext.get().getRegistry().register(EventFormatData.class, EventByteArray.class, new ProtobufDataLoader.Factory()); + trackerContext.getRegistry().register(EventFormatData.class, EventByteArray.class, new ProtobufDataLoader.Factory()); DatabaseLibraryModule module = new DatabaseLibraryModule(); - TrackerRegistry trackerRegistry = new TrackerRegistry(); - module.registerComponents(application, trackerRegistry); + module.registerComponents(trackerContext); CustomEvent customEvent = new CustomEvent.Builder() .setEventName("databaseTest") .build(); - ModelLoader modelLoader = trackerRegistry.getModelLoader(EventDatabase.class, EventDbResult.class); + ModelLoader modelLoader = trackerContext.getRegistry().getModelLoader(EventDatabase.class, EventDbResult.class); ModelLoader.LoadData loadData = modelLoader.buildLoadData(EventDatabase.insert(customEvent)); DataFetcher dataFetcher = loadData.fetcher; Truth.assertThat(dataFetcher.getDataClass()).isAssignableTo(EventDbResult.class); @@ -137,7 +138,7 @@ public void dataModuleTest() { @Test public void contentProviderTest() { - TrackerContext.get().getRegistry().register(EventFormatData.class, EventByteArray.class, new JsonDataLoader.Factory()); + trackerContext.getRegistry().register(EventFormatData.class, EventByteArray.class, new JsonDataLoader.Factory()); controller.create(providerInfo).get(); sqLite.removeOverdueEvents(); CustomEvent customEvent = new CustomEvent.Builder() @@ -188,7 +189,8 @@ public void dbHelperTest() throws IOException { @Test public void migrateTest() { - EventDataManager dataManager = new EventDataManager(application); + trackerContext.getRegistry().register(EventFormatData.class, EventByteArray.class, new ProtobufDataLoader.Factory()); + EventDataManager dataManager = new EventDataManager(trackerContext); DeprecatedEventSQLite sqLite = new DeprecatedEventSQLite(application, dataManager); CustomEvent customEvent = new CustomEvent.Builder() .setEventName("databaseTest") diff --git a/growingio-data/encoder/build.gradle b/growingio-data/encoder/build.gradle index e2c356dc..0c19d508 100644 --- a/growingio-data/encoder/build.gradle +++ b/growingio-data/encoder/build.gradle @@ -1,53 +1,53 @@ plugins { - id 'com.android.library' + id 'com.android.library' } android { - namespace 'com.growingio.android.encoder' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } + namespace 'com.growingio.android.encoder' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } - testOptions { - unitTests.all { - jacoco { - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - } - } + testOptions { + unitTests.all { + jacoco { + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } } dependencies { - testImplementation libs.bundles.test - testImplementation libs.androidx.test.core + testImplementation libs.bundles.test + testImplementation libs.androidx.test.core - implementation project(':growingio-tracker-core') - debugImplementation project(':growingio-tools:snappy') - releaseImplementation libs.growingio.snappy + implementation project(':growingio-tracker-core') + debugImplementation project(':growingio-tools:snappy') + releaseImplementation libs.growingio.snappy - implementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + implementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" apply from: "${rootProject.projectDir}/gradle/jacocoModule.gradle" \ No newline at end of file diff --git a/growingio-data/encoder/src/main/java/com/growingio/android/encoder/EncoderDataFetcher.java b/growingio-data/encoder/src/main/java/com/growingio/android/encoder/EncoderDataFetcher.java index 6991b9c9..628484b9 100644 --- a/growingio-data/encoder/src/main/java/com/growingio/android/encoder/EncoderDataFetcher.java +++ b/growingio-data/encoder/src/main/java/com/growingio/android/encoder/EncoderDataFetcher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.encoder; import com.growingio.android.sdk.track.middleware.http.EventEncoder; diff --git a/growingio-data/encoder/src/main/java/com/growingio/android/encoder/EncoderDataLoader.java b/growingio-data/encoder/src/main/java/com/growingio/android/encoder/EncoderDataLoader.java index 7dd7a3d7..db0af878 100644 --- a/growingio-data/encoder/src/main/java/com/growingio/android/encoder/EncoderDataLoader.java +++ b/growingio-data/encoder/src/main/java/com/growingio/android/encoder/EncoderDataLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.encoder; import com.growingio.android.sdk.track.middleware.http.EventEncoder; diff --git a/growingio-data/encoder/src/main/java/com/growingio/android/encoder/EncoderLibraryGioModule.java b/growingio-data/encoder/src/main/java/com/growingio/android/encoder/EncoderLibraryGioModule.java index 17c6d623..280980d9 100644 --- a/growingio-data/encoder/src/main/java/com/growingio/android/encoder/EncoderLibraryGioModule.java +++ b/growingio-data/encoder/src/main/java/com/growingio/android/encoder/EncoderLibraryGioModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,11 +15,10 @@ */ package com.growingio.android.encoder; -import android.content.Context; import com.growingio.android.sdk.LibraryGioModule; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.middleware.http.EventEncoder; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import com.growingio.sdk.annotation.GIOLibraryModule; /** @@ -30,7 +29,7 @@ @GIOLibraryModule public class EncoderLibraryGioModule extends LibraryGioModule { @Override - public void registerComponents(Context context, TrackerRegistry registry) { - registry.register(EventEncoder.class, EventEncoder.class, new EncoderDataLoader.Factory()); + public void registerComponents(TrackerContext context) { + context.getRegistry().register(EventEncoder.class, EventEncoder.class, new EncoderDataLoader.Factory()); } } diff --git a/growingio-data/encoder/src/test/java/com/growingio/android/encoder/EncoderTest.java b/growingio-data/encoder/src/test/java/com/growingio/android/encoder/EncoderTest.java index 3956152e..7ac9d1fe 100644 --- a/growingio-data/encoder/src/test/java/com/growingio/android/encoder/EncoderTest.java +++ b/growingio-data/encoder/src/test/java/com/growingio/android/encoder/EncoderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,18 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.encoder; -import android.content.Context; -import androidx.test.core.app.ApplicationProvider; import com.google.common.truth.Truth; import com.growingio.android.sdk.track.middleware.http.EventEncoder; import com.growingio.android.sdk.track.middleware.http.EventUrl; import com.growingio.android.sdk.track.modelloader.DataFetcher; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import com.growingio.android.snappy.Snappy; import com.growingio.android.snappy.XORUtils; @@ -36,13 +32,8 @@ @Config(manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) public class EncoderTest { - private Context context = ApplicationProvider.getApplicationContext(); - @Test public void encoder() { - EncoderLibraryGioModule module = new EncoderLibraryGioModule(); - TrackerRegistry trackerRegistry = new TrackerRegistry(); - module.registerComponents(context, trackerRegistry); EventUrl eventUrl = new EventUrl("https://localhost", 10000L) .addPath("v3") @@ -53,9 +44,7 @@ public void encoder() { .addParam("stm", String.valueOf(10000L)); EventEncoder encoder = new EventEncoder(eventUrl); - DataFetcher dataFetcher = trackerRegistry.getModelLoader(EventEncoder.class, EventEncoder.class) - .buildLoadData(encoder) - .fetcher; + DataFetcher dataFetcher = new EncoderDataFetcher(encoder); Truth.assertThat(dataFetcher.getDataClass()).isAssignableTo(EventEncoder.class); EventEncoder data = dataFetcher.executeData(); EventUrl eurl = data.getEventUrl(); diff --git a/growingio-data/json/build.gradle b/growingio-data/json/build.gradle index f2151590..685169bc 100644 --- a/growingio-data/json/build.gradle +++ b/growingio-data/json/build.gradle @@ -1,51 +1,51 @@ plugins { - id 'com.android.library' + id 'com.android.library' } android { - namespace 'com.growingio.android.json' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } + namespace 'com.growingio.android.json' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } - testOptions { - unitTests.all { - jacoco { - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - } - } + testOptions { + unitTests.all { + jacoco { + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } } dependencies { - testImplementation libs.bundles.test - testImplementation libs.androidx.test.core + testImplementation libs.bundles.test + testImplementation libs.androidx.test.core - implementation project(':growingio-tracker-core') + implementation project(':growingio-tracker-core') - implementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + implementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" apply from: "${rootProject.projectDir}/gradle/jacocoModule.gradle" \ No newline at end of file diff --git a/growingio-data/json/src/main/java/com/growingio/android/json/JsonDataFetcher.java b/growingio-data/json/src/main/java/com/growingio/android/json/JsonDataFetcher.java index ddeaffa0..c31c2832 100644 --- a/growingio-data/json/src/main/java/com/growingio/android/json/JsonDataFetcher.java +++ b/growingio-data/json/src/main/java/com/growingio/android/json/JsonDataFetcher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.json; import android.os.Build; @@ -24,7 +23,7 @@ import com.growingio.android.sdk.track.middleware.format.EventByteArray; import com.growingio.android.sdk.track.middleware.GEvent; import com.growingio.android.sdk.track.middleware.format.FormatDataFetcher; -import com.growingio.android.sdk.track.providers.EventStateProvider; +import com.growingio.android.sdk.track.providers.EventBuilderProvider; import org.json.JSONObject; @@ -65,7 +64,7 @@ public EventByteArray executeData() { @Override public EventByteArray format(GEvent gEvent) { if (gEvent instanceof BaseEvent) { - JSONObject eventJson = EventStateProvider.get().toJson((BaseEvent) gEvent); + JSONObject eventJson = EventBuilderProvider.toJson((BaseEvent) gEvent); return new EventByteArray(eventJson.toString().getBytes(), "application/json"); } return new EventByteArray(null); diff --git a/growingio-data/json/src/main/java/com/growingio/android/json/JsonDataLoader.java b/growingio-data/json/src/main/java/com/growingio/android/json/JsonDataLoader.java index 3914c6a5..a048760e 100644 --- a/growingio-data/json/src/main/java/com/growingio/android/json/JsonDataLoader.java +++ b/growingio-data/json/src/main/java/com/growingio/android/json/JsonDataLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.json; import com.growingio.android.sdk.track.middleware.format.EventFormatData; diff --git a/growingio-data/json/src/main/java/com/growingio/android/json/JsonLibraryModule.java b/growingio-data/json/src/main/java/com/growingio/android/json/JsonLibraryModule.java index cb271aa4..8cdd8905 100644 --- a/growingio-data/json/src/main/java/com/growingio/android/json/JsonLibraryModule.java +++ b/growingio-data/json/src/main/java/com/growingio/android/json/JsonLibraryModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +15,11 @@ */ package com.growingio.android.json; -import android.content.Context; import com.growingio.android.sdk.LibraryGioModule; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.middleware.format.EventFormatData; import com.growingio.android.sdk.track.middleware.format.EventByteArray; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import com.growingio.sdk.annotation.GIOLibraryModule; /** @@ -31,7 +30,7 @@ @GIOLibraryModule public class JsonLibraryModule extends LibraryGioModule { @Override - public void registerComponents(Context context, TrackerRegistry registry) { - registry.register(EventFormatData.class, EventByteArray.class, new JsonDataLoader.Factory()); + public void registerComponents(TrackerContext context) { + context.getRegistry().register(EventFormatData.class, EventByteArray.class, new JsonDataLoader.Factory()); } } diff --git a/growingio-data/json/src/test/java/JsonDataTest.java b/growingio-data/json/src/test/java/JsonDataTest.java index 0ac408ef..a6ca7c5a 100644 --- a/growingio-data/json/src/test/java/JsonDataTest.java +++ b/growingio-data/json/src/test/java/JsonDataTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,25 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import android.app.Application; - -import androidx.test.core.app.ApplicationProvider; - import com.google.common.truth.Truth; -import com.growingio.android.json.JsonLibraryModule; -import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.json.JsonDataFetcher; import com.growingio.android.sdk.track.events.CustomEvent; import com.growingio.android.sdk.track.middleware.format.EventFormatData; import com.growingio.android.sdk.track.middleware.format.EventByteArray; import com.growingio.android.sdk.track.modelloader.DataFetcher; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; -import com.growingio.android.sdk.track.providers.EventStateProvider; +import com.growingio.android.sdk.track.providers.EventBuilderProvider; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -42,27 +34,14 @@ @Config(manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) public class JsonDataTest { - private Application application = ApplicationProvider.getApplicationContext(); - - @Before - public void setup() { - TrackerContext.init(application); - } - @Test public void dataFormat() { - JsonLibraryModule module = new JsonLibraryModule(); - TrackerRegistry trackerRegistry = new TrackerRegistry(); - module.registerComponents(application, trackerRegistry); - CustomEvent customEvent = new CustomEvent.Builder() .setEventName("jsonTest") .build(); EventFormatData eventData = EventFormatData.format(customEvent); - DataFetcher dataFetcher = - trackerRegistry.getModelLoader(EventFormatData.class, EventByteArray.class) - .buildLoadData(eventData).fetcher; + DataFetcher dataFetcher = new JsonDataFetcher(eventData); Truth.assertThat(dataFetcher.getDataClass()).isAssignableTo(EventByteArray.class); EventByteArray data = dataFetcher.executeData(); try { @@ -79,10 +58,6 @@ public void dataFormat() { @Test public void dataMerge() { - JsonLibraryModule module = new JsonLibraryModule(); - TrackerRegistry trackerRegistry = new TrackerRegistry(); - module.registerComponents(application, trackerRegistry); - ArrayList arrayList = new ArrayList<>(); CustomEvent customEvent = new CustomEvent.Builder() .setEventName("merge") @@ -91,12 +66,11 @@ public void dataMerge() { .setEventName("cpacm") .build(); - arrayList.add(EventStateProvider.get().toJson(customEvent).toString().getBytes()); - arrayList.add(EventStateProvider.get().toJson(customEvent2).toString().getBytes()); + arrayList.add(EventBuilderProvider.toJson(customEvent).toString().getBytes()); + arrayList.add(EventBuilderProvider.toJson(customEvent2).toString().getBytes()); EventFormatData eventData = EventFormatData.merge(arrayList); - DataFetcher dataFetcher = trackerRegistry.getModelLoader(EventFormatData.class, EventByteArray.class) - .buildLoadData(eventData).fetcher; + DataFetcher dataFetcher = new JsonDataFetcher(eventData); Truth.assertThat(dataFetcher.getDataClass()).isAssignableTo(EventByteArray.class); EventByteArray data = dataFetcher.executeData(); diff --git a/growingio-data/protobuf/build.gradle b/growingio-data/protobuf/build.gradle index bc0d8d6f..c55590b3 100644 --- a/growingio-data/protobuf/build.gradle +++ b/growingio-data/protobuf/build.gradle @@ -1,80 +1,80 @@ plugins { - id 'com.android.library' - id 'com.google.protobuf' + id 'com.android.library' + id 'com.google.protobuf' } android { - namespace 'com.growingio.android.protobuf' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } + namespace 'com.growingio.android.protobuf' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - consumerProguardFiles 'consumer-rules.pro' - } - } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + consumerProguardFiles 'consumer-rules.pro' + } + } - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } - testOptions { - unitTests.all { - jacoco { - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - } - } + testOptions { + unitTests.all { + jacoco { + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } - sourceSets { - main { - java { - srcDir 'src/main/java' - } - proto { - srcDir 'src/main/proto' - include '**/*.proto' - } - } - } + sourceSets { + main { + java { + srcDir 'src/main/java' + } + proto { + srcDir 'src/main/proto' + include '**/*.proto' + } + } + } } protobuf { - protoc { - artifact = "com.google.protobuf:protoc:${libs.versions.protobuf.get()}" - } - generateProtoTasks { - all().each { task -> - task.builtins { - java { option "lite" } - } - } - } + protoc { + artifact = "com.google.protobuf:protoc:${libs.versions.protobuf.get()}" + } + generateProtoTasks { + all().each { task -> + task.builtins { + java { option "lite" } + } + } + } } dependencies { - testImplementation libs.bundles.test - testImplementation libs.androidx.test.core + testImplementation libs.bundles.test + testImplementation libs.androidx.test.core - implementation libs.protobuf.javalite + implementation libs.protobuf.javalite - implementation project(':growingio-tracker-core') + implementation project(':growingio-tracker-core') - implementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + implementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" apply from: "${rootProject.projectDir}/gradle/jacocoModule.gradle" \ No newline at end of file diff --git a/growingio-data/protobuf/src/main/java/com/growingio/android/protobuf/EventProtocolTransfer.java b/growingio-data/protobuf/src/main/java/com/growingio/android/protobuf/EventProtocolTransfer.java index 432d651b..0a4a20d3 100644 --- a/growingio-data/protobuf/src/main/java/com/growingio/android/protobuf/EventProtocolTransfer.java +++ b/growingio-data/protobuf/src/main/java/com/growingio/android/protobuf/EventProtocolTransfer.java @@ -1,32 +1,35 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.protobuf; import android.text.TextUtils; import com.google.protobuf.InvalidProtocolBufferException; import com.growingio.android.sdk.track.events.ActivateEvent; +import com.growingio.android.sdk.track.events.AppClosedEvent; import com.growingio.android.sdk.track.events.AutotrackEventType; +import com.growingio.android.sdk.track.events.ConversionVariablesEvent; import com.growingio.android.sdk.track.events.CustomEvent; +import com.growingio.android.sdk.track.events.LoginUserAttributesEvent; import com.growingio.android.sdk.track.events.PageEvent; import com.growingio.android.sdk.track.events.PageLevelCustomEvent; import com.growingio.android.sdk.track.events.TrackEventType; import com.growingio.android.sdk.track.events.ViewElementEvent; import com.growingio.android.sdk.track.events.VisitEvent; +import com.growingio.android.sdk.track.events.VisitorAttributesEvent; import com.growingio.android.sdk.track.events.base.BaseAttributesEvent; import com.growingio.android.sdk.track.events.base.BaseEvent; import com.growingio.android.sdk.track.events.hybrid.HybridCustomEvent; @@ -34,7 +37,7 @@ import com.growingio.android.sdk.track.events.hybrid.HybridViewElementEvent; import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.middleware.GEvent; -import com.growingio.android.sdk.track.providers.EventStateProvider; +import com.growingio.android.sdk.track.providers.EventBuilderProvider; import org.json.JSONException; import org.json.JSONObject; @@ -63,7 +66,7 @@ public static EventV3Protocol.EventV3Dto covertToProtobuf(byte[] byteArray) { if (data.startsWith("{") && data.endsWith("}")) { //ensure json format JSONObject json = new JSONObject(data); BaseEvent.BaseBuilder builder = generateEventBuilder(json); - EventStateProvider.get().parseFrom(builder, json); + EventBuilderProvider.parseFrom(builder, json); if (builder != null) { return protocol(builder.build()); } @@ -110,10 +113,17 @@ private static BaseEvent.BaseBuilder generateEventBuilder(JSONObject event) { if (TrackEventType.VISIT.equals(eventType)) { return new VisitEvent.Builder(); } else if (AutotrackEventType.VIEW_CLICK.equals(eventType) - || AutotrackEventType.VIEW_CHANGE.equals(eventType)) { - return new HybridViewElementEvent.Builder(eventType); + || AutotrackEventType.VIEW_CHANGE.equals(eventType) + || TrackEventType.FORM_SUBMIT.equals(eventType)) { + if (!TextUtils.isEmpty(event.optString("query"))) { + return new HybridViewElementEvent.Builder(eventType); + } + return new ViewElementEvent.Builder(eventType); } else if (AutotrackEventType.PAGE.equals(eventType)) { - return new HybridPageEvent.Builder(); + if (!TextUtils.isEmpty(event.optString("query"))) { + return new HybridPageEvent.Builder(); + } + return new PageEvent.Builder(); } else if (TrackEventType.CUSTOM.equals(eventType)) { if (!TextUtils.isEmpty(event.optString("path"))) { return new PageLevelCustomEvent.Builder(); @@ -124,6 +134,14 @@ private static BaseEvent.BaseBuilder generateEventBuilder(JSONObject event) { return new CustomEvent.Builder(); //custom } else if (TrackEventType.ACTIVATE.equals(eventType)) { return new ActivateEvent.Builder(); + } else if (TrackEventType.VISITOR_ATTRIBUTES.equals(eventType)) { + return new VisitorAttributesEvent.Builder(); + } else if (TrackEventType.LOGIN_USER_ATTRIBUTES.equals(eventType)) { + return new LoginUserAttributesEvent.Builder(); + } else if (TrackEventType.CONVERSION_VARIABLES.equals(eventType)) { + return new ConversionVariablesEvent.Builder(); + } else if (TrackEventType.APP_CLOSED.equals(eventType)) { + return new AppClosedEvent.Builder(); } else { return new BaseAttributesEvent.Builder(eventType) { @Override diff --git a/growingio-data/protobuf/src/main/java/com/growingio/android/protobuf/ProtobufDataFetcher.java b/growingio-data/protobuf/src/main/java/com/growingio/android/protobuf/ProtobufDataFetcher.java index 57119ce0..573dc22f 100644 --- a/growingio-data/protobuf/src/main/java/com/growingio/android/protobuf/ProtobufDataFetcher.java +++ b/growingio-data/protobuf/src/main/java/com/growingio/android/protobuf/ProtobufDataFetcher.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.protobuf; diff --git a/growingio-data/protobuf/src/main/java/com/growingio/android/protobuf/ProtobufDataLoader.java b/growingio-data/protobuf/src/main/java/com/growingio/android/protobuf/ProtobufDataLoader.java index 8bf8ffeb..ef6f5448 100644 --- a/growingio-data/protobuf/src/main/java/com/growingio/android/protobuf/ProtobufDataLoader.java +++ b/growingio-data/protobuf/src/main/java/com/growingio/android/protobuf/ProtobufDataLoader.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.protobuf; import com.growingio.android.sdk.track.middleware.format.EventByteArray; diff --git a/growingio-data/protobuf/src/main/java/com/growingio/android/protobuf/ProtobufLibraryModule.java b/growingio-data/protobuf/src/main/java/com/growingio/android/protobuf/ProtobufLibraryModule.java index 9165c571..26c82af6 100644 --- a/growingio-data/protobuf/src/main/java/com/growingio/android/protobuf/ProtobufLibraryModule.java +++ b/growingio-data/protobuf/src/main/java/com/growingio/android/protobuf/ProtobufLibraryModule.java @@ -1,26 +1,25 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.growingio.android.protobuf; -import android.content.Context; import com.growingio.android.sdk.LibraryGioModule; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.middleware.format.EventByteArray; import com.growingio.android.sdk.track.middleware.format.EventFormatData; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import com.growingio.sdk.annotation.GIOLibraryModule; /** @@ -31,7 +30,7 @@ @GIOLibraryModule public class ProtobufLibraryModule extends LibraryGioModule { @Override - public void registerComponents(Context context, TrackerRegistry registry) { - registry.register(EventFormatData.class, EventByteArray.class, new ProtobufDataLoader.Factory()); + public void registerComponents(TrackerContext context) { + context.getRegistry().register(EventFormatData.class, EventByteArray.class, new ProtobufDataLoader.Factory()); } } diff --git a/growingio-data/protobuf/src/main/proto/event_v3.proto b/growingio-data/protobuf/src/main/proto/event_v3.proto index a562f5cc..635cda99 100644 --- a/growingio-data/protobuf/src/main/proto/event_v3.proto +++ b/growingio-data/protobuf/src/main/proto/event_v3.proto @@ -59,8 +59,8 @@ message EventV3Dto { int64 send_time = 54; string user_key = 55; - string xindex = 56;//new in sdk 4.0 - string xcontent = 57;//new in sdk 4.0 + string xcontent = 56;//new in sdk 4.0 + string xindex = 57;//new in sdk 4.0 } message ResourceItem { diff --git a/growingio-data/protobuf/src/test/java/com/growingio/android/protobuf/ProtocolDataTest.java b/growingio-data/protobuf/src/test/java/com/growingio/android/protobuf/ProtocolDataTest.java index 25a6e04d..78a22c1c 100644 --- a/growingio-data/protobuf/src/test/java/com/growingio/android/protobuf/ProtocolDataTest.java +++ b/growingio-data/protobuf/src/test/java/com/growingio/android/protobuf/ProtocolDataTest.java @@ -1,34 +1,29 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.growingio.android.protobuf; -import android.app.Application; -import androidx.test.core.app.ApplicationProvider; import com.google.common.truth.Truth; import com.google.protobuf.InvalidProtocolBufferException; -import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.events.CustomEvent; import com.growingio.android.sdk.track.middleware.format.EventByteArray; import com.growingio.android.sdk.track.middleware.format.EventFormatData; import com.growingio.android.sdk.track.modelloader.DataFetcher; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -39,28 +34,14 @@ @Config(manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) public class ProtocolDataTest { - private Application application = ApplicationProvider.getApplicationContext(); - - @Before - public void setup() { - TrackerContext.init(application); - } - @Test public void dataFormat() { - ProtobufLibraryModule module = new ProtobufLibraryModule(); - TrackerRegistry trackerRegistry = new TrackerRegistry(); - module.registerComponents(application, trackerRegistry); - CustomEvent customEvent = new CustomEvent.Builder() .setEventName("jsonTest") .build(); EventFormatData eventData = EventFormatData.format(customEvent); - DataFetcher dataFetcher = - trackerRegistry.getModelLoader(EventFormatData.class, EventByteArray.class) - .buildLoadData(eventData).fetcher; - Truth.assertThat(dataFetcher.getDataClass()).isAssignableTo(EventByteArray.class); + DataFetcher dataFetcher = new ProtobufDataFetcher(eventData); EventByteArray data = dataFetcher.executeData(); try { EventV3Protocol.EventV3Dto event = EventV3Protocol.EventV3Dto.parseFrom(data.getBodyData()); @@ -75,10 +56,6 @@ public void dataFormat() { @Test public void dataMerge() { - ProtobufLibraryModule module = new ProtobufLibraryModule(); - TrackerRegistry trackerRegistry = new TrackerRegistry(); - module.registerComponents(application, trackerRegistry); - ArrayList arrayList = new ArrayList<>(); CustomEvent customEvent = new CustomEvent.Builder() .setEventName("merge") @@ -90,8 +67,7 @@ public void dataMerge() { arrayList.add(EventProtocolTransfer.protocolByte(customEvent2)); EventFormatData eventData = EventFormatData.merge(arrayList); - DataFetcher dataFetcher = trackerRegistry.getModelLoader(EventFormatData.class, EventByteArray.class) - .buildLoadData(eventData).fetcher; + DataFetcher dataFetcher = new ProtobufDataFetcher(eventData); Truth.assertThat(dataFetcher.getDataClass()).isAssignableTo(EventByteArray.class); EventByteArray data = dataFetcher.executeData(); diff --git a/growingio-data/protobuf/src/test/java/com/growingio/android/protobuf/ProtocolTest.java b/growingio-data/protobuf/src/test/java/com/growingio/android/protobuf/ProtocolTest.java index 1825490c..24e17091 100644 --- a/growingio-data/protobuf/src/test/java/com/growingio/android/protobuf/ProtocolTest.java +++ b/growingio-data/protobuf/src/test/java/com/growingio/android/protobuf/ProtocolTest.java @@ -1,28 +1,24 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.protobuf; -import android.app.Application; -import androidx.test.core.app.ApplicationProvider; import com.google.common.truth.Truth; import com.google.protobuf.InvalidProtocolBufferException; -import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.events.AppClosedEvent; import com.growingio.android.sdk.track.events.ConversionVariablesEvent; import com.growingio.android.sdk.track.events.CustomEvent; @@ -36,7 +32,6 @@ import com.growingio.android.sdk.track.events.hybrid.HybridViewElementEvent; import com.growingio.android.sdk.track.middleware.GEvent; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -51,13 +46,6 @@ */ @RunWith(RobolectricTestRunner.class) public class ProtocolTest { - private final Application application = ApplicationProvider.getApplicationContext(); - - @Before - public void setup() { - TrackerContext.init(application); - } - @Test public void protocolTest() throws InvalidProtocolBufferException { Map defaultMap = new HashMap<>(); diff --git a/growingio-flutter/build.gradle b/growingio-flutter/build.gradle index de13c067..fbf97930 100644 --- a/growingio-flutter/build.gradle +++ b/growingio-flutter/build.gradle @@ -1,54 +1,53 @@ plugins { - id 'com.android.library' + id 'com.android.library' } android { - namespace 'com.growingio.android.flutter' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - consumerProguardFiles 'consumer-rules.pro' - } - } - - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } - - testOptions { - unitTests.all { - jacoco { - jvmArgs '-noverify' - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - } - } + namespace 'com.growingio.android.flutter' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + consumerProguardFiles 'consumer-rules.pro' + } + } + + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } + + testOptions { + unitTests.all { + jacoco { + jvmArgs '-noverify' + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } } dependencies { - testImplementation libs.bundles.test - testImplementation libs.androidx.test.core + testImplementation libs.bundles.test + testImplementation libs.androidx.test.core - compileOnly project(':growingio-tracker-core') - implementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + compileOnly project(':growingio-tracker-core') + implementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" diff --git a/growingio-flutter/src/main/java/com/growingio/android/flutter/FlutterDataLoader.java b/growingio-flutter/src/main/java/com/growingio/android/flutter/FlutterDataLoader.java index effbf6b8..e0385451 100644 --- a/growingio-flutter/src/main/java/com/growingio/android/flutter/FlutterDataLoader.java +++ b/growingio-flutter/src/main/java/com/growingio/android/flutter/FlutterDataLoader.java @@ -1,23 +1,22 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.flutter; -import android.content.Context; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.middleware.EventFlutter; import com.growingio.android.sdk.track.modelloader.DataFetcher; import com.growingio.android.sdk.track.modelloader.ModelLoader; @@ -30,18 +29,26 @@ */ public class FlutterDataLoader implements ModelLoader { + private final TrackerContext context; + private final FlutterPluginProvider flutterPluginProvider; + + public FlutterDataLoader(TrackerContext context) { + this.context = context; + flutterPluginProvider = context.getProvider(FlutterPluginProvider.class); + } + @Override public LoadData buildLoadData(EventFlutter eventFlutter) { return new LoadData<>(new DataFetcher() { @Override public Void executeData() { if (eventFlutter.isCircleEnabled()) { - FlutterPluginProvider.get().startFlutterCircle(); + flutterPluginProvider.startFlutterCircle(); } else if (eventFlutter.isDebuggerEnabled()) { - FlutterPluginProvider.get().startFlutterDebugger(); + flutterPluginProvider.startFlutterDebugger(); } else { - FlutterPluginProvider.get().stopFlutterCircle(); - FlutterPluginProvider.get().stopFlutterDebugger(); + flutterPluginProvider.stopFlutterCircle(); + flutterPluginProvider.stopFlutterDebugger(); } return null; } @@ -55,12 +62,15 @@ public Class getDataClass() { public static class Factory implements ModelLoaderFactory { - protected Factory(Context context) { + private final TrackerContext trackerContext; + + protected Factory(TrackerContext context) { + this.trackerContext = context; } @Override public ModelLoader build() { - return new FlutterDataLoader(); + return new FlutterDataLoader(trackerContext); } } } diff --git a/growingio-flutter/src/main/java/com/growingio/android/flutter/FlutterLibraryGioModule.java b/growingio-flutter/src/main/java/com/growingio/android/flutter/FlutterLibraryGioModule.java index 16450dc0..d2f66a66 100644 --- a/growingio-flutter/src/main/java/com/growingio/android/flutter/FlutterLibraryGioModule.java +++ b/growingio-flutter/src/main/java/com/growingio/android/flutter/FlutterLibraryGioModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,16 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.flutter; -import android.content.Context; import com.growingio.android.sdk.LibraryGioModule; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.middleware.EventFlutter; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; import com.growingio.sdk.annotation.GIOLibraryModule; +import java.util.Map; + /** *

* @@ -31,8 +32,12 @@ @GIOLibraryModule public class FlutterLibraryGioModule extends LibraryGioModule { @Override - public void registerComponents(Context context, TrackerRegistry registry) { - registry.register(EventFlutter.class, Void.class, new FlutterDataLoader.Factory(context)); + public void registerComponents(TrackerContext context) { + context.getRegistry().register(EventFlutter.class, Void.class, new FlutterDataLoader.Factory(context)); } + @Override + protected void setupProviders(Map, TrackerLifecycleProvider> providerStore) { + providerStore.put(FlutterPluginProvider.class, FlutterPluginProvider.get()); + } } diff --git a/growingio-flutter/src/main/java/com/growingio/android/flutter/FlutterMethodInterface.java b/growingio-flutter/src/main/java/com/growingio/android/flutter/FlutterMethodInterface.java index e9b41d92..443f1a0b 100644 --- a/growingio-flutter/src/main/java/com/growingio/android/flutter/FlutterMethodInterface.java +++ b/growingio-flutter/src/main/java/com/growingio/android/flutter/FlutterMethodInterface.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.flutter; /** diff --git a/growingio-flutter/src/main/java/com/growingio/android/flutter/FlutterPluginProvider.java b/growingio-flutter/src/main/java/com/growingio/android/flutter/FlutterPluginProvider.java index 88942c6a..5e23dccc 100644 --- a/growingio-flutter/src/main/java/com/growingio/android/flutter/FlutterPluginProvider.java +++ b/growingio-flutter/src/main/java/com/growingio/android/flutter/FlutterPluginProvider.java @@ -1,33 +1,34 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.flutter; import android.util.Base64; import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.events.AttributesBuilder; import com.growingio.android.sdk.track.events.PageEvent; import com.growingio.android.sdk.track.events.ViewElementEvent; import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.modelloader.LoadDataFetcher; -import com.growingio.android.sdk.track.providers.EventStateProvider; -import com.growingio.android.sdk.track.webservices.Circler; -import com.growingio.android.sdk.track.webservices.Debugger; -import com.growingio.android.sdk.track.webservices.WebService; +import com.growingio.android.sdk.track.modelloader.TrackerRegistry; +import com.growingio.android.sdk.track.middleware.webservice.Circler; +import com.growingio.android.sdk.track.middleware.webservice.Debugger; +import com.growingio.android.sdk.track.middleware.webservice.WebService; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; import java.util.List; import java.util.Map; @@ -38,7 +39,7 @@ * * @author cpacm 2022/12/28 */ -public class FlutterPluginProvider { +public class FlutterPluginProvider implements TrackerLifecycleProvider { private static final String TAG = "FlutterPluginProvider"; @@ -46,11 +47,23 @@ private static class SingleInstance { private static final FlutterPluginProvider INSTANCE = new FlutterPluginProvider(); } + public static FlutterPluginProvider get() { + return SingleInstance.INSTANCE; + } + private FlutterPluginProvider() { } - public static FlutterPluginProvider get() { - return SingleInstance.INSTANCE; + private TrackerRegistry registry; + + @Override + public void setup(TrackerContext context) { + registry = context.getRegistry(); + } + + @Override + public void shutdown() { + setFlutterMethodInterface(null); } FlutterMethodInterface flutterMethodInterface; @@ -92,7 +105,7 @@ public void trackFlutterPage(Map args) { builder.addAttribute(attributes); long timeStamp = (Long) args.get("timestamp"); - EventStateProvider.get().cacheEvent( + TrackMainThread.trackMain().cacheEventToTrackMain( new PageEvent.Builder() .setPath(path) .setTitle(title) @@ -113,7 +126,7 @@ public void trackClickEvent(Map args) { String title = (String) args.get("textValue"); int index = (int) args.get("index"); - EventStateProvider.get().cacheEvent( + TrackMainThread.trackMain().cacheEventToTrackMain( new ViewElementEvent.Builder(eventType) .setPath(path) .setXpath(xpath) @@ -126,7 +139,7 @@ public void trackClickEvent(Map args) { } public void trackCircleData(Map args, byte[] screenshot) { - if (!TrackerContext.initializedSuccessfully()) return; + if (registry == null) return; try { List> elements = (List>) args.get("elements"); List> pages = (List>) args.get("pages"); @@ -145,8 +158,7 @@ public void trackCircleData(Map args, byte[] screenshot) { circlerData.setHeight(height); circlerData.setWidth(width); - - TrackerContext.get().loadData(new Circler(circlerData), Circler.class, WebService.class, new LoadDataFetcher.DataCallback() { + registry.loadData(new Circler(circlerData), Circler.class, WebService.class, new LoadDataFetcher.DataCallback() { @Override public void onDataReady(WebService data) { Logger.d(TAG, "send circle data success"); @@ -163,7 +175,8 @@ public void onLoadFailed(Exception e) { } public void trackDebuggerData(byte[] screenshot) { - TrackerContext.get().loadData(new Debugger(screenshot), Debugger.class, WebService.class, new LoadDataFetcher.DataCallback() { + if (registry == null) return; + registry.loadData(new Debugger(screenshot), Debugger.class, WebService.class, new LoadDataFetcher.DataCallback() { @Override public void onDataReady(WebService data) { Logger.d(TAG, "send debugger data success"); diff --git a/growingio-flutter/src/test/java/com/growingio/android/flutter/ExampleUnitTest.java b/growingio-flutter/src/test/java/com/growingio/android/flutter/ExampleUnitTest.java index 41515e8b..06c1fec5 100644 --- a/growingio-flutter/src/test/java/com/growingio/android/flutter/ExampleUnitTest.java +++ b/growingio-flutter/src/test/java/com/growingio/android/flutter/ExampleUnitTest.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.flutter; import static org.junit.Assert.assertEquals; diff --git a/growingio-hybrid/build.gradle b/growingio-hybrid/build.gradle index d9f65894..4b6ce7d8 100644 --- a/growingio-hybrid/build.gradle +++ b/growingio-hybrid/build.gradle @@ -1,57 +1,56 @@ plugins { - id 'com.android.library' + id 'com.android.library' } android { - namespace 'com.growingio.android.hybrid' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - consumerProguardFiles 'consumer-rules.pro' - } - } - - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } - - testOptions { - unitTests.all { - jacoco { - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - } - } + namespace 'com.growingio.android.hybrid' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + consumerProguardFiles 'consumer-rules.pro' + } + } + + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } + + testOptions { + unitTests.all { + jacoco { + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } } dependencies { - testImplementation libs.bundles.test - testImplementation libs.androidx.test.core - testImplementation libs.androidx.appcompat - testImplementation fileTree(include: ['*.jar'], dir: '../thirdLibs') + testImplementation libs.bundles.test + testImplementation libs.androidx.test.core + testImplementation libs.androidx.appcompat + testImplementation fileTree(include: ['*.jar'], dir: '../thirdLibs') - compileOnly fileTree(include: ['*.jar'], dir: '../thirdLibs') - implementation project(':growingio-tracker-core') - implementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + compileOnly fileTree(include: ['*.jar'], dir: '../thirdLibs') + implementation project(':growingio-tracker-core') + implementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" diff --git a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridBridgeLoader.java b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridBridgeLoader.java index 8c5e5f3f..c9a33185 100644 --- a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridBridgeLoader.java +++ b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridBridgeLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.hybrid; import android.webkit.WebView; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.modelloader.DataFetcher; import com.growingio.android.sdk.track.modelloader.ModelLoader; import com.growingio.android.sdk.track.modelloader.ModelLoaderFactory; @@ -31,15 +31,28 @@ */ public class HybridBridgeLoader implements ModelLoader { + private final HybridBridgeProvider hybridBridgeProvider; + + public HybridBridgeLoader(TrackerContext context) { + hybridBridgeProvider = context.getProvider(HybridBridgeProvider.class); + } + @Override public LoadData buildLoadData(HybridBridge eventData) { - return new LoadData<>(new HybridDataFetcher(eventData)); + return new LoadData<>(new HybridDataFetcher(eventData, hybridBridgeProvider)); } public static class Factory implements ModelLoaderFactory { + + private final TrackerContext context; + + public Factory(TrackerContext context) { + this.context = context; + } + @Override public ModelLoader build() { - return new HybridBridgeLoader(); + return new HybridBridgeLoader(context); } } @@ -47,20 +60,22 @@ public ModelLoader build() { public static class HybridDataFetcher implements DataFetcher { private final HybridBridge bridge; + private final HybridBridgeProvider hybridBridgeProvider; - public HybridDataFetcher(HybridBridge eventData) { + public HybridDataFetcher(HybridBridge eventData, HybridBridgeProvider hybridBridgeProvider) { this.bridge = eventData; + this.hybridBridgeProvider = hybridBridgeProvider; } @Override public Boolean executeData() { if (bridge.getView() instanceof WebView) { - HybridBridgeProvider.get().bridgeForWebView(SuperWebView.make((WebView) bridge.getView())); + hybridBridgeProvider.bridgeForWebView(SuperWebView.make((WebView) bridge.getView())); } else if (ClassExistHelper.instanceOfX5WebView(bridge.getView())) { - HybridBridgeProvider.get().bridgeForWebView(SuperWebView.makeX5((com.tencent.smtt.sdk.WebView) bridge.getView())); + hybridBridgeProvider.bridgeForWebView(SuperWebView.makeX5((com.tencent.smtt.sdk.WebView) bridge.getView())); } else if (ClassExistHelper.instanceOfUcWebView(bridge.getView())) { - HybridBridgeProvider.get().bridgeForWebView(SuperWebView.makeUC((com.uc.webview.export.WebView) bridge.getView())); + hybridBridgeProvider.bridgeForWebView(SuperWebView.makeUC((com.uc.webview.export.WebView) bridge.getView())); } else { return false; } diff --git a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridBridgeProvider.java b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridBridgeProvider.java index d0e86a0b..f512cf8f 100644 --- a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridBridgeProvider.java +++ b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridBridgeProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.hybrid; import android.annotation.SuppressLint; @@ -21,40 +20,48 @@ import android.text.TextUtils; import android.webkit.ValueCallback; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.SDKConfig; -import com.growingio.android.sdk.track.async.Callback; -import com.growingio.android.sdk.track.async.Disposable; -import com.growingio.android.sdk.track.async.HandlerDisposable; +import com.growingio.android.sdk.track.listener.Callback; import com.growingio.android.sdk.track.listener.ListenerContainer; import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.providers.AppInfoProvider; import com.growingio.android.sdk.track.providers.ConfigurationProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; +import com.growingio.android.sdk.track.providers.UserInfoProvider; import org.json.JSONException; import org.json.JSONObject; import static com.growingio.android.hybrid.WebViewBridgeJavascriptInterface.JAVASCRIPT_GET_DOM_TREE_METHOD; -public class HybridBridgeProvider extends ListenerContainer { +public class HybridBridgeProvider extends ListenerContainer implements TrackerLifecycleProvider { private static final String TAG = "HybridBridgePolicy"; - private static final int EVALUATE_JAVASCRIPT_TIMEOUT = 5000; - - private static class SingleInstance { - private static final HybridBridgeProvider INSTANCE = new HybridBridgeProvider(); + HybridBridgeProvider() { } - private HybridBridgeProvider() { + + private ConfigurationProvider configurationProvider; + private AppInfoProvider appInfoProvider; + private UserInfoProvider userInfoProvider; + + @Override + public void setup(TrackerContext context) { + configurationProvider = context.getConfigurationProvider(); + appInfoProvider = context.getProvider(AppInfoProvider.class); + userInfoProvider = context.getUserInfoProvider(); } - public static HybridBridgeProvider get() { - return SingleInstance.INSTANCE; + @Override + public void shutdown() { + } private WebViewJavascriptBridgeConfiguration getJavascriptBridgeConfiguration() { - String projectId = ConfigurationProvider.core().getProjectId(); - String appId = ConfigurationProvider.core().getUrlScheme(); - String appPackage = AppInfoProvider.get().getPackageName(); + String projectId = configurationProvider.core().getProjectId(); + String appId = configurationProvider.core().getUrlScheme(); + String appPackage = appInfoProvider.getPackageName(); String nativeSdkVersion = SDKConfig.SDK_VERSION; int nativeSdkVersionCode = SDKConfig.SDK_VERSION_CODE; return new WebViewJavascriptBridgeConfiguration(projectId, appId, appPackage, nativeSdkVersion, nativeSdkVersionCode); @@ -79,20 +86,17 @@ public void bridgeForWebView(SuperWebView webView) { Logger.d(TAG, "JavascriptInterface has already been added to the WebView"); return; } - webView.addJavascriptInterface(new WebViewBridgeJavascriptInterface(getJavascriptBridgeConfiguration()), WebViewBridgeJavascriptInterface.JAVASCRIPT_INTERFACE_NAME); + webView.addJavascriptInterface( + new WebViewBridgeJavascriptInterface(getJavascriptBridgeConfiguration(), this, userInfoProvider), + WebViewBridgeJavascriptInterface.JAVASCRIPT_INTERFACE_NAME); webView.setAddJavaScript(); } - public Disposable getWebViewDomTree(SuperWebView webView, final Callback callback) { + public void getWebViewDomTree(SuperWebView webView, final Callback callback) { Logger.d(TAG, "getWebViewDomTree"); if (callback == null) { - return Disposable.EMPTY_DISPOSABLE; + return; } - final Disposable disposable = new HandlerDisposable().schedule(() -> { - Logger.e(TAG, "getWebViewDomTree timeout"); - callback.onFailed(); - }, EVALUATE_JAVASCRIPT_TIMEOUT); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { int[] location = new int[2]; webView.getLocationOnScreen(location); @@ -101,10 +105,6 @@ public Disposable getWebViewDomTree(SuperWebView webView, final Callback() { @Override public void onReceiveValue(String value) { - if (disposable.isDisposed()) { - return; - } - disposable.dispose(); if (TextUtils.isEmpty(value) || "null".equals(value)) { Logger.e(TAG, "getWebViewDomTree ValueCallback is NULL"); callback.onFailed(); @@ -121,12 +121,8 @@ public void onReceiveValue(String value) { }); } else { Logger.e(TAG, "You need use after Android 4.4 to getWebViewDomTree"); - if (!disposable.isDisposed()) { - disposable.dispose(); - } callback.onFailed(); } - return disposable; } @Override diff --git a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridDomLoader.java b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridDomLoader.java index 79a717e3..ba04641e 100644 --- a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridDomLoader.java +++ b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridDomLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.hybrid; import android.view.View; import android.webkit.WebView; -import com.growingio.android.sdk.track.async.Callback; +import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.track.listener.Callback; import com.growingio.android.sdk.track.modelloader.LoadDataFetcher; import com.growingio.android.sdk.track.modelloader.ModelLoader; import com.growingio.android.sdk.track.modelloader.ModelLoaderFactory; @@ -38,19 +38,32 @@ public class HybridDomLoader implements ModelLoader { private HybridDom.OnDomChangedListener onDomChangedListener; + private final HybridBridgeProvider hybridBridgeProvider; + + public HybridDomLoader(TrackerContext context) { + hybridBridgeProvider = context.getProvider(HybridBridgeProvider.class); + } + @Override public LoadData buildLoadData(HybridDom eventData) { if (eventData.getOnDomChangedListener() != null) { this.onDomChangedListener = eventData.getOnDomChangedListener(); - HybridBridgeProvider.get().registerDomChangedListener(() -> onDomChangedListener.onDomChanged()); + hybridBridgeProvider.registerDomChangedListener(() -> onDomChangedListener.onDomChanged()); } - return new LoadData<>(new HybridDataFetcher(eventData)); + return new LoadData<>(new HybridDataFetcher(eventData, hybridBridgeProvider)); } public static class Factory implements ModelLoaderFactory { + + private final TrackerContext context; + + public Factory(TrackerContext context) { + this.context = context; + } + @Override public ModelLoader build() { - return new HybridDomLoader(); + return new HybridDomLoader(context); } } @@ -59,9 +72,11 @@ public static class HybridDataFetcher implements LoadDataFetcher { private static final String TAG = "HybridDataFetcher"; private final HybridDom dom; + private final HybridBridgeProvider hybridBridgeProvider; - public HybridDataFetcher(HybridDom eventData) { + public HybridDataFetcher(HybridDom eventData, HybridBridgeProvider hybridBridgeProvider) { this.dom = eventData; + this.hybridBridgeProvider = hybridBridgeProvider; } @Override @@ -75,7 +90,7 @@ public void loadData(DataCallback callback) { callback.onLoadFailed(new IllegalArgumentException(dom.getView().getClass().getName() + "is not webView")); return; } - HybridBridgeProvider.get().getWebViewDomTree(superWebView, new Callback() { + hybridBridgeProvider.getWebViewDomTree(superWebView, new Callback() { @Override public void onSuccess(JSONObject result) { callback.onDataReady(new HybridJson(result)); diff --git a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridLibraryGioModule.java b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridLibraryGioModule.java index 232199ba..9c6efd1d 100644 --- a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridLibraryGioModule.java +++ b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridLibraryGioModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +15,17 @@ */ package com.growingio.android.hybrid; -import android.content.Context; import com.growingio.android.sdk.LibraryGioModule; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.middleware.hybrid.HybridBridge; import com.growingio.android.sdk.track.middleware.hybrid.HybridDom; import com.growingio.android.sdk.track.middleware.hybrid.HybridJson; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; import com.growingio.sdk.annotation.GIOLibraryModule; +import java.util.Map; + /** *

* @@ -32,8 +34,13 @@ @GIOLibraryModule public class HybridLibraryGioModule extends LibraryGioModule { @Override - public void registerComponents(Context context, TrackerRegistry registry) { - registry.register(HybridBridge.class, Boolean.class, new HybridBridgeLoader.Factory()); - registry.register(HybridDom.class, HybridJson.class, new HybridDomLoader.Factory()); + public void registerComponents(TrackerContext context) { + context.getRegistry().register(HybridBridge.class, Boolean.class, new HybridBridgeLoader.Factory(context)); + context.getRegistry().register(HybridDom.class, HybridJson.class, new HybridDomLoader.Factory(context)); + } + + @Override + protected void setupProviders(Map, TrackerLifecycleProvider> providerStore) { + providerStore.put(HybridBridgeProvider.class, new HybridBridgeProvider()); } } diff --git a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridTransformer.java b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridTransformer.java index 96f7d90e..fa45258c 100644 --- a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridTransformer.java +++ b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.hybrid; diff --git a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridTransformerImp.java b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridTransformerImp.java index f992adc5..c2d11229 100644 --- a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridTransformerImp.java +++ b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/HybridTransformerImp.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.hybrid; import com.growingio.android.sdk.track.events.hybrid.HybridCustomEvent; @@ -26,7 +25,7 @@ import com.growingio.android.sdk.track.events.VisitorAttributesEvent; import com.growingio.android.sdk.track.events.base.BaseEvent; import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.providers.EventStateProvider; +import com.growingio.android.sdk.track.providers.EventBuilderProvider; import org.json.JSONException; import org.json.JSONObject; @@ -41,7 +40,7 @@ public BaseEvent.BaseBuilder transform(String hybridEvent) { String type = eventJson.getString(BaseEvent.EVENT_TYPE); if (AutotrackEventType.PAGE.equals(type)) { HybridPageEvent.Builder builder = new HybridPageEvent.Builder(); - EventStateProvider.get().parseFrom(builder, eventJson); + EventBuilderProvider.parseFrom(builder, eventJson); return builder; } else if (AutotrackEventType.VIEW_CLICK.equals(type) || AutotrackEventType.VIEW_CHANGE.equals(type) || @@ -50,21 +49,21 @@ public BaseEvent.BaseBuilder transform(String hybridEvent) { return transformViewElementEventBuilder(type, eventJson); } else if (TrackEventType.CUSTOM.equals(type)) { HybridCustomEvent.Builder builder = new HybridCustomEvent.Builder(); - EventStateProvider.get().parseFrom(builder, eventJson); + EventBuilderProvider.parseFrom(builder, eventJson); return builder; } else if (TrackEventType.LOGIN_USER_ATTRIBUTES.equals(type)) { LoginUserAttributesEvent.Builder builder = new LoginUserAttributesEvent.Builder(); - EventStateProvider.get().parseFrom(builder, eventJson); + EventBuilderProvider.parseFrom(builder, eventJson); return builder; } else if (TrackEventType.VISITOR_ATTRIBUTES.equals(type)) { VisitorAttributesEvent.Builder builder = new VisitorAttributesEvent.Builder(); - EventStateProvider.get().parseFrom(builder, eventJson); + EventBuilderProvider.parseFrom(builder, eventJson); return builder; } else if (TrackEventType.CONVERSION_VARIABLES.equals(type)) { ConversionVariablesEvent.Builder builder = new ConversionVariablesEvent.Builder(); - EventStateProvider.get().parseFrom(builder, eventJson); + EventBuilderProvider.parseFrom(builder, eventJson); return builder; } } catch (JSONException e) { @@ -75,7 +74,7 @@ public BaseEvent.BaseBuilder transform(String hybridEvent) { private HybridViewElementEvent.Builder transformViewElementEventBuilder(String eventType, JSONObject eventJson) throws JSONException { HybridViewElementEvent.Builder builder = new HybridViewElementEvent.Builder(eventType); - EventStateProvider.get().parseFrom(builder, eventJson); + EventBuilderProvider.parseFrom(builder, eventJson); return builder; } } diff --git a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/NativeBridge.java b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/NativeBridge.java index 37142d6b..dda89554 100644 --- a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/NativeBridge.java +++ b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/NativeBridge.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.hybrid; import com.growingio.android.sdk.track.TrackMainThread; @@ -21,9 +20,11 @@ class NativeBridge { private final HybridTransformer mHybridTransformer; + private final UserInfoProvider mUserInfoProvider; - NativeBridge() { + NativeBridge(UserInfoProvider userInfoProvider) { mHybridTransformer = new HybridTransformerImp(); + mUserInfoProvider = userInfoProvider; } void dispatchEvent(String event) { @@ -34,7 +35,7 @@ void setNativeUserId(String userId) { TrackMainThread.trackMain().postActionToTrackMain(new Runnable() { @Override public void run() { - UserInfoProvider.get().setLoginUserId(userId); + mUserInfoProvider.setLoginUserId(userId); } }); } @@ -43,7 +44,7 @@ void clearNativeUserId() { TrackMainThread.trackMain().postActionToTrackMain(new Runnable() { @Override public void run() { - UserInfoProvider.get().setLoginUserId(null); + mUserInfoProvider.setLoginUserId(null); } }); } @@ -52,7 +53,7 @@ void setNativeUserIdAndUserKey(String userId, String userKey) { TrackMainThread.trackMain().postActionToTrackMain(new Runnable() { @Override public void run() { - UserInfoProvider.get().setLoginUserId(userId, userKey); + mUserInfoProvider.setLoginUserId(userId, userKey); } }); } @@ -61,7 +62,7 @@ void clearNativeUserIdAndUserKey() { TrackMainThread.trackMain().postActionToTrackMain(new Runnable() { @Override public void run() { - UserInfoProvider.get().setLoginUserId(null, null); + mUserInfoProvider.setLoginUserId(null, null); } }); } diff --git a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/OnDomChangedListener.java b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/OnDomChangedListener.java index 6b894cae..24561bbe 100644 --- a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/OnDomChangedListener.java +++ b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/OnDomChangedListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.hybrid; public interface OnDomChangedListener { diff --git a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/SuperWebView.java b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/SuperWebView.java index 272f078f..1abdff5c 100644 --- a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/SuperWebView.java +++ b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/SuperWebView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.hybrid; import android.annotation.SuppressLint; diff --git a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/WebViewBridgeJavascriptInterface.java b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/WebViewBridgeJavascriptInterface.java index 8bc97c2f..339f3646 100644 --- a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/WebViewBridgeJavascriptInterface.java +++ b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/WebViewBridgeJavascriptInterface.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,23 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.hybrid; import android.text.TextUtils; import android.webkit.JavascriptInterface; import com.growingio.android.sdk.track.log.Logger; +import com.growingio.android.sdk.track.providers.UserInfoProvider; class WebViewBridgeJavascriptInterface { static final String JAVASCRIPT_INTERFACE_NAME = "GrowingWebViewJavascriptBridge"; static final String JAVASCRIPT_GET_DOM_TREE_METHOD = "window.GrowingWebViewJavascriptBridge.getDomTree"; private static final String TAG = "WebViewHybridBridge"; private final WebViewJavascriptBridgeConfiguration mConfiguration; - private final NativeBridge mNativeBridge = new NativeBridge(); + private final NativeBridge mNativeBridge; + + private final HybridBridgeProvider mHybridBridgeProvider; - WebViewBridgeJavascriptInterface(WebViewJavascriptBridgeConfiguration configuration) { + WebViewBridgeJavascriptInterface(WebViewJavascriptBridgeConfiguration configuration, + HybridBridgeProvider hybridBridgeProvider, + UserInfoProvider userInfoProvider) { mConfiguration = configuration; + this.mHybridBridgeProvider = hybridBridgeProvider; + mNativeBridge = new NativeBridge(userInfoProvider); } @JavascriptInterface @@ -41,7 +47,7 @@ public String getConfiguration() { @JavascriptInterface @com.uc.webview.export.JavascriptInterface public void onDomChanged() { - HybridBridgeProvider.get().onDomChanged(); + mHybridBridgeProvider.onDomChanged(); } @JavascriptInterface diff --git a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/WebViewJavascriptBridgeConfiguration.java b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/WebViewJavascriptBridgeConfiguration.java index 6952a3e0..e31185c2 100644 --- a/growingio-hybrid/src/main/java/com/growingio/android/hybrid/WebViewJavascriptBridgeConfiguration.java +++ b/growingio-hybrid/src/main/java/com/growingio/android/hybrid/WebViewJavascriptBridgeConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.hybrid; import com.growingio.android.sdk.track.log.Logger; diff --git a/growingio-hybrid/src/test/java/com/growingio/android/hybrid/HybridTest.java b/growingio-hybrid/src/test/java/com/growingio/android/hybrid/HybridTest.java index 700facb7..25c86a2f 100644 --- a/growingio-hybrid/src/test/java/com/growingio/android/hybrid/HybridTest.java +++ b/growingio-hybrid/src/test/java/com/growingio/android/hybrid/HybridTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,35 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.hybrid; import android.app.Application; import android.webkit.WebView; -import android.widget.TextView; import androidx.test.core.app.ApplicationProvider; import com.google.common.truth.Truth; import com.google.common.util.concurrent.Uninterruptibles; import com.growingio.android.sdk.CoreConfiguration; +import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.events.hybrid.HybridCustomEvent; import com.growingio.android.sdk.track.events.hybrid.HybridPageEvent; import com.growingio.android.sdk.track.events.hybrid.HybridViewElementEvent; -import com.growingio.android.sdk.track.async.Callback; import com.growingio.android.sdk.track.events.ConversionVariablesEvent; import com.growingio.android.sdk.track.events.LoginUserAttributesEvent; import com.growingio.android.sdk.track.events.VisitorAttributesEvent; -import com.growingio.android.sdk.track.modelloader.DataFetcher; -import com.growingio.android.sdk.track.modelloader.LoadDataFetcher; -import com.growingio.android.sdk.track.modelloader.ModelLoader; -import com.growingio.android.sdk.track.middleware.hybrid.HybridBridge; -import com.growingio.android.sdk.track.middleware.hybrid.HybridDom; -import com.growingio.android.sdk.track.middleware.hybrid.HybridJson; +import com.growingio.android.sdk.track.listener.Callback; import com.growingio.android.sdk.track.providers.ConfigurationProvider; -import com.growingio.android.sdk.track.providers.EventStateProvider; +import com.growingio.android.sdk.track.providers.EventBuilderProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProviderFactory; import com.growingio.android.sdk.track.providers.UserInfoProvider; import org.json.JSONObject; @@ -60,12 +54,14 @@ public class HybridTest { Application application = ApplicationProvider.getApplicationContext(); + private TrackerContext context; @Before public void setup() { - TrackerContext.init(application); - TrackerContext.initSuccess(); - ConfigurationProvider.initWithConfig(new CoreConfiguration("HybridTest", "growingio://Hybrid"), new HashMap<>()); + TrackerLifecycleProviderFactory.create().createConfigurationProviderWithConfig(new CoreConfiguration("HybridTest", "growingio://Hybrid"), new HashMap<>()); + Tracker tracker = new Tracker(application); + tracker.registerComponent(new HybridLibraryGioModule()); + context = tracker.getContext(); } @@ -76,7 +72,7 @@ public void transformTest() { String customJson = "{\"eventType\":\"CUSTOM\",\"query\":\"something\",\"domain\":\"growingio.com\",\"path\":\"/webview/button/\",\"eventName\":\"test event\",\"pageShowTimestamp\":12345678,\"attributes\":{\"grow_index\":\"苹果\",\"grow_click\":14}}"; HybridCustomEvent customEvent = (HybridCustomEvent) hybridTransformerImp.transform(customJson).build(); Truth.assertThat(customEvent.getQuery()).isEqualTo("something"); - Truth.assertThat(EventStateProvider.get().toJson(customEvent).toString()).contains("test event"); + Truth.assertThat(EventBuilderProvider.toJson(customEvent).toString()).contains("test event"); Truth.assertThat(customEvent.getAttributes().size()).isEqualTo(2); String userAttrJson = "{\"eventType\":\"LOGIN_USER_ATTRIBUTES\",\"attributes\":{\"grow_index\":\"苹果\",\"grow_click\":14}}"; @@ -119,10 +115,12 @@ public void providerTest() { SuperWebView superWebView = SuperWebView.make(webView); OnDomChangedListener testListener = () -> { }; - HybridBridgeProvider.get().registerDomChangedListener(testListener); - HybridBridgeProvider.get().unregisterDomChangedListener(testListener); - HybridBridgeProvider.get().bridgeForWebView(superWebView); - HybridBridgeProvider.get().getWebViewDomTree(superWebView, new Callback() { + HybridBridgeProvider hybridBridgeProvider = context.getProvider(HybridBridgeProvider.class); + + hybridBridgeProvider.registerDomChangedListener(testListener); + hybridBridgeProvider.unregisterDomChangedListener(testListener); + hybridBridgeProvider.bridgeForWebView(superWebView); + hybridBridgeProvider.getWebViewDomTree(superWebView, new Callback() { @Override public void onSuccess(JSONObject result) { System.out.println(result); @@ -138,26 +136,30 @@ public void onFailed() { @Test public void bridgeInterfaceTest() { + HybridBridgeProvider hybridBridgeProvider = context.getProvider(HybridBridgeProvider.class); + UserInfoProvider userInfoProvider = context.getUserInfoProvider(); + ConfigurationProvider configurationProvider = context.getConfigurationProvider(); + WebViewJavascriptBridgeConfiguration configuration = new WebViewJavascriptBridgeConfiguration("test", "test", "test", "test", 23); - WebViewBridgeJavascriptInterface webInterface = new WebViewBridgeJavascriptInterface(configuration); + WebViewBridgeJavascriptInterface webInterface = new WebViewBridgeJavascriptInterface(configuration, hybridBridgeProvider, userInfoProvider); webInterface.setNativeUserId("cpacm"); Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - Truth.assertThat(UserInfoProvider.get().getLoginUserId()).isEqualTo("cpacm"); + Truth.assertThat(userInfoProvider.getLoginUserId()).isEqualTo("cpacm"); webInterface.clearNativeUserId(); Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - Truth.assertThat(UserInfoProvider.get().getLoginUserId()).isEqualTo(""); + Truth.assertThat(userInfoProvider.getLoginUserId()).isEqualTo(""); - ConfigurationProvider.core().setIdMappingEnabled(true); + configurationProvider.core().setIdMappingEnabled(true); webInterface.setNativeUserIdAndUserKey("cpacm", "email"); Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - Truth.assertThat(UserInfoProvider.get().getLoginUserId()).isEqualTo("cpacm"); - Truth.assertThat(UserInfoProvider.get().getLoginUserKey()).isEqualTo("email"); + Truth.assertThat(userInfoProvider.getLoginUserId()).isEqualTo("cpacm"); + Truth.assertThat(userInfoProvider.getLoginUserKey()).isEqualTo("email"); webInterface.clearNativeUserIdAndUserKey(); Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - Truth.assertThat(UserInfoProvider.get().getLoginUserId()).isEqualTo(""); - Truth.assertThat(UserInfoProvider.get().getLoginUserKey()).isEqualTo(""); - ConfigurationProvider.core().setIdMappingEnabled(false); + Truth.assertThat(userInfoProvider.getLoginUserId()).isEqualTo(""); + Truth.assertThat(userInfoProvider.getLoginUserKey()).isEqualTo(""); + configurationProvider.core().setIdMappingEnabled(false); String testJson = "{\"eventType\":\"LOGIN_USER_ATTRIBUTES\",\"attributes\":{\"grow_index\":\"苹果\",\"grow_click\":14}}"; webInterface.dispatchEvent(testJson); @@ -168,51 +170,4 @@ public void bridgeInterfaceTest() { Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); } - @Test - public void moduleBridgeTest() { - HybridLibraryGioModule hybridLibraryGioModule = new HybridLibraryGioModule(); - hybridLibraryGioModule.registerComponents(application, TrackerContext.get().getRegistry()); - RobolectricActivity activity = Robolectric.buildActivity(RobolectricActivity.class).create().get(); - WebView webView = new WebView(activity); - com.tencent.smtt.sdk.WebView x5Wv = new com.tencent.smtt.sdk.WebView(activity); - com.uc.webview.export.WebView ucWv = new com.uc.webview.export.WebView(activity); - - ModelLoader modelLoader = TrackerContext.get().getRegistry().getModelLoader(HybridBridge.class, Boolean.class); - DataFetcher dataFetcher = modelLoader.buildLoadData(new HybridBridge(webView)).fetcher; - boolean result = dataFetcher.executeData(); - Truth.assertThat(result).isTrue(); - Truth.assertThat(dataFetcher.getDataClass()).isAssignableTo(Boolean.class); - Truth.assertThat(modelLoader.buildLoadData(new HybridBridge(x5Wv)).fetcher.executeData()).isTrue(); - Truth.assertThat(modelLoader.buildLoadData(new HybridBridge(ucWv)).fetcher.executeData()).isTrue(); - - TextView textView = new TextView(activity); - Truth.assertThat(modelLoader.buildLoadData(new HybridBridge(textView)).fetcher.executeData()).isFalse(); - } - - @Test - public void moduleDomTest() { - HybridLibraryGioModule hybridLibraryGioModule = new HybridLibraryGioModule(); - hybridLibraryGioModule.registerComponents(application, TrackerContext.get().getRegistry()); - RobolectricActivity activity = Robolectric.buildActivity(RobolectricActivity.class).create().get(); - WebView webView = new WebView(activity); - - ModelLoader modelLoader = TrackerContext.get().getRegistry().getModelLoader(HybridDom.class, HybridJson.class); - modelLoader.buildLoadData(new HybridDom(() -> { - })).fetcher.executeData(); - - LoadDataFetcher dataFetcher = (LoadDataFetcher) modelLoader.buildLoadData(new HybridDom(webView)).fetcher; - dataFetcher.loadData(new LoadDataFetcher.DataCallback() { - @Override - public void onDataReady(HybridJson data) { - System.out.println(data.getJsonObject()); - } - - @Override - public void onLoadFailed(Exception e) { - - } - }); - Truth.assertThat(dataFetcher.getDataClass()).isAssignableTo(HybridJson.class); - } - } diff --git a/growingio-hybrid/src/test/java/com/growingio/android/hybrid/RobolectricActivity.java b/growingio-hybrid/src/test/java/com/growingio/android/hybrid/RobolectricActivity.java index d57bf632..280fac62 100644 --- a/growingio-hybrid/src/test/java/com/growingio/android/hybrid/RobolectricActivity.java +++ b/growingio-hybrid/src/test/java/com/growingio/android/hybrid/RobolectricActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.hybrid; import android.content.Intent; diff --git a/growingio-network/okhttp3/build.gradle b/growingio-network/okhttp3/build.gradle index 9e1b6e4f..b880900d 100644 --- a/growingio-network/okhttp3/build.gradle +++ b/growingio-network/okhttp3/build.gradle @@ -1,55 +1,54 @@ plugins { - id 'com.android.library' + id 'com.android.library' } android { - namespace 'com.growingio.android.okhttp3' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } + namespace 'com.growingio.android.okhttp3' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } - testOptions { - unitTests.all { - jacoco { - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - } - } + testOptions { + unitTests.all { + jacoco { + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } } dependencies { - testImplementation libs.bundles.test - testImplementation libs.androidx.test.core - testImplementation libs.bundles.powermock - testImplementation libs.okhttp3.mockwebserver + testImplementation libs.bundles.test + testImplementation libs.androidx.test.core + testImplementation libs.okhttp3.mockwebserver - implementation project(':growingio-tracker-core') - implementation libs.okhttp3 - //debugImplementation libs.okhttp3.logging + implementation project(':growingio-tracker-core') + implementation libs.okhttp3 + //debugImplementation libs.okhttp3.logging - implementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + implementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" apply from: "${rootProject.projectDir}/gradle/jacocoModule.gradle" \ No newline at end of file diff --git a/growingio-network/okhttp3/src/main/java/com/growingio/android/okhttp3/OkHttpDataFetcher.java b/growingio-network/okhttp3/src/main/java/com/growingio/android/okhttp3/OkHttpDataFetcher.java index 2007b66f..d698c194 100644 --- a/growingio-network/okhttp3/src/main/java/com/growingio/android/okhttp3/OkHttpDataFetcher.java +++ b/growingio-network/okhttp3/src/main/java/com/growingio/android/okhttp3/OkHttpDataFetcher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.okhttp3; import com.growingio.android.sdk.track.middleware.http.EventResponse; diff --git a/growingio-network/okhttp3/src/main/java/com/growingio/android/okhttp3/OkHttpDataLoader.java b/growingio-network/okhttp3/src/main/java/com/growingio/android/okhttp3/OkHttpDataLoader.java index 6f6c5548..6b37fc0e 100644 --- a/growingio-network/okhttp3/src/main/java/com/growingio/android/okhttp3/OkHttpDataLoader.java +++ b/growingio-network/okhttp3/src/main/java/com/growingio/android/okhttp3/OkHttpDataLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.okhttp3; import com.growingio.android.sdk.track.middleware.http.EventResponse; diff --git a/growingio-network/okhttp3/src/main/java/com/growingio/android/okhttp3/OkhttpLibraryGioModule.java b/growingio-network/okhttp3/src/main/java/com/growingio/android/okhttp3/OkhttpLibraryGioModule.java index a90889e9..df629185 100644 --- a/growingio-network/okhttp3/src/main/java/com/growingio/android/okhttp3/OkhttpLibraryGioModule.java +++ b/growingio-network/okhttp3/src/main/java/com/growingio/android/okhttp3/OkhttpLibraryGioModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,15 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.okhttp3; -import android.content.Context; import com.growingio.android.sdk.LibraryGioModule; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.middleware.http.EventResponse; import com.growingio.android.sdk.track.middleware.http.EventUrl; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import com.growingio.sdk.annotation.GIOLibraryModule; /** @@ -32,7 +30,7 @@ @GIOLibraryModule public class OkhttpLibraryGioModule extends LibraryGioModule { @Override - public void registerComponents(Context context, TrackerRegistry registry) { - registry.register(EventUrl.class, EventResponse.class, new OkHttpDataLoader.Factory()); + public void registerComponents(TrackerContext context) { + context.getRegistry().register(EventUrl.class, EventResponse.class, new OkHttpDataLoader.Factory()); } } diff --git a/growingio-network/okhttp3/src/main/java/com/growingio/android/okhttp3/SecurityExceptionInterceptor.java b/growingio-network/okhttp3/src/main/java/com/growingio/android/okhttp3/SecurityExceptionInterceptor.java index 089e943b..35ac3e84 100644 --- a/growingio-network/okhttp3/src/main/java/com/growingio/android/okhttp3/SecurityExceptionInterceptor.java +++ b/growingio-network/okhttp3/src/main/java/com/growingio/android/okhttp3/SecurityExceptionInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.okhttp3; import com.growingio.android.sdk.track.log.Logger; @@ -25,8 +24,7 @@ /** * 1.用于OkHttp防止部分机型关闭网络权限导致崩溃(特定Rom对permission的管理问题或Root后关闭权限),转为IO异常回调失败 - * 2.避免Monitor进行上传时调用Monitor#capture循环抛出异常(MonitorUncaughtExceptionHandler是多线程) - * 3.加强保护,将所有异常转为io异常回调失败,避免部分okhttp内部错误导致异常 + * 2.加强保护,将所有异常转为io异常回调失败,避免部分okhttp内部错误导致异常 */ public class SecurityExceptionInterceptor implements Interceptor { private static final String TAG = "SecurityExceptionInterceptor"; @@ -37,7 +35,7 @@ public Response intercept(Chain chain) throws IOException { return chain.proceed(chain.request()); } catch (Exception e) { Logger.e(TAG, "HTTP FAILED: " + e.getMessage()); - throw new IOException("Failed due to an Exception: " + e.getMessage()); + throw e; } } } diff --git a/growingio-network/okhttp3/src/test/java/com/growingio/android/okhttp3/MockServer.java b/growingio-network/okhttp3/src/test/java/com/growingio/android/okhttp3/MockServer.java index 3fffef00..47704920 100644 --- a/growingio-network/okhttp3/src/test/java/com/growingio/android/okhttp3/MockServer.java +++ b/growingio-network/okhttp3/src/test/java/com/growingio/android/okhttp3/MockServer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.okhttp3; import java.io.IOException; diff --git a/growingio-network/okhttp3/src/test/java/com/growingio/android/okhttp3/Okhttp3Test.java b/growingio-network/okhttp3/src/test/java/com/growingio/android/okhttp3/Okhttp3Test.java index 7067bded..371524b5 100644 --- a/growingio-network/okhttp3/src/test/java/com/growingio/android/okhttp3/Okhttp3Test.java +++ b/growingio-network/okhttp3/src/test/java/com/growingio/android/okhttp3/Okhttp3Test.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +15,6 @@ */ package com.growingio.android.okhttp3; -import android.content.Context; -import android.util.Log; import com.google.common.truth.Truth; import com.growingio.android.sdk.track.middleware.http.EventResponse; @@ -29,10 +27,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; +import org.junit.runners.JUnit4; import java.io.IOException; import java.io.InputStream; @@ -43,21 +38,17 @@ import okhttp3.mockwebserver.RecordedRequest; import static com.google.common.truth.Truth.assertThat; -import static org.powermock.api.mockito.PowerMockito.mockStatic; /** *

* * @author cpacm 2021/6/3 */ -@RunWith(PowerMockRunner.class) -@PrepareForTest(Log.class) -@PowerMockIgnore("javax.net.ssl.*") +@RunWith(JUnit4.class) public class Okhttp3Test extends MockServer { @Before public void prepare() throws IOException { - mockStatic(Log.class); mockEventsApiServer(); start(); } @@ -79,9 +70,6 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio setDispatcher(dispatcher); } - @Mock - Context fakeContext; - private EventUrl initEventUrl(String host) { long time = System.currentTimeMillis(); return new EventUrl(host, time) @@ -105,9 +93,8 @@ private void dispatchReceivedEvents(String json) { @Test public void sendTest() { - OkhttpLibraryGioModule module = new OkhttpLibraryGioModule(); TrackerRegistry trackerRegistry = new TrackerRegistry(); - module.registerComponents(fakeContext, trackerRegistry); + trackerRegistry.register(EventUrl.class, EventResponse.class, new OkHttpDataLoader.Factory()); ModelLoader modelLoader = trackerRegistry.getModelLoader(EventUrl.class, EventResponse.class); EventUrl eventUrl = initEventUrl("http://localhost:8910/"); diff --git a/growingio-network/urlconnection/build.gradle b/growingio-network/urlconnection/build.gradle index fe950762..42000ff8 100644 --- a/growingio-network/urlconnection/build.gradle +++ b/growingio-network/urlconnection/build.gradle @@ -1,52 +1,52 @@ plugins { - id 'com.android.library' + id 'com.android.library' } android { - namespace 'com.growingio.android.urlconnection' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } + namespace 'com.growingio.android.urlconnection' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } - testOptions { - unitTests.all { - jacoco { - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - } - } + testOptions { + unitTests.all { + jacoco { + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } } dependencies { - testImplementation libs.bundles.test - testImplementation libs.androidx.test.core - testImplementation libs.okhttp3.mockwebserver + testImplementation libs.bundles.test + testImplementation libs.androidx.test.core + testImplementation libs.okhttp3.mockwebserver - implementation project(':growingio-tracker-core') + implementation project(':growingio-tracker-core') - implementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + implementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" apply from: "${rootProject.projectDir}/gradle/jacocoModule.gradle" \ No newline at end of file diff --git a/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/HttpException.java b/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/HttpException.java index 89ed4fdb..98e2dafb 100644 --- a/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/HttpException.java +++ b/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/HttpException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.urlconnection; import java.io.IOException; diff --git a/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/LogTime.java b/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/LogTime.java index 9f1e73ba..19e3dcb2 100644 --- a/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/LogTime.java +++ b/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/LogTime.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.urlconnection; import android.annotation.TargetApi; diff --git a/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/UrlConnectionDataLoader.java b/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/UrlConnectionDataLoader.java index 28180f6b..bf9b9b09 100644 --- a/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/UrlConnectionDataLoader.java +++ b/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/UrlConnectionDataLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.urlconnection; import com.growingio.android.sdk.track.middleware.http.EventResponse; diff --git a/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/UrlConnectionFetcher.java b/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/UrlConnectionFetcher.java index efd2a3d2..f31f47b6 100644 --- a/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/UrlConnectionFetcher.java +++ b/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/UrlConnectionFetcher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.urlconnection; import android.text.TextUtils; diff --git a/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/UrlConnectionGioModule.java b/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/UrlConnectionGioModule.java index 466795d6..8aa867ce 100644 --- a/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/UrlConnectionGioModule.java +++ b/growingio-network/urlconnection/src/main/java/com/growingio/android/urlconnection/UrlConnectionGioModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,15 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.urlconnection; -import android.content.Context; import com.growingio.android.sdk.LibraryGioModule; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.middleware.http.EventResponse; import com.growingio.android.sdk.track.middleware.http.EventUrl; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import com.growingio.sdk.annotation.GIOLibraryModule; /** @@ -32,7 +30,7 @@ @GIOLibraryModule public class UrlConnectionGioModule extends LibraryGioModule { @Override - public void registerComponents(Context context, TrackerRegistry registry) { - registry.register(EventUrl.class, EventResponse.class, new UrlConnectionDataLoader.Factory()); + public void registerComponents(TrackerContext context) { + context.getRegistry().register(EventUrl.class, EventResponse.class, new UrlConnectionDataLoader.Factory()); } } diff --git a/growingio-network/urlconnection/src/test/java/com/growingio/android/uriconnection/UrlConnectionTest.java b/growingio-network/urlconnection/src/test/java/com/growingio/android/uriconnection/UrlConnectionTest.java index 9b2fddc1..3688ba14 100644 --- a/growingio-network/urlconnection/src/test/java/com/growingio/android/uriconnection/UrlConnectionTest.java +++ b/growingio-network/urlconnection/src/test/java/com/growingio/android/uriconnection/UrlConnectionTest.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.uriconnection; @@ -30,7 +29,7 @@ import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import com.growingio.android.urlconnection.HttpException; import com.growingio.android.urlconnection.LogTime; -import com.growingio.android.urlconnection.UrlConnectionGioModule; +import com.growingio.android.urlconnection.UrlConnectionDataLoader; import org.junit.Before; import org.junit.Test; @@ -98,9 +97,8 @@ private EventUrl initEventUrl(String host) { @Test public void sendTest() throws IOException { mockWebServer.start(8910); - UrlConnectionGioModule module = new UrlConnectionGioModule(); TrackerRegistry trackerRegistry = new TrackerRegistry(); - module.registerComponents(application, trackerRegistry); + trackerRegistry.register(EventUrl.class, EventResponse.class, new UrlConnectionDataLoader.Factory()); ModelLoader modelLoader = trackerRegistry.getModelLoader(EventUrl.class, EventResponse.class); EventUrl eventUrl = initEventUrl("http://localhost:8910/"); diff --git a/growingio-network/volley/build.gradle b/growingio-network/volley/build.gradle index be4141e7..da1e5543 100644 --- a/growingio-network/volley/build.gradle +++ b/growingio-network/volley/build.gradle @@ -1,55 +1,54 @@ plugins { - id 'com.android.library' + id 'com.android.library' } android { - namespace 'com.growingio.android.volley' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } - - testOptions { - unitTests.all { - jacoco { - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - useLibrary 'org.apache.http.legacy' - } - } + namespace 'com.growingio.android.volley' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } + + testOptions { + unitTests.all { + jacoco { + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + useLibrary 'org.apache.http.legacy' + } + } } dependencies { - testImplementation libs.bundles.test - testImplementation libs.androidx.test.core - testImplementation libs.okhttp3.mockwebserver + testImplementation libs.bundles.test + testImplementation libs.androidx.test.core + testImplementation libs.okhttp3.mockwebserver - implementation project(':growingio-tracker-core') - implementation libs.google.volley + implementation project(':growingio-tracker-core') + implementation libs.google.volley - implementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + implementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" apply from: "${rootProject.projectDir}/gradle/jacocoModule.gradle" \ No newline at end of file diff --git a/growingio-network/volley/src/main/java/com/growingio/android/volley/VolleyDataFetcher.java b/growingio-network/volley/src/main/java/com/growingio/android/volley/VolleyDataFetcher.java index f6e87415..3840fb94 100644 --- a/growingio-network/volley/src/main/java/com/growingio/android/volley/VolleyDataFetcher.java +++ b/growingio-network/volley/src/main/java/com/growingio/android/volley/VolleyDataFetcher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.volley; import android.util.Log; diff --git a/growingio-network/volley/src/main/java/com/growingio/android/volley/VolleyDataLoader.java b/growingio-network/volley/src/main/java/com/growingio/android/volley/VolleyDataLoader.java index 02cb54be..d634b57d 100644 --- a/growingio-network/volley/src/main/java/com/growingio/android/volley/VolleyDataLoader.java +++ b/growingio-network/volley/src/main/java/com/growingio/android/volley/VolleyDataLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.volley; import android.content.Context; diff --git a/growingio-network/volley/src/main/java/com/growingio/android/volley/VolleyLibraryGioModule.java b/growingio-network/volley/src/main/java/com/growingio/android/volley/VolleyLibraryGioModule.java index 0bd8dcf1..263e64a6 100644 --- a/growingio-network/volley/src/main/java/com/growingio/android/volley/VolleyLibraryGioModule.java +++ b/growingio-network/volley/src/main/java/com/growingio/android/volley/VolleyLibraryGioModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,15 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.volley; -import android.content.Context; import com.growingio.android.sdk.LibraryGioModule; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.middleware.http.EventResponse; import com.growingio.android.sdk.track.middleware.http.EventUrl; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import com.growingio.sdk.annotation.GIOLibraryModule; /** @@ -32,7 +30,7 @@ @GIOLibraryModule public class VolleyLibraryGioModule extends LibraryGioModule { @Override - public void registerComponents(Context context, TrackerRegistry registry) { - registry.register(EventUrl.class, EventResponse.class, new VolleyDataLoader.Factory(context)); + public void registerComponents(TrackerContext context) { + context.getRegistry().register(EventUrl.class, EventResponse.class, new VolleyDataLoader.Factory(context.getBaseContext())); } } diff --git a/growingio-network/volley/src/main/java/com/growingio/android/volley/VolleyRequestFactory.java b/growingio-network/volley/src/main/java/com/growingio/android/volley/VolleyRequestFactory.java index 2a8eaa4f..4ed7470e 100644 --- a/growingio-network/volley/src/main/java/com/growingio/android/volley/VolleyRequestFactory.java +++ b/growingio-network/volley/src/main/java/com/growingio/android/volley/VolleyRequestFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.volley; import com.android.volley.Request; diff --git a/growingio-network/volley/src/test/java/com/growingio/android/volley/VolleyTest.java b/growingio-network/volley/src/test/java/com/growingio/android/volley/VolleyTest.java index 3d9cb5a7..4f411f51 100644 --- a/growingio-network/volley/src/test/java/com/growingio/android/volley/VolleyTest.java +++ b/growingio-network/volley/src/test/java/com/growingio/android/volley/VolleyTest.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.volley; @@ -89,9 +88,8 @@ private EventUrl initEventUrl(String host) { @Test public void sendTest() { - VolleyLibraryGioModule module = new VolleyLibraryGioModule(); TrackerRegistry trackerRegistry = new TrackerRegistry(); - module.registerComponents(application, trackerRegistry); + trackerRegistry.register(EventUrl.class, EventResponse.class, new VolleyDataLoader.Factory(application)); ModelLoader modelLoader = trackerRegistry.getModelLoader(EventUrl.class, EventResponse.class); EventUrl eventUrl = initEventUrl("http://localhost:8910/"); diff --git a/growingio-sdk-bom/build.gradle b/growingio-sdk-bom/build.gradle index 2ebaa734..548eec4e 100644 --- a/growingio-sdk-bom/build.gradle +++ b/growingio-sdk-bom/build.gradle @@ -15,56 +15,56 @@ */ plugins { - id 'java-platform' - id 'maven-publish' + id 'java-platform' + id 'maven-publish' } javaPlatform { - allowDependencies() + allowDependencies() } dependencies { - constraints { + constraints { - project.rootProject.subprojects.stream() - .filter { - !it.name.startsWith("demo") && - it.name != project.name && - it.name != "gio-sdk" && - it.name != "growingio-tools" && - it.name != "growingio-network" && - it.name != "growingio-webservice" && - it.name != "growingio-data" && - it.name != "growingio-adapter" && - it.name != "snappy" - } - .map { - it.name.replace("growingio-", "") - } - .forEach { - api "com.growingio.android:" + it + ":" + libs.versions.growingio.get() - } + project.rootProject.subprojects.stream() + .filter { + !it.name.startsWith("demo") && + it.name != project.name && + it.name != "gio-sdk" && + it.name != "growingio-tools" && + it.name != "growingio-network" && + it.name != "growingio-webservice" && + it.name != "growingio-data" && + it.name != "growingio-adapter" && + it.name != "snappy" + } + .map { + it.name.replace("growingio-", "") + } + .forEach { + api "com.growingio.android:" + it + ":" + libs.versions.growingio.get() + } - api "com.growingio.android:analytics-fa:3.4.1" - api "com.growingio.android:analytics-ga:3.4.1" - api "com.growingio.android:analytics-sa:3.4.7" - api "com.growingio.android:dummy-sa:3.4.7" + api "com.growingio.android:analytics-fa:3.4.1" + api "com.growingio.android:analytics-ga:3.4.1" + api "com.growingio.android:analytics-sa:3.4.7" + api "com.growingio.android:dummy-sa:3.4.7" - // 2升3模块 - api 'com.growingio.android.sdk.upgrade:autotracker-upgrade-2to3-cdp:1.2.0' + // 2升3模块 + api 'com.growingio.android.sdk.upgrade:autotracker-upgrade-2to3-cdp:1.2.0' - // giokit - api 'com.growingio.giokit:giokit:1.4.0' - api 'com.growingio.giokit:giokit-no-op:1.4.0' + // giokit + api 'com.growingio.giokit:giokit:1.4.0' + api 'com.growingio.giokit:giokit-no-op:1.4.0' - // saas sdk - //api 'com.growingio.android:vds-android-agent:autotrack-2.9.13' - //api 'com.growingio.android:vds-android-agent:track-2.9.13' + // saas sdk + //api 'com.growingio.android:vds-android-agent:autotrack-2.9.13' + //api 'com.growingio.android:vds-android-agent:track-2.9.13' - // tool - //runtime "com.growingio.android:snappy:1.1.0" - //runtime 'com.growingio.android:gmonitor:1.5.0' - } + // tool + //runtime "com.growingio.android:snappy:1.1.0" + //runtime 'com.growingio.android:gmonitor:1.5.0' + } } apply from: "publishMaven.gradle" \ No newline at end of file diff --git a/growingio-sdk-bom/publishMaven.gradle b/growingio-sdk-bom/publishMaven.gradle index 3f937a52..5e1e345d 100644 --- a/growingio-sdk-bom/publishMaven.gradle +++ b/growingio-sdk-bom/publishMaven.gradle @@ -18,14 +18,14 @@ apply plugin: 'maven-publish' apply plugin: 'signing' def getSelectVersion(){ - def version = libs.versions.growingio.get() - if (project.hasProperty('CI_SELECT_MODULE') && project.hasProperty("CI_SELECT_VERSION")) { - def cmdModuleId = CI_SELECT_MODULE - def moduleId = POM_ARTIFACT_ID - if (cmdModuleId == moduleId && CI_SELECT_VERSION != "DEFAULT") - return CI_SELECT_VERSION - } - return version + def version = libs.versions.growingio.get() + if (project.hasProperty('CI_SELECT_MODULE') && project.hasProperty("CI_SELECT_VERSION")) { + def cmdModuleId = CI_SELECT_MODULE + def moduleId = POM_ARTIFACT_ID + if (cmdModuleId == moduleId && CI_SELECT_VERSION != "DEFAULT") + return CI_SELECT_VERSION + } + return version } version = getSelectVersion() @@ -33,98 +33,98 @@ group = GROUP @SuppressWarnings("GrMethodMayBeStatic") def isReleaseBuild() { - return !version.contains("SNAPSHOT") + return !version.contains("SNAPSHOT") } def getReleaseRepositoryUrl() { - return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL - : 'https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/' + return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL + : 'https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/' } def getSnapshotRepositoryUrl() { - return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL - : 'https://s01.oss.sonatype.org/content/repositories/snapshots/' + return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL + : 'https://s01.oss.sonatype.org/content/repositories/snapshots/' } def getRepositoryUsername() { - return hasProperty('USERNAME') ? USERNAME : (hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : '') + return hasProperty('USERNAME') ? USERNAME : (hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : '') } def getRepositoryPassword() { - return hasProperty('PASSWORD') ? PASSWORD : (hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : '') + return hasProperty('PASSWORD') ? PASSWORD : (hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : '') } def configurePom(pom) { - pom.name = POM_NAME - pom.description = POM_DESCRIPTION - pom.url = POM_URL - - pom.scm { - url = POM_SCM_URL - connection = POM_SCM_CONNECTION - developerConnection = POM_SCM_DEV_CONNECTION - tag = 'HEAD' - } - - pom.licenses { - license { - name = POM_LICENCE_NAME - url = POM_LICENCE_URL - distribution = POM_LICENCE_DIST - } - } - - pom.issueManagement { - system = 'GitHub Issues' - url = POM_SCM_ISSUES - } - - pom.organization { - name = POM_COMPANY - url = POM_OFFICIAL_WEBSITE - } - - pom.developers { - developer { - id = POM_DEVELOPER_ID - name = POM_DEVELOPER_NAME - email = POM_DEVELOPER_EMAIL - } - } + pom.name = POM_NAME + pom.description = POM_DESCRIPTION + pom.url = POM_URL + + pom.scm { + url = POM_SCM_URL + connection = POM_SCM_CONNECTION + developerConnection = POM_SCM_DEV_CONNECTION + tag = 'HEAD' + } + + pom.licenses { + license { + name = POM_LICENCE_NAME + url = POM_LICENCE_URL + distribution = POM_LICENCE_DIST + } + } + + pom.issueManagement { + system = 'GitHub Issues' + url = POM_SCM_ISSUES + } + + pom.organization { + name = POM_COMPANY + url = POM_OFFICIAL_WEBSITE + } + + pom.developers { + developer { + id = POM_DEVELOPER_ID + name = POM_DEVELOPER_NAME + email = POM_DEVELOPER_EMAIL + } + } } publishing { - publications { - bom(MavenPublication) { - from components.javaPlatform - - artifactId = POM_ARTIFACT_ID - version = version - pom { - configurePom(owner) - } - } - } - // Publish to build output repository. - repositories { - maven { - def releasesRepoUrl = getReleaseRepositoryUrl() - def snapshotsRepoUrl = getSnapshotRepositoryUrl() - url = isReleaseBuild() ? releasesRepoUrl : snapshotsRepoUrl - credentials(PasswordCredentials) { - username = getRepositoryUsername() - password = getRepositoryPassword() - } - } - } + publications { + bom(MavenPublication) { + from components.javaPlatform + + artifactId = POM_ARTIFACT_ID + version = version + pom { + configurePom(owner) + } + } + } + // Publish to build output repository. + repositories { + maven { + def releasesRepoUrl = getReleaseRepositoryUrl() + def snapshotsRepoUrl = getSnapshotRepositoryUrl() + url = isReleaseBuild() ? releasesRepoUrl : snapshotsRepoUrl + credentials(PasswordCredentials) { + username = getRepositoryUsername() + password = getRepositoryPassword() + } + } + } } signing { - required { isReleaseBuild() && gradle.taskGraph.hasTask("publish") } - publishing.publications.all { publication -> - sign publication - } + required { isReleaseBuild() && gradle.taskGraph.hasTask("publish") } + publishing.publications.all { publication -> + sign publication + } } apply from: "${rootProject.projectDir}/gradle/existDenpendices.gradle" \ No newline at end of file diff --git a/growingio-tools/oaid/build.gradle b/growingio-tools/oaid/build.gradle index 7249c0ca..3d23605a 100644 --- a/growingio-tools/oaid/build.gradle +++ b/growingio-tools/oaid/build.gradle @@ -1,57 +1,56 @@ plugins { - id 'com.android.library' + id 'com.android.library' } android { - namespace 'com.growingio.android.oaid' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - consumerProguardFiles 'consumer-proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } - - testOptions { - unitTests.all { - jacoco { - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - } - } + namespace 'com.growingio.android.oaid' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + consumerProguardFiles 'consumer-proguard-rules.pro' + } + } + + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } + + testOptions { + unitTests.all { + jacoco { + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } } dependencies { - testImplementation libs.bundles.test - testImplementation libs.androidx.test.core - testImplementation files('resources/oaid_sdk_1.1.0.jar') + testImplementation libs.bundles.test + testImplementation libs.androidx.test.core + testImplementation files('resources/oaid_sdk_1.1.0.jar') - compileOnly files('resources/oaid_sdk_1.1.0.jar') - implementation files('libs/msa_helper.jar') - implementation project(':growingio-tracker-core') + compileOnly files('resources/oaid_sdk_1.1.0.jar') + implementation files('libs/msa_helper.jar') + implementation project(':growingio-tracker-core') - implementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + implementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" diff --git a/growingio-tools/oaid/resources/OaidHelper1010.java b/growingio-tools/oaid/resources/OaidHelper1010.java index e0f90ce6..a7c0d0fe 100644 --- a/growingio-tools/oaid/resources/OaidHelper1010.java +++ b/growingio-tools/oaid/resources/OaidHelper1010.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.oaid; import android.content.Context; diff --git a/growingio-tools/oaid/resources/OaidHelper1013.java b/growingio-tools/oaid/resources/OaidHelper1013.java index dbbc8ae9..b2e98e16 100644 --- a/growingio-tools/oaid/resources/OaidHelper1013.java +++ b/growingio-tools/oaid/resources/OaidHelper1013.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.oaid; import android.content.Context; diff --git a/growingio-tools/oaid/resources/OaidHelper1025.java b/growingio-tools/oaid/resources/OaidHelper1025.java index 73afccd9..7c391b67 100644 --- a/growingio-tools/oaid/resources/OaidHelper1025.java +++ b/growingio-tools/oaid/resources/OaidHelper1025.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.oaid; import android.content.Context; diff --git a/growingio-tools/oaid/resources/OaidHelper1100.java b/growingio-tools/oaid/resources/OaidHelper1100.java index 887e8d73..47e92d24 100644 --- a/growingio-tools/oaid/resources/OaidHelper1100.java +++ b/growingio-tools/oaid/resources/OaidHelper1100.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.oaid; import android.content.Context; diff --git a/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/IOaidHelper.java b/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/IOaidHelper.java index 55eac45d..946bdbeb 100644 --- a/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/IOaidHelper.java +++ b/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/IOaidHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.oaid; import android.content.Context; diff --git a/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidCertHelper.java b/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidCertHelper.java index 536f9f19..6269380a 100644 --- a/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidCertHelper.java +++ b/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidCertHelper.java @@ -1,26 +1,24 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.oaid; import android.content.Context; import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.utils.ThreadUtils; import java.io.BufferedReader; import java.io.IOException; @@ -111,7 +109,7 @@ private String getOaidCert(Context context) { @Override public String getOaid() { // Check whether it is running on the main thread - if (ThreadUtils.runningOnUiThread()) { + if (TrackMainThread.trackMain().runningOnUiThread()) { return this.oaid; } diff --git a/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidConfig.java b/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidConfig.java index 5f0682b4..c3c0c417 100644 --- a/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidConfig.java +++ b/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidConfig.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.oaid; diff --git a/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidDataFetcher.java b/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidDataFetcher.java index a647dff1..964dac92 100644 --- a/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidDataFetcher.java +++ b/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidDataFetcher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.oaid; import android.content.Context; diff --git a/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidDataLoader.java b/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidDataLoader.java index c73eedb4..35a14ae2 100644 --- a/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidDataLoader.java +++ b/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidDataLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,65 +13,48 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.oaid; -import android.content.Context; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.modelloader.ModelLoader; import com.growingio.android.sdk.track.modelloader.ModelLoaderFactory; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; import com.growingio.android.sdk.track.middleware.OaidHelper; public class OaidDataLoader implements ModelLoader { - private final Context mContext; - private final IOaidHelper mOaidHelper; + private final TrackerContext mContext; + private volatile IOaidHelper mOaidHelper; - public OaidDataLoader(Context context, IOaidHelper oaidHelper) { + public OaidDataLoader(TrackerContext context) { this.mContext = context; - this.mOaidHelper = oaidHelper; } @Override public LoadData buildLoadData(OaidHelper oaidHelper) { + OaidConfig config = mContext.getConfigurationProvider().getConfiguration(OaidConfig.class); + if (config == null) config = new OaidConfig(); + if (config.getProvideOaidCallback() != null || (config.getProvideOaid() != null && !config.getProvideOaid().isEmpty())) { + if (mOaidHelper == null || mOaidHelper instanceof OaidCertHelper) { + mOaidHelper = new OaidDirectlyHelper(mContext.getBaseContext(), config); + } + } else { + if (mOaidHelper == null || mOaidHelper instanceof OaidDirectlyHelper) { + mOaidHelper = new OaidCertHelper(mContext.getBaseContext(), config); + } + } return new LoadData<>(new OaidDataFetcher(mContext, mOaidHelper)); } public static class Factory implements ModelLoaderFactory { - private final Context mContext; - private volatile IOaidHelper sOaidHelper; - - public Factory(Context context) { - this.mContext = context; - OaidConfig config = ConfigurationProvider.get().getConfiguration(OaidConfig.class); - if (config == null) config = new OaidConfig(); - initOaidSdk(context, config); - } + private final TrackerContext mContext; - protected Factory(Context context, OaidConfig config) { + public Factory(TrackerContext context) { this.mContext = context; - initOaidSdk(context, config); - } - - private void initOaidSdk(Context context, OaidConfig config) { - //提前初始化,以便oaid sdk 加载so包 - if (sOaidHelper == null) { - synchronized (Factory.class) { - if (sOaidHelper == null) { - // 用户直接提供oaid值情况下 - if (config.getProvideOaidCallback() != null || (config.getProvideOaid() != null && !config.getProvideOaid().isEmpty())) { - sOaidHelper = new OaidDirectlyHelper(context, config); - } else { - sOaidHelper = new OaidCertHelper(context, config); - } - } - } - } } @Override public ModelLoader build() { - return new OaidDataLoader(mContext, sOaidHelper); + return new OaidDataLoader(mContext); } } } diff --git a/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidDirectlyHelper.java b/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidDirectlyHelper.java index 07f9004a..b63e9e7c 100644 --- a/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidDirectlyHelper.java +++ b/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidDirectlyHelper.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.oaid; import android.content.Context; diff --git a/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidLibraryGioModule.java b/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidLibraryGioModule.java index b3007aac..47b28e06 100644 --- a/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidLibraryGioModule.java +++ b/growingio-tools/oaid/src/main/java/com/growingio/android/oaid/OaidLibraryGioModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,20 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.oaid; -import android.content.Context; import com.growingio.android.sdk.LibraryGioModule; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.middleware.OaidHelper; import com.growingio.sdk.annotation.GIOLibraryModule; @GIOLibraryModule(config = OaidConfig.class) public class OaidLibraryGioModule extends LibraryGioModule { @Override - public void registerComponents(Context context, TrackerRegistry registry) { - registry.register(OaidHelper.class, String.class, new OaidDataLoader.Factory(context)); + public void registerComponents(TrackerContext context) { + context.getRegistry().register(OaidHelper.class, String.class, new OaidDataLoader.Factory(context)); } } diff --git a/growingio-tools/oaid/src/test/java/com/growingio/android/oaid/OaidTest.java b/growingio-tools/oaid/src/test/java/com/growingio/android/oaid/OaidTest.java index 1fca000c..dec08591 100644 --- a/growingio-tools/oaid/src/test/java/com/growingio/android/oaid/OaidTest.java +++ b/growingio-tools/oaid/src/test/java/com/growingio/android/oaid/OaidTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,10 +19,12 @@ import androidx.test.core.app.ApplicationProvider; +import com.growingio.android.sdk.CoreConfiguration; +import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.modelloader.ModelLoader; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import com.growingio.android.sdk.track.middleware.OaidHelper; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProviderFactory; import org.junit.Before; import org.junit.Test; @@ -33,39 +35,41 @@ import static com.google.common.truth.Truth.assertThat; import static java.lang.Thread.sleep; +import java.util.HashMap; + @Config(manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) public class OaidTest { - private final Application context = ApplicationProvider.getApplicationContext(); + private final Application application = ApplicationProvider.getApplicationContext(); + + private TrackerContext context; @Before public void setup() { - TrackerContext.init(context); + TrackerLifecycleProviderFactory.create() + .createConfigurationProviderWithConfig(new CoreConfiguration("OaidTest", "growingio://oaid"), new HashMap<>()); + Tracker tracker = new Tracker(application); + tracker.registerComponent(new OaidLibraryGioModule()); + context = tracker.getContext(); } @Test public void oaid() { - OaidLibraryGioModule module = new OaidLibraryGioModule(); - TrackerRegistry trackerRegistry = new TrackerRegistry(); - module.registerComponents(context, trackerRegistry); - trackerRegistry.register(OaidHelper.class, String.class, new OaidDataLoader.Factory(context)); - ModelLoader modelLoader = trackerRegistry.getModelLoader(OaidHelper.class, String.class); + ModelLoader modelLoader = context.getRegistry().getModelLoader(OaidHelper.class, String.class); assertThat(String.class).isEqualTo(modelLoader.buildLoadData(new OaidHelper()).fetcher.getDataClass()); } @Test public void oaidConfig1() { - TrackerRegistry trackerRegistry = TrackerContext.get().getRegistry(); OaidConfig config = new OaidConfig().setProvideOaid("cpacm"); - trackerRegistry.register(OaidHelper.class, String.class, new OaidDataLoader.Factory(context, config)); - ModelLoader modelLoader = trackerRegistry.getModelLoader(OaidHelper.class, String.class); + context.getConfigurationProvider().addConfiguration(config); + ModelLoader modelLoader = context.getRegistry().getModelLoader(OaidHelper.class, String.class); String oaid = modelLoader.buildLoadData(new OaidHelper()).fetcher.executeData(); assertThat(oaid).isEqualTo("cpacm"); } @Test public void oaidConfig2() { - TrackerRegistry trackerRegistry = TrackerContext.get().getRegistry(); OaidConfig config = new OaidConfig().setProvideOaidCallback(context -> { try { sleep(5000); @@ -74,8 +78,8 @@ public void oaidConfig2() { } return "cpacm_job"; }); - trackerRegistry.register(OaidHelper.class, String.class, new OaidDataLoader.Factory(context, config)); - ModelLoader modelLoader = trackerRegistry.getModelLoader(OaidHelper.class, String.class); + context.getConfigurationProvider().addConfiguration(config); + ModelLoader modelLoader = context.getRegistry().getModelLoader(OaidHelper.class, String.class); String oaid = modelLoader.buildLoadData(new OaidHelper()).fetcher.executeData(); assertThat(oaid).isNull(); try { @@ -89,10 +93,9 @@ public void oaidConfig2() { @Test public void oaidConfig3() { - TrackerRegistry trackerRegistry = TrackerContext.get().getRegistry(); OaidConfig config = new OaidConfig().setProvideCert("UNKNOWN CERT"); - trackerRegistry.register(OaidHelper.class, String.class, new OaidDataLoader.Factory(context, config)); - ModelLoader modelLoader = trackerRegistry.getModelLoader(OaidHelper.class, String.class); + context.getConfigurationProvider().addConfiguration(config); + ModelLoader modelLoader = context.getRegistry().getModelLoader(OaidHelper.class, String.class); String oaid = modelLoader.buildLoadData(new OaidHelper()).fetcher.executeData(); assertThat(oaid).isNull(); } diff --git a/growingio-tools/snappy/build.gradle b/growingio-tools/snappy/build.gradle index 8ec90706..13c6175f 100644 --- a/growingio-tools/snappy/build.gradle +++ b/growingio-tools/snappy/build.gradle @@ -1,45 +1,45 @@ plugins { - id 'com.android.library' + id 'com.android.library' } android { - namespace 'com.growingio.android.snappy' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } + namespace 'com.growingio.android.snappy' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } - testOptions { - unitTests.all { - jacoco { - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - } - } + testOptions { + unitTests.all { + jacoco { + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } } dependencies { - testImplementation libs.bundles.test + testImplementation libs.bundles.test } // snappy为工具库,固定版本为1.1.0,不再随着sdk版本迭代 diff --git a/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/BufferRecycler.java b/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/BufferRecycler.java index 66e721d1..78ea09f9 100644 --- a/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/BufferRecycler.java +++ b/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/BufferRecycler.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - package com.growingio.android.snappy; import java.lang.ref.SoftReference; diff --git a/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/Memory.java b/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/Memory.java index 39d2e8d9..af129135 100644 --- a/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/Memory.java +++ b/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/Memory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/SlowMemory.java b/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/SlowMemory.java index 1afcf46c..9ec0e1bb 100644 --- a/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/SlowMemory.java +++ b/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/SlowMemory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/Snappy.java b/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/Snappy.java index 44f086c6..f0eb2234 100644 --- a/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/Snappy.java +++ b/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/Snappy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/SnappyCompressor.java b/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/SnappyCompressor.java index 9dbb8e60..2d3116c7 100644 --- a/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/SnappyCompressor.java +++ b/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/SnappyCompressor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.snappy; import android.annotation.SuppressLint; diff --git a/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/SnappyInternalUtils.java b/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/SnappyInternalUtils.java index c4d90943..8277fd99 100644 --- a/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/SnappyInternalUtils.java +++ b/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/SnappyInternalUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.snappy; final class SnappyInternalUtils { diff --git a/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/XORUtils.java b/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/XORUtils.java index 9df95343..30584c97 100644 --- a/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/XORUtils.java +++ b/growingio-tools/snappy/src/main/java/com/growingio/android/snappy/XORUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.snappy; public class XORUtils { diff --git a/growingio-tools/snappy/src/test/java/com/growingio/android/snappy/SnappyTest.java b/growingio-tools/snappy/src/test/java/com/growingio/android/snappy/SnappyTest.java index d71c454b..a4c28cf9 100644 --- a/growingio-tools/snappy/src/test/java/com/growingio/android/snappy/SnappyTest.java +++ b/growingio-tools/snappy/src/test/java/com/growingio/android/snappy/SnappyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/growingio-tracker-core/build.gradle b/growingio-tracker-core/build.gradle index 1e9e5770..1fb2395f 100644 --- a/growingio-tracker-core/build.gradle +++ b/growingio-tracker-core/build.gradle @@ -1,81 +1,81 @@ plugins { - id 'com.android.library' - //id 'org.jetbrains.kotlin.android' + id 'com.android.library' + //id 'org.jetbrains.kotlin.android' } def gitSha = 'git rev-parse --short HEAD'.execute([], project.rootDir).text.trim() as String def buildTime = new Date().format("yyyyMMddHHmmss", TimeZone.getTimeZone("GMT+8")) android { - namespace 'com.growingio.android.sdk.track' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() + namespace 'com.growingio.android.sdk.track' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() - buildConfigField "String", "GIT_SHA", "\"${gitSha}\"" - buildConfigField "String", "BUILD_TIME", "\"${buildTime}\"" - buildConfigField("int", "VERSION_CODE", "${defaultConfig.versionCode}") - buildConfigField("String", "VERSION_NAME", "\"${defaultConfig.versionName}\"") - } + buildConfigField "String", "GIT_SHA", "\"${gitSha}\"" + buildConfigField "String", "BUILD_TIME", "\"${buildTime}\"" + buildConfigField("int", "VERSION_CODE", "${defaultConfig.versionCode}") + buildConfigField("String", "VERSION_NAME", "\"${defaultConfig.versionName}\"") + } - buildTypes { -// debug { -// testCoverageEnabled = true -// } + buildTypes { + // debug { + // testCoverageEnabled = true + // } - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - consumerProguardFiles 'consumer-proguard-rules.pro' - } - } + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + consumerProguardFiles 'consumer-proguard-rules.pro' + } + } - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } - testOptions { - unitTests.all { - jacoco { - jvmArgs '-noverify' - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - } - } - - lintOptions { - abortOnError false - } + testOptions { + unitTests.all { + jacoco { + jvmArgs '-noverify' + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } + lintOptions { + abortOnError false + } } dependencies { - //implementation libs.kotlin.stdlib + //implementation libs.kotlin.stdlib - compileOnly fileTree(dir: '../thirdLibs', include: ['*.jar']) - compileOnly libs.bundles.androidx + compileOnly fileTree(dir: '../thirdLibs', include: ['*.jar']) + compileOnly libs.bundles.androidx - testImplementation fileTree(dir: '../thirdLibs', include: ['*.jar']) - testImplementation libs.bundles.test - testImplementation libs.androidx.test.core - testImplementation libs.bundles.androidx - testImplementation libs.protobuf.javalite + testImplementation fileTree(dir: '../thirdLibs', include: ['*.jar']) + testImplementation libs.bundles.test + testImplementation libs.androidx.test.core + testImplementation libs.bundles.androidx + testImplementation libs.protobuf.javalite - testImplementation project(path: ':growingio-data:database') - testImplementation project(path: ':growingio-data:protobuf') - testImplementation project(path: ':growingio-data:json') + testImplementation project(path: ':growingio-data:database') + testImplementation project(path: ':growingio-data:protobuf') + testImplementation project(path: ':growingio-data:json') - implementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:json-serializer") + implementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:json-serializer") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/AppGioModule.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/AppGioModule.java index 46f27d39..1ce8047a 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/AppGioModule.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/AppGioModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk; /** diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/Configurable.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/Configurable.java index cc4e9827..bbc233da 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/Configurable.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/Configurable.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk; public interface Configurable { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/CoreConfiguration.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/CoreConfiguration.java index f11f5703..daeef28d 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/CoreConfiguration.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/CoreConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk; import android.text.TextUtils; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/GeneratedGioModule.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/GeneratedGioModule.java index e6551e7d..a4501e27 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/GeneratedGioModule.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/GeneratedGioModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk; abstract class GeneratedGioModule extends AppGioModule { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/LibraryGioModule.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/LibraryGioModule.java index 08c3422d..7c53f63b 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/LibraryGioModule.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/LibraryGioModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,13 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk; -import android.content.Context; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; +import java.util.Map; /** * Registers a set of components to use when initializing GrowingIO whthin an library when GrowingIO's @@ -29,10 +27,9 @@ */ public abstract class LibraryGioModule { - public void registerComponents(Context context, TrackerRegistry registry) { + public void registerComponents(TrackerContext context) { } - protected T getConfiguration(Class clazz) { - return ConfigurationProvider.get().getConfiguration(clazz); + protected void setupProviders(Map, TrackerLifecycleProvider> providerStore) { } } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/Tracker.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/Tracker.java index 3fbf6988..630f3695 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/Tracker.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/Tracker.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk; import android.app.Activity; @@ -25,22 +24,16 @@ import android.view.View; import com.growingio.android.sdk.track.TrackMainThread; -import com.growingio.android.sdk.track.ipc.PersistentDataProvider; import com.growingio.android.sdk.track.events.TrackEventGenerator; -import com.growingio.android.sdk.track.listener.event.ActivityLifecycleEvent; import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.middleware.advert.DeepLinkCallback; import com.growingio.android.sdk.track.modelloader.ModelLoader; import com.growingio.android.sdk.track.middleware.hybrid.HybridBridge; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; -import com.growingio.android.sdk.track.providers.EventStateProvider; import com.growingio.android.sdk.track.providers.ConfigurationProvider; import com.growingio.android.sdk.track.providers.DeepLinkProvider; -import com.growingio.android.sdk.track.providers.DeviceInfoProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProviderFactory; import com.growingio.android.sdk.track.providers.SessionProvider; -import com.growingio.android.sdk.track.providers.UserInfoProvider; -import com.growingio.android.sdk.track.timer.TimerCenter; -import com.growingio.android.sdk.track.utils.ActivityUtil; import com.growingio.android.sdk.track.utils.ClassExistHelper; import java.lang.reflect.InvocationTargetException; @@ -49,77 +42,91 @@ public class Tracker { + private final TrackerContext trackerContext; + private static final String TAG = "Tracker"; protected volatile boolean isInited; public Tracker(Context context) { + isInited = false; if (context == null) { - isInited = false; Logger.e(TAG, "GrowingIO Track SDK is UNINITIALIZED, please initialized before use API"); - return; + throw new IllegalArgumentException("GrowingIO Track SDK is UNINITIALIZED, please initialized before use API"); + } + + if ((!(context instanceof Application) && !(context instanceof Activity))) { + Logger.e(TAG, "GrowingIO Track SDK is UNINITIALIZED, please initialized SDK with Application or Activity"); + throw new IllegalArgumentException("GrowingIO Track SDK is UNINITIALIZED, please initialized SDK with Application or Activity"); } - // 配置 - setup(context); + // 初始化 + trackerContext = initTrackerContext(context); + + // 注册所有的模块 + loadAnnotationGeneratedModule(context); - TrackerContext.initSuccess(); - // 业务逻辑 - start(context); + // 配置业务逻辑 + trackerContext.setup(); isInited = true; + + TrackMainThread.trackMain().setupWithContext(trackerContext); //need setup + + startAfterSdkSetup(trackerContext); } - protected void setup(Context context) { - if (context instanceof Application) { - Application application = (Application) context; - application.registerActivityLifecycleCallbacks(ActivityStateProvider.get()); - } else if (context instanceof Activity) { - Activity activity = (Activity) context; - activity.getApplication().registerActivityLifecycleCallbacks(ActivityStateProvider.get()); - } else { - Logger.e(TAG, "GrowingIO Track SDK is UNINITIALIZED, please initialized SDK with Application or Activity"); - return; - } - TrackerContext.init(context.getApplicationContext()); - // init core service - DeepLinkProvider.get().init(); - SessionProvider.get().init(); - PersistentDataProvider.get().setup(); + private TrackerContext initTrackerContext(Context context) { + + // add Providers in order. + TrackerLifecycleProviderFactory.create().createActivityStateProvider(context); + TrackerLifecycleProviderFactory.create().createPersistentDataProvider(context); + + Map, TrackerLifecycleProvider> providers = TrackerLifecycleProviderFactory.create().providers(); + Map, TrackerLifecycleProvider> extraProviders = extraProviders(); + providers.putAll(extraProviders); + return new TrackerContext(context.getApplicationContext(), providers); + } + + private void loadAnnotationGeneratedModule(Context context) { loadAnnotationGeneratedModules(context); // 支持配置中注册模块, 如加密模块等事件模块需要先于所有事件发送注册 - for (LibraryGioModule component : ConfigurationProvider.core().getPreloadComponents()) { - component.registerComponents(context, TrackerContext.get().getRegistry()); + for (LibraryGioModule component : trackerContext.getConfigurationProvider().core().getPreloadComponents()) { + // add component provider first. + component.setupProviders(trackerContext.getProviderStore()); + component.registerComponents(trackerContext); } } - private void start(Context context) { - PersistentDataProvider.get().start(); - - EventStateProvider.get().releaseCaches(); - - makeupActivityLifecycle(context); + protected Map, TrackerLifecycleProvider> extraProviders() { + return TrackerLifecycleProviderFactory.emptyMap(); } - private void makeupActivityLifecycle(Context context) { - if (context instanceof Activity) { - Activity activity = (Activity) context; - ActivityLifecycleEvent.EVENT_TYPE state = ActivityUtil.judgeContextState(activity); - if (state != null) { - Logger.i(TAG, "initSdk with Activity, makeup ActivityLifecycle before current state:" + state.name()); - if (state.compareTo(ActivityLifecycleEvent.EVENT_TYPE.ON_CREATED) >= 0) { - ActivityStateProvider.get().onActivityCreated(activity, null); - } - if (state.compareTo(ActivityLifecycleEvent.EVENT_TYPE.ON_STARTED) >= 0) { - ActivityStateProvider.get().onActivityStarted(activity); - } - if (state.compareTo(ActivityLifecycleEvent.EVENT_TYPE.ON_RESUMED) >= 0) { - ActivityStateProvider.get().onActivityResumed(activity); - } - } + public TrackerContext getContext() { + if (null == trackerContext) { + Logger.e(TAG, new NullPointerException("You should init growingio sdk first!!")); } + return trackerContext; + } + + private void startAfterSdkSetup(TrackerContext context) { + + //generate first visit + SessionProvider sessionProvider = context.getProvider(SessionProvider.class); + sessionProvider.createVisitAfterAppStart(); + + // release event caches + TrackMainThread.trackMain().releaseCaches(); + + // makeup activity lifecycle + context.getActivityStateProvider().makeupActivityLifecycle(context); + } + public void shutdown() { + isInited = false; + this.trackerContext.shutdown(); + } public void trackCustomEvent(String eventName) { if (!isInited) return; @@ -168,7 +175,7 @@ public void setVisitorAttributes(Map attributes) { public String getDeviceId() { if (!isInited) return "UNKNOWN"; - return DeviceInfoProvider.get().getDeviceId(); + return trackerContext.getDeviceInfoProvider().getDeviceId(); } public void setDataCollectionEnabled(boolean enabled) { @@ -176,17 +183,21 @@ public void setDataCollectionEnabled(boolean enabled) { TrackMainThread.trackMain().postActionToTrackMain(new Runnable() { @Override public void run() { - if (enabled != ConfigurationProvider.core().isDataCollectionEnabled()) { + ConfigurationProvider configurationProvider = trackerContext.getConfigurationProvider(); + if (enabled != configurationProvider.core().isDataCollectionEnabled()) { Logger.d(TAG, "isDataCollectionEnabled = " + enabled); - ConfigurationProvider.core().setDataCollectionEnabled(enabled); + configurationProvider.core().setDataCollectionEnabled(enabled); if (enabled) { - SessionProvider.get().refreshSessionId(); - SessionProvider.get().generateVisit(); + SessionProvider sessionProvider = trackerContext.getProvider(SessionProvider.class); + sessionProvider.refreshSessionId(); + sessionProvider.generateVisit(); + + TrackMainThread.trackMain().releaseCaches(); } else { - TimerCenter.get().clearTimer(); + trackerContext.getTimingEventProvider().clearTimer(); } // use for modules - ConfigurationProvider.get().onDataCollectionChanged(enabled); + configurationProvider.onDataCollectionChanged(enabled); } } }); @@ -194,32 +205,34 @@ public void run() { public void setLoginUserId(final String userId) { if (!isInited) return; - TrackMainThread.trackMain().postActionToTrackMain(() -> UserInfoProvider.get().setLoginUserId(userId)); + TrackMainThread.trackMain().postActionToTrackMain(() -> trackerContext.getUserInfoProvider().setLoginUserId(userId)); } public void setLoginUserId(final String userId, final String userKey) { if (!isInited) return; - TrackMainThread.trackMain().postActionToTrackMain(() -> UserInfoProvider.get().setLoginUserId(userId, userKey)); + TrackMainThread.trackMain().postActionToTrackMain(() -> trackerContext.getUserInfoProvider().setLoginUserId(userId, userKey)); } public void cleanLoginUserId() { if (!isInited) return; - TrackMainThread.trackMain().postActionToTrackMain(() -> UserInfoProvider.get().setLoginUserId(null)); + TrackMainThread.trackMain().postActionToTrackMain(() -> trackerContext.getUserInfoProvider().setLoginUserId(null)); } public void setLocation(final double latitude, final double longitude) { if (!isInited) return; - TrackMainThread.trackMain().postActionToTrackMain(() -> SessionProvider.get().setLocation(latitude, longitude)); + TrackMainThread.trackMain().postActionToTrackMain(() -> + trackerContext.getDeviceInfoProvider().setLocation(latitude, longitude) + ); } public void cleanLocation() { if (!isInited) return; - TrackMainThread.trackMain().postActionToTrackMain(() -> SessionProvider.get().cleanLocation()); + TrackMainThread.trackMain().postActionToTrackMain(() -> trackerContext.getDeviceInfoProvider().cleanLocation()); } public void onActivityNewIntent(Activity activity, Intent intent) { if (!isInited) return; - ActivityStateProvider.get().onActivityNewIntent(activity, intent); + trackerContext.getActivityStateProvider().onActivityNewIntent(activity, intent); } /** @@ -238,7 +251,7 @@ public void bridgeWebView(View webView) { private void bridgeInnerWebView(View view) { boolean result = false; - ModelLoader modelLoader = TrackerContext.get().getRegistry().getModelLoader(HybridBridge.class, Boolean.class); + ModelLoader modelLoader = trackerContext.getRegistry().getModelLoader(HybridBridge.class, Boolean.class); if (modelLoader != null) { result = modelLoader.buildLoadData(new HybridBridge(view)).fetcher.executeData(); } @@ -249,53 +262,54 @@ public String trackTimerStart(final String eventName) { if (!isInited || TextUtils.isEmpty(eventName)) { return null; } - return TimerCenter.get().startTimer(eventName); + return trackerContext.getTimingEventProvider().startTimer(eventName); } public void trackTimerPause(final String timerId) { if (!isInited || TextUtils.isEmpty(timerId)) { return; } - TimerCenter.get().updateTimer(timerId, false); + trackerContext.getTimingEventProvider().updateTimer(timerId, false); } public void trackTimerResume(final String timerId) { if (!isInited || TextUtils.isEmpty(timerId)) { return; } - TimerCenter.get().updateTimer(timerId, true); + trackerContext.getTimingEventProvider().updateTimer(timerId, true); } public void trackTimerEnd(final String timerId) { if (!isInited || TextUtils.isEmpty(timerId)) { return; } - TimerCenter.get().endTimer(timerId); + trackerContext.getTimingEventProvider().endTimer(timerId); } public void trackTimerEnd(final String timerId, Map attributes) { if (!isInited || TextUtils.isEmpty(timerId)) { return; } - TimerCenter.get().endTimer(timerId, attributes); + trackerContext.getTimingEventProvider().endTimer(timerId, attributes); } public void removeTimer(final String timerId) { if (!isInited || TextUtils.isEmpty(timerId)) { return; } - TimerCenter.get().removeTimer(timerId); + trackerContext.getTimingEventProvider().removeTimer(timerId); } public void clearTrackTimer() { if (!isInited) { return; } - TimerCenter.get().clearTimer(); + trackerContext.getTimingEventProvider().clearTimer(); } public boolean doDeepLinkByUrl(String url, DeepLinkCallback callback) { - return DeepLinkProvider.get().doDeepLinkByUrl(url, callback); + DeepLinkProvider deepLinkProvider = trackerContext.getProvider(DeepLinkProvider.class); + return deepLinkProvider.doDeepLinkByUrl(url, callback); } @SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter"}) @@ -305,7 +319,7 @@ private void loadAnnotationGeneratedModules(Context context) { (Class) Class.forName("com.growingio.android.sdk.GeneratedGioModuleImpl"); GeneratedGioModule generatedGioModule = clazz.getDeclaredConstructor(Context.class).newInstance(context.getApplicationContext()); - generatedGioModule.registerComponents(context, TrackerContext.get().getRegistry()); + generatedGioModule.registerComponents(trackerContext); } catch (ClassNotFoundException e) { if (Log.isLoggable(TAG, Log.WARN)) { Log.w( @@ -333,14 +347,29 @@ private void loadAnnotationGeneratedModules(Context context) { * @param module GIOLibraryModule */ public void registerComponent(LibraryGioModule module) { - if (!isInited || module == null) return; - module.registerComponents(TrackerContext.get(), TrackerContext.get().getRegistry()); + addComponent(module, null); } public void registerComponent(LibraryGioModule module, Configurable config) { - if (!isInited || module == null || config == null) return; - ConfigurationProvider.get().addConfiguration(config); - module.registerComponents(TrackerContext.get(), TrackerContext.get().getRegistry()); + addComponent(module, config); + } + + private void addComponent(LibraryGioModule module, Configurable config) { + if (module == null) return; + if (config != null) { + trackerContext.getConfigurationProvider().addConfiguration(config); + } + Map, TrackerLifecycleProvider> emptyMap = TrackerLifecycleProviderFactory.emptyMap(); + module.setupProviders(emptyMap); + for (Class key : emptyMap.keySet()) { + TrackerLifecycleProvider provider = emptyMap.get(key); + if (isInited) { + provider.setup(trackerContext); + } + trackerContext.getProviderStore().put(key, provider); + } + module.registerComponents(trackerContext); + } private void throwIncorrectGioModule(Exception e) { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/TrackerContext.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/TrackerContext.java index e58e2c53..62d0fb03 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/TrackerContext.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/TrackerContext.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,25 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk; import android.content.Context; import android.content.ContextWrapper; -import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.modelloader.DataFetcher; -import com.growingio.android.sdk.track.modelloader.LoadDataFetcher; -import com.growingio.android.sdk.track.modelloader.ModelLoader; import com.growingio.android.sdk.track.modelloader.TrackerRegistry; +import com.growingio.android.sdk.track.providers.ActivityStateProvider; +import com.growingio.android.sdk.track.providers.ConfigurationProvider; +import com.growingio.android.sdk.track.providers.DeviceInfoProvider; +import com.growingio.android.sdk.track.providers.TimingEventProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; +import com.growingio.android.sdk.track.providers.UserInfoProvider; + +import java.util.LinkedHashMap; +import java.util.Map; public class TrackerContext extends ContextWrapper { - private final static String TAG = "ContextProvider"; - private static volatile TrackerContext INSTANCE = null; + private final static String TAG = "TrackerContext"; private static volatile boolean sInitializedSuccessfully = false; public static boolean initializedSuccessfully() { - if (INSTANCE == null) return false; return sInitializedSuccessfully; } @@ -39,49 +41,67 @@ public static void initSuccess() { sInitializedSuccessfully = true; } - private TrackerContext(Context context) { + private final LinkedHashMap, TrackerLifecycleProvider> providerStore; + + public TrackerContext(Context context, Map, TrackerLifecycleProvider> providers) { super(context); registry = new TrackerRegistry(); + + providerStore = new LinkedHashMap<>(); + providerStore.putAll(providers); + providers.clear(); + } + + LinkedHashMap, TrackerLifecycleProvider> getProviderStore() { + return providerStore; } - public static void init(Context context) { - synchronized (TrackerContext.class) { - if (null == INSTANCE) { - INSTANCE = new TrackerContext(context); - } + void setup() { + for (Class key : providerStore.keySet()) { + TrackerLifecycleProvider provider = providerStore.get(key); + provider.setup(this); } + sInitializedSuccessfully = true; } - public static TrackerContext get() { - if (null == INSTANCE) { - Logger.e(TAG, new NullPointerException("you should init growingio sdk first")); + void shutdown() { + sInitializedSuccessfully = false; + for (Class key : providerStore.keySet()) { + TrackerLifecycleProvider provider = providerStore.get(key); + provider.shutdown(); } - return INSTANCE; } - private final TrackerRegistry registry; + @SuppressWarnings("unchecked") + public T getProvider(Class clazz) { + return (T) providerStore.get(clazz); + } - public TrackerRegistry getRegistry() { - return registry; + public ActivityStateProvider getActivityStateProvider() { + return getProvider(ActivityStateProvider.class); } - public Data executeData(Model model, Class modelClass, Class dataClass) { - ModelLoader modelLoader = getRegistry().getModelLoader(modelClass, dataClass); - if (modelLoader != null) { - return modelLoader.buildLoadData(model).fetcher.executeData(); - } - return null; + public ConfigurationProvider getConfigurationProvider() { + ConfigurationProvider configurationProvider = getProvider(ConfigurationProvider.class); + if (configurationProvider == null) return ConfigurationProvider.empty(); + return configurationProvider; } - public void loadData(Model model, Class modelClass, Class dataClass, LoadDataFetcher.DataCallback callback) { - ModelLoader modelLoader = getRegistry().getModelLoader(modelClass, dataClass); - if (modelLoader != null) { - DataFetcher fetcher = modelLoader.buildLoadData(model).fetcher; - if (fetcher instanceof LoadDataFetcher) { - ((LoadDataFetcher) fetcher).loadData(callback); - } - } else { - callback.onLoadFailed(new NullPointerException(String.format("please register %s component first", modelClass.getSimpleName()))); - } + public DeviceInfoProvider getDeviceInfoProvider() { + return getProvider(DeviceInfoProvider.class); + } + + public UserInfoProvider getUserInfoProvider() { + return getProvider(UserInfoProvider.class); + } + + public TimingEventProvider getTimingEventProvider() { + return getProvider(TimingEventProvider.class); + } + + private final TrackerRegistry registry; + + public TrackerRegistry getRegistry() { + return registry; } } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ErrorLog.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ErrorLog.java deleted file mode 100644 index 05e4586f..00000000 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ErrorLog.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.growingio.android.sdk.track; - -/** - * 显示给用户的错误提示 - */ -public class ErrorLog { - - public static final String EVENT_NAME_ILLEGAL = "当前数据的标识符不合法。合法的标识符的详细定义请参考:https://docs.growingio.com/docs/developer-manual/sdkintegrated/android-sdk/android-sdk-api/customize-api"; - public static final String VALUE_BE_EMPTY = "当前事件类型数据的值不合法。值不能为空或者null"; - public static final String JSON_VALUE_EMPTY_VALID = "当前JSONObject没有任何有效数据, 发送失败"; - public static final String JSON_TOO_LONG = "JSONObject传参最长仅支持100个键值对"; - public static final String JSON_KEY_VALUE_NOT_VALID = "JSONObject传参中键值对不合法, 发送失败"; - public static final String TRACK_FRAGMENT_ERROR = "trackAllFragment策略与全局策略相同, 不需要单独设置"; - public static final String USER_KEY_TOO_LONG = "GrowingIO.setUserId(KEY, VALUE):KEY长度大于1000,不发送"; - public static final String USER_ID_TOO_LONG = "GrowingIO.setUserId(KEY, VALUE):VALUE长度大于1000,不发送"; - - private ErrorLog() { - } - -// public static String jsonValueTooLong(String key) { -// return "JSON Object中有键值对,值的字符个数在1000个以下, 发送失败, key: " + key; -// } -// -// public static String jsonObjArrayNotSupport(String key) { -// return "jsonObject 不支持嵌套JSONObject或者JsonArray, 发送失败, key:" + key; -// } -// -// public static String jsonNullValue(String key) { -// return "value 不能为null, 可以传字符串\"null\"代替: " + key; -// } -// -// public static String jsonInValidKey(String key) { -// return "JSONObject中的key要求: 1. 非空, 2. 长度小于50: " + key; -// } -// -// public static String argumentBeNull(String name) { -// return "参数:" + name + "不能为null"; -// } -} diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/SDKConfig.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/SDKConfig.java index a0f01788..ca579634 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/SDKConfig.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/SDKConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track; public class SDKConfig { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/TrackMainThread.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/TrackMainThread.java index 68bd9478..1fefbe4d 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/TrackMainThread.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/TrackMainThread.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,77 +13,69 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track; +import android.app.Activity; +import android.content.Context; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; -import android.os.Message; -import android.text.TextUtils; - -import androidx.annotation.VisibleForTesting; import com.growingio.android.sdk.CoreConfiguration; -import com.growingio.android.sdk.track.events.CustomEvent; -import com.growingio.android.sdk.track.events.EventFilterInterceptor; -import com.growingio.android.sdk.track.events.PageEvent; -import com.growingio.android.sdk.track.events.PageLevelCustomEvent; -import com.growingio.android.sdk.track.events.ViewElementEvent; -import com.growingio.android.sdk.track.events.helper.DefaultEventFilterInterceptor; -import com.growingio.android.sdk.track.ipc.PersistentDataProvider; -import com.growingio.android.sdk.track.events.EventBuildInterceptor; +import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.track.log.CircularFifoQueue; +import com.growingio.android.sdk.track.providers.ActivityStateProvider; +import com.growingio.android.sdk.track.providers.PersistentDataProvider; import com.growingio.android.sdk.track.events.base.BaseEvent; -import com.growingio.android.sdk.track.listener.OnTrackMainInitSDKCallback; import com.growingio.android.sdk.track.listener.TrackThread; -import com.growingio.android.sdk.track.listener.ListenerContainer; import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.middleware.EventSender; import com.growingio.android.sdk.track.middleware.GEvent; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; -import com.growingio.android.sdk.track.providers.EventStateProvider; +import com.growingio.android.sdk.track.providers.EventBuilderProvider; import com.growingio.android.sdk.track.providers.SessionProvider; import com.growingio.android.sdk.track.middleware.EventHttpSender; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - /** * GrowingIO主线程 */ -public final class TrackMainThread extends ListenerContainer { +public final class TrackMainThread { private static final String TAG = "TrackMainThread"; - private static final int MSG_INIT_SDK = 1; - private final Looper mMainLooper; - private final Handler mMainHandler; - private final EventSender mEventSender; - private final CoreConfiguration coreConfiguration; + private final CircularFifoQueue> caches; - private final List mEventBuildInterceptors = new ArrayList<>(); - - private TrackMainThread() { - this(ConfigurationProvider.core()); - } + private final Handler mainHandler; + private final Handler uiHandler; + private EventSender eventSender; + private CoreConfiguration coreConfiguration; + private EventBuilderProvider eventBuilderProvider; + private PersistentDataProvider persistentDataProvider; + private SessionProvider sessionProvider; - @VisibleForTesting - TrackMainThread(CoreConfiguration configuration) { - this.coreConfiguration = configuration; - int uploadInterval = configuration.isDebugEnabled() ? 0 : configuration.getDataUploadInterval(); - mEventSender = new EventSender(new EventHttpSender(), uploadInterval, configuration.getCellularDataLimit()); + private ActivityStateProvider activityStateProvider; + private Context context; + private TrackMainThread() { + caches = new CircularFifoQueue<>(200); HandlerThread handlerThread = new HandlerThread(TAG); handlerThread.start(); - mMainLooper = handlerThread.getLooper(); - mMainHandler = new H(mMainLooper); - mMainHandler.sendEmptyMessage(MSG_INIT_SDK); - } - - @Override - protected void singleAction(OnTrackMainInitSDKCallback listener, Void action) { - listener.onTrackMainInitSDK(); + mainHandler = new Handler(handlerThread.getLooper()); + uiHandler = new Handler(Looper.getMainLooper()); + } + + public void setupWithContext(TrackerContext context) { + this.context = context.getBaseContext(); + this.coreConfiguration = context.getConfigurationProvider().core(); + this.eventBuilderProvider = context.getProvider(EventBuilderProvider.class); + this.persistentDataProvider = context.getProvider(PersistentDataProvider.class); + this.sessionProvider = context.getProvider(SessionProvider.class); + this.activityStateProvider = context.getActivityStateProvider(); + int uploadInterval = coreConfiguration.isDebugEnabled() ? 0 : coreConfiguration.getDataUploadInterval(); + eventSender = new EventSender( + this.context, + context.getRegistry(), + new EventHttpSender(context), + uploadInterval, + coreConfiguration.getCellularDataLimit()); } private static class SingleInstance { @@ -95,211 +87,116 @@ public static TrackMainThread trackMain() { } public Handler getMainHandler() { - return mMainHandler; - } - - @TrackThread - void initSDK() { - dispatchActions(null); - } - - public void postEventToTrackMain(final BaseEvent.BaseBuilder eventBuilder) { - postActionToTrackMain(buildEventSendRunnable(eventBuilder)); + return mainHandler; } /** - * generate event send runnable - * - * @param eventBuilder the event whether to be send - * @return runnable + * send events in the current thread */ - public Runnable buildEventSendRunnable(final BaseEvent.BaseBuilder eventBuilder) { - return new Runnable() { - @Override - public void run() { - if (eventBuilder == null) { - return; - } - - if (coreConfiguration.isDataCollectionEnabled()) { - onGenerateGEvent(eventBuilder); - } - } - }; - } - - public void postActionToTrackMain(Runnable runnable) { - if (mMainHandler.getLooper() == Looper.myLooper()) { - runnable.run(); + public void sendEventSync(final BaseEvent.BaseBuilder eventBuilder) { + if (eventBuilder == null) { return; } - - mMainHandler.post(runnable); + if (coreConfiguration != null && coreConfiguration.isDataCollectionEnabled()) { + onGenerateGEvent(eventBuilder); + } } - @TrackThread - void onGenerateGEvent(BaseEvent.BaseBuilder gEvent) { - dispatchEventWillBuild(gEvent); - - if (!filterEvent(gEvent)) return; - gEvent.readPropertyInTrackThread(); - - BaseEvent event = gEvent.build(); - dispatchEventDidBuild(event); + public void postEventToTrackMain(final BaseEvent.BaseBuilder eventBuilder) { + postActionToTrackMain(() -> { + if (eventBuilder == null) { + return; + } - saveEvent(event); + if (coreConfiguration.isDataCollectionEnabled()) { + onGenerateGEvent(eventBuilder); + } + }); } - private EventFilterInterceptor defaultFilterInterceptor; - - EventFilterInterceptor getEventFilterInterceptor() { - if (coreConfiguration.getEventFilterInterceptor() != null) { - return coreConfiguration.getEventFilterInterceptor(); - } else { - if (defaultFilterInterceptor == null) { - defaultFilterInterceptor = new DefaultEventFilterInterceptor(); + public void releaseCaches() { + if (caches.size() > 0 && coreConfiguration != null && coreConfiguration.isDataCollectionEnabled()) { + for (BaseEvent.BaseBuilder eventBuilder : caches) { + TrackMainThread.trackMain().postEventToTrackMain(eventBuilder); } - return defaultFilterInterceptor; + Logger.d(TAG, "release cache events after sdk init: count-" + caches.size()); + caches.clear(); } } - @TrackThread - boolean filterEvent(BaseEvent.BaseBuilder eventBuilder) { - EventFilterInterceptor eventFilterInterceptor = getEventFilterInterceptor(); - if (eventFilterInterceptor == null) return true; - - if (!eventFilterInterceptor.filterEventType(eventBuilder.getEventType())) { - Logger.w(TAG, "filter [" + eventBuilder.getEventType() + "] event by type"); - return false; + public void cacheEventToTrackMain(BaseEvent.BaseBuilder eventBuilder) { + if (coreConfiguration == null || !coreConfiguration.isDataCollectionEnabled()) { + Logger.w(TAG, "cache event before sdk init: " + eventBuilder.getEventType()); + caches.add(eventBuilder); + } else { + postEventToTrackMain(eventBuilder); } + } - String eventPath = getEventPath(eventBuilder); - if (!TextUtils.isEmpty(eventPath) && !eventFilterInterceptor.filterEventPath(eventPath)) { - Logger.w(TAG, "filter [" + eventBuilder.getEventType() + "] event by path=" + eventPath); - return false; + /** + * cache events in the current thread + */ + public void cacheEventSync(final BaseEvent.BaseBuilder eventBuilder) { + if (coreConfiguration == null || !coreConfiguration.isDataCollectionEnabled()) { + Logger.w(TAG, "cache event before sdk init: " + eventBuilder.getEventType()); + caches.add(eventBuilder); + } else { + sendEventSync(eventBuilder); } + } - String eventName = getEventName(eventBuilder); - if (!TextUtils.isEmpty(eventName) && !eventFilterInterceptor.filterEventName(eventName)) { - Logger.w(TAG, "filter [CUSTOM] event by name=" + eventName); - return false; + public void postActionToTrackMain(Runnable runnable) { + if (mainHandler.getLooper() == Looper.myLooper()) { + runnable.run(); + return; } - - Map filterFields = eventFilterInterceptor.filterEventField(eventBuilder.getEventType(), eventBuilder.getFilterMap()); - eventBuilder.filterFieldProperty(filterFields); - - return true; + mainHandler.post(runnable); } - String getEventName(BaseEvent.BaseBuilder eventBuilder) { - if (eventBuilder instanceof CustomEvent.Builder) { - return ((CustomEvent.Builder) eventBuilder).getEventName(); - } - return null; + private void onGenerateGEvent(BaseEvent.BaseBuilder gEvent) { + BaseEvent event = eventBuilderProvider.onGenerateGEvent(gEvent); + if (event != null) saveEvent(event); } - String getEventPath(BaseEvent.BaseBuilder eventBuilder) { - if (eventBuilder instanceof PageEvent.Builder) { - return ((PageEvent.Builder) eventBuilder).getPath(); - } - if (eventBuilder instanceof ViewElementEvent.Builder) { - return ((ViewElementEvent.Builder) eventBuilder).getPath(); + @TrackThread + private void saveEvent(GEvent event) { + if (event instanceof BaseEvent) { + Logger.printJson(TAG, "save: event, type is " + event.getEventType(), EventBuilderProvider.toJson((BaseEvent) event).toString()); } - if (eventBuilder instanceof PageLevelCustomEvent.Builder) { - return ((PageLevelCustomEvent.Builder) eventBuilder).getPath(); + if (!persistentDataProvider.isSendVisitAfterRefreshSessionId()) { + // we should resend visitEvent when sessionId refreshed + sessionProvider.generateVisit(); } - return null; + eventSender.sendEvent(event); } - - public void removeEventBuildInterceptor(EventBuildInterceptor interceptor) { - synchronized (mEventBuildInterceptors) { - if (interceptor != null) { - mEventBuildInterceptors.remove(interceptor); - } - } + public synchronized Activity getForegroundActivity() { + if (activityStateProvider == null) return null; + return activityStateProvider.getForegroundActivity(); } - public void addEventBuildInterceptor(EventBuildInterceptor interceptor) { - synchronized (mEventBuildInterceptors) { - boolean needsAdd = true; - Iterator refIter = mEventBuildInterceptors.iterator(); - while (refIter.hasNext()) { - EventBuildInterceptor storedInterceptor = refIter.next(); - if (null == storedInterceptor) { - refIter.remove(); - } else if (storedInterceptor == interceptor) { - needsAdd = false; - } - } - if (needsAdd) { - mEventBuildInterceptors.add(interceptor); - } - } + public Context getContext() { + return context; } - private void dispatchEventWillBuild(BaseEvent.BaseBuilder eventBuilder) { - synchronized (mEventBuildInterceptors) { - Iterator refIter = mEventBuildInterceptors.iterator(); - while (refIter.hasNext()) { - EventBuildInterceptor interceptor = refIter.next(); - if (null == interceptor) { - refIter.remove(); - } else { - try { - interceptor.eventWillBuild(eventBuilder); - } catch (Exception e) { - Logger.e(TAG, e); - } - } - } - } - } - private void dispatchEventDidBuild(GEvent event) { - synchronized (mEventBuildInterceptors) { - Iterator refIter = mEventBuildInterceptors.iterator(); - while (refIter.hasNext()) { - EventBuildInterceptor interceptor = refIter.next(); - if (null == interceptor) { - refIter.remove(); - } else { - try { - interceptor.eventDidBuild(event); - } catch (Exception e) { - Logger.e(TAG, e); - } - } - } + public void runOnUiThread(Runnable r) { + if (runningOnUiThread()) { + r.run(); + } else { + uiHandler.post(r); } } - @TrackThread - private void saveEvent(GEvent event) { - if (event instanceof BaseEvent) { - Logger.printJson(TAG, "save: event, type is " + event.getEventType(), EventStateProvider.get().toJson((BaseEvent) event).toString()); - } - if (!PersistentDataProvider.get().isSendVisitAfterRefreshSessionId()) { - // we should resend visitEvent when sessionId refreshed - SessionProvider.get().generateVisit(); - } - mEventSender.sendEvent(event); + public boolean runningOnUiThread() { + return uiHandler.getLooper() == Looper.myLooper(); } - private class H extends Handler { - private H(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - if (msg.what == MSG_INIT_SDK) { - initSDK(); - return; - } - - throw new IllegalStateException("Unexpected value: " + msg.what); - } + public void postOnUiThreadDelayed(Runnable task, long delayMillis) { + uiHandler.postDelayed(task, delayMillis); } + public void removeOnUiThreadCallbacks(Runnable task) { + uiHandler.removeCallbacks(task); + } } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/async/EmptyDisposable.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/async/EmptyDisposable.java deleted file mode 100644 index df58cbc4..00000000 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/async/EmptyDisposable.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.growingio.android.sdk.track.async; - -enum EmptyDisposable implements Disposable { - INSTANCE; - - @Override - public void dispose() { - - } - - @Override - public boolean isDisposed() { - return this == INSTANCE; - } -} diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/async/HandlerDisposable.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/async/HandlerDisposable.java deleted file mode 100644 index 245f9a6a..00000000 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/async/HandlerDisposable.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.growingio.android.sdk.track.async; - - -import android.os.Handler; -import android.os.Looper; -import android.os.Message; - -public class HandlerDisposable implements Disposable { - private final Handler mHandler; - private volatile boolean mDisposed; - - public HandlerDisposable() { - mHandler = new Handler(Looper.getMainLooper()); - } - - public Disposable schedule(Runnable run, long timeout) { - if (mDisposed) { - return EmptyDisposable.INSTANCE; - } - - ScheduledRunnable scheduled = new ScheduledRunnable(mHandler, run); - - Message message = Message.obtain(mHandler, scheduled); - message.obj = this; - - mHandler.sendMessageDelayed(message, timeout); - - return scheduled; - } - - @Override - public void dispose() { - mDisposed = true; - mHandler.removeCallbacksAndMessages(this); - } - - @Override - public boolean isDisposed() { - return mDisposed; - } - - private static final class ScheduledRunnable implements Runnable, Disposable { - private final Handler mScheduledHandler; - private final Runnable mDelegate; - - private volatile boolean mScheduledDisposed; // Tracked solely for isDisposed(). - - ScheduledRunnable(Handler handler, Runnable delegate) { - mScheduledHandler = handler; - mDelegate = delegate; - } - - @Override - public void run() { - mScheduledDisposed = true; - mDelegate.run(); - } - - @Override - public void dispose() { - mScheduledHandler.removeCallbacks(this); - mScheduledDisposed = true; - } - - @Override - public boolean isDisposed() { - return mScheduledDisposed; - } - } -} diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/ActivateEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/ActivateEvent.java index b1210616..0d18a039 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/ActivateEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/ActivateEvent.java @@ -1,25 +1,25 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.events; import android.text.TextUtils; import androidx.annotation.Nullable; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.events.base.BaseAttributesEvent; import com.growingio.android.sdk.track.providers.DeviceInfoProvider; import com.growingio.sdk.annotation.json.JsonAlias; @@ -127,12 +127,12 @@ public Builder setAdvertData(String linkId, String clickId, String clickTm, Stri } @Override - public void readPropertyInTrackThread() { - super.readPropertyInTrackThread(); - DeviceInfoProvider deviceInfo = DeviceInfoProvider.get(); + public void readPropertyInTrackThread(TrackerContext context) { + super.readPropertyInTrackThread(context); + DeviceInfoProvider deviceInfo = context.getDeviceInfoProvider(); oaid = deviceInfo.getOaid(); googleId = deviceInfo.getGoogleAdId(); - ua = DeviceInfoProvider.get().getUserAgent(); + ua = deviceInfo.getUserAgent(); androidId = deviceInfo.getAndroidId(); imei = deviceInfo.getImei(); } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/AppClosedEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/AppClosedEvent.java index c383d6d9..0154f8f8 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/AppClosedEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/AppClosedEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events; import com.growingio.android.sdk.track.events.base.BaseEvent; @@ -37,10 +36,5 @@ public Builder() { public AppClosedEvent build() { return new AppClosedEvent(this); } - - @Override - public void readPropertyInTrackThread() { - super.readPropertyInTrackThread(); - } } } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/AttributesBuilder.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/AttributesBuilder.java index 509fbc2b..c513ff80 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/AttributesBuilder.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/AttributesBuilder.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.events; import android.util.SparseArray; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/AutotrackEventType.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/AutotrackEventType.java index a57e201a..633978b7 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/AutotrackEventType.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/AutotrackEventType.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events; public class AutotrackEventType { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/ConversionVariablesEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/ConversionVariablesEvent.java index 5ae3d008..0df79e4b 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/ConversionVariablesEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/ConversionVariablesEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events; import com.growingio.android.sdk.track.events.base.BaseAttributesEvent; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/CustomEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/CustomEvent.java index 616684eb..c3ef304a 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/CustomEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/CustomEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events; import com.growingio.android.sdk.track.events.base.BaseAttributesEvent; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/EventBuildInterceptor.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/EventBuildInterceptor.java index 6976f043..6871b94b 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/EventBuildInterceptor.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/EventBuildInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events; import com.growingio.android.sdk.track.events.base.BaseEvent; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/EventFilterInterceptor.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/EventFilterInterceptor.java index 73b9d18e..920697a3 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/EventFilterInterceptor.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/EventFilterInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events; import java.util.Map; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/LoginUserAttributesEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/LoginUserAttributesEvent.java index 22c200ce..e9327394 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/LoginUserAttributesEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/LoginUserAttributesEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events; import com.growingio.android.sdk.track.events.base.BaseAttributesEvent; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/PageEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/PageEvent.java index ef399d15..80b1f0a7 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/PageEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/PageEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events; import android.content.res.Configuration; @@ -108,11 +107,11 @@ public PageEvent build() { } @Override - public void readPropertyInTrackThread() { + public void readPropertyInTrackThread(TrackerContext context) { if (orientation == null) { - orientation = TrackerContext.get().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT ? PageEvent.ORIENTATION_PORTRAIT : PageEvent.ORIENTATION_LANDSCAPE; + orientation = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT ? PageEvent.ORIENTATION_PORTRAIT : PageEvent.ORIENTATION_LANDSCAPE; } - super.readPropertyInTrackThread(); + super.readPropertyInTrackThread(context); } public Builder setTimestamp(long timestamp) { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/PageLevelCustomEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/PageLevelCustomEvent.java index deddd877..989ec026 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/PageLevelCustomEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/PageLevelCustomEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events; import com.growingio.sdk.annotation.json.JsonSerializer; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/TrackEventGenerator.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/TrackEventGenerator.java index 3887646c..5d42a307 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/TrackEventGenerator.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/TrackEventGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events; import com.growingio.android.sdk.track.TrackMainThread; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/TrackEventType.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/TrackEventType.java index 822437b7..943481b2 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/TrackEventType.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/TrackEventType.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events; public class TrackEventType { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/ViewElementEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/ViewElementEvent.java index 5a8392d4..cbeb8af8 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/ViewElementEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/ViewElementEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events; +import androidx.annotation.IntRange; import androidx.annotation.Nullable; import com.growingio.android.sdk.track.events.base.BaseAttributesEvent; @@ -34,6 +34,7 @@ public class ViewElementEvent extends BaseAttributesEvent { * keep for v3.0 sdk */ @Deprecated + @IntRange(from = 0) private final long pageShowTimestamp; private final String xpath; private final int index; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/VisitEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/VisitEvent.java index 5184b5da..586e50a5 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/VisitEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/VisitEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events; import androidx.annotation.Nullable; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.events.base.BaseEvent; import com.growingio.android.sdk.track.providers.DeviceInfoProvider; import com.growingio.sdk.annotation.json.JsonSerializer; @@ -90,10 +90,10 @@ public Builder() { } @Override - public void readPropertyInTrackThread() { - super.readPropertyInTrackThread(); + public void readPropertyInTrackThread(TrackerContext context) { + super.readPropertyInTrackThread(context); - DeviceInfoProvider deviceInfo = DeviceInfoProvider.get(); + DeviceInfoProvider deviceInfo = context.getDeviceInfoProvider(); imei = deviceInfo.getImei(); androidId = deviceInfo.getAndroidId(); oaid = deviceInfo.getOaid(); diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/VisitorAttributesEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/VisitorAttributesEvent.java index 4e79c448..ca5b2d21 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/VisitorAttributesEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/VisitorAttributesEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events; import com.growingio.android.sdk.track.events.base.BaseAttributesEvent; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/base/BaseAttributesEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/base/BaseAttributesEvent.java index 56b783c5..9a81d5f2 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/base/BaseAttributesEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/base/BaseAttributesEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events.base; import androidx.annotation.Nullable; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/base/BaseEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/base/BaseEvent.java index 7b9e4562..aad18294 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/base/BaseEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/base/BaseEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events.base; -import android.content.Context; import android.text.TextUtils; import androidx.annotation.FloatRange; @@ -26,14 +24,13 @@ import com.growingio.android.sdk.track.SDKConfig; import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.events.helper.DefaultEventFilterInterceptor; -import com.growingio.android.sdk.track.ipc.PersistentDataProvider; +import com.growingio.android.sdk.track.providers.PersistentDataProvider; import com.growingio.android.sdk.track.listener.TrackThread; import com.growingio.android.sdk.track.middleware.GEvent; import com.growingio.android.sdk.track.providers.ActivityStateProvider; import com.growingio.android.sdk.track.providers.AppInfoProvider; import com.growingio.android.sdk.track.providers.ConfigurationProvider; import com.growingio.android.sdk.track.providers.DeviceInfoProvider; -import com.growingio.android.sdk.track.providers.SessionProvider; import com.growingio.android.sdk.track.providers.UserInfoProvider; import com.growingio.android.sdk.track.utils.ConstantPool; import com.growingio.android.sdk.track.utils.NetworkUtil; @@ -269,13 +266,11 @@ public static abstract class BaseBuilder { protected BaseBuilder(String eventType) { this.eventType = eventType; platform = ConstantPool.ANDROID; - platformVersion = DeviceInfoProvider.get().getOperatingSystemVersion(); } @Deprecated protected BaseBuilder() { platform = ConstantPool.ANDROID; - platformVersion = DeviceInfoProvider.get().getOperatingSystemVersion(); } protected Map mFilterField = new HashMap<>(); @@ -316,42 +311,45 @@ private boolean isEventSequenceIdType(String type) { } @TrackThread - public void readPropertyInTrackThread() { + public void readPropertyInTrackThread(TrackerContext context) { if (eventType == null) eventType = getEventType(); - appState = ActivityStateProvider.get().getForegroundActivity() != null ? APP_STATE_FOREGROUND : APP_STATE_BACKGROUND; - urlScheme = ConfigurationProvider.core().getUrlScheme(); + ActivityStateProvider activityStateProvider = context.getActivityStateProvider(); + appState = activityStateProvider.getForegroundActivity() != null ? APP_STATE_FOREGROUND : APP_STATE_BACKGROUND; + // filter field area + if (!getFieldDefault(BaseField.APP_STATE)) { + appState = null; + } - timestamp = (timestamp != 0) ? timestamp : System.currentTimeMillis(); - deviceId = DeviceInfoProvider.get().getDeviceId(); - sessionId = PersistentDataProvider.get().getSessionId(); - userKey = UserInfoProvider.get().getLoginUserKey(); - userId = UserInfoProvider.get().getLoginUserId(); + PersistentDataProvider persistentDataProvider = context.getProvider(PersistentDataProvider.class); + sessionId = persistentDataProvider.getSessionId(); if (isEventSequenceIdType(eventType)) { - eventSequenceId = PersistentDataProvider.get().getGlobalEventSequenceIdAndIncrement(); + eventSequenceId = persistentDataProvider.getGlobalEventSequenceIdAndIncrement(); } else { eventSequenceId = 0L; } - dataSourceId = ConfigurationProvider.core().getDataSourceId(); + UserInfoProvider userInfoProvider = context.getUserInfoProvider(); + userKey = userInfoProvider.getLoginUserKey(); + userId = userInfoProvider.getLoginUserId(); - // filter field area - if (!getFieldDefault(BaseField.APP_STATE)) { - appState = null; - } - - Context context = TrackerContext.get().getApplicationContext(); - networkState = getFieldDefault(BaseField.NETWORK_STATE) ? NetworkUtil.getActiveNetworkState(context).getNetworkName() : null; + ConfigurationProvider configurationProvider = context.getConfigurationProvider(); + urlScheme = configurationProvider.core().getUrlScheme(); + dataSourceId = configurationProvider.core().getDataSourceId(); + appChannel = getFieldDefault(BaseField.APP_CHANNEL) ? configurationProvider.core().getChannel() : null; - DeviceInfoProvider deviceInfo = DeviceInfoProvider.get(); + DeviceInfoProvider deviceInfo = context.getDeviceInfoProvider(); + platformVersion = deviceInfo.getOperatingSystemVersion(); + deviceId = deviceInfo.getDeviceId(); screenHeight = getFieldDefault(BaseField.SCREEN_HEIGHT) ? deviceInfo.getScreenHeight() : 0; screenWidth = getFieldDefault(BaseField.SCREEN_WIDTH) ? deviceInfo.getScreenWidth() : 0; deviceBrand = getFieldDefault(BaseField.DEVICE_BRAND) ? deviceInfo.getDeviceBrand() : null; deviceModel = getFieldDefault(BaseField.DEVICE_MODEL) ? deviceInfo.getDeviceModel() : null; deviceType = getFieldDefault(BaseField.DEVICE_TYPE) ? deviceInfo.getDeviceType() : null; + latitude = getFieldDefault(BaseField.LATITUDE) ? deviceInfo.getLatitude() : 0; + longitude = getFieldDefault(BaseField.LONGITUDE) ? deviceInfo.getLongitude() : 0; - AppInfoProvider appInfo = AppInfoProvider.get(); - appChannel = getFieldDefault(BaseField.APP_CHANNEL) ? appInfo.getAppChannel() : null; + AppInfoProvider appInfo = context.getProvider(AppInfoProvider.class); appName = getFieldDefault(BaseField.APP_NAME) ? appInfo.getAppName() : null; appVersion = getFieldDefault(BaseField.APP_VERSION) ? appInfo.getAppVersion() : null; if (domain == null || domain.isEmpty()) { @@ -359,10 +357,8 @@ public void readPropertyInTrackThread() { domain = appInfo.getPackageName(); } - SessionProvider session = SessionProvider.get(); - latitude = getFieldDefault(BaseField.LATITUDE) ? session.getLatitude() : 0; - longitude = getFieldDefault(BaseField.LONGITUDE) ? session.getLongitude() : 0; - + timestamp = (timestamp != 0) ? timestamp : System.currentTimeMillis(); + networkState = getFieldDefault(BaseField.NETWORK_STATE) ? NetworkUtil.getActiveNetworkState(context).getNetworkName() : null; sdkVersion = getFieldDefault(BaseField.SDK_VERSION) ? SDKConfig.SDK_VERSION : null; language = getFieldDefault(BaseField.LANGUAGE) ? Locale.getDefault().getLanguage() : null; } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/base/BaseField.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/base/BaseField.java index ef22a6a5..bfee2e44 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/base/BaseField.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/base/BaseField.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.events.base; /** diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/helper/DefaultEventFilterInterceptor.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/helper/DefaultEventFilterInterceptor.java index bc7314a2..b0f9107b 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/helper/DefaultEventFilterInterceptor.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/helper/DefaultEventFilterInterceptor.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.events.helper; import com.growingio.android.sdk.track.events.AutotrackEventType; @@ -65,7 +64,6 @@ public static class FilterEventType { public static final String CONVERSION_VARIABLES = TrackEventType.CONVERSION_VARIABLES; public static final String APP_CLOSED = TrackEventType.APP_CLOSED; public static final String PAGE = AutotrackEventType.PAGE; - public static final String PAGE_ATTRIBUTES = AutotrackEventType.PAGE_ATTRIBUTES; public static final String VIEW_CLICK = AutotrackEventType.VIEW_CLICK; public static final String VIEW_CHANGE = AutotrackEventType.VIEW_CHANGE; public static final String FORM_SUBMIT = TrackEventType.FORM_SUBMIT; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/helper/JsonSerializable.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/helper/JsonSerializable.java index 576ef538..7ddfc40a 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/helper/JsonSerializable.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/helper/JsonSerializable.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.events.helper; import org.json.JSONObject; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/hybrid/HybridCustomEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/hybrid/HybridCustomEvent.java index 8781e5fc..9809ac00 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/hybrid/HybridCustomEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/hybrid/HybridCustomEvent.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.events.hybrid; import com.growingio.android.sdk.track.events.PageLevelCustomEvent; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/hybrid/HybridPageEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/hybrid/HybridPageEvent.java index 7973021f..cc5b8f89 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/hybrid/HybridPageEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/hybrid/HybridPageEvent.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.events.hybrid; import com.growingio.android.sdk.track.events.PageEvent; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/hybrid/HybridViewElementEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/hybrid/HybridViewElementEvent.java index f5e4ace4..f2876b67 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/hybrid/HybridViewElementEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/events/hybrid/HybridViewElementEvent.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.events.hybrid; import com.growingio.android.sdk.track.events.ViewElementEvent; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/IDataSharer.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/IDataSharer.java index 565d987a..6eb24cfb 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/IDataSharer.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/IDataSharer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.ipc; import androidx.annotation.Nullable; @@ -60,4 +59,6 @@ public interface IDataSharer { int getAndDecrementInt(String key, int startValue); int getAndDelInt(String key, int delta, int startValue); + + void release(); } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/MultiProcessDataSharer.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/MultiProcessDataSharer.java index 2613d246..9e68c400 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/MultiProcessDataSharer.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/MultiProcessDataSharer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.ipc; import android.content.Context; @@ -66,6 +65,18 @@ public void run() { }.start(); } + @Override + public void release() { + if (randomAccessFile == null) return; + try { + mMappedByteBuffer.clear(); + mFileChannel.close(); + randomAccessFile.close(); + } catch (IOException e) { + Logger.e(TAG, e.getMessage()); + } + } + // synchronized 是可重入锁 // 加上synchronized 规避sonar检查wait() 在锁中调用 private synchronized void awaitLoadedLocked() { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/ProcessLock.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/ProcessLock.java index 77c2e280..764d8d73 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/ProcessLock.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/ProcessLock.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.ipc; import android.content.Context; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/SharedEntry.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/SharedEntry.java index e5e54086..8178be71 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/SharedEntry.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/SharedEntry.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.ipc; import androidx.annotation.Nullable; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/async/Callback.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/Callback.java similarity index 80% rename from growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/async/Callback.java rename to growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/Callback.java index bf412348..1aa414ba 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/async/Callback.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/Callback.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,11 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.growingio.android.sdk.track.listener; -package com.growingio.android.sdk.track.async; - +/** + *

+ * + * @author cpacm 2023/7/27 + */ public interface Callback { void onSuccess(T result); void onFailed(); -} +} \ No newline at end of file diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/IActivityLifecycle.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/IActivityLifecycle.java index ee212844..d3b439a7 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/IActivityLifecycle.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/IActivityLifecycle.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.listener; import com.growingio.android.sdk.track.listener.event.ActivityLifecycleEvent; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/ListenerContainer.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/ListenerContainer.java index 13c63787..4f0448a6 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/ListenerContainer.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/ListenerContainer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.listener; import com.growingio.android.sdk.track.log.Logger; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/ListenerDispatcher.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/ListenerDispatcher.java index 67146a2e..d909ba64 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/ListenerDispatcher.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/ListenerDispatcher.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.listener; import com.growingio.android.sdk.track.log.Logger; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/OnConfigurationChangeListener.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/OnConfigurationChangeListener.java index 713f44e2..d8462b95 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/OnConfigurationChangeListener.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/OnConfigurationChangeListener.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.listener; /** diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/OnTrackMainInitSDKCallback.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/OnTrackMainInitSDKCallback.java deleted file mode 100644 index 911ecfab..00000000 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/OnTrackMainInitSDKCallback.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.growingio.android.sdk.track.listener; - -public interface OnTrackMainInitSDKCallback { - @TrackThread - void onTrackMainInitSDK(); -} diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/OnUserIdChangedListener.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/OnUserIdChangedListener.java index f11e634f..063a5c18 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/OnUserIdChangedListener.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/OnUserIdChangedListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.listener; public interface OnUserIdChangedListener { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/TrackThread.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/TrackThread.java index 50fdb6b7..3c7b1162 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/TrackThread.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/TrackThread.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.listener; import java.lang.annotation.Documented; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/event/ActivityLifecycleEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/event/ActivityLifecycleEvent.java index 38255c79..e402a440 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/event/ActivityLifecycleEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/listener/event/ActivityLifecycleEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.listener.event; import android.app.Activity; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/BaseLogger.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/BaseLogger.java index b243f18a..22ac2f14 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/BaseLogger.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/BaseLogger.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.log; import android.util.Log; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/CacheLogger.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/CacheLogger.java index bf1fffe3..f0e5d595 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/CacheLogger.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/CacheLogger.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.log; import java.util.ArrayList; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/CircularFifoQueue.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/CircularFifoQueue.java index 2c840d5e..048dd9b5 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/CircularFifoQueue.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/CircularFifoQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.log; import java.io.IOException; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/DebugLogger.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/DebugLogger.java index 8006d21c..f6adeb97 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/DebugLogger.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/DebugLogger.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.log; import android.util.Log; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/ErrorLogger.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/ErrorLogger.java index 9807870e..0c5b65dd 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/ErrorLogger.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/ErrorLogger.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.log; import android.util.Log; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/ILogger.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/ILogger.java index 392c40c1..9a9c24d0 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/ILogger.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/ILogger.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.log; public interface ILogger { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/LogItem.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/LogItem.java index 004340d7..21dc9cfe 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/LogItem.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/LogItem.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.log; public class LogItem { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/Logger.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/Logger.java index 976becc5..112d0cd5 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/Logger.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/log/Logger.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.log; import org.json.JSONArray; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventDatabase.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventDatabase.java index 6cc0007d..6b20c707 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventDatabase.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventDatabase.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware; import java.util.ArrayList; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventDbResult.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventDbResult.java index f6b21c70..3425ca12 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventDbResult.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventDbResult.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware; /** diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventFlutter.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventFlutter.java index ca79295c..1b15354f 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventFlutter.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventFlutter.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware; /** diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventHttpSender.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventHttpSender.java index 74b1d307..15297456 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventHttpSender.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventHttpSender.java @@ -1,30 +1,29 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware; import android.text.TextUtils; -import com.growingio.android.sdk.CoreConfiguration; import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.middleware.http.EventEncoder; import com.growingio.android.sdk.track.middleware.http.EventResponse; import com.growingio.android.sdk.track.middleware.http.EventUrl; import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.modelloader.ModelLoader; +import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import com.growingio.android.sdk.track.providers.ConfigurationProvider; public class EventHttpSender implements IEventNetSender { @@ -32,15 +31,17 @@ public class EventHttpSender implements IEventNetSender { private final String mProjectId; private final String mServerHost; + private final TrackerRegistry trackerRegistry; - public EventHttpSender() { - CoreConfiguration configuration = ConfigurationProvider.core(); - mProjectId = configuration.getProjectId(); - mServerHost = configuration.getDataCollectionServerHost(); + public EventHttpSender(TrackerContext context) { + ConfigurationProvider configurationProvider = context.getConfigurationProvider(); + this.trackerRegistry = context.getRegistry(); + mProjectId = configurationProvider.core().getProjectId(); + mServerHost = configurationProvider.core().getDataCollectionServerHost(); } private ModelLoader getNetworkModelLoader() { - return TrackerContext.get().getRegistry().getModelLoader(EventUrl.class, EventResponse.class); + return trackerRegistry.getModelLoader(EventUrl.class, EventResponse.class); } @Override @@ -62,13 +63,13 @@ public SendResponse send(byte[] events, String mediaType) { .setBodyData(events); if (!TextUtils.isEmpty(mediaType)) eventUrl.setMediaType(mediaType); //data encoder - https://codes.growingio.com/w/api_v3_interface/ - EventEncoder encoder = TrackerContext.get().executeData(new EventEncoder(eventUrl), EventEncoder.class, EventEncoder.class); + EventEncoder encoder = trackerRegistry.executeData(new EventEncoder(eventUrl), EventEncoder.class, EventEncoder.class); if (encoder != null) { eventUrl = encoder.getEventUrl(); } byte[] data = eventUrl.getRequestBody(); - printEventUrlLog(eventUrl); + Logger.d(TAG, "send event to url: " + eventUrl.toString()); ModelLoader.LoadData loadData = getNetworkModelLoader().buildLoadData(eventUrl); @@ -82,10 +83,4 @@ public SendResponse send(byte[] events, String mediaType) { long totalUsed = data == null ? 0L : data.length; return new SendResponse(successful, totalUsed); } - - private void printEventUrlLog(EventUrl eventUrl) { - if (ConfigurationProvider.core().isDebugEnabled()) { - Logger.d(TAG, "send event to url: " + eventUrl.toString()); - } - } } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventSender.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventSender.java index 51fb2a49..59a186e1 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventSender.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/EventSender.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.middleware; import android.annotation.SuppressLint; @@ -27,10 +26,10 @@ import androidx.annotation.NonNull; -import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.ipc.ProcessLock; import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.modelloader.ModelLoader; +import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import com.growingio.android.sdk.track.utils.NetworkUtil; import java.text.SimpleDateFormat; @@ -59,6 +58,8 @@ public class EventSender { private int mCacheEventNum = 0; + private final TrackerRegistry mRegistry; + /** * 事件发送管理类 * @@ -67,8 +68,9 @@ public class EventSender { * @param cellularDataLimit 事件发送的移动网络的流量限制,单位 MB */ @SuppressLint("WrongConstant") - public EventSender(IEventNetSender sender, long dataUploadInterval, long cellularDataLimit) { - mContext = TrackerContext.get().getApplicationContext(); + public EventSender(Context context, TrackerRegistry registry, IEventNetSender sender, long dataUploadInterval, long cellularDataLimit) { + mContext = context.getApplicationContext(); + mRegistry = registry; mCellularDataLimit = cellularDataLimit * 1024L * 1024L; mDataUploadInterval = dataUploadInterval * 1000L; mEventNetSender = sender; @@ -80,7 +82,7 @@ public EventSender(IEventNetSender sender, long dataUploadInterval, long cellula } private ModelLoader getDatabaseModelLoader() { - return TrackerContext.get().getRegistry().getModelLoader(EventDatabase.class, EventDbResult.class); + return mRegistry.getModelLoader(EventDatabase.class, EventDbResult.class); } private EventDbResult databaseOperation(EventDatabase eventDatabase) { @@ -220,15 +222,20 @@ && mCellularDataLimit < todayBytes(0)) { } EventDbResult dbResult = databaseOperation(EventDatabase.query(policy, numOfMaxEventsPerRequest())); if (dbResult.isSuccess() && dbResult.getSum() > 0) { - SendResponse sendResponse = mEventNetSender.send(dbResult.getData(), dbResult.getMediaType()); - succeeded = sendResponse.isSucceeded(); - if (succeeded) { - String eventType = dbResult.getEventType(); - databaseOperation(EventDatabase.delete(dbResult.getLastId(), policy, eventType)); - if (networkState.isMobileData()) { - todayBytes(sendResponse.getUsedBytes()); + if (mEventNetSender == null) { + succeeded = false; + } else { + SendResponse sendResponse = mEventNetSender.send(dbResult.getData(), dbResult.getMediaType()); + succeeded = sendResponse.isSucceeded(); + if (succeeded) { + String eventType = dbResult.getEventType(); + databaseOperation(EventDatabase.delete(dbResult.getLastId(), policy, eventType)); + if (networkState.isMobileData()) { + todayBytes(sendResponse.getUsedBytes()); + } } } + } else { break; } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/GEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/GEvent.java index 6a482c21..348d2d3c 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/GEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/GEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.middleware; import androidx.annotation.IntDef; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/IEventNetSender.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/IEventNetSender.java index 59e6be8c..29e3aa4b 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/IEventNetSender.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/IEventNetSender.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.middleware; public interface IEventNetSender { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/OaidHelper.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/OaidHelper.java index 43f90fad..ea4e1961 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/OaidHelper.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/OaidHelper.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware; public class OaidHelper { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/SendResponse.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/SendResponse.java index 36ca60a8..2b9bfd36 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/SendResponse.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/SendResponse.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.middleware; public class SendResponse { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/Serializer.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/Serializer.java index 8f04d16f..7b9f8182 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/Serializer.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/Serializer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.middleware; import com.growingio.android.sdk.track.log.Logger; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/advert/Activate.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/advert/Activate.java index d3027073..5a26170d 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/advert/Activate.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/advert/Activate.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware.advert; import android.net.Uri; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/advert/AdvertResult.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/advert/AdvertResult.java index fd0a14e8..d02cc19b 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/advert/AdvertResult.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/advert/AdvertResult.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware.advert; /** diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/advert/DeepLink.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/advert/DeepLink.java index 43e668af..2215ee6c 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/advert/DeepLink.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/advert/DeepLink.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware.advert; import android.net.Uri; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/advert/DeepLinkCallback.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/advert/DeepLinkCallback.java index 86778510..bb0baed1 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/advert/DeepLinkCallback.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/advert/DeepLinkCallback.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware.advert; import java.util.Map; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/apm/EventApm.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/apm/EventApm.java index f88e5aec..89cc34ee 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/apm/EventApm.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/apm/EventApm.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware.apm; /** diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/format/EventByteArray.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/format/EventByteArray.java index 3de691cf..49633e97 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/format/EventByteArray.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/format/EventByteArray.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware.format; /** diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/format/EventFormatData.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/format/EventFormatData.java index c24d1641..17591b43 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/format/EventFormatData.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/format/EventFormatData.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware.format; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/format/FormatDataFetcher.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/format/FormatDataFetcher.java index a926b5e5..9a98f16a 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/format/FormatDataFetcher.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/format/FormatDataFetcher.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware.format; import com.growingio.android.sdk.track.middleware.GEvent; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/http/EventEncoder.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/http/EventEncoder.java index 6e7312ec..796be082 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/http/EventEncoder.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/http/EventEncoder.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware.http; public class EventEncoder { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/http/EventResponse.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/http/EventResponse.java index a34adc84..dcde66b6 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/http/EventResponse.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/http/EventResponse.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware.http; import java.io.InputStream; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/http/EventUrl.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/http/EventUrl.java index 7828e8cc..d0d40ded 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/http/EventUrl.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/http/EventUrl.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware.http; import java.util.ArrayList; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/http/HttpDataFetcher.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/http/HttpDataFetcher.java index 9f9721df..c1536fd2 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/http/HttpDataFetcher.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/http/HttpDataFetcher.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware.http; import com.growingio.android.sdk.track.modelloader.LoadDataFetcher; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/hybrid/HybridBridge.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/hybrid/HybridBridge.java index 95b359e9..3accb5b3 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/hybrid/HybridBridge.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/hybrid/HybridBridge.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware.hybrid; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/hybrid/HybridDom.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/hybrid/HybridDom.java index d953a0a1..1b25f88c 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/hybrid/HybridDom.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/hybrid/HybridDom.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware.hybrid; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/hybrid/HybridJson.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/hybrid/HybridJson.java index 67a35664..550986aa 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/hybrid/HybridJson.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/hybrid/HybridJson.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware.hybrid; import org.json.JSONObject; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/Circler.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/webservice/Circler.java similarity index 95% rename from growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/Circler.java rename to growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/webservice/Circler.java index dbcb9102..64272e28 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/Circler.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/webservice/Circler.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,8 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.growingio.android.sdk.track.webservices; +package com.growingio.android.sdk.track.middleware.webservice; import java.util.List; import java.util.Map; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/Debugger.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/webservice/Debugger.java similarity index 92% rename from growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/Debugger.java rename to growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/webservice/Debugger.java index b9bdf751..1e5a4a8d 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/Debugger.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/webservice/Debugger.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,8 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.growingio.android.sdk.track.webservices; +package com.growingio.android.sdk.track.middleware.webservice; import java.util.HashMap; import java.util.Map; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/WebService.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/webservice/WebService.java similarity index 84% rename from growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/WebService.java rename to growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/webservice/WebService.java index c6c797d1..5aea2d84 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/WebService.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/middleware/webservice/WebService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,8 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.growingio.android.sdk.track.webservices; +package com.growingio.android.sdk.track.middleware.webservice; /** *

diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/DataFetcher.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/DataFetcher.java index 023872f2..a8e4ee05 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/DataFetcher.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/DataFetcher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.modelloader; /** diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/LoadDataFetcher.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/LoadDataFetcher.java index 593b9118..ba9d0856 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/LoadDataFetcher.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/LoadDataFetcher.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.modelloader; /** diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/ModelLoader.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/ModelLoader.java index 3b3e320c..2bd7353c 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/ModelLoader.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/ModelLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.modelloader; /** diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/ModelLoaderFactory.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/ModelLoaderFactory.java index 27ec6552..034e96ec 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/ModelLoaderFactory.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/ModelLoaderFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.modelloader; /** diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/ModelLoaderRegistry.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/ModelLoaderRegistry.java index e34bc2cd..8e7a9124 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/ModelLoaderRegistry.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/ModelLoaderRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.modelloader; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/TrackerRegistry.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/TrackerRegistry.java index 569bdd4c..bbed8b7a 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/TrackerRegistry.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/modelloader/TrackerRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.modelloader; /** @@ -50,4 +49,24 @@ public TrackerRegistry register(Class modelClass, Class void unregister(Class modelClass, Class dataClass) { modelLoaderRegistry.remove(modelClass, dataClass); } + + public Data executeData(Model model, Class modelClass, Class dataClass) { + ModelLoader modelLoader = getModelLoader(modelClass, dataClass); + if (modelLoader != null) { + return modelLoader.buildLoadData(model).fetcher.executeData(); + } + return null; + } + + public void loadData(Model model, Class modelClass, Class dataClass, LoadDataFetcher.DataCallback callback) { + ModelLoader modelLoader = getModelLoader(modelClass, dataClass); + if (modelLoader != null) { + DataFetcher fetcher = modelLoader.buildLoadData(model).fetcher; + if (fetcher instanceof LoadDataFetcher) { + ((LoadDataFetcher) fetcher).loadData(callback); + } + } else { + callback.onLoadFailed(new NullPointerException(String.format("please register %s component first", modelClass.getSimpleName()))); + } + } } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/ActivityStateProvider.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/ActivityStateProvider.java index 420c7a3e..9a101bea 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/ActivityStateProvider.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/ActivityStateProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,36 +13,69 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.providers; import android.app.Activity; import android.app.Application; +import android.content.Context; import android.content.Intent; import android.os.Bundle; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.listener.IActivityLifecycle; import com.growingio.android.sdk.track.listener.ListenerContainer; import com.growingio.android.sdk.track.listener.event.ActivityLifecycleEvent; +import com.growingio.android.sdk.track.log.Logger; +import com.growingio.android.sdk.track.utils.ActivityUtil; import com.growingio.android.sdk.track.utils.SysTrace; import java.lang.ref.WeakReference; -public class ActivityStateProvider extends ListenerContainer implements Application.ActivityLifecycleCallbacks { +public class ActivityStateProvider extends ListenerContainer implements Application.ActivityLifecycleCallbacks, TrackerLifecycleProvider { + private static final String TAG = "ActivityStateProvider"; private WeakReference mResumeActivity = new WeakReference<>(null); private WeakReference mForegroundActivity = new WeakReference<>(null); - private int mCurrentRootWindowsHashCode = -1; + private ConfigurationProvider configurationProvider; - private static class SingleInstance { - private static final ActivityStateProvider INSTANCE = new ActivityStateProvider(); - } + private WeakReference applicationWeakReference; - private ActivityStateProvider() { + ActivityStateProvider(Context context) { + if (context instanceof Application) { + Application application = (Application) context; + applicationWeakReference = new WeakReference<>(application); + application.registerActivityLifecycleCallbacks(this); + } else if (context instanceof Activity) { + Activity activity = (Activity) context; + applicationWeakReference = new WeakReference<>(activity.getApplication()); + activity.getApplication().registerActivityLifecycleCallbacks(this); + }/* else { + // inaccessible + }*/ } - public static ActivityStateProvider get() { - return SingleInstance.INSTANCE; + @Override + public void setup(TrackerContext context) { + configurationProvider = context.getConfigurationProvider(); + } + + public void makeupActivityLifecycle(Context context) { + if (context instanceof Activity) { + Activity activity = (Activity) context; + ActivityLifecycleEvent.EVENT_TYPE state = ActivityUtil.judgeContextState(activity); + if (state != null) { + Logger.i(TAG, "initSdk with Activity, makeup ActivityLifecycle before current state:" + state.name()); + if (state.compareTo(ActivityLifecycleEvent.EVENT_TYPE.ON_CREATED) >= 0) { + onActivityCreated(activity, null); + } + if (state.compareTo(ActivityLifecycleEvent.EVENT_TYPE.ON_STARTED) >= 0) { + onActivityStarted(activity); + } + if (state.compareTo(ActivityLifecycleEvent.EVENT_TYPE.ON_RESUMED) >= 0) { + onActivityResumed(activity); + } + } + } } private void dispatchActivityLifecycle(ActivityLifecycleEvent event) { @@ -86,15 +119,6 @@ private synchronized void setForegroundActivity(Activity activity) { mForegroundActivity = new WeakReference<>(activity); } - public int getCurrentRootWindowsHashCode() { - if (mCurrentRootWindowsHashCode == -1 - && mForegroundActivity != null && mForegroundActivity.get() != null) { - //该时间点, 用户理论上setContentView已经结束 - mCurrentRootWindowsHashCode = mForegroundActivity.get().getWindow().getDecorView().hashCode(); - } - return mCurrentRootWindowsHashCode; - } - public void registerActivityLifecycleListener(IActivityLifecycle lifecycle) { register(lifecycle); } @@ -105,30 +129,34 @@ public void unregisterActivityLifecycleListener(IActivityLifecycle lifecycle) { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { - SysTrace.beginSection("gio.ActivityOnCreate"); + boolean debug = configurationProvider.core().isDebugEnabled(); + SysTrace.beginSection("gio.ActivityOnCreate", debug); dispatchActivityLifecycle(ActivityLifecycleEvent.createOnCreatedEvent(activity, savedInstanceState)); - SysTrace.endSection(); + SysTrace.endSection(debug); } @Override public void onActivityStarted(Activity activity) { - SysTrace.beginSection("gio.onActivityStart"); + boolean debug = configurationProvider.core().isDebugEnabled(); + SysTrace.beginSection("gio.onActivityStart", debug); dispatchActivityLifecycle(ActivityLifecycleEvent.createOnStartedEvent(activity)); - SysTrace.endSection(); + SysTrace.endSection(debug); } @Override public void onActivityResumed(Activity activity) { - SysTrace.beginSection("gio.onActivityResumed"); + boolean debug = configurationProvider.core().isDebugEnabled(); + SysTrace.beginSection("gio.onActivityResumed", debug); dispatchActivityLifecycle(ActivityLifecycleEvent.createOnResumedEvent(activity)); - SysTrace.endSection(); + SysTrace.endSection(debug); } @Override public void onActivityPaused(Activity activity) { - SysTrace.beginSection("gio.onActivityPaused"); + boolean debug = configurationProvider.core().isDebugEnabled(); + SysTrace.beginSection("gio.onActivityPaused", debug); dispatchActivityLifecycle(ActivityLifecycleEvent.createOnPausedEvent(activity)); - SysTrace.endSection(); + SysTrace.endSection(debug); } @Override @@ -155,4 +183,13 @@ protected void singleAction(IActivityLifecycle listener, ActivityLifecycleEvent listener.onActivityLifecycle(action); } + @Override + public void shutdown() { + if (applicationWeakReference != null) { + Application application = applicationWeakReference.get(); + if (application != null) { + application.unregisterActivityLifecycleCallbacks(this); + } + } + } } \ No newline at end of file diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/AppInfoProvider.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/AppInfoProvider.java index 7304906c..d5ea0ddd 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/AppInfoProvider.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/AppInfoProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.providers; import android.content.Context; @@ -24,64 +23,57 @@ import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.log.Logger; -public class AppInfoProvider { +public class AppInfoProvider implements TrackerLifecycleProvider { private static final String TAG = "AppInfoProvider"; - private final Context mContext; + private String packageName; + private String appName; + private String appVersion; - private String mPackageName; - private String mAppName; - private String mAppVersion; - private String mAppChannel; + private Context context; - private static class SingleInstance { - private static final AppInfoProvider INSTANCE = new AppInfoProvider(); + AppInfoProvider() { } - private AppInfoProvider() { - mContext = TrackerContext.get().getApplicationContext(); + @Override + public void setup(TrackerContext context) { + this.context = context.getBaseContext(); } - public static AppInfoProvider get() { - return SingleInstance.INSTANCE; + @Override + public void shutdown() { + } public String getPackageName() { - if (TextUtils.isEmpty(mPackageName)) { - mPackageName = mContext.getPackageName(); + if (TextUtils.isEmpty(packageName)) { + packageName = context.getPackageName(); } - return mPackageName; + return packageName; } public String getAppName() { - if (TextUtils.isEmpty(mAppName)) { + if (TextUtils.isEmpty(appName)) { try { - PackageManager packageManager = mContext.getPackageManager(); - mAppName = packageManager.getApplicationLabel(mContext.getApplicationInfo()).toString(); + PackageManager packageManager = context.getPackageManager(); + appName = packageManager.getApplicationLabel(context.getApplicationInfo()).toString(); } catch (Exception e) { Logger.e(TAG, e); } } - return mAppName; + return appName; } public String getAppVersion() { - if (TextUtils.isEmpty(mAppVersion)) { - PackageManager packageManager = mContext.getPackageManager(); + if (TextUtils.isEmpty(appVersion)) { + PackageManager packageManager = context.getPackageManager(); try { - PackageInfo info = packageManager.getPackageInfo(mContext.getPackageName(), 0); - mAppVersion = info.versionName; + PackageInfo info = packageManager.getPackageInfo(context.getPackageName(), 0); + appVersion = info.versionName; } catch (PackageManager.NameNotFoundException e) { Logger.e(TAG, e); } } - return mAppVersion; - } - - public String getAppChannel() { - if (TextUtils.isEmpty(mAppChannel)) { - mAppChannel = ConfigurationProvider.core().getChannel(); - } - return mAppChannel; + return appVersion; } } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/ConfigurationProvider.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/ConfigurationProvider.java index bcc1ba06..dcba7553 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/ConfigurationProvider.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/ConfigurationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,31 +13,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.providers; import android.text.TextUtils; import com.growingio.android.sdk.CoreConfiguration; import com.growingio.android.sdk.Configurable; +import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.listener.ListenerDispatcher; import com.growingio.android.sdk.track.listener.OnConfigurationChangeListener; import com.growingio.android.sdk.track.log.DebugLogger; import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.utils.ConstantPool; import com.growingio.android.sdk.track.utils.ObjectUtils; -import com.growingio.android.sdk.track.utils.ThreadUtils; import java.util.HashMap; import java.util.Map; -public class ConfigurationProvider extends ListenerDispatcher { +public class ConfigurationProvider extends ListenerDispatcher implements TrackerLifecycleProvider { private static final String TAG = "ConfigurationProvider"; private final CoreConfiguration mCoreConfiguration; private final Map, Configurable> sModuleConfigs = new HashMap<>(); - private static ConfigurationProvider INSTANCE = null; - private ConfigurationProvider(CoreConfiguration coreConfiguration, Map, Configurable> moduleConfigs) { + ConfigurationProvider(CoreConfiguration coreConfiguration, Map, Configurable> moduleConfigs) { if (TextUtils.isEmpty(coreConfiguration.getProjectId())) { throw new IllegalStateException("ProjectId is NULL"); } @@ -53,7 +52,7 @@ private ConfigurationProvider(CoreConfiguration coreConfiguration, Map, Configurable> moduleConfigs) { - INSTANCE = new ConfigurationProvider(coreConfiguration, moduleConfigs); - } - - private static ConfigurationProvider empty() { - return new ConfigurationProvider(new CoreConfiguration(ConstantPool.UNKNOWN, ConstantPool.UNKNOWN), null); + @Override + public void setup(TrackerContext context) { + Logger.d(TAG, "Setup ConfigurationProvider"); } public void addConfiguration(Configurable config) { @@ -96,9 +82,9 @@ public void addConfiguration(Configurable config) { } } - public String getAllConfigurationInfo() { + public String printAllConfigurationInfo() { StringBuilder info = new StringBuilder(); - if (!core().isDebugEnabled()) { + if (!mCoreConfiguration.isDebugEnabled()) { return "GrowingSDK display config info only in debug environment."; } info.append(ObjectUtils.reflectToString(core())); @@ -116,10 +102,10 @@ public T getConfiguration(Class clazz) { return (T) sModuleConfigs.get(clazz); } - public static CoreConfiguration core() { - return get().mCoreConfiguration; - } + public CoreConfiguration core() { + return mCoreConfiguration; + } public void addConfigurationListener(OnConfigurationChangeListener listener) { register(listener); @@ -138,4 +124,9 @@ public void onDataCollectionChanged(boolean enable) { } } } + + @Override + public void shutdown() { + + } } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/DeepLinkProvider.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/DeepLinkProvider.java index f924bdea..3eaa1da3 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/DeepLinkProvider.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/DeepLinkProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.providers; import android.content.Intent; @@ -30,9 +29,10 @@ import com.growingio.android.sdk.track.middleware.advert.AdvertResult; import com.growingio.android.sdk.track.middleware.advert.DeepLinkCallback; import com.growingio.android.sdk.track.modelloader.LoadDataFetcher; -import com.growingio.android.sdk.track.webservices.Circler; -import com.growingio.android.sdk.track.webservices.Debugger; -import com.growingio.android.sdk.track.webservices.WebService; +import com.growingio.android.sdk.track.modelloader.TrackerRegistry; +import com.growingio.android.sdk.track.middleware.webservice.Circler; +import com.growingio.android.sdk.track.middleware.webservice.Debugger; +import com.growingio.android.sdk.track.middleware.webservice.WebService; import java.lang.ref.WeakReference; @@ -49,7 +49,7 @@ * * @author cpacm 2021/3/25 */ -public class DeepLinkProvider implements IActivityLifecycle, OnConfigurationChangeListener { +public class DeepLinkProvider implements IActivityLifecycle, OnConfigurationChangeListener, TrackerLifecycleProvider { private static final String TAG = "DeepLinkProvider"; @@ -57,11 +57,27 @@ public class DeepLinkProvider implements IActivityLifecycle, OnConfigurationChan private Uri needResendUri = null; private boolean needResendActivate = false; - private static class SingleInstance { - private static final DeepLinkProvider INSTANCE = new DeepLinkProvider(); + private ConfigurationProvider configurationProvider; + private ActivityStateProvider activityStateProvider; + + private TrackerRegistry registry; + + DeepLinkProvider() { } - private DeepLinkProvider() { + @Override + public void setup(TrackerContext context) { + configurationProvider = context.getConfigurationProvider(); + configurationProvider.addConfigurationListener(this); + activityStateProvider = context.getActivityStateProvider(); + activityStateProvider.registerActivityLifecycleListener(this); + registry = context.getRegistry(); + } + + @Override + public void shutdown() { + configurationProvider.removeConfigurationListener(this); + activityStateProvider.unregisterActivityLifecycleListener(this); } @Override @@ -76,21 +92,12 @@ public void onDataCollectionChanged(boolean isEnable) { } } - public void init() { - ActivityStateProvider.get().registerActivityLifecycleListener(this); - ConfigurationProvider.get().addConfigurationListener(this); - } - - public static DeepLinkProvider get() { - return DeepLinkProvider.SingleInstance.INSTANCE; - } - @Override public void onActivityLifecycle(ActivityLifecycleEvent event) { if (event.eventType == ON_CREATED || event.eventType == ON_NEW_INTENT) { if (checkIsValid(event.getIntent())) { Uri data = event.getIntent().getData(); - String urlScheme = ConfigurationProvider.core().getUrlScheme(); + String urlScheme = configurationProvider.core().getUrlScheme(); if (urlScheme.equals(data.getScheme()) && WEB_SERVICES_HOST.equals(data.getHost()) && WEB_SERVICES_PATH.equals(data.getPath())) { TrackMainThread.trackMain().postActionToTrackMain(() -> dispatchWebServiceUri(data)); return; @@ -111,13 +118,13 @@ public void onActivityLifecycle(ActivityLifecycleEvent event) { } private AdvertResult handleDeepLink(Uri data) { - if (ConfigurationProvider.core().isDataCollectionEnabled()) { + if (configurationProvider.core().isDataCollectionEnabled()) { if (data != null) { needResendUri = null; - return TrackerContext.get().executeData(Activate.deeplink(data), Activate.class, AdvertResult.class); + return registry.executeData(Activate.deeplink(data), Activate.class, AdvertResult.class); } else { needResendActivate = false; - return TrackerContext.get().executeData(Activate.activate(), Activate.class, AdvertResult.class); + return registry.executeData(Activate.activate(), Activate.class, AdvertResult.class); } } else { if (data != null) { @@ -134,7 +141,7 @@ public boolean doDeepLinkByUrl(String url, DeepLinkCallback callback) { return false; } Uri uri = Uri.parse(url); - AdvertResult result = TrackerContext.get().executeData(Activate.handleDeeplink(uri, callback), Activate.class, AdvertResult.class); + AdvertResult result = registry.executeData(Activate.handleDeeplink(uri, callback), Activate.class, AdvertResult.class); if (result == null) { Logger.e(TAG, "AdvertModule is null, please register advert component first."); return false; @@ -163,7 +170,7 @@ void dispatchWebServiceUri(Uri data) { params.put(parameterName, data.getQueryParameter(parameterName)); } if (serviceType.equals(SERVICE_DEBUGGER_TYPE)) { - TrackerContext.get().loadData(new Debugger(params), Debugger.class, WebService.class, new LoadDataFetcher.DataCallback() { + registry.loadData(new Debugger(params), Debugger.class, WebService.class, new LoadDataFetcher.DataCallback() { @Override public void onDataReady(WebService data) { Logger.d(TAG, "start debugger success"); @@ -175,7 +182,7 @@ public void onLoadFailed(Exception e) { } }); } else if (serviceType.equals(SERVICE_CIRCLE_TYPE)) { - TrackerContext.get().loadData(new Circler(params), Circler.class, WebService.class, new LoadDataFetcher.DataCallback() { + registry.loadData(new Circler(params), Circler.class, WebService.class, new LoadDataFetcher.DataCallback() { @Override public void onDataReady(WebService data) { Logger.d(TAG, "start circle choose success"); diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/DeviceInfoProvider.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/DeviceInfoProvider.java index 4e23d027..8f5cd64b 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/DeviceInfoProvider.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/DeviceInfoProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.providers; import android.annotation.SuppressLint; @@ -27,8 +26,9 @@ import android.webkit.WebView; import com.growingio.android.sdk.TrackerContext; -import com.growingio.android.sdk.track.ipc.PersistentDataProvider; +import com.growingio.android.sdk.track.listener.TrackThread; import com.growingio.android.sdk.track.log.Logger; +import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import com.growingio.android.sdk.track.utils.ConstantPool; import com.growingio.android.sdk.track.utils.DeviceUtil; import com.growingio.android.sdk.track.middleware.OaidHelper; @@ -37,7 +37,7 @@ import java.nio.charset.Charset; import java.util.UUID; -public class DeviceInfoProvider { +public class DeviceInfoProvider implements TrackerLifecycleProvider { private static final String TAG = "DeviceInfoProvider"; private static final String DEVICE_TYPE_PHONE = "PHONE"; @@ -59,19 +59,30 @@ public class DeviceInfoProvider { private String mGoogleAdId; private String mDeviceId; - private static class SingleInstance { - private static final DeviceInfoProvider INSTANCE = new DeviceInfoProvider(); - } + private double mLatitude = 0; + + private double mLongitude = 0; + + private Context context; + private TrackerRegistry registry; + private ConfigurationProvider configurationProvider; + private PersistentDataProvider persistentDataProvider; - private DeviceInfoProvider() { + DeviceInfoProvider() { } - private Context getContext() { - return TrackerContext.get().getApplicationContext(); + @Override + public void setup(TrackerContext context) { + this.context = context.getBaseContext(); + this.registry = context.getRegistry(); + this.configurationProvider = context.getConfigurationProvider(); + this.persistentDataProvider = context.getProvider(PersistentDataProvider.class); + } - public static DeviceInfoProvider get() { - return SingleInstance.INSTANCE; + @Override + public void shutdown() { + } public String getOperatingSystemVersion() { @@ -97,14 +108,14 @@ public String getDeviceModel() { public String getDeviceType() { if (TextUtils.isEmpty(mDeviceType)) { - mDeviceType = DeviceUtil.isPhone(getContext()) ? DEVICE_TYPE_PHONE : DEVICE_TYPE_PAD; + mDeviceType = DeviceUtil.isPhone(context) ? DEVICE_TYPE_PHONE : DEVICE_TYPE_PAD; } return mDeviceType; } public int getScreenHeight() { if (mScreenHeight <= 0) { - DisplayMetrics metrics = DeviceUtil.getDisplayMetrics(getContext()); + DisplayMetrics metrics = DeviceUtil.getDisplayMetrics(context); mScreenHeight = metrics.heightPixels; } return mScreenHeight; @@ -112,7 +123,7 @@ public int getScreenHeight() { public int getScreenWidth() { if (mScreenWidth <= 0) { - DisplayMetrics metrics = DeviceUtil.getDisplayMetrics(getContext()); + DisplayMetrics metrics = DeviceUtil.getDisplayMetrics(context); mScreenWidth = metrics.widthPixels; } return mScreenWidth; @@ -120,10 +131,10 @@ public int getScreenWidth() { @SuppressLint({"MissingPermission", "HardwareIds", "WrongConstant"}) public String getImei() { - if (TextUtils.isEmpty(mImei) && ConfigurationProvider.core().isImeiEnabled()) { + if (TextUtils.isEmpty(mImei) && configurationProvider.core().isImeiEnabled()) { if (PermissionUtil.checkReadPhoneStatePermission()) { try { - TelephonyManager tm = (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE); + TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mImei = tm.getImei(); } else { @@ -141,7 +152,7 @@ public String getAndroidId() { // ensure androidid call once in a process if (TextUtils.isEmpty(mAndroidId)) { try { - mAndroidId = Settings.System.getString(getContext().getContentResolver(), Settings.System.ANDROID_ID); + mAndroidId = Settings.System.getString(context.getContentResolver(), Settings.System.ANDROID_ID); if (TextUtils.isEmpty(mAndroidId) || MAGIC_ANDROID_ID.equals(mAndroidId)) { mAndroidId = MAGIC_ANDROID_ID; } else { @@ -156,7 +167,7 @@ public String getAndroidId() { public String getOaid() { if (TextUtils.isEmpty(mOaid)) { - mOaid = TrackerContext.get().executeData(null, OaidHelper.class, String.class); + mOaid = registry.executeData(null, OaidHelper.class, String.class); } return mOaid; } @@ -167,7 +178,7 @@ public String getGoogleAdId() { public String getDeviceId() { if (TextUtils.isEmpty(mDeviceId)) { - mDeviceId = PersistentDataProvider.get().getDeviceId(); + mDeviceId = persistentDataProvider.getDeviceId(); if (TextUtils.isEmpty(mDeviceId)) { mDeviceId = calculateDeviceId(); } @@ -192,7 +203,7 @@ private String calculateDeviceId() { result = UUID.randomUUID().toString(); } if (result != null && result.length() != 0) { - PersistentDataProvider.get().setDeviceId(result); + persistentDataProvider.setDeviceId(result); } return result; } @@ -203,11 +214,11 @@ public String getUserAgent() { if (TextUtils.isEmpty(mUserAgent) && PermissionUtil.hasInternetPermission()) { try { - mUserAgent = new WebView(getContext()).getSettings().getUserAgentString(); + mUserAgent = new WebView(context).getSettings().getUserAgentString(); } catch (Exception e) { Logger.e(TAG, e.getMessage()); try { - mUserAgent = WebSettings.getDefaultUserAgent(getContext()); + mUserAgent = WebSettings.getDefaultUserAgent(context); } catch (Exception badException) { Logger.e(TAG, badException.getMessage()); } @@ -215,4 +226,34 @@ public String getUserAgent() { } return mUserAgent; } + + @TrackThread + public void setLocation(double latitude, double longitude) { + double eps = 1e-5; + if (Math.abs(latitude) < eps && Math.abs(longitude) < eps) { + Logger.w(TAG, "invalid latitude and longitude, and return"); + return; + } + + mLatitude = latitude; + mLongitude = longitude; + Logger.d(TAG, "set location with " + mLatitude + "-" + mLongitude); + } + + @TrackThread + public void cleanLocation() { + Logger.d(TAG, "clean location by User, Doesn't send visit event."); + mLatitude = 0; + mLongitude = 0; + } + + @TrackThread + public double getLatitude() { + return mLatitude; + } + + @TrackThread + public double getLongitude() { + return mLongitude; + } } \ No newline at end of file diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/EventBuilderProvider.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/EventBuilderProvider.java new file mode 100644 index 00000000..edc0e67b --- /dev/null +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/EventBuilderProvider.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.growingio.android.sdk.track.providers; + +import android.text.TextUtils; + +import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.track.events.CustomEvent; +import com.growingio.android.sdk.track.events.EventBuildInterceptor; +import com.growingio.android.sdk.track.events.EventFilterInterceptor; +import com.growingio.android.sdk.track.events.PageEvent; +import com.growingio.android.sdk.track.events.PageLevelCustomEvent; +import com.growingio.android.sdk.track.events.ViewElementEvent; +import com.growingio.android.sdk.track.events.base.BaseEvent; +import com.growingio.android.sdk.track.events.helper.DefaultEventFilterInterceptor; +import com.growingio.android.sdk.track.events.helper.JsonSerializableFactory; +import com.growingio.android.sdk.track.listener.TrackThread; +import com.growingio.android.sdk.track.log.Logger; +import com.growingio.android.sdk.track.middleware.GEvent; + +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + *

+ * + * @author cpacm 2023/3/21 + */ +public class EventBuilderProvider implements TrackerLifecycleProvider { + + private static final String TAG = "EventBuilderProvider"; + + private final List mEventBuildInterceptors = new ArrayList<>(); + private EventFilterInterceptor defaultFilterInterceptor; + + private static final JsonSerializableFactory serializableFactory = new JsonSerializableFactory(); + + private ConfigurationProvider configurationProvider; + private TrackerContext context; + + EventBuilderProvider() { + } + + @Override + public void setup(TrackerContext context) { + configurationProvider = context.getConfigurationProvider(); + this.context = context; + } + + @Override + public void shutdown() { + mEventBuildInterceptors.clear(); + } + + public static JSONObject toJson(BaseEvent event) { + JSONObject jsonObject = new JSONObject(); + serializableFactory.toJson(jsonObject, event); + return jsonObject; + } + + public static void toJson(JSONObject jsonObject, BaseEvent event) { + serializableFactory.toJson(jsonObject, event); + } + + public static void parseFrom(BaseEvent.BaseBuilder builder, JSONObject jsonObject) { + serializableFactory.parseFrom(builder, jsonObject); + } + + @TrackThread + public BaseEvent onGenerateGEvent(BaseEvent.BaseBuilder gEvent) { + dispatchEventWillBuild(gEvent); + + if (!filterEvent(gEvent)) return null; + gEvent.readPropertyInTrackThread(context); + + BaseEvent event = gEvent.build(); + dispatchEventDidBuild(event); + + return event; + } + + public void removeEventBuildInterceptor(EventBuildInterceptor interceptor) { + synchronized (mEventBuildInterceptors) { + if (interceptor != null) { + mEventBuildInterceptors.remove(interceptor); + } + } + } + + public void addEventBuildInterceptor(EventBuildInterceptor interceptor) { + synchronized (mEventBuildInterceptors) { + boolean needsAdd = true; + Iterator refIterator = mEventBuildInterceptors.iterator(); + while (refIterator.hasNext()) { + EventBuildInterceptor storedInterceptor = refIterator.next(); + if (null == storedInterceptor) { + refIterator.remove(); + } else if (storedInterceptor == interceptor) { + needsAdd = false; + } + } + if (needsAdd) { + mEventBuildInterceptors.add(interceptor); + } + } + } + + private void dispatchEventWillBuild(BaseEvent.BaseBuilder eventBuilder) { + synchronized (mEventBuildInterceptors) { + Iterator refIter = mEventBuildInterceptors.iterator(); + while (refIter.hasNext()) { + EventBuildInterceptor interceptor = refIter.next(); + if (null == interceptor) { + refIter.remove(); + } else { + try { + interceptor.eventWillBuild(eventBuilder); + } catch (Exception e) { + Logger.e(TAG, e); + } + } + } + } + } + + private void dispatchEventDidBuild(GEvent event) { + synchronized (mEventBuildInterceptors) { + Iterator refIterator = mEventBuildInterceptors.iterator(); + while (refIterator.hasNext()) { + EventBuildInterceptor interceptor = refIterator.next(); + if (null == interceptor) { + refIterator.remove(); + } else { + try { + interceptor.eventDidBuild(event); + } catch (Exception e) { + Logger.e(TAG, e); + } + } + } + } + } + + + private EventFilterInterceptor getEventFilterInterceptor() { + if (configurationProvider.core().getEventFilterInterceptor() != null) { + return configurationProvider.core().getEventFilterInterceptor(); + } else { + if (defaultFilterInterceptor == null) { + defaultFilterInterceptor = new DefaultEventFilterInterceptor(); + } + return defaultFilterInterceptor; + } + } + + @TrackThread + boolean filterEvent(BaseEvent.BaseBuilder eventBuilder) { + EventFilterInterceptor eventFilterInterceptor = getEventFilterInterceptor(); + if (eventFilterInterceptor == null) return true; + + if (!eventFilterInterceptor.filterEventType(eventBuilder.getEventType())) { + Logger.w(TAG, "filter [" + eventBuilder.getEventType() + "] event by type"); + return false; + } + + String eventPath = getEventPath(eventBuilder); + if (!TextUtils.isEmpty(eventPath) && !eventFilterInterceptor.filterEventPath(eventPath)) { + Logger.w(TAG, "filter [" + eventBuilder.getEventType() + "] event by path=" + eventPath); + return false; + } + + String eventName = getEventName(eventBuilder); + if (!TextUtils.isEmpty(eventName) && !eventFilterInterceptor.filterEventName(eventName)) { + Logger.w(TAG, "filter [CUSTOM] event by name=" + eventName); + return false; + } + + Map filterFields = eventFilterInterceptor.filterEventField(eventBuilder.getEventType(), eventBuilder.getFilterMap()); + eventBuilder.filterFieldProperty(filterFields); + + return true; + } + + String getEventName(BaseEvent.BaseBuilder eventBuilder) { + if (eventBuilder instanceof CustomEvent.Builder) { + return ((CustomEvent.Builder) eventBuilder).getEventName(); + } + return null; + } + + String getEventPath(BaseEvent.BaseBuilder eventBuilder) { + if (eventBuilder instanceof PageEvent.Builder) { + return ((PageEvent.Builder) eventBuilder).getPath(); + } + if (eventBuilder instanceof ViewElementEvent.Builder) { + return ((ViewElementEvent.Builder) eventBuilder).getPath(); + } + if (eventBuilder instanceof PageLevelCustomEvent.Builder) { + return ((PageLevelCustomEvent.Builder) eventBuilder).getPath(); + } + return null; + } +} diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/EventStateProvider.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/EventStateProvider.java deleted file mode 100644 index a344a109..00000000 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/EventStateProvider.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.growingio.android.sdk.track.providers; - -import com.growingio.android.sdk.TrackerContext; -import com.growingio.android.sdk.track.TrackMainThread; -import com.growingio.android.sdk.track.events.base.BaseEvent; -import com.growingio.android.sdk.track.events.helper.JsonSerializableFactory; -import com.growingio.android.sdk.track.log.CircularFifoQueue; -import com.growingio.android.sdk.track.log.Logger; - -import org.json.JSONObject; - -/** - *

- * - * @author cpacm 2023/3/21 - */ -public class EventStateProvider { - - private static final String TAG = "EventStateProvider"; - - private static class SingleInstance { - private static final EventStateProvider INSTANCE = new EventStateProvider(); - } - - private EventStateProvider() { - serializableFactory = new JsonSerializableFactory(); - } - - private final CircularFifoQueue> caches = new CircularFifoQueue<>(200); - - private final JsonSerializableFactory serializableFactory; - - public static EventStateProvider get() { - return EventStateProvider.SingleInstance.INSTANCE; - } - - public JSONObject toJson(BaseEvent event) { - JSONObject jsonObject = new JSONObject(); - serializableFactory.toJson(jsonObject, event); - return jsonObject; - } - - public void toJson(JSONObject jsonObject, BaseEvent event) { - serializableFactory.toJson(jsonObject, event); - } - - public void parseFrom(BaseEvent.BaseBuilder builder, JSONObject jsonObject) { - serializableFactory.parseFrom(builder, jsonObject); - } - - public void releaseCaches() { - if (caches.size() > 0 && TrackerContext.initializedSuccessfully() && ConfigurationProvider.core().isDataCollectionEnabled()) { - for (BaseEvent.BaseBuilder eventBuilder : caches) { - TrackMainThread.trackMain().postEventToTrackMain(eventBuilder); - } - Logger.d(TAG, "release cache events after sdk init: count-" + caches.size()); - caches.clear(); - } else if (caches.size() > 0) { - Logger.w(TAG, "drop events when data collect disabled"); - caches.clear(); - } - } - - public void cacheEvent(BaseEvent.BaseBuilder eventBuilder) { - if (!TrackerContext.initializedSuccessfully()) { - Logger.w(TAG, "cache event before sdk init: " + eventBuilder.getEventType()); - caches.add(eventBuilder); - } else { - TrackMainThread.trackMain().postEventToTrackMain(eventBuilder); - } - } - -} diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/PersistentDataProvider.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/PersistentDataProvider.java similarity index 55% rename from growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/PersistentDataProvider.java rename to growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/PersistentDataProvider.java index f7995f14..68e54312 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/ipc/PersistentDataProvider.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/PersistentDataProvider.java @@ -1,33 +1,31 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ +package com.growingio.android.sdk.track.providers; -package com.growingio.android.sdk.track.ipc; - -import static android.content.Context.ACTIVITY_SERVICE; - +import android.annotation.SuppressLint; import android.app.ActivityManager; import android.content.Context; import android.os.Process; import android.text.TextUtils; import com.growingio.android.sdk.TrackerContext; -import com.growingio.android.sdk.track.listener.TrackThread; +import com.growingio.android.sdk.track.ipc.IDataSharer; +import com.growingio.android.sdk.track.ipc.MultiProcessDataSharer; +import com.growingio.android.sdk.track.ipc.ProcessLock; import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; -import com.growingio.android.sdk.track.providers.SessionProvider; import com.growingio.android.sdk.track.utils.SystemUtil; import java.util.ArrayList; @@ -36,7 +34,7 @@ import java.util.List; import java.util.Set; -public class PersistentDataProvider { +public class PersistentDataProvider implements TrackerLifecycleProvider { private static final String TAG = "PersistentDataProvider"; private static final String SHARER_NAME = "PersistentSharerDataProvider"; private static final int SHARER_MAX_SIZE = 50; @@ -53,131 +51,123 @@ public class PersistentDataProvider { private static final String KEY_ACTIVITY_COUNT = "ACTIVITY_COUNT"; private static final String KEY_SEND_VISIT_AFTER_REFRESH_SESSION_ID = "SEND_VISIT_AFTER_REFRESH_SESSION_ID"; - private final IDataSharer mDataSharer; - private final ProcessLock mProcessLock; - private final Context mContext; + private final IDataSharer dataSharer; + private final ProcessLock processLock; private boolean isFirstProcess = true; - private static class SingleInstance { - private static final PersistentDataProvider INSTANCE = new PersistentDataProvider(); - } - - private PersistentDataProvider() { - mContext = TrackerContext.get().getApplicationContext(); - mDataSharer = new MultiProcessDataSharer(mContext, SHARER_NAME, SHARER_MAX_SIZE); - mProcessLock = new ProcessLock(mContext, PersistentDataProvider.class.getName()); + PersistentDataProvider(Context context) { + dataSharer = new MultiProcessDataSharer(context, SHARER_NAME, SHARER_MAX_SIZE); + processLock = new ProcessLock(context, PersistentDataProvider.class.getName()); } - public static PersistentDataProvider get() { - return SingleInstance.INSTANCE; + @Override + public void setup(TrackerContext context) { + repairPid(context, context.getConfigurationProvider(), context.getProvider(SessionProvider.class)); } - @TrackThread - public void setup() { - repairPid(); + @Override + public void shutdown() { + dataSharer.release(); + processLock.release(); } - @TrackThread - public void start() { - if (isFirstProcess && !isSendVisitAfterRefreshSessionId()) { - SessionProvider.get().generateVisit(); - } + public boolean isFirstProcess() { + return isFirstProcess; } public long getGlobalEventSequenceIdAndIncrement() { - return mDataSharer.getAndIncrementLong(KEY_TYPE_GLOBAL, 1L); + return dataSharer.getAndIncrementLong(KEY_TYPE_GLOBAL, 1L); } public String getSessionId() { - return mDataSharer.getString(KEY_SESSION_ID, ""); + return dataSharer.getString(KEY_SESSION_ID, ""); } public void setSessionId(String sessionId) { - mDataSharer.putString(KEY_SESSION_ID, sessionId); + dataSharer.putString(KEY_SESSION_ID, sessionId); } public String getDeviceId() { - return mDataSharer.getString(KEY_DEVICE_ID, ""); + return dataSharer.getString(KEY_DEVICE_ID, ""); } public void setDeviceId(String deviceId) { if (TextUtils.isEmpty(deviceId)) { return; } - mDataSharer.putString(KEY_DEVICE_ID, deviceId); + dataSharer.putString(KEY_DEVICE_ID, deviceId); } public String getLoginUserKey() { - return mDataSharer.getString(KEY_LOGIN_USER_KEY, ""); + return dataSharer.getString(KEY_LOGIN_USER_KEY, ""); } public String getLoginUserId() { - return mDataSharer.getString(KEY_LOGIN_USER_ID, ""); + return dataSharer.getString(KEY_LOGIN_USER_ID, ""); } public void setLoginUserIdAndUserKey(String userId, String userKey) { HashMap map = new HashMap<>(); map.put(KEY_LOGIN_USER_ID, userId); map.put(KEY_LOGIN_USER_KEY, userKey); - mDataSharer.putMultiString(map); + dataSharer.putMultiString(map); } public String getLatestNonNullUserId() { - return mDataSharer.getString(KEY_LATEST_NON_NULL_USER_ID, ""); + return dataSharer.getString(KEY_LATEST_NON_NULL_USER_ID, ""); } public void setLatestNonNullUserId(String latestNonNullUserId) { - mDataSharer.putString(KEY_LATEST_NON_NULL_USER_ID, latestNonNullUserId); + dataSharer.putString(KEY_LATEST_NON_NULL_USER_ID, latestNonNullUserId); } public long getLatestPauseTime() { - return mDataSharer.getLong(KEY_LATEST_PAUSE_TIME, 0L); + return dataSharer.getLong(KEY_LATEST_PAUSE_TIME, 0L); } public void setLatestPauseTime(long latestPauseTime) { - mDataSharer.putLong(KEY_LATEST_PAUSE_TIME, latestPauseTime); + dataSharer.putLong(KEY_LATEST_PAUSE_TIME, latestPauseTime); } public int getActivityCount() { - return mDataSharer.getInt(KEY_ACTIVITY_COUNT, 0); + return dataSharer.getInt(KEY_ACTIVITY_COUNT, 0); } public void setActivityCount(int activityCount) { - mDataSharer.putInt(KEY_ACTIVITY_COUNT, activityCount); + dataSharer.putInt(KEY_ACTIVITY_COUNT, activityCount); } public void addActivityCount() { - mDataSharer.getAndIncrementInt(KEY_ACTIVITY_COUNT, 0); + dataSharer.getAndIncrementInt(KEY_ACTIVITY_COUNT, 0); } public void delActivityCount() { - mDataSharer.getAndDecrementInt(KEY_ACTIVITY_COUNT, 0); + dataSharer.getAndDecrementInt(KEY_ACTIVITY_COUNT, 0); } public boolean isSendVisitAfterRefreshSessionId() { - return mDataSharer.getBoolean(KEY_SEND_VISIT_AFTER_REFRESH_SESSION_ID, false); + return dataSharer.getBoolean(KEY_SEND_VISIT_AFTER_REFRESH_SESSION_ID, false); } public void setSendVisitAfterRefreshSessionId(boolean sendVisitAfterRefreshSessionId) { - mDataSharer.putBoolean(KEY_SEND_VISIT_AFTER_REFRESH_SESSION_ID, sendVisitAfterRefreshSessionId); + dataSharer.putBoolean(KEY_SEND_VISIT_AFTER_REFRESH_SESSION_ID, sendVisitAfterRefreshSessionId); } public void putString(String key, String value) { - mDataSharer.putString(key, value); + dataSharer.putString(key, value); } public String getString(String key, String defValue) { - return mDataSharer.getString(key, defValue); + return dataSharer.getString(key, defValue); } - private void repairPid() { - - mProcessLock.lockedRun(() -> { + private void repairPid(Context context, ConfigurationProvider configurationProvider, SessionProvider sessionProvider) { + processLock.lockedRun(() -> { isFirstProcess = false; - if (ConfigurationProvider.core().isRequireAppProcessesEnabled() && ConfigurationProvider.core().isDataCollectionEnabled()) { + if (configurationProvider.core().isRequireAppProcessesEnabled() && configurationProvider.core().isDataCollectionEnabled()) { List alivePid = new ArrayList<>(); - Set runningProcess = getRunningProcess(mContext); + Set runningProcess = getRunningProcess(context); for (int pid : getAlivePid()) { if (runningProcess.contains(pid)) { alivePid.add(pid); @@ -189,7 +179,7 @@ private void repairPid() { alivePid.add(Process.myPid()); putAlivePid(alivePid); } else { - isFirstProcess = SystemUtil.isMainProcess(mContext); + isFirstProcess = SystemUtil.isMainProcess(context); // 将主进程id加入alive pid 中 if (isFirstProcess) putAlivePid(new ArrayList<>(Process.myPid())); } @@ -198,23 +188,23 @@ private void repairPid() { setActivityCount(0); setLatestPauseTime(0L); setLatestNonNullUserId(getLoginUserId()); - SessionProvider.get().refreshSessionId(); + sessionProvider.refreshSessionId(); } }); } private List getAlivePid() { - return mDataSharer.getIntArray(KEY_ALIVE_PID, new ArrayList<>()); + return dataSharer.getIntArray(KEY_ALIVE_PID, new ArrayList<>()); } private void putAlivePid(List value) { - mDataSharer.putIntArray(KEY_ALIVE_PID, value); + dataSharer.putIntArray(KEY_ALIVE_PID, value); } private Set getRunningProcess(Context context) { Set myRunningProcess = new HashSet<>(); try { - ActivityManager manager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE); + @SuppressLint("WrongConstant") ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List runningAppProcesses = manager.getRunningAppProcesses(); int myUid = Process.myUid(); for (ActivityManager.RunningAppProcessInfo info : runningAppProcesses) { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/SessionProvider.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/SessionProvider.java index 76da5f53..9c4dc593 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/SessionProvider.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/SessionProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,46 +13,55 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.providers; import android.app.Activity; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.TrackMainThread; -import com.growingio.android.sdk.track.ipc.PersistentDataProvider; import com.growingio.android.sdk.track.events.TrackEventGenerator; import com.growingio.android.sdk.track.listener.TrackThread; import com.growingio.android.sdk.track.listener.IActivityLifecycle; import com.growingio.android.sdk.track.listener.event.ActivityLifecycleEvent; -import com.growingio.android.sdk.track.log.Logger; import java.util.ArrayList; import java.util.List; import java.util.UUID; -public class SessionProvider implements IActivityLifecycle { +public class SessionProvider implements IActivityLifecycle, TrackerLifecycleProvider { private static final String TAG = "SessionProvider"; - private final List mActivityList = new ArrayList<>(); - private final long mSessionInterval; - private double mLatitude = 0; - private double mLongitude = 0; + private final List activityList = new ArrayList<>(); + private long sessionInterval = 30000L; + private ConfigurationProvider configurationProvider; + private PersistentDataProvider persistentDataProvider; + private ActivityStateProvider activityStateProvider; - private static class SingleInstance { - private static final SessionProvider INSTANCE = new SessionProvider(); + protected SessionProvider() { } - private SessionProvider() { - mSessionInterval = ConfigurationProvider.core().getSessionInterval() * 1000L; + + @Override + public void setup(TrackerContext context) { + configurationProvider = context.getConfigurationProvider(); + sessionInterval = configurationProvider.core().getSessionInterval() * 1000L; + persistentDataProvider = context.getProvider(PersistentDataProvider.class); + activityList.clear(); + activityStateProvider = context.getActivityStateProvider(); + activityStateProvider.registerActivityLifecycleListener(this); } - public void init() { - mActivityList.clear(); - ActivityStateProvider.get().registerActivityLifecycleListener(this); + public void createVisitAfterAppStart() { + if (persistentDataProvider.isFirstProcess() && !persistentDataProvider.isSendVisitAfterRefreshSessionId()) { + generateVisit(); + } } - public static SessionProvider get() { - return SingleInstance.INSTANCE; + + @Override + public void shutdown() { + activityList.clear(); + activityStateProvider.unregisterActivityLifecycleListener(this); } /** @@ -63,62 +72,30 @@ public static SessionProvider get() { */ @TrackThread public void refreshSessionId() { - PersistentDataProvider.get().setSessionId(UUID.randomUUID().toString()); - PersistentDataProvider.get().setSendVisitAfterRefreshSessionId(false); + persistentDataProvider.setSessionId(UUID.randomUUID().toString()); + persistentDataProvider.setSendVisitAfterRefreshSessionId(false); } @TrackThread public void generateVisit() { - if (!ConfigurationProvider.core().isDataCollectionEnabled()) { + if (!configurationProvider.core().isDataCollectionEnabled()) { return; } - PersistentDataProvider.get().setSendVisitAfterRefreshSessionId(true); + persistentDataProvider.setSendVisitAfterRefreshSessionId(true); TrackEventGenerator.generateVisitEvent(); } - @TrackThread - public void setLocation(double latitude, double longitude) { - double eps = 1e-5; - if (Math.abs(latitude) < eps && Math.abs(longitude) < eps) { - Logger.w(TAG, "invalid latitude and longitude, and return"); - return; - } - - mLatitude = latitude; - mLongitude = longitude; - Logger.d(TAG, "set location with " + mLatitude + "-" + mLongitude); - } - - @TrackThread - public void cleanLocation() { - Logger.d(TAG, "clean location by User, Doesn't send visit event."); - mLatitude = 0; - mLongitude = 0; - } - - @TrackThread - public double getLatitude() { - return mLatitude; - } - - @TrackThread - public double getLongitude() { - return mLongitude; - } - - public boolean checkSessionIntervalAndSendVisit() { - if (PersistentDataProvider.get().getActivityCount() == 0) { - long latestPauseTime = PersistentDataProvider.get().getLatestPauseTime(); - if (latestPauseTime != 0 && (System.currentTimeMillis() - latestPauseTime >= mSessionInterval)) { + public void checkSessionIntervalAndSendVisit() { + if (persistentDataProvider.getActivityCount() == 0) { + long latestPauseTime = persistentDataProvider.getLatestPauseTime(); + if (latestPauseTime != 0 && (System.currentTimeMillis() - latestPauseTime >= sessionInterval)) { TrackMainThread.trackMain().postActionToTrackMain(() -> { refreshSessionId(); generateVisit(); }); - PersistentDataProvider.get().setLatestPauseTime(System.currentTimeMillis()); - return true; + persistentDataProvider.setLatestPauseTime(System.currentTimeMillis()); } } - return false; } @Override @@ -127,15 +104,15 @@ public void onActivityLifecycle(final ActivityLifecycleEvent event) { if (activity == null) return; if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_STARTED) { checkSessionIntervalAndSendVisit(); - mActivityList.add(activity.toString()); - PersistentDataProvider.get().addActivityCount(); + activityList.add(activity.toString()); + persistentDataProvider.addActivityCount(); } else if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_STOPPED) { - if (mActivityList.contains(activity.toString())) { - if (mActivityList.remove(activity.toString())) { - PersistentDataProvider.get().delActivityCount(); + if (activityList.contains(activity.toString())) { + if (activityList.remove(activity.toString())) { + persistentDataProvider.delActivityCount(); } - if (PersistentDataProvider.get().getActivityCount() == 0) { - PersistentDataProvider.get().setLatestPauseTime(System.currentTimeMillis()); + if (persistentDataProvider.getActivityCount() == 0) { + persistentDataProvider.setLatestPauseTime(System.currentTimeMillis()); TrackMainThread.trackMain().postActionToTrackMain(new Runnable() { @Override public void run() { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/timer/TimerCenter.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/TimingEventProvider.java similarity index 63% rename from growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/timer/TimerCenter.java rename to growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/TimingEventProvider.java index 07c1181f..30fd9a60 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/timer/TimerCenter.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/TimingEventProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,56 +13,62 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.growingio.android.sdk.track.timer; +package com.growingio.android.sdk.track.providers; import android.os.SystemClock; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.events.CustomEvent; -import com.growingio.android.sdk.track.ipc.PersistentDataProvider; +import com.growingio.android.sdk.track.utils.TimerEvent; import com.growingio.android.sdk.track.listener.IActivityLifecycle; import com.growingio.android.sdk.track.listener.event.ActivityLifecycleEvent; import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; import java.util.HashMap; import java.util.Map; import java.util.UUID; -public class TimerCenter implements IActivityLifecycle { - - +public class TimingEventProvider implements IActivityLifecycle, TrackerLifecycleProvider { private static final String TAG = "TimerCenter"; public static final String ATTR_EVENT_DURATION = "event_duration"; - private final Map mTimers = new HashMap<>(); - private boolean mEnterBackground = false; + private final Map timers = new HashMap<>(); + private boolean enterBackground = false; + + private ActivityStateProvider activityStateProvider; + private ConfigurationProvider configurationProvider; + private PersistentDataProvider persistentDataProvider; - private static class SingleInstance { - private static final TimerCenter INSTANCE = new TimerCenter(); + + @Override + public void setup(TrackerContext context) { + configurationProvider = context.getConfigurationProvider(); + persistentDataProvider = context.getProvider(PersistentDataProvider.class); + activityStateProvider = context.getActivityStateProvider(); + activityStateProvider.registerActivityLifecycleListener(this); } - private TimerCenter() { - ActivityStateProvider.get().registerActivityLifecycleListener(this); + @Override + public void shutdown() { + activityStateProvider.unregisterActivityLifecycleListener(this); } - public static TimerCenter get() { - return TimerCenter.SingleInstance.INSTANCE; + TimingEventProvider() { + } public String startTimer(String eventName) { - if (!ConfigurationProvider.core().isDataCollectionEnabled()) { + if (!configurationProvider.core().isDataCollectionEnabled()) { return null; } long startTime = SystemClock.elapsedRealtime(); String timerId = String.format("%s_%s", eventName, UUID.randomUUID().toString()); - Timer timer = new Timer(startTime, eventName); - synchronized (mTimers) { - if (!ConfigurationProvider.core().isDataCollectionEnabled()) { + TimerEvent timer = new TimerEvent(startTime, eventName); + synchronized (timers) { + if (!configurationProvider.core().isDataCollectionEnabled()) { return null; } - mTimers.put(timerId, timer); + timers.put(timerId, timer); Logger.d(TAG, "start a timer with %s", timerId); } @@ -71,8 +77,8 @@ public String startTimer(String eventName) { public void updateTimer(String timerId, boolean isResume) { long currentTime = SystemClock.elapsedRealtime(); - synchronized (mTimers) { - Timer timer = mTimers.get(timerId); + synchronized (timers) { + TimerEvent timer = timers.get(timerId); if (timer != null) { timer.updateState(currentTime, isResume); Logger.d(TAG, "update a timer with %s", timerId); @@ -86,8 +92,8 @@ public void endTimer(String timerId) { public void endTimer(String timerId, Map attributes) { long currentTime = SystemClock.elapsedRealtime(); - synchronized (mTimers) { - Timer timer = mTimers.get(timerId); + synchronized (timers) { + TimerEvent timer = timers.get(timerId); if (timer != null) { attributes = (attributes == null) ? new HashMap<>() : new HashMap<>(attributes); attributes.put(ATTR_EVENT_DURATION, timer.computeElapsedTime(currentTime)); @@ -96,32 +102,32 @@ public void endTimer(String timerId, Map attributes) { .setEventName(timer.getEventName()) .setAttributes(attributes) ); - mTimers.remove(timerId); + timers.remove(timerId); Logger.d(TAG, "remove a timer with %s", timerId); } } } public void removeTimer(String timerId) { - synchronized (mTimers) { - mTimers.remove(timerId); + synchronized (timers) { + timers.remove(timerId); } } public void clearTimer() { - synchronized (mTimers) { - mTimers.clear(); + synchronized (timers) { + timers.clear(); } } @Override public void onActivityLifecycle(ActivityLifecycleEvent event) { if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_STARTED) { - if (mEnterBackground) { - mEnterBackground = false; + if (enterBackground) { + enterBackground = false; long currentTime = SystemClock.elapsedRealtime(); - synchronized (mTimers) { - for (Timer timer : mTimers.values()) { + synchronized (timers) { + for (TimerEvent timer : timers.values()) { if (timer != null) { timer.resetStartTimeBeforeEnterForeground(currentTime); } @@ -129,11 +135,11 @@ public void onActivityLifecycle(ActivityLifecycleEvent event) { } } } else if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_STOPPED) { - if (PersistentDataProvider.get().getActivityCount() == 0) { - mEnterBackground = true; + if (persistentDataProvider.getActivityCount() == 0) { + enterBackground = true; long currentTime = SystemClock.elapsedRealtime(); - synchronized (mTimers) { - for (Timer timer : mTimers.values()) { + synchronized (timers) { + for (TimerEvent timer : timers.values()) { if (timer != null) { timer.computeElapsedTimeBeforeEnterBackground(currentTime); } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/async/Disposable.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/TrackerLifecycleProvider.java similarity index 65% rename from growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/async/Disposable.java rename to growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/TrackerLifecycleProvider.java index 44f298c7..30d29398 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/async/Disposable.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/TrackerLifecycleProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,13 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.growingio.android.sdk.track.providers; -package com.growingio.android.sdk.track.async; +import com.growingio.android.sdk.TrackerContext; -public interface Disposable { - Disposable EMPTY_DISPOSABLE = EmptyDisposable.INSTANCE; +/** + *

+ * + * @author cpacm 2023/7/25 + */ +public interface TrackerLifecycleProvider { - void dispose(); + void setup(TrackerContext context); - boolean isDisposed(); + void shutdown(); } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/TrackerLifecycleProviderFactory.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/TrackerLifecycleProviderFactory.java new file mode 100644 index 00000000..c98cceaa --- /dev/null +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/TrackerLifecycleProviderFactory.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.growingio.android.sdk.track.providers; + +import android.content.Context; + +import com.growingio.android.sdk.Configurable; +import com.growingio.android.sdk.CoreConfiguration; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + *

+ * + * @author cpacm 2023/7/25 + */ +public class TrackerLifecycleProviderFactory { + + private static volatile TrackerLifecycleProviderFactory instance; + + public static TrackerLifecycleProviderFactory create() { + if (instance == null) { + instance = new TrackerLifecycleProviderFactory(); + } + return instance; + } + + private final Map, TrackerLifecycleProvider> providerStore; + + private TrackerLifecycleProviderFactory() { + providerStore = new LinkedHashMap<>(); + } + + public Map, TrackerLifecycleProvider> providers() { + providerStore.put(AppInfoProvider.class, new AppInfoProvider()); + providerStore.put(DeviceInfoProvider.class, new DeviceInfoProvider()); + + providerStore.put(DeepLinkProvider.class, new DeepLinkProvider()); + providerStore.put(SessionProvider.class, new SessionProvider()); + + providerStore.put(EventBuilderProvider.class, new EventBuilderProvider()); + providerStore.put(TimingEventProvider.class, new TimingEventProvider()); + providerStore.put(UserInfoProvider.class, new UserInfoProvider()); + + return providerStore; + } + + private void addProvider(Class clazz, TrackerLifecycleProvider provider) { + if (provider != null) { + providerStore.put(clazz, provider); + } + } + + public void createPersistentDataProvider(Context context) { + providerStore.put(PersistentDataProvider.class, new PersistentDataProvider(context)); + } + + public void createActivityStateProvider(Context context) { + providerStore.put(ActivityStateProvider.class, new ActivityStateProvider(context)); + } + + public void createConfigurationProviderWithConfig(CoreConfiguration coreConfiguration, Map, Configurable> moduleConfigs) { + providerStore.put(ConfigurationProvider.class, new ConfigurationProvider(coreConfiguration, moduleConfigs)); + } + + public static Map, TrackerLifecycleProvider> emptyMap() { + return new HashMap<>(); + } +} diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/UserInfoProvider.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/UserInfoProvider.java index 91882193..f3fd55ff 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/UserInfoProvider.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/providers/UserInfoProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,37 +13,44 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.providers; import android.text.TextUtils; -import com.growingio.android.sdk.track.ErrorLog; -import com.growingio.android.sdk.track.ipc.PersistentDataProvider; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.listener.TrackThread; import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.utils.ObjectUtils; -public class UserInfoProvider { +public class UserInfoProvider implements TrackerLifecycleProvider { private static final String TAG = "UserInfoPolicy"; - private static class SingleInstance { - private static final UserInfoProvider INSTANCE = new UserInfoProvider(); + private PersistentDataProvider persistentDataProvider; + private SessionProvider sessionProvider; + private ConfigurationProvider configurationProvider; + + UserInfoProvider() { } - private UserInfoProvider() { + @Override + public void setup(TrackerContext context) { + configurationProvider = context.getConfigurationProvider(); + persistentDataProvider = context.getProvider(PersistentDataProvider.class); + sessionProvider = context.getProvider(SessionProvider.class); } - public static UserInfoProvider get() { - return SingleInstance.INSTANCE; + @Override + public void shutdown() { + } + public String getLoginUserKey() { - return PersistentDataProvider.get().getLoginUserKey(); + return persistentDataProvider.getLoginUserKey(); } public String getLoginUserId() { - return PersistentDataProvider.get().getLoginUserId(); + return persistentDataProvider.getLoginUserId(); } @TrackThread @@ -52,7 +59,7 @@ public void setLoginUserId(String userId) { } public void setLoginUserId(String userId, String userKey) { - if (!ConfigurationProvider.core().isIdMappingEnabled()) { + if (!configurationProvider.core().isIdMappingEnabled()) { if (userKey != null) { Logger.w(TAG, "setUserId with UserKey should enable idMapping in sdk Configuration."); userKey = null; @@ -61,17 +68,17 @@ public void setLoginUserId(String userId, String userKey) { // 考虑DataSharer存储限制 if (userKey != null && userKey.length() > 1000) { - Logger.e(TAG, ErrorLog.USER_KEY_TOO_LONG); + Logger.e(TAG, "userKey max length is 1000."); return; } if (userId != null && userId.length() > 1000) { - Logger.e(TAG, ErrorLog.USER_ID_TOO_LONG); + Logger.e(TAG, "userId max length is 1000."); return; } if (TextUtils.isEmpty(userId)) { // to null, never send visit, just return - PersistentDataProvider.get().setLoginUserIdAndUserKey(null, null); + persistentDataProvider.setLoginUserIdAndUserKey(null, null); Logger.d(TAG, "clean the userId (and will also clean the userKey"); return; } @@ -80,7 +87,7 @@ public void setLoginUserId(String userId, String userKey) { if (ObjectUtils.equals(userId, lastUserId)) { if (!ObjectUtils.equals(getLoginUserKey(), userKey)) { Logger.d(TAG, "setUserId, the userId=" + userId + " is same as the old userId, but the userKey=" + userKey + " is different."); - PersistentDataProvider.get().setLoginUserIdAndUserKey(userId, userKey); + persistentDataProvider.setLoginUserIdAndUserKey(userId, userKey); } else { Logger.d(TAG, "setUserId, the userId is same as the old userId, just return"); } @@ -88,20 +95,20 @@ public void setLoginUserId(String userId, String userKey) { } Logger.d(TAG, "userIdChange: newUserId = " + userId + ", latestUserId = " + lastUserId); - PersistentDataProvider.get().setLoginUserIdAndUserKey(userId, (TextUtils.isEmpty(userKey) ? null : userKey)); + persistentDataProvider.setLoginUserIdAndUserKey(userId, (TextUtils.isEmpty(userKey) ? null : userKey)); needSendVisit(userId); } @TrackThread private void needSendVisit(String newUserId) { - String mLatestNonNullUserId = PersistentDataProvider.get().getLatestNonNullUserId(); + String mLatestNonNullUserId = persistentDataProvider.getLatestNonNullUserId(); if (newUserId != null && newUserId.length() != 0) { if (!TextUtils.isEmpty(mLatestNonNullUserId) && !newUserId.equals(mLatestNonNullUserId)) { Logger.d(TAG, "resend visit after UserIdChanged"); - SessionProvider.get().refreshSessionId(); - SessionProvider.get().generateVisit(); + sessionProvider.refreshSessionId(); + sessionProvider.generateVisit(); } - PersistentDataProvider.get().setLatestNonNullUserId(newUserId); + persistentDataProvider.setLatestNonNullUserId(newUserId); } } } \ No newline at end of file diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ActivityUtil.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ActivityUtil.java index d752fd31..031ead3b 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ActivityUtil.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ActivityUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.utils; import android.app.Activity; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ClassExistHelper.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ClassExistHelper.java index 31557488..2a0ddf65 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ClassExistHelper.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ClassExistHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.utils; import android.webkit.WebView; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ConstantPool.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ConstantPool.java index 27f0185f..5a5a76aa 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ConstantPool.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ConstantPool.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.utils; public class ConstantPool { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/DeviceUtil.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/DeviceUtil.java index 69d07334..3906cd5b 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/DeviceUtil.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/DeviceUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.utils; import android.content.Context; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/NetworkUtil.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/NetworkUtil.java index f127b478..c1625983 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/NetworkUtil.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/NetworkUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.utils; import android.content.Context; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ObjectUtils.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ObjectUtils.java index 46d81ee2..793ab7ee 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ObjectUtils.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ObjectUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.utils; import java.lang.reflect.AccessibleObject; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/PermissionUtil.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/PermissionUtil.java index 968e725c..9f7f3d1f 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/PermissionUtil.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/PermissionUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.utils; import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; -import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.log.Logger; public class PermissionUtil { @@ -55,7 +54,7 @@ public static boolean checkReadPhoneStatePermission() { private static boolean checkPermission(String permissionName, int flag) { if (sPackageManager == null) { - Context context = TrackerContext.get().getApplicationContext(); + Context context = TrackMainThread.trackMain().getContext(); sPackageManager = context.getPackageManager(); sPackageName = context.getPackageName(); } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/SysTrace.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/SysTrace.java index 73a85495..fc83f1a6 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/SysTrace.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/SysTrace.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,13 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.utils; import android.os.Build; import android.os.Trace; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; /** * Just Delegate to Trace @@ -29,14 +27,14 @@ public final class SysTrace { private SysTrace() { } - public static void beginSection(String sectionName) { - if (ConfigurationProvider.core().isDebugEnabled() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + public static void beginSection(String sectionName, boolean enabled) { + if (enabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { Trace.beginSection(sectionName); } } - public static void endSection() { - if (ConfigurationProvider.core().isDebugEnabled() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + public static void endSection(boolean enabled) { + if (enabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { Trace.endSection(); } } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/SystemUtil.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/SystemUtil.java index 199c20d8..45e9d4fb 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/SystemUtil.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/SystemUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.utils; import android.app.Application; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ThreadUtils.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ThreadUtils.java deleted file mode 100644 index 34af3377..00000000 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/ThreadUtils.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.growingio.android.sdk.track.utils; - -import android.os.Handler; -import android.os.Looper; - -/** - * Helper methods to deal with threading related tasks. - */ -public class ThreadUtils { - - private static final Object LOCK = new Object(); - - private static boolean sWillOverride = false; - - private static Handler sUiThreadHandler = null; - - private ThreadUtils() { - } - - public static void setWillOverrideUiThread() { - synchronized (LOCK) { - sWillOverride = true; - } - } - - public static void setUiThread(Looper looper) { - synchronized (LOCK) { - if (sUiThreadHandler != null && sUiThreadHandler.getLooper() != looper) { - throw new RuntimeException("UI thread looper is already set to " - + sUiThreadHandler.getLooper() + " (Main thread looper is " - + Looper.getMainLooper() + "), cannot set to new looper " + looper); - } else { - sUiThreadHandler = new Handler(looper); - } - } - } - - private static Handler getUiThreadHandler() { - synchronized (LOCK) { - if (sUiThreadHandler == null) { - if (sWillOverride) { - throw new RuntimeException("Did not yet override the UI thread"); - } - sUiThreadHandler = new Handler(Looper.getMainLooper()); - } - return sUiThreadHandler; - } - } - - /** - * Run the supplied Runnable on the main thread. The method will block only if the current - * thread is the main thread. - * - * @param r The Runnable to run - */ - public static void runOnUiThread(Runnable r) { - if (runningOnUiThread()) { - r.run(); - } else { - getUiThreadHandler().post(r); - } - } - - /** - * Post the supplied Runnable to run on the main thread after the given amount of time. The - * method will not block, even if called on the UI thread. - * - * @param task The Runnable to run - * @param delayMillis The delay in milliseconds until the Runnable will be run - */ - public static void postOnUiThreadDelayed(Runnable task, long delayMillis) { - getUiThreadHandler().postDelayed(task, delayMillis); - } - - /** - * @return true if the current thread is the main (UI) thread. - */ - public static boolean runningOnUiThread() { - return getUiThreadHandler().getLooper() == Looper.myLooper(); - } -} diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/timer/Timer.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/TimerEvent.java similarity index 58% rename from growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/timer/Timer.java rename to growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/TimerEvent.java index b10bf6a9..65378f1f 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/timer/Timer.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/TimerEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,25 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.growingio.android.sdk.track.utils; -package com.growingio.android.sdk.track.timer; +public class TimerEvent { + private final String eventName; + private long startTime = 0; + private long elapsedTime = 0; -public class Timer { - private final String mEventName; - private long mStartTime = 0; - private long mElapsedTime = 0; - - public Timer(long startTime, final String eventName) { - this.mStartTime = startTime; - this.mEventName = eventName; + public TimerEvent(long startTime, final String eventName) { + this.startTime = startTime; + this.eventName = eventName; } public String getEventName() { - return this.mEventName; + return this.eventName; } public boolean isResume() { - return mStartTime != 0; + return startTime != 0; } public void updateState(long currentTime, boolean isResume) { @@ -40,34 +39,34 @@ public void updateState(long currentTime, boolean isResume) { } if (isResume) { - mStartTime = currentTime; + startTime = currentTime; } else { - mElapsedTime += currentTime - mStartTime; - mStartTime = 0; + elapsedTime += currentTime - startTime; + startTime = 0; } } public void resetStartTimeBeforeEnterForeground(long currentTime) { if (isResume()) { - this.mStartTime = currentTime; + this.startTime = currentTime; } } public void computeElapsedTimeBeforeEnterBackground(long currentTime) { if (isResume()) { - this.mElapsedTime += currentTime - mStartTime; - this.mStartTime = currentTime; + this.elapsedTime += currentTime - startTime; + this.startTime = currentTime; } } public String computeElapsedTime(long currentTime) { if (isResume()) { - mElapsedTime += currentTime - mStartTime; + elapsedTime += currentTime - startTime; } - if (mElapsedTime < 0 || mElapsedTime > 24 * 60 * 60 * 1000L) { + if (elapsedTime < 0 || elapsedTime > 24 * 60 * 60 * 1000L) { return "0"; } - return String.valueOf(mElapsedTime / 1000F); + return String.valueOf(elapsedTime / 1000F); } } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/DecorView.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/DecorView.java index 967fe216..d77535c7 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/DecorView.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/DecorView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.view; import android.graphics.Rect; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/OnViewStateChangedListener.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/OnViewStateChangedListener.java index 1d533c44..ce78aa16 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/OnViewStateChangedListener.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/OnViewStateChangedListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.view; public interface OnViewStateChangedListener { diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/ScreenshotUtil.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/ScreenshotUtil.java index 0f1e4fb9..e2490454 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/ScreenshotUtil.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/ScreenshotUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.view; import android.app.Activity; +import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; @@ -28,12 +28,9 @@ import android.view.Window; import android.view.WindowManager; -import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; import com.growingio.android.sdk.track.utils.DeviceUtil; -import com.growingio.android.sdk.track.webservices.widget.TipView; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -51,8 +48,8 @@ public static Bitmap getScreenshotBitmap() { break; } } - - DisplayMetrics metrics = DeviceUtil.getDisplayMetrics(TrackerContext.get().getApplicationContext()); + Context context = TrackMainThread.trackMain().getContext(); + DisplayMetrics metrics = DeviceUtil.getDisplayMetrics(context); Bitmap bitmap = Bitmap.createBitmap(metrics.widthPixels, metrics.heightPixels, Bitmap.Config.ARGB_8888); drawDecorViewsToBitmap(decorViews, bitmap); return bitmap; @@ -95,7 +92,7 @@ public static void getScreenshotBitmap(float scale, ScreenshotCallback callback) // PixelCopy // https://muyangmin.github.io/glide-docs-cn/doc/hardwarebitmaps.html if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - Activity activity = ActivityStateProvider.get().getForegroundActivity(); + Activity activity = TrackMainThread.trackMain().getForegroundActivity(); if (activity == null) { getScreenShotBitmapDefault(scale, callback); return; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/widget/TipView.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/TipView.java similarity index 97% rename from growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/widget/TipView.java rename to growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/TipView.java index 6068d32c..f4377e64 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/widget/TipView.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/TipView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.growingio.android.sdk.track.view; -package com.growingio.android.sdk.track.webservices.widget; - +import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.graphics.Color; @@ -51,6 +51,7 @@ public class TipView extends FrameLayout { private int mViewLastY; private float mTouchDownY; + @SuppressLint("WrongConstant") public TipView(Context context) { super(context); mContext = context; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/ViewStateChangedEvent.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/ViewStateChangedEvent.java index d10c5975..0f9ce880 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/ViewStateChangedEvent.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/ViewStateChangedEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.view; import android.view.View; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/ViewTreeStatusListener.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/ViewTreeStatusListener.java index 0afaca5a..bb32b701 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/ViewTreeStatusListener.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/ViewTreeStatusListener.java @@ -1,74 +1,89 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.view; import android.app.Activity; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.R; -import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.events.EventBuildInterceptor; import com.growingio.android.sdk.track.events.base.BaseEvent; import com.growingio.android.sdk.track.listener.IActivityLifecycle; import com.growingio.android.sdk.track.listener.event.ActivityLifecycleEvent; import com.growingio.android.sdk.track.middleware.GEvent; import com.growingio.android.sdk.track.providers.ActivityStateProvider; +import com.growingio.android.sdk.track.providers.EventBuilderProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; /** *

* * @author cpacm 2023/5/5 */ -public abstract class ViewTreeStatusListener implements IActivityLifecycle, OnViewStateChangedListener, EventBuildInterceptor { +public abstract class ViewTreeStatusListener implements IActivityLifecycle, OnViewStateChangedListener, EventBuildInterceptor, TrackerLifecycleProvider { + + private final ViewTreeStatusObserver viewTreeStatusObserver; - private final ViewTreeStatusObserver mViewTreeStatusObserver; + protected ActivityStateProvider activityStateProvider; + protected EventBuilderProvider eventBuilderProvider; public ViewTreeStatusListener() { - mViewTreeStatusObserver = new ViewTreeStatusObserver(this, R.id.growing_tracker_monitoring_view_tree_listener); + viewTreeStatusObserver = new ViewTreeStatusObserver(this, R.id.growing_tracker_monitoring_view_tree_listener); + } + + @Override + public void setup(TrackerContext context) { + activityStateProvider = context.getActivityStateProvider(); + eventBuilderProvider = context.getProvider(EventBuilderProvider.class); + } + + @Override + public void shutdown() { + unRegister(); } @Override public void onActivityLifecycle(ActivityLifecycleEvent event) { Activity activity = event.getActivity(); if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_RESUMED) { - mViewTreeStatusObserver.onActivityResumed(activity); + viewTreeStatusObserver.onActivityResumed(activity); } else if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_PAUSED) { - mViewTreeStatusObserver.onActivityPaused(activity); + viewTreeStatusObserver.onActivityPaused(activity); } } public void register() { - ActivityStateProvider.get().registerActivityLifecycleListener(this); + activityStateProvider.registerActivityLifecycleListener(this); registerResumedActivity(); - TrackMainThread.trackMain().addEventBuildInterceptor(this); + eventBuilderProvider.addEventBuildInterceptor(this); } public void unRegister() { - ActivityStateProvider.get().unregisterActivityLifecycleListener(this); - Activity activity = ActivityStateProvider.get().getResumedActivity(); + activityStateProvider.unregisterActivityLifecycleListener(this); + Activity activity = activityStateProvider.getResumedActivity(); if (activity != null) { - mViewTreeStatusObserver.onActivityPaused(activity); + viewTreeStatusObserver.onActivityPaused(activity); } - TrackMainThread.trackMain().removeEventBuildInterceptor(this); + eventBuilderProvider.removeEventBuildInterceptor(this); } private void registerResumedActivity() { - Activity activity = ActivityStateProvider.get().getResumedActivity(); + Activity activity = activityStateProvider.getResumedActivity(); if (activity != null && !activity.isDestroyed()) { - mViewTreeStatusObserver.onActivityResumed(activity); + viewTreeStatusObserver.onActivityResumed(activity); } } @@ -78,6 +93,6 @@ public void eventWillBuild(BaseEvent.BaseBuilder eventBuilder) { @Override public void eventDidBuild(GEvent event) { - mViewTreeStatusObserver.sendManualStateChangedEvent(); + viewTreeStatusObserver.sendManualStateChangedEvent(); } } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/ViewTreeStatusObserver.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/ViewTreeStatusObserver.java index 4f6ad277..1fc751c5 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/ViewTreeStatusObserver.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/ViewTreeStatusObserver.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.view; import android.app.Activity; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/WindowHelper.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/WindowHelper.java index df66324f..a227b645 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/WindowHelper.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/WindowHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.view; import android.app.Activity; @@ -24,8 +23,8 @@ import android.view.View; import android.view.WindowManager; +import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; import java.util.ArrayList; import java.util.List; @@ -33,7 +32,6 @@ public class WindowHelper { private static final String TAG = "WindowHelper"; - private final WindowManagerShadow mWindowManager; private WindowHelper() { @@ -95,7 +93,7 @@ public boolean isDecorView(View rootView) { public List getTopActivityViews() { List topViews = new ArrayList<>(); - Activity activity = ActivityStateProvider.get().getForegroundActivity(); + Activity activity = TrackMainThread.trackMain().getForegroundActivity(); if (activity == null) return topViews; List decorViews = getAllWindowDecorViews(); boolean findTopActivity = false; @@ -131,7 +129,7 @@ public List getAllWindowDecorViews() { } public View getTopActivityDecorView() { - Activity current = ActivityStateProvider.get().getForegroundActivity(); + Activity current = TrackMainThread.trackMain().getForegroundActivity(); if (current != null) { try { return current.getWindow().getDecorView(); diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/WindowManagerShadow.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/WindowManagerShadow.java index 1dd03c7a..cdbde122 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/WindowManagerShadow.java +++ b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/view/WindowManagerShadow.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.view; import android.annotation.SuppressLint; diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/message/ClientInfoMessage.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/message/ClientInfoMessage.java deleted file mode 100644 index d7d5ea5c..00000000 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/message/ClientInfoMessage.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.growingio.android.sdk.track.webservices.message; - -import com.growingio.android.sdk.track.SDKConfig; -import com.growingio.android.sdk.track.providers.AppInfoProvider; -import com.growingio.android.sdk.track.providers.DeviceInfoProvider; -import com.growingio.android.sdk.track.utils.ConstantPool; - -import org.json.JSONException; -import org.json.JSONObject; - -public class ClientInfoMessage { - public static final String MSG_TYPE = "client_info"; - - private final String mMsgType; - private final String mSdkVersion; - private final JSONObject mData = new JSONObject(); - - public ClientInfoMessage(String sdkVersion, String os, String appVersion, String appChannel, String osVersion, String deviceType, String deviceBrand, String deviceModel) { - mMsgType = MSG_TYPE; - mSdkVersion = sdkVersion; - - try { - mData.put("os", os); - mData.put("appVersion", appVersion); - mData.put("appChannel", appChannel); - mData.put("osVersion", osVersion); - mData.put("deviceType", deviceType); - mData.put("deviceBrand", deviceBrand); - mData.put("deviceModel", deviceModel); - } catch (JSONException ignored) { - } - } - - public static ClientInfoMessage createMessage() { - DeviceInfoProvider deviceInfo = DeviceInfoProvider.get(); - AppInfoProvider appInfo = AppInfoProvider.get(); - return new ClientInfoMessage(SDKConfig.SDK_VERSION, ConstantPool.ANDROID, appInfo.getAppVersion(), appInfo.getAppChannel(), - deviceInfo.getOperatingSystemVersion(), deviceInfo.getDeviceType(), deviceInfo.getDeviceBrand(), deviceInfo.getDeviceModel()); - } - - public JSONObject toJSONObject() { - JSONObject json = new JSONObject(); - try { - json.put("msgType", mMsgType); - json.put("sdkVersion", mSdkVersion); - json.put("data", mData); - } catch (JSONException ignored) { - } - return json; - } -} diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/message/QuitMessage.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/message/QuitMessage.java deleted file mode 100644 index 967fb8ba..00000000 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/message/QuitMessage.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.growingio.android.sdk.track.webservices.message; - -import org.json.JSONException; -import org.json.JSONObject; - -public class QuitMessage { - private static final String TAG = "QuitMessage"; - - public static final String MSG_TYPE = "quit"; - private final String mMsgType; - - public QuitMessage() { - mMsgType = MSG_TYPE; - } - - public JSONObject toJSONObject() { - JSONObject json = new JSONObject(); - try { - json.put("msgType", mMsgType); - } catch (JSONException ignored) { - } - return json; - } -} diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/message/ReadyMessage.java b/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/message/ReadyMessage.java deleted file mode 100644 index 4f98f316..00000000 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/message/ReadyMessage.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.growingio.android.sdk.track.webservices.message; - -import android.content.Context; -import android.util.DisplayMetrics; - -import com.growingio.android.sdk.TrackerContext; -import com.growingio.android.sdk.track.SDKConfig; -import com.growingio.android.sdk.track.providers.AppInfoProvider; -import com.growingio.android.sdk.track.providers.ConfigurationProvider; -import com.growingio.android.sdk.track.utils.DeviceUtil; - -import org.json.JSONException; -import org.json.JSONObject; - -public class ReadyMessage { - public static final String MSG_TYPE = "ready"; - - private final String mProjectId; - private final String mMsgType; - private final long mTimestamp; - private final String mDomain; - private final String mSdkVersion; - private final int mSdkVersionCode; - private final String mOs; - private final int mScreenWidth; - private final int mScreenHeight; - private final String mUrlScheme; - - - private ReadyMessage(String projectId, String domain, String sdkVersion, int sdkVersionCode, int screenWidth, int screenHeight, String urlScheme) { - mMsgType = MSG_TYPE; - mOs = "Android"; - mTimestamp = System.currentTimeMillis(); - mProjectId = projectId; - mDomain = domain; - mSdkVersion = sdkVersion; - mSdkVersionCode = sdkVersionCode; - mScreenWidth = screenWidth; - mScreenHeight = screenHeight; - mUrlScheme = urlScheme; - } - - public static ReadyMessage createMessage() { - Context context = TrackerContext.get().getApplicationContext(); - String projectId = ConfigurationProvider.core().getProjectId(); - String domain = AppInfoProvider.get().getPackageName(); - String sdkVersion = SDKConfig.SDK_VERSION; - int sdkVersionCode = SDKConfig.SDK_VERSION_CODE; - - DisplayMetrics metrics = DeviceUtil.getDisplayMetrics(context); - String urlScheme = ConfigurationProvider.core().getUrlScheme(); - return new ReadyMessage(projectId, domain, sdkVersion, sdkVersionCode, metrics.widthPixels, metrics.heightPixels, urlScheme); - } - - public JSONObject toJSONObject() { - JSONObject json = new JSONObject(); - try { - json.put("projectId", mProjectId); - json.put("msgType", mMsgType); - json.put("timestamp", mTimestamp); - json.put("domain", mDomain); - json.put("sdkVersion", mSdkVersion); - json.put("sdkVersionCode", mSdkVersionCode); - json.put("os", mOs); - json.put("screenWidth", mScreenWidth); - json.put("screenHeight", mScreenHeight); - json.put("urlScheme", mUrlScheme); - } catch (JSONException ignored) { - } - return json; - } -} diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/TrackerTest.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/TrackerTest.java index 652a8e33..c2f8c150 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/TrackerTest.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/TrackerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,17 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk; import android.app.Application; -import android.content.Context; import android.webkit.WebView; import androidx.test.core.app.ApplicationProvider; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,8 +40,11 @@ public class TrackerTest { @Test public void initTest() { - Tracker nullTracker = new Tracker(null); - assertThat(nullTracker.isInited).isFalse(); + try { + Tracker nullTracker = new Tracker(null); + }catch (Exception e){ + assertThat(e).isInstanceOf(IllegalArgumentException.class); + } } @Test @@ -77,8 +77,7 @@ public void apiTest() { tracker.cleanLoginUserId(); TestLibraryGioModule testLibraryGioModule = new TestLibraryGioModule(); - testLibraryGioModule.registerComponents(application, TrackerContext.get().getRegistry()); - testLibraryGioModule.getConfiguration(TestLibraryGioModule.class); + testLibraryGioModule.registerComponents(tracker.getContext()); tracker.registerComponent(testLibraryGioModule); } @@ -101,8 +100,8 @@ public void coreConfiguration() { public static class TestLibraryGioModule extends LibraryGioModule implements Configurable { @Override - public void registerComponents(Context context, TrackerRegistry registry) { - super.registerComponents(context, registry); + public void registerComponents(TrackerContext context) { + super.registerComponents(context); } } diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/AsyncTest.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/AsyncTest.java deleted file mode 100644 index d763117f..00000000 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/AsyncTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.growingio.android.sdk.track; - - -import com.google.common.truth.Truth; -import com.google.common.util.concurrent.Uninterruptibles; -import com.growingio.android.sdk.track.async.Disposable; -import com.growingio.android.sdk.track.async.HandlerDisposable; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.Robolectric; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -import java.util.concurrent.TimeUnit; - -@Config(manifest = Config.NONE) -@RunWith(RobolectricTestRunner.class) -public class AsyncTest { - - @Test - public void handlerDisposableTest() { - HandlerDisposable hd = new HandlerDisposable(); - Disposable d = hd.schedule(hd::dispose, 1000); - Truth.assertThat(d.isDisposed()).isFalse(); - Truth.assertThat(hd.isDisposed()).isFalse(); - //ShadowLooper.runUiThreadTasks(); - Robolectric.flushForegroundThreadScheduler(); - Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - Truth.assertThat(d.isDisposed()).isTrue(); - Disposable empty = hd.schedule(() -> { - }, 1000); - Truth.assertThat(empty.isDisposed()).isTrue(); - } -} diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/IpcTest.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/IpcTest.java index 6f2d1d24..e55a0b00 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/IpcTest.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/IpcTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track; diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/LogTest.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/LogTest.java index ce4602ce..c9704184 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/LogTest.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/LogTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track; diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/ModelLoaderTest.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/ModelLoaderTest.java index 5234f35c..1eb005bf 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/ModelLoaderTest.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/ModelLoaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track; import com.google.common.truth.Truth; diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/events/EventsTest.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/events/EventsTest.java index f928d02d..41dea838 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/events/EventsTest.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/events/EventsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,20 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.events; -import android.app.Application; - -import androidx.test.core.app.ApplicationProvider; import com.google.common.truth.Truth; -import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.events.base.BaseEvent; -import com.growingio.android.sdk.track.providers.EventStateProvider; +import com.growingio.android.sdk.track.events.base.BaseEventJsonSerializableFactory; import org.json.JSONObject; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -41,14 +35,6 @@ @Config(manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) public class EventsTest { - - Application application = ApplicationProvider.getApplicationContext(); - - @Before - public void setup() { - TrackerContext.init(application); - } - @Test public void eventAppClose() { AppClosedEvent event = new AppClosedEvent.Builder().build(); @@ -199,7 +185,8 @@ public void eventVisitAttr() { } public void inRobolectric(BaseEvent event) { - JSONObject jsonObject = EventStateProvider.get().toJson(event); + JSONObject jsonObject = new JSONObject(); + BaseEventJsonSerializableFactory.create().toJson(jsonObject, event); Truth.assertThat(jsonObject.opt("platform")).isEqualTo("Android"); } diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/middleware/EventSenderTest.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/middleware/EventSenderTest.java index 978e4016..32dc2b13 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/middleware/EventSenderTest.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/middleware/EventSenderTest.java @@ -1,17 +1,17 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.growingio.android.sdk.track.middleware; @@ -24,8 +24,10 @@ import com.google.common.util.concurrent.Uninterruptibles; import com.google.protobuf.InvalidProtocolBufferException; import com.growingio.android.json.JsonDataLoader; +import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.events.CustomEvent; +import com.growingio.android.sdk.track.events.TrackEventType; import com.growingio.android.sdk.track.middleware.format.EventByteArray; import com.growingio.android.sdk.track.middleware.format.EventFormatData; import com.growingio.android.database.DatabaseDataLoader; @@ -55,13 +57,15 @@ public class EventSenderTest { private final Application application = ApplicationProvider.getApplicationContext(); private EventSender eventSender; + private TrackerContext context; + @Before public void setup() { - TrackerContext.init(application); + context = new Tracker(application).getContext(); ProviderInfo providerInfo = new ProviderInfo(); providerInfo.authority = application.getPackageName() + "." + EventDataContentProvider.class.getSimpleName(); - eventSender = new EventSender(null, 0, 10); + eventSender = new EventSender(application, context.getRegistry(), null, 0, 10); controller.create(providerInfo).get(); } @@ -87,9 +91,9 @@ public void eventSendTest() { @Test public void eventCacheTestPb() throws InvalidProtocolBufferException { - TrackerContext.get().getRegistry().register(EventDatabase.class, EventDbResult.class, new DatabaseDataLoader.Factory(application)); - TrackerContext.get().getRegistry().register(EventFormatData.class, EventByteArray.class, new ProtobufDataLoader.Factory()); - + context.getRegistry().register(EventDatabase.class, EventDbResult.class, new DatabaseDataLoader.Factory(context)); + context.getRegistry().register(EventFormatData.class, EventByteArray.class, new ProtobufDataLoader.Factory()); + eventSender.removeAllEvents(); CustomEvent ce = new CustomEvent.Builder() .setEventName("cpacm").build(); eventSender.cacheEvent(ce); @@ -102,8 +106,11 @@ public void eventCacheTestPb() throws InvalidProtocolBufferException { eventSender.setEventNetSender((events, mediaType) -> { try { EventV3Protocol.EventV3List list1 = EventV3Protocol.EventV3List.parseFrom(events); - Truth.assertThat(list1.getValuesCount()).isEqualTo(2); - Truth.assertThat(list1.getValues(0).getEventName()).isEqualTo("cpacm"); + for (EventV3Protocol.EventV3Dto dto : list1.getValuesList()) { + if (dto.getEventType().toString().equals(TrackEventType.CUSTOM)) { + Truth.assertThat(dto.getEventName()).isEqualTo("cpacm"); + } + } } catch (InvalidProtocolBufferException e) { e.printStackTrace(); } @@ -112,13 +119,13 @@ public void eventCacheTestPb() throws InvalidProtocolBufferException { eventSender.cacheEvent(ce); eventSender.cacheEvent(ce); eventSender.sendEvents(false); - Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); + Uninterruptibles.sleepUninterruptibly(2, TimeUnit.SECONDS); } @Test public void eventCacheTestJson() throws JSONException { - TrackerContext.get().getRegistry().register(EventDatabase.class, EventDbResult.class, new DatabaseDataLoader.Factory(application)); - TrackerContext.get().getRegistry().register(EventFormatData.class, EventByteArray.class, new JsonDataLoader.Factory()); + context.getRegistry().register(EventDatabase.class, EventDbResult.class, new DatabaseDataLoader.Factory(context)); + context.getRegistry().register(EventFormatData.class, EventByteArray.class, new JsonDataLoader.Factory()); CustomEvent ce = new CustomEvent.Builder() .setEventName("cpacm").build(); @@ -133,10 +140,13 @@ public void eventCacheTestJson() throws JSONException { eventSender.setEventNetSender((events, mediaType) -> { try { JSONArray array = new JSONArray(new String(events)); - Truth.assertThat(array.length()).isEqualTo(2); - - JSONObject obj1 = array.getJSONObject(0); - Truth.assertThat(obj1.opt("eventName")).isEqualTo("cpacm"); + for (int i = 0; i < array.length(); i++) { + JSONObject obj = array.getJSONObject(i); + String eventType = obj.getString("eventType"); + if (eventType.equals(TrackEventType.CUSTOM)) { + Truth.assertThat(obj.opt("eventName")).isEqualTo("cpacm"); + } + } } catch (JSONException e) { e.printStackTrace(); } diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/middleware/HttpSenderTest.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/middleware/HttpSenderTest.java index fefde285..92289a2d 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/middleware/HttpSenderTest.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/middleware/HttpSenderTest.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware; @@ -22,6 +21,7 @@ import androidx.test.core.app.ApplicationProvider; import com.google.common.truth.Truth; +import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.events.CustomEvent; import com.growingio.android.sdk.track.middleware.http.EventEncoder; @@ -42,10 +42,12 @@ public class HttpSenderTest { private final Application application = ApplicationProvider.getApplicationContext(); private EventHttpSender eventHttpSender; + private TrackerContext context; + @Before public void setup() { - TrackerContext.init(application); - eventHttpSender = new EventHttpSender(); + context = new Tracker(application).getContext(); + eventHttpSender = new EventHttpSender(context); } @Test @@ -67,8 +69,7 @@ public void sendTest() throws IOException { Truth.assertThat(eventHttpSender.send(null, null).isSucceeded()).isFalse(); GEvent gEvent = new CustomEvent.Builder().build(); Truth.assertThat(eventHttpSender.send(Serializer.objectSerialize(gEvent), "").isSucceeded()).isFalse(); - TrackerContext.get().getRegistry() - .register(EventUrl.class, EventResponse.class, new TestModelFactory<>(new EventResponse(true))); + context.getRegistry().register(EventUrl.class, EventResponse.class, new TestModelFactory<>(new EventResponse(true))); EventUrl eventUrl = new EventUrl("https://localhost", 10000L) .addPath("v3") @@ -77,8 +78,7 @@ public void sendTest() throws IOException { .addPath("collect") .addParam("stm", String.valueOf(10000L)) .setBodyData("cpacm".getBytes()); - TrackerContext.get().getRegistry() - .register(EventEncoder.class, EventEncoder.class, new TestModelFactory<>(new EventEncoder(eventUrl))); + context.getRegistry().register(EventEncoder.class, EventEncoder.class, new TestModelFactory<>(new EventEncoder(eventUrl))); SendResponse response = eventHttpSender.send(Serializer.objectSerialize(gEvent), ""); Truth.assertThat(response.getUsedBytes()).isEqualTo("cpacm".getBytes().length); diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/middleware/TestModelFactory.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/middleware/TestModelFactory.java index 2e18fd1b..5db85b9c 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/middleware/TestModelFactory.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/middleware/TestModelFactory.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.sdk.track.middleware; diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/TrackMainFilterTest.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/providers/EventBuilderProviderFilterTest.java similarity index 67% rename from growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/TrackMainFilterTest.java rename to growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/providers/EventBuilderProviderFilterTest.java index b5dca9ce..8c8ad896 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/TrackMainFilterTest.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/providers/EventBuilderProviderFilterTest.java @@ -1,20 +1,19 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - -package com.growingio.android.sdk.track; +package com.growingio.android.sdk.track.providers; import android.app.Application; @@ -22,6 +21,7 @@ import com.google.common.truth.Truth; import com.growingio.android.sdk.CoreConfiguration; +import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.events.CustomEvent; import com.growingio.android.sdk.track.events.EventBuildInterceptor; @@ -42,14 +42,12 @@ @Config(manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) -public class TrackMainFilterTest { +public class EventBuilderProviderFilterTest { Application application = ApplicationProvider.getApplicationContext(); - TrackMainThread trackMainThread; CustomEventFilterInterceptor eventFilterInterceptor; static class CustomEventFilterInterceptor extends DefaultEventFilterInterceptor { - @Override public boolean filterEventType(String eventType) { if ("PAGE".equals(eventType)) return false; @@ -78,18 +76,23 @@ public boolean filterEventName(String eventName) { } } + private TrackerContext context; + @Before public void setup() { - TrackerContext.init(application); eventFilterInterceptor = new CustomEventFilterInterceptor(); CoreConfiguration coreConfiguration = new CoreConfiguration("TrackMainFilterTest", "growingio://trackMainfilter").setEventFilterInterceptor(eventFilterInterceptor); - trackMainThread = new TrackMainThread(coreConfiguration); + TrackerLifecycleProviderFactory.create().createConfigurationProviderWithConfig(coreConfiguration, null); + Tracker tracker = new Tracker(application); + context = tracker.getContext(); } @Test public void filterEventType() { - trackMainThread.addEventBuildInterceptor(new EventBuildInterceptor() { + EventBuilderProvider eventBuilderProvider = context.getProvider(EventBuilderProvider.class); + + eventBuilderProvider.addEventBuildInterceptor(new EventBuildInterceptor() { @Override public void eventWillBuild(BaseEvent.BaseBuilder eventBuilder) { } @@ -103,13 +106,14 @@ public void eventDidBuild(GEvent event) { Truth.assertThat(event instanceof PageEvent).isFalse(); } }); - trackMainThread.onGenerateGEvent(new PageEvent.Builder()); - trackMainThread.onGenerateGEvent(new CustomEvent.Builder().setEventName("cpacm")); + eventBuilderProvider.onGenerateGEvent(new PageEvent.Builder()); + eventBuilderProvider.onGenerateGEvent(new CustomEvent.Builder().setEventName("cpacm")); } @Test public void filterEventName() { - trackMainThread.addEventBuildInterceptor(new EventBuildInterceptor() { + EventBuilderProvider eventBuilderProvider = context.getProvider(EventBuilderProvider.class); + eventBuilderProvider.addEventBuildInterceptor(new EventBuildInterceptor() { @Override public void eventWillBuild(BaseEvent.BaseBuilder eventBuilder) { } @@ -122,13 +126,14 @@ public void eventDidBuild(GEvent event) { } } }); - trackMainThread.onGenerateGEvent(new CustomEvent.Builder().setEventName("gio")); - trackMainThread.onGenerateGEvent(new CustomEvent.Builder().setEventName("cpacm")); + eventBuilderProvider.onGenerateGEvent(new CustomEvent.Builder().setEventName("gio")); + eventBuilderProvider.onGenerateGEvent(new CustomEvent.Builder().setEventName("cpacm")); } @Test public void filterEventField() { - trackMainThread.addEventBuildInterceptor(new EventBuildInterceptor() { + EventBuilderProvider eventBuilderProvider = context.getProvider(EventBuilderProvider.class); + eventBuilderProvider.addEventBuildInterceptor(new EventBuildInterceptor() { @Override public void eventWillBuild(BaseEvent.BaseBuilder eventBuilder) { } @@ -143,7 +148,7 @@ public void eventDidBuild(GEvent event) { } } }); - trackMainThread.onGenerateGEvent(new CustomEvent.Builder().setEventName("cpacm")); + eventBuilderProvider.onGenerateGEvent(new CustomEvent.Builder().setEventName("cpacm")); } } diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/providers/ProviderTest.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/providers/ProviderTest.java index 765fd5be..45c4684f 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/providers/ProviderTest.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/providers/ProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,8 @@ import com.google.common.util.concurrent.Uninterruptibles; import com.growingio.android.sdk.Configurable; import com.growingio.android.sdk.CoreConfiguration; +import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.TrackerContext; -import com.growingio.android.sdk.track.ipc.PersistentDataProvider; import com.growingio.android.sdk.track.listener.IActivityLifecycle; import com.growingio.android.sdk.track.listener.event.ActivityLifecycleEvent; @@ -49,16 +49,20 @@ public class ProviderTest { private final Application application = ApplicationProvider.getApplicationContext(); + private TrackerContext context; + @Before public void setup() { - TrackerContext.init(application); + TrackerLifecycleProviderFactory.create().createConfigurationProviderWithConfig(new CoreConfiguration("ProviderTest", "growingio://provider"), new HashMap<>()); + Tracker tracker = new Tracker(application); + context = tracker.getContext(); } @Test public void activityStateProvider() { + ActivityStateProvider activityStateProvider = context.getActivityStateProvider(); ActivityController activityController = Robolectric.buildActivity(RobolectricActivity.class); RobolectricActivity activity = activityController.get(); - application.registerActivityLifecycleCallbacks(ActivityStateProvider.get()); Bundle testBundle = new Bundle(); @@ -75,34 +79,33 @@ public void onActivityLifecycle(ActivityLifecycleEvent event) { } } }; - ActivityStateProvider.get().register(null); - ActivityStateProvider.get().register(iActivityLifecycle); - ActivityStateProvider.get().register(null); + activityStateProvider.register(null); + activityStateProvider.register(iActivityLifecycle); + activityStateProvider.register(null); activityController.create(testBundle); activityController.resume(); activityController.pause(); activityController.stop(); activityController.destroy(); - ActivityStateProvider.get().unregisterActivityLifecycleListener(iActivityLifecycle); + activityStateProvider.shutdown(); } @Test public void appInfoProvider() { - Truth.assertThat(AppInfoProvider.get().getPackageName()).isEqualTo(application.getPackageName()); - Truth.assertThat(AppInfoProvider.get().getAppChannel()).isNull(); - Truth.assertThat(AppInfoProvider.get().getAppName()).isEqualTo(application.getPackageManager().getApplicationLabel(application.getApplicationInfo()).toString()); - Truth.assertThat(AppInfoProvider.get().getAppVersion()).isNull(); + AppInfoProvider appInfoProvider = context.getProvider(AppInfoProvider.class); + Truth.assertThat(appInfoProvider.getPackageName()).isEqualTo(application.getPackageName()); + Truth.assertThat(appInfoProvider.getAppName()).isEqualTo(application.getPackageManager().getApplicationLabel(application.getApplicationInfo()).toString()); + Truth.assertThat(appInfoProvider.getAppVersion()).isNull(); } @Test public void configProvider() { - ConfigurationProvider.initWithConfig(new CoreConfiguration("ProviderTest", "growingio://provider"), new HashMap<>()); - Truth.assertThat(ConfigurationProvider.core().getUrlScheme()).isEqualTo("growingio://provider"); - ConfigurationProvider.initWithConfig(new CoreConfiguration("ProviderTest", "growingio://provider"), new HashMap<>()); + ConfigurationProvider configurationProvider = context.getConfigurationProvider(); + Truth.assertThat(configurationProvider.core().getUrlScheme()).isEqualTo("growingio://provider"); TestConfigurable testConfigurable = new TestConfigurable(); - ConfigurationProvider.get().addConfiguration(testConfigurable); - Truth.assertThat((TestConfigurable) ConfigurationProvider.get().getConfiguration(TestConfigurable.class)).isEqualTo(testConfigurable); + configurationProvider.addConfiguration(testConfigurable); + Truth.assertThat((TestConfigurable) configurationProvider.getConfiguration(TestConfigurable.class)).isEqualTo(testConfigurable); } public static class TestConfigurable implements Configurable { @@ -110,8 +113,7 @@ public static class TestConfigurable implements Configurable { @Test public void deeplinkProvider() { - application.registerActivityLifecycleCallbacks(ActivityStateProvider.get()); - DeepLinkProvider.get().init(); + DeepLinkProvider deepLinkProvider = context.getProvider(DeepLinkProvider.class); //empty Robolectric.buildActivity(RobolectricActivity.class).create().get(); @@ -145,19 +147,22 @@ public void deeplinkProvider() { @Test public void deviceInfoProvider() { - Truth.assertThat(DeviceInfoProvider.get().getAndroidId()).isNull(); - Truth.assertThat(DeviceInfoProvider.get().getDeviceBrand()).isEqualTo("robolectric"); - Truth.assertThat(DeviceInfoProvider.get().getDeviceId()).isNotEmpty(); - Truth.assertThat(DeviceInfoProvider.get().getDeviceModel()).isEqualTo("robolectric"); - Truth.assertThat(DeviceInfoProvider.get().getDeviceType()).isEqualTo("PHONE"); - Truth.assertThat(DeviceInfoProvider.get().getGoogleAdId()).isNull(); - Truth.assertThat(DeviceInfoProvider.get().getImei()).isNull(); - Truth.assertThat(DeviceInfoProvider.get().getOaid()).isNull(); - Truth.assertThat(DeviceInfoProvider.get().getOperatingSystemVersion()).isEqualTo("13"); - Truth.assertThat(DeviceInfoProvider.get().getScreenHeight()).isEqualTo(470); - Truth.assertThat(DeviceInfoProvider.get().getScreenWidth()).isEqualTo(320); - PersistentDataProvider.get().setDeviceId(""); - Truth.assertThat(PersistentDataProvider.get().getDeviceId()).isNotEmpty(); + DeviceInfoProvider deviceInfoProvider = context.getDeviceInfoProvider(); + Truth.assertThat(deviceInfoProvider.getAndroidId()).isNull(); + Truth.assertThat(deviceInfoProvider.getDeviceBrand()).isEqualTo("robolectric"); + Truth.assertThat(deviceInfoProvider.getDeviceId()).isNotEmpty(); + Truth.assertThat(deviceInfoProvider.getDeviceModel()).isEqualTo("robolectric"); + Truth.assertThat(deviceInfoProvider.getDeviceType()).isEqualTo("PHONE"); + Truth.assertThat(deviceInfoProvider.getGoogleAdId()).isNull(); + Truth.assertThat(deviceInfoProvider.getImei()).isNull(); + Truth.assertThat(deviceInfoProvider.getOaid()).isNull(); + Truth.assertThat(deviceInfoProvider.getOperatingSystemVersion()).isEqualTo("13"); + Truth.assertThat(deviceInfoProvider.getScreenHeight()).isEqualTo(470); + Truth.assertThat(deviceInfoProvider.getScreenWidth()).isEqualTo(320); + + PersistentDataProvider persistentDataProvider = context.getProvider(PersistentDataProvider.class); + persistentDataProvider.setDeviceId(""); + Truth.assertThat(persistentDataProvider.getDeviceId()).isNotEmpty(); } } diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/providers/RobolectricActivity.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/providers/RobolectricActivity.java index 65a7b3b3..ea1dda69 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/providers/RobolectricActivity.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/providers/RobolectricActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/timer/TimerTest.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/timer/TimerTest.java index 724aed50..7fb058f0 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/timer/TimerTest.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/timer/TimerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.timer; import android.os.SystemClock; import com.google.common.truth.Truth; +import com.growingio.android.sdk.track.utils.TimerEvent; import org.junit.Test; import org.junit.runner.RunWith; @@ -35,7 +35,7 @@ public void startThenEnd() { Robolectric.getForegroundThreadScheduler().advanceTo(1); long startTime = SystemClock.elapsedRealtime(); String eventName = "Test1"; - Timer timer = new Timer(startTime, eventName); + TimerEvent timer = new TimerEvent(startTime, eventName); Robolectric.getForegroundThreadScheduler().advanceTo(1001); long endTime = SystemClock.elapsedRealtime(); float elapsedTime = (endTime - startTime) / 1000F; @@ -50,7 +50,7 @@ public void pauseThenResume() { Robolectric.getForegroundThreadScheduler().advanceTo(1); long startTime = SystemClock.elapsedRealtime(); String eventName = "Test2"; - Timer timer = new Timer(startTime, eventName); + TimerEvent timer = new TimerEvent(startTime, eventName); Robolectric.getForegroundThreadScheduler().advanceTo(1001); long pauseTime = SystemClock.elapsedRealtime(); timer.updateState(pauseTime, false); @@ -73,7 +73,7 @@ public void backgroundThenForeground() { Robolectric.getForegroundThreadScheduler().advanceTo(1); long startTime = SystemClock.elapsedRealtime(); String eventName = "Test3"; - Timer timer = new Timer(startTime, eventName); + TimerEvent timer = new TimerEvent(startTime, eventName); Robolectric.getForegroundThreadScheduler().advanceTo(1001); long backgroundTime = SystemClock.elapsedRealtime(); timer.computeElapsedTimeBeforeEnterBackground(backgroundTime); diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/timer/TimerCenterTest.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/timer/TimingEventProviderTest.java similarity index 77% rename from growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/timer/TimerCenterTest.java rename to growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/timer/TimingEventProviderTest.java index 1fb478d9..c5928051 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/timer/TimerCenterTest.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/timer/TimingEventProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.timer; import android.app.Application; @@ -22,13 +21,14 @@ import androidx.test.core.app.ApplicationProvider; import com.google.common.truth.Truth; +import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.TrackerContext; -import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.events.CustomEvent; import com.growingio.android.sdk.track.events.EventBuildInterceptor; import com.growingio.android.sdk.track.events.base.BaseEvent; import com.growingio.android.sdk.track.middleware.GEvent; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; +import com.growingio.android.sdk.track.providers.EventBuilderProvider; +import com.growingio.android.sdk.track.providers.TimingEventProvider; import com.growingio.android.sdk.track.providers.RobolectricActivity; import org.junit.Before; @@ -44,19 +44,23 @@ @Config(manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) -public class TimerCenterTest { +public class TimingEventProviderTest { private final Application application = ApplicationProvider.getApplicationContext(); + private TrackerContext context; + @Before public void setup() { - TrackerContext.init(application); + Tracker tracker = new Tracker(application); + context = tracker.getContext(); } @Test public void backgroundThenForeground() throws InterruptedException { + TimingEventProvider timingEventProvider = context.getProvider(TimingEventProvider.class); + EventBuilderProvider eventBuilderProvider = context.getProvider(EventBuilderProvider.class); ActivityController activityController = Robolectric.buildActivity(RobolectricActivity.class); - application.registerActivityLifecycleCallbacks(ActivityStateProvider.get()); activityController.create().start().resume(); final class TimerResult { @@ -68,7 +72,7 @@ final class TimerResult { long totalTime = 0; Robolectric.getForegroundThreadScheduler().advanceTo(1); long startTime = SystemClock.elapsedRealtime(); - String timerId = TimerCenter.get().startTimer(timerResult.eventName); + String timerId = timingEventProvider.startTimer(timerResult.eventName); Robolectric.getForegroundThreadScheduler().advanceTo(1001); long backgroundTime = SystemClock.elapsedRealtime(); @@ -80,7 +84,7 @@ final class TimerResult { activityController.restart().start().resume(); CountDownLatch countDownLatch = new CountDownLatch(1); - TrackMainThread.trackMain().addEventBuildInterceptor(new EventBuildInterceptor() { + eventBuilderProvider.addEventBuildInterceptor(new EventBuildInterceptor() { @Override public void eventWillBuild(BaseEvent.BaseBuilder eventBuilder) { } @@ -90,7 +94,7 @@ public void eventDidBuild(GEvent event) { if (event instanceof CustomEvent) { CustomEvent timerEvent = (CustomEvent) event; Truth.assertThat(timerEvent.getEventName()).isEqualTo(timerResult.eventName); - Truth.assertThat(timerEvent.getAttributes().get(TimerCenter.ATTR_EVENT_DURATION)).isEqualTo(String.valueOf(timerResult.elapsedTime)); + Truth.assertThat(timerEvent.getAttributes().get(TimingEventProvider.ATTR_EVENT_DURATION)).isEqualTo(String.valueOf(timerResult.elapsedTime)); countDownLatch.countDown(); } } @@ -99,12 +103,15 @@ public void eventDidBuild(GEvent event) { long endTime = SystemClock.elapsedRealtime(); totalTime += endTime - foregroundTime; timerResult.elapsedTime = totalTime / 1000F; - TimerCenter.get().endTimer(timerId); + timingEventProvider.endTimer(timerId); countDownLatch.await(3000, TimeUnit.MILLISECONDS); } @Test public void pauseThenResume() throws InterruptedException { + + TimingEventProvider timingEventProvider = context.getProvider(TimingEventProvider.class); + EventBuilderProvider eventBuilderProvider = context.getProvider(EventBuilderProvider.class); final class TimerResult { public String eventName; public float elapsedTime; @@ -115,19 +122,19 @@ final class TimerResult { long totalTime = 0; Robolectric.getForegroundThreadScheduler().advanceTo(1); long startTime = SystemClock.elapsedRealtime(); - String timerId = TimerCenter.get().startTimer(timerResult.eventName); + String timerId = timingEventProvider.startTimer(timerResult.eventName); Robolectric.getForegroundThreadScheduler().advanceTo(1001); long pauseTime = SystemClock.elapsedRealtime(); totalTime += pauseTime - startTime; - TimerCenter.get().updateTimer(timerId, false); + timingEventProvider.updateTimer(timerId, false); Robolectric.getForegroundThreadScheduler().advanceTo(2001); long resumeTime = SystemClock.elapsedRealtime(); - TimerCenter.get().updateTimer(timerId, true); + timingEventProvider.updateTimer(timerId, true); CountDownLatch countDownLatch = new CountDownLatch(1); - TrackMainThread.trackMain().addEventBuildInterceptor(new EventBuildInterceptor() { + eventBuilderProvider.addEventBuildInterceptor(new EventBuildInterceptor() { @Override public void eventWillBuild(BaseEvent.BaseBuilder eventBuilder) { } @@ -137,7 +144,7 @@ public void eventDidBuild(GEvent event) { if (event instanceof CustomEvent) { CustomEvent timerEvent = (CustomEvent) event; Truth.assertThat(timerEvent.getEventName()).isEqualTo(timerResult.eventName); - Truth.assertThat(timerEvent.getAttributes().get(TimerCenter.ATTR_EVENT_DURATION)).isEqualTo(String.valueOf(timerResult.elapsedTime)); + Truth.assertThat(timerEvent.getAttributes().get(TimingEventProvider.ATTR_EVENT_DURATION)).isEqualTo(String.valueOf(timerResult.elapsedTime)); countDownLatch.countDown(); } } @@ -146,7 +153,7 @@ public void eventDidBuild(GEvent event) { long endTime = SystemClock.elapsedRealtime(); totalTime += endTime - resumeTime; timerResult.elapsedTime = totalTime / 1000F; - TimerCenter.get().endTimer(timerId); + timingEventProvider.endTimer(timerId); countDownLatch.await(3000, TimeUnit.MILLISECONDS); } } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/JsonUtil.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/utils/JsonUtil.java similarity index 96% rename from growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/JsonUtil.java rename to growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/utils/JsonUtil.java index e1646124..bc13c163 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/utils/JsonUtil.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/utils/JsonUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.utils; import org.json.JSONException; diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/utils/UtilsTest.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/utils/UtilsTest.java index 68cb817d..1cfda096 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/utils/UtilsTest.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/utils/UtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.utils; import android.app.Activity; @@ -21,7 +20,6 @@ import android.app.Dialog; import android.content.ContextWrapper; import android.net.ConnectivityManager; -import android.os.Looper; import android.telephony.TelephonyManager; import android.util.DisplayMetrics; import android.webkit.WebView; @@ -34,12 +32,11 @@ import androidx.viewpager.widget.ViewPager; import com.google.common.truth.Truth; -import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.track.providers.RobolectricActivity; import org.json.JSONException; import org.json.JSONObject; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; @@ -125,11 +122,6 @@ public void jsonTest() throws JSONException { Application application = ApplicationProvider.getApplicationContext(); - @Before - public void setup() { - TrackerContext.init(application); - } - @Test public void networkTest() { NetworkUtil.NetworkState state = NetworkUtil.getActiveNetworkState(application); @@ -154,7 +146,7 @@ public void objectTest() { @Test public void permissionTest() { - TrackerContext.init(application); + Tracker tracker = new Tracker(application); Truth.assertThat(PermissionUtil.hasAccessNetworkStatePermission()).isTrue(); Truth.assertThat(PermissionUtil.hasInternetPermission()).isTrue(); Truth.assertThat(PermissionUtil.hasWriteExternalPermission()).isFalse(); @@ -166,17 +158,8 @@ public void systemTest() { Truth.assertThat(SystemUtil.isMainProcess(application)).isTrue(); //SystemUtil.killAppProcess(application); - SysTrace.beginSection("test"); - SysTrace.endSection(); - } - - @Test - public void threadTest() { - ThreadUtils.setUiThread(Looper.myLooper()); - Truth.assertThat(ThreadUtils.runningOnUiThread()).isTrue(); - ThreadUtils.setWillOverrideUiThread(); - ThreadUtils.runOnUiThread(this::objectTest); - ThreadUtils.postOnUiThreadDelayed(this::objectTest, 1000L); + SysTrace.beginSection("test", true); + SysTrace.endSection(true); } } diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/view/FakeWindowManagerGlobal.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/view/FakeWindowManagerGlobal.java index 0c6da7ff..67352c44 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/view/FakeWindowManagerGlobal.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/view/FakeWindowManagerGlobal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.view; import android.view.View; diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/webservice/WebServiceTest.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/view/TipViewTest.java similarity index 50% rename from growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/webservice/WebServiceTest.java rename to growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/view/TipViewTest.java index d1ddf89a..01984b4a 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/webservice/WebServiceTest.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/view/TipViewTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,92 +13,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.growingio.android.sdk.track.view; -package com.growingio.android.sdk.track.webservice; - -import android.annotation.SuppressLint; import android.app.Activity; -import android.app.Application; import android.net.Uri; import android.view.MotionEvent; import android.view.WindowManager; -import androidx.test.core.app.ApplicationProvider; - -import com.growingio.android.sdk.TrackerContext; -import com.growingio.android.sdk.track.log.LogItem; -import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.providers.RobolectricActivity; -import com.growingio.android.sdk.track.webservices.Circler; -import com.growingio.android.sdk.track.webservices.Debugger; +import com.growingio.android.sdk.track.middleware.webservice.Circler; +import com.growingio.android.sdk.track.middleware.webservice.Debugger; import com.growingio.android.sdk.track.middleware.advert.DeepLink; -import com.growingio.android.sdk.track.webservices.log.LoggerDataMessage; -import com.growingio.android.sdk.track.webservices.log.WsLogger; -import com.growingio.android.sdk.track.webservices.message.ClientInfoMessage; -import com.growingio.android.sdk.track.webservices.message.QuitMessage; -import com.growingio.android.sdk.track.webservices.message.ReadyMessage; -import com.growingio.android.sdk.track.webservices.widget.TipView; -import org.json.JSONException; -import org.json.JSONObject; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import static com.google.common.truth.Truth.assertThat; @Config() @RunWith(RobolectricTestRunner.class) -public class WebServiceTest { - - @Test - public void logTest() { - WsLogger wsLogger = new WsLogger(); - wsLogger.setCallback(new WsLogger.Callback() { - @SuppressLint("CheckResult") - @Override - public void disposeLog(String logMessage) { - assertThat(logMessage.contains("this is test log")); - } - }); - wsLogger.openLog(); - Logger.v("test", "this is test log"); - wsLogger.closeLog(); - wsLogger.printOut(); - wsLogger.setCallback(null); - - } - - Application application = ApplicationProvider.getApplicationContext(); - - @Test - public void messageTest() throws JSONException { - JSONObject quit = new QuitMessage().toJSONObject(); - assertThat(quit.opt("msgType")).isEqualTo("quit"); - - TrackerContext.init(application); - JSONObject ready = ReadyMessage.createMessage().toJSONObject(); - assertThat(ready.opt("msgType")).isEqualTo("ready"); - - ClientInfoMessage info = ClientInfoMessage.createMessage(); - assertThat(info.toJSONObject().opt("msgType")).isEqualTo("client_info"); - - LogItem logItem = new LogItem.Builder().setMessage("webService") - .setPriority(0).setTag("webservice").setThrowable(null).setTimeStamp(0L).build(); - List list = new ArrayList<>(); - list.add(logItem); - LoggerDataMessage message = LoggerDataMessage - .createTrackMessage(list); - assertThat(message.toJSONObject().optJSONArray("data").getJSONObject(0).opt("message")).isEqualTo("webService"); - } - +public class TipViewTest { @Test public void tipTest() { diff --git a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/view/ViewTest.java b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/view/ViewTest.java index 271c7cd3..4f935883 100644 --- a/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/view/ViewTest.java +++ b/growingio-tracker-core/src/test/java/com/growingio/android/sdk/track/view/ViewTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.sdk.track.view; import android.app.Activity; @@ -23,10 +22,12 @@ import androidx.test.core.app.ApplicationProvider; import com.google.common.truth.Truth; +import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.providers.ActivityStateProvider; import com.growingio.android.sdk.track.providers.RobolectricActivity; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; @@ -42,13 +43,23 @@ @RunWith(RobolectricTestRunner.class) public class ViewTest { + private TrackerContext context; + + @Before + public void setup() { + Application application = ApplicationProvider.getApplicationContext(); + Tracker tracker = new Tracker(application); + context = tracker.getContext(); + } + @Test public void windowHelperTest() { + ActivityStateProvider activityStateProvider = context.getActivityStateProvider(); Activity activity = Robolectric.buildActivity(RobolectricActivity.class).create().resume().get(); Truth.assertThat(WindowHelper.get().isDecorView(activity.getWindow().getDecorView())).isTrue(); - ActivityStateProvider.get().onActivityResumed(activity); + activityStateProvider.onActivityResumed(activity); Truth.assertThat(WindowHelper.get().getTopActivityDecorView()).isEqualTo(activity.getWindow().getDecorView()); List views = WindowHelper.get().getTopActivityViews(); views.forEach(new Consumer() { @@ -70,9 +81,9 @@ public void windowManagerShadow() throws Exception { @Test public void screenshotTest() throws Exception { - TrackerContext.init(application); + ActivityStateProvider activityStateProvider = context.getActivityStateProvider(); Activity activity = Robolectric.buildActivity(RobolectricActivity.class).create().resume().get(); - ActivityStateProvider.get().onActivityResumed(activity); + activityStateProvider.onActivityResumed(activity); ScreenshotUtil.getScreenshotBitmap(1, bitmap -> { Truth.assertThat(bitmap).isNotNull(); diff --git a/growingio-webservice/circler/build.gradle b/growingio-webservice/circler/build.gradle index e1f1928e..113cb5ed 100644 --- a/growingio-webservice/circler/build.gradle +++ b/growingio-webservice/circler/build.gradle @@ -1,58 +1,58 @@ plugins { - id 'com.android.library' + id 'com.android.library' } android { - namespace 'com.growingio.android.circler' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } - - testOptions { - unitTests.all { - jacoco { - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - } - } + namespace 'com.growingio.android.circler' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } + + testOptions { + unitTests.all { + jacoco { + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } } dependencies { - testImplementation libs.bundles.test - testImplementation libs.okhttp3.mockwebserver - testImplementation libs.androidx.test.core - testImplementation project(':growingio-hybrid') + testImplementation libs.bundles.test + testImplementation libs.okhttp3.mockwebserver + testImplementation libs.androidx.test.core + testImplementation project(':growingio-hybrid') - //compileOnly fileTree(include: ['*.jar'], dir: '../../thirdLibs') + //compileOnly fileTree(include: ['*.jar'], dir: '../../thirdLibs') - implementation project(':growingio-autotracker-core') - implementation project(':growingio-tracker-core') + implementation project(':growingio-autotracker-core') + implementation project(':growingio-tracker-core') - implementation libs.okhttp3 + implementation libs.okhttp3 - implementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + implementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" apply from: "${rootProject.projectDir}/gradle/jacocoModule.gradle" \ No newline at end of file diff --git a/growingio-webservice/circler/src/main/java/com/growingio/android/circler/screenshot/CircleScreenshot.java b/growingio-webservice/circler/src/main/java/com/growingio/android/circler/CircleScreenshot.java similarity index 79% rename from growingio-webservice/circler/src/main/java/com/growingio/android/circler/screenshot/CircleScreenshot.java rename to growingio-webservice/circler/src/main/java/com/growingio/android/circler/CircleScreenshot.java index bd2341c6..b8e57c60 100644 --- a/growingio-webservice/circler/src/main/java/com/growingio/android/circler/screenshot/CircleScreenshot.java +++ b/growingio-webservice/circler/src/main/java/com/growingio/android/circler/CircleScreenshot.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,15 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.growingio.android.circler; -package com.growingio.android.circler.screenshot; - -import android.util.DisplayMetrics; - -import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.autotrack.view.ViewNodeProvider; -import com.growingio.android.sdk.track.async.Callback; -import com.growingio.android.sdk.track.utils.DeviceUtil; +import com.growingio.android.sdk.track.listener.Callback; import com.growingio.android.sdk.track.view.DecorView; import com.growingio.android.sdk.track.view.WindowHelper; @@ -84,20 +79,9 @@ public static final class Builder { private final JSONArray mPages = new JSONArray(); private Callback mScreenshotResultCallback; - public Builder() { - DisplayMetrics displayMetrics = DeviceUtil.getDisplayMetrics(TrackerContext.get().getApplicationContext()); - mScreenWidth = displayMetrics.widthPixels; - mScreenHeight = displayMetrics.heightPixels; - } - - public Builder setScreenWidth(int screenWidth) { - this.mScreenWidth = screenWidth; - return this; - } - - public Builder setScreenHeight(int screenHeight) { - this.mScreenHeight = screenHeight; - return this; + public Builder(int width, int height) { + mScreenWidth = width; + mScreenHeight = height; } public Builder setScale(float scale) { @@ -125,13 +109,16 @@ public Builder addPage(JSONObject page) { return this; } - public void build(Callback callback) { + public void buildWithNodes(ViewNodeProvider viewNodeProvider, Callback callback) { List decorViews = WindowHelper.get().getTopActivityViews(); - build(decorViews, callback); + build(decorViews, viewNodeProvider, callback); + } + + public void build(Callback callback) { } - public void build(List decorViews, Callback callback) { - if (callback == null) { + void build(List decorViews, ViewNodeProvider viewNodeProvider, Callback callback) { + if (callback == null || viewNodeProvider == null) { return; } @@ -143,11 +130,11 @@ public void build(List decorViews, Callback callbac mScreenshotResultCallback = callback; try { - JSONArray p = ViewNodeProvider.get().buildScreenPages(decorViews); + JSONArray p = viewNodeProvider.buildScreenPages(decorViews); for (int i = 0; i < p.length(); i++) { mPages.put(p.getJSONObject(i)); } - JSONArray v = ViewNodeProvider.get().buildScreenViews(decorViews); + JSONArray v = viewNodeProvider.buildScreenViews(decorViews); for (int i = 0; i < v.length(); i++) { mViewElements.put(v.getJSONObject(i)); } diff --git a/growingio-webservice/circler/src/main/java/com/growingio/android/circler/CirclerDataLoader.java b/growingio-webservice/circler/src/main/java/com/growingio/android/circler/CirclerDataLoader.java index fb674010..8dedbef5 100644 --- a/growingio-webservice/circler/src/main/java/com/growingio/android/circler/CirclerDataLoader.java +++ b/growingio-webservice/circler/src/main/java/com/growingio/android/circler/CirclerDataLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.circler; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.modelloader.ModelLoader; import com.growingio.android.sdk.track.modelloader.ModelLoaderFactory; -import com.growingio.android.sdk.track.webservices.Circler; -import com.growingio.android.sdk.track.webservices.WebService; +import com.growingio.android.sdk.track.middleware.webservice.Circler; +import com.growingio.android.sdk.track.middleware.webservice.WebService; import java.util.concurrent.TimeUnit; @@ -34,12 +34,8 @@ public class CirclerDataLoader implements ModelLoader { private final CirclerService circlerService; - public CirclerDataLoader(OkHttpClient client) { - circlerService = new CirclerService(client); - } - - public CirclerService getCirclerService() { - return circlerService; + public CirclerDataLoader(OkHttpClient client, TrackerContext context) { + circlerService = new CirclerService(client, context); } @Override @@ -68,12 +64,15 @@ private static OkHttpClient getsInternalClient() { return sInternalClient; } - public Factory() { + private final TrackerContext context; + + public Factory(TrackerContext context) { + this.context = context; } @Override public ModelLoader build() { - return new CirclerDataLoader(getsInternalClient()); + return new CirclerDataLoader(getsInternalClient(), context); } } } diff --git a/growingio-webservice/circler/src/main/java/com/growingio/android/circler/CirclerLibraryGioModule.java b/growingio-webservice/circler/src/main/java/com/growingio/android/circler/CirclerLibraryGioModule.java index e39edb90..209b1e47 100644 --- a/growingio-webservice/circler/src/main/java/com/growingio/android/circler/CirclerLibraryGioModule.java +++ b/growingio-webservice/circler/src/main/java/com/growingio/android/circler/CirclerLibraryGioModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,14 +15,15 @@ */ package com.growingio.android.circler; -import android.content.Context; - import com.growingio.android.sdk.LibraryGioModule; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; -import com.growingio.android.sdk.track.webservices.Circler; -import com.growingio.android.sdk.track.webservices.WebService; +import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; +import com.growingio.android.sdk.track.middleware.webservice.Circler; +import com.growingio.android.sdk.track.middleware.webservice.WebService; import com.growingio.sdk.annotation.GIOLibraryModule; +import java.util.Map; + /** *

* @@ -31,7 +32,12 @@ @GIOLibraryModule public class CirclerLibraryGioModule extends LibraryGioModule { @Override - public void registerComponents(Context context, TrackerRegistry registry) { - registry.register(Circler.class, WebService.class, new CirclerDataLoader.Factory()); + public void registerComponents(TrackerContext context) { + context.getRegistry().register(Circler.class, WebService.class, new CirclerDataLoader.Factory(context)); + } + + @Override + protected void setupProviders(Map, TrackerLifecycleProvider> providerStore) { + providerStore.put(ScreenshotProvider.class, new ScreenshotProvider()); } } diff --git a/growingio-webservice/circler/src/main/java/com/growingio/android/circler/CirclerService.java b/growingio-webservice/circler/src/main/java/com/growingio/android/circler/CirclerService.java index 505571cb..148adc24 100644 --- a/growingio-webservice/circler/src/main/java/com/growingio/android/circler/CirclerService.java +++ b/growingio-webservice/circler/src/main/java/com/growingio/android/circler/CirclerService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,20 +16,12 @@ package com.growingio.android.circler; -import com.growingio.android.circler.screenshot.CircleScreenshot; -import com.growingio.android.circler.screenshot.ScreenshotProvider; import com.growingio.android.sdk.TrackerContext; -import com.growingio.android.sdk.track.listener.IActivityLifecycle; -import com.growingio.android.sdk.track.listener.event.ActivityLifecycleEvent; +import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.middleware.EventFlutter; import com.growingio.android.sdk.track.modelloader.LoadDataFetcher; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; -import com.growingio.android.sdk.track.utils.ThreadUtils; -import com.growingio.android.sdk.track.webservices.Circler; -import com.growingio.android.sdk.track.webservices.WebService; -import com.growingio.android.sdk.track.webservices.message.ClientInfoMessage; -import com.growingio.android.sdk.track.webservices.message.QuitMessage; +import com.growingio.android.sdk.track.middleware.webservice.Circler; +import com.growingio.android.sdk.track.middleware.webservice.WebService; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @@ -42,7 +34,7 @@ * * @author cpacm 5/19/21 */ -public class CirclerService implements LoadDataFetcher, IActivityLifecycle, +public class CirclerService implements LoadDataFetcher, ScreenshotProvider.OnScreenshotRefreshedListener, WebSocketHandler.OnWebSocketListener { @@ -53,26 +45,26 @@ public class CirclerService implements LoadDataFetcher, IActivityLif private static final int SOCKET_STATE_CLOSED = 2; private final OkHttpClient client; - private final ThreadSafeTipView safeTipView; private final WebSocketHandler webSocketHandler; private Map params; private int circleDataType; protected final AtomicInteger socketState = new AtomicInteger(SOCKET_STATE_INITIALIZE); + private final ScreenshotProvider screenshotProvider; + void sendCircleData(Circler circler) { circleDataType = circler.circleDataType; if (circleDataType == Circler.CIRCLE_INIT) { this.params = circler.getParams(); } else if (circleDataType == Circler.CIRCLE_DATA) { - ScreenshotProvider.get().generateCircleData(circler.getCirclerData()); + screenshotProvider.generateCircleData(circler.getCirclerData()); } } - public CirclerService(OkHttpClient client) { + public CirclerService(OkHttpClient client, TrackerContext context) { this.client = client; - ActivityStateProvider.get().registerActivityLifecycleListener(this); - safeTipView = new ThreadSafeTipView(TrackerContext.get().getApplicationContext()); - webSocketHandler = new WebSocketHandler(this); + screenshotProvider = context.getProvider(ScreenshotProvider.class); + webSocketHandler = new WebSocketHandler(this, screenshotProvider); } @Override @@ -103,17 +95,13 @@ public void loadData(DataCallback callback) { Request request = new Request.Builder().url(wsUrl).build(); client.newWebSocket(request, webSocketHandler); - ActivityStateProvider.get().registerActivityLifecycleListener(this); - safeTipView.enableShow(); - - ThreadUtils.postOnUiThreadDelayed( - new Runnable() { - @Override - public void run() { - if (socketState.get() < SOCKET_STATE_READIED) { - Logger.e(TAG, "start WebSocketService timeout"); - onFailed(); - } + screenshotProvider.enableTipViewShow(); + + TrackMainThread.trackMain().postOnUiThreadDelayed( + () -> { + if (socketState.get() < SOCKET_STATE_READIED) { + Logger.e(TAG, "start WebSocketService timeout"); + onFailed(); } }, 10000); } @@ -129,6 +117,7 @@ public WebService executeData() { } public void cleanup() { + sendMessage(screenshotProvider.buildQuitMessage()); cancel(); } @@ -137,9 +126,8 @@ public void cancel() { if (webSocketHandler.getWebSocket() != null) { webSocketHandler.getWebSocket().close(1000, "exit"); } - ScreenshotProvider.get().unregisterScreenshotRefreshedListener(); - safeTipView.dismiss(); - ActivityStateProvider.get().unregisterActivityLifecycleListener(this); + screenshotProvider.disableTipView(); + screenshotProvider.unregisterScreenshotRefreshedListener(); } @Override @@ -151,23 +139,16 @@ public Class getDataClass() { /************************** WebSocket Handler ************************/ @Override public void onReady() { - sendMessage(ClientInfoMessage.createMessage().toJSONObject().toString()); + sendMessage(screenshotProvider.buildClientInfoMessage()); socketState.set(SOCKET_STATE_READIED); - ScreenshotProvider.get().registerScreenshotRefreshedListener(this); - safeTipView.onReady(this::exitCircler); - - TrackerContext.get().executeData(EventFlutter.flutterCircle(true), EventFlutter.class, Void.class); + screenshotProvider.registerScreenshotRefreshedListener(this); + screenshotProvider.readyTipView(this::cleanup); } @Override public void onMessage(String msg) { } - protected void exitCircler() { - TrackerContext.get().executeData(EventFlutter.flutterCircle(false), EventFlutter.class, Void.class); - sendMessage(new QuitMessage().toJSONObject().toString()); - cleanup(); - } @Override public void onFailed() { @@ -175,9 +156,10 @@ public void onFailed() { return; } socketState.set(SOCKET_STATE_CLOSED); - safeTipView.setErrorMessage(R.string.growing_circler_connected_to_web_failed); Logger.e(TAG, "Start CirclerService Failed"); - safeTipView.showQuitedDialog(this::exitCircler); + + screenshotProvider.setTipViewMessage(R.string.growing_circler_connected_to_web_failed); + screenshotProvider.showQuitDialog(this::cleanup); } @Override @@ -187,7 +169,7 @@ public void onQuited() { } cancel(); socketState.set(SOCKET_STATE_CLOSED); - safeTipView.showQuitedDialog(this::exitCircler); + screenshotProvider.showQuitDialog(this::cleanup); } /************************** Screenshot ************************/ @@ -198,16 +180,4 @@ public void onScreenshotRefreshed(CircleScreenshot screenshot) { sendMessage(screenshot.toJSONObject().toString()); } } - - - /************************** Activity Lifecycle ************************/ - - @Override - public void onActivityLifecycle(ActivityLifecycleEvent event) { - if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_RESUMED) { - safeTipView.show(event.getActivity()); - } else if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_PAUSED) { - safeTipView.removeOnly(); - } - } } diff --git a/growingio-webservice/circler/src/main/java/com/growingio/android/circler/screenshot/ScreenshotProvider.java b/growingio-webservice/circler/src/main/java/com/growingio/android/circler/ScreenshotProvider.java similarity index 50% rename from growingio-webservice/circler/src/main/java/com/growingio/android/circler/screenshot/ScreenshotProvider.java rename to growingio-webservice/circler/src/main/java/com/growingio/android/circler/ScreenshotProvider.java index dcb5acbb..60bcc586 100644 --- a/growingio-webservice/circler/src/main/java/com/growingio/android/circler/screenshot/ScreenshotProvider.java +++ b/growingio-webservice/circler/src/main/java/com/growingio/android/circler/ScreenshotProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,8 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.growingio.android.circler.screenshot; +package com.growingio.android.circler; import android.app.Activity; import android.os.Handler; @@ -22,20 +21,29 @@ import android.os.Message; import android.util.DisplayMetrics; +import com.growingio.android.sdk.CoreConfiguration; import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.autotrack.view.ScreenElementHelper; -import com.growingio.android.sdk.track.async.Callback; +import com.growingio.android.sdk.autotrack.view.ViewNodeProvider; +import com.growingio.android.sdk.track.SDKConfig; +import com.growingio.android.sdk.track.listener.event.ActivityLifecycleEvent; import com.growingio.android.sdk.track.log.Logger; +import com.growingio.android.sdk.track.listener.Callback; +import com.growingio.android.sdk.track.middleware.EventFlutter; import com.growingio.android.sdk.track.modelloader.ModelLoader; import com.growingio.android.sdk.track.middleware.hybrid.HybridDom; import com.growingio.android.sdk.track.middleware.hybrid.HybridJson; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; +import com.growingio.android.sdk.track.modelloader.TrackerRegistry; +import com.growingio.android.sdk.track.providers.AppInfoProvider; +import com.growingio.android.sdk.track.providers.ConfigurationProvider; +import com.growingio.android.sdk.track.providers.DeviceInfoProvider; import com.growingio.android.sdk.track.utils.DeviceUtil; import com.growingio.android.sdk.track.view.ScreenshotUtil; import com.growingio.android.sdk.track.view.ViewStateChangedEvent; import com.growingio.android.sdk.track.view.ViewTreeStatusListener; -import com.growingio.android.sdk.track.webservices.Circler; +import com.growingio.android.sdk.track.middleware.webservice.Circler; +import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; @@ -50,34 +58,49 @@ public class ScreenshotProvider extends ViewTreeStatusListener { private static final long MAX_REFRESH_INTERVAL = 3000L; private long lastSendTime = System.currentTimeMillis(); // 记录上次发送的事件,用来避免当界面刷新频率过快时一直无法发送圈选的情况。 - private final float mScale; - private final Handler mHandler; - private final Runnable mRefreshScreenshotRunnable = this::dispatchScreenshot; - private ModelLoader modelLoader; - - private OnScreenshotRefreshedListener mListener; - + private float scale; + private final Handler screenshotHandler; + private final Runnable refreshScreenshotRunnable = this::dispatchScreenshot; + private OnScreenshotRefreshedListener refreshListener; - private static class SingleInstance { - private static final ScreenshotProvider INSTANCE = new ScreenshotProvider(); - } + private ThreadSafeTipView safeTipView; - private ScreenshotProvider() { - DisplayMetrics metrics = DeviceUtil.getDisplayMetrics(TrackerContext.get().getApplicationContext()); - mScale = SCREENSHOT_STANDARD_WIDTH / Math.min(metrics.widthPixels, metrics.heightPixels); + private ConfigurationProvider configurationProvider; + private AppInfoProvider appInfoProvider; + private DeviceInfoProvider deviceInfoProvider; + private ViewNodeProvider viewNodeProvider; + private TrackerRegistry registry; + ScreenshotProvider() { HandlerThread mHandlerThread = new HandlerThread("ScreenshotProvider"); mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); + screenshotHandler = new Handler(mHandlerThread.getLooper()); + } - getHybridModelLoader(); + @Override + public void setup(TrackerContext context) { + super.setup(context); + configurationProvider = context.getConfigurationProvider(); + appInfoProvider = context.getProvider(AppInfoProvider.class); + viewNodeProvider = context.getProvider(ViewNodeProvider.class); + deviceInfoProvider = context.getDeviceInfoProvider(); + registry = context.getRegistry(); + DisplayMetrics metrics = DeviceUtil.getDisplayMetrics(context.getBaseContext()); + scale = SCREENSHOT_STANDARD_WIDTH / Math.min(metrics.widthPixels, metrics.heightPixels); + + safeTipView = new ThreadSafeTipView(context.getBaseContext(), activityStateProvider, appInfoProvider.getAppVersion()); + + ModelLoader modelLoader = context.getRegistry().getModelLoader(HybridDom.class, HybridJson.class); + if (modelLoader != null) { + modelLoader.buildLoadData(new HybridDom(this::refreshScreenshot)).fetcher.executeData(); + } } @Override public void onViewStateChanged(ViewStateChangedEvent changedEvent) { if (System.currentTimeMillis() - lastSendTime >= MAX_REFRESH_INTERVAL) { lastSendTime = System.currentTimeMillis(); - mHandler.post(this::dispatchScreenshot); + screenshotHandler.post(this::dispatchScreenshot); } else { if (changedEvent.getStateType() == ViewStateChangedEvent.StateType.MANUAL_CHANGED) { refreshScreenshot(EVENT_REFRESH_INTERVAL); @@ -87,26 +110,16 @@ public void onViewStateChanged(ViewStateChangedEvent changedEvent) { } } - ModelLoader getHybridModelLoader() { - if (modelLoader == null) { - modelLoader = TrackerContext.get().getRegistry().getModelLoader(HybridDom.class, HybridJson.class); - if (modelLoader != null) { - modelLoader.buildLoadData(new HybridDom(this::refreshScreenshot)).fetcher.executeData(); - } - } - return modelLoader; - } - private void dispatchScreenshot() { - if (mListener == null) return; - Activity activity = ActivityStateProvider.get().getForegroundActivity(); + if (refreshListener == null) return; + Activity activity = activityStateProvider.getForegroundActivity(); if (activity == null) return; try { - ScreenshotUtil.getScreenshotBitmap(mScale, bitmap -> { + ScreenshotUtil.getScreenshotBitmap(scale, bitmap -> { try { String screenshotBase64 = ScreenshotUtil.getScreenshotBase64(bitmap); - sendScreenshotRefreshed(screenshotBase64, mScale); + sendScreenshotRefreshed(screenshotBase64, scale); } catch (IOException e) { Logger.e(TAG, "base64 screenshot failed:" + e.getMessage()); } @@ -117,27 +130,27 @@ private void dispatchScreenshot() { } private void refreshScreenshot(long duration) { - mHandler.removeCallbacks(mRefreshScreenshotRunnable); - mHandler.postDelayed(mRefreshScreenshotRunnable, duration); + screenshotHandler.removeCallbacks(refreshScreenshotRunnable); + screenshotHandler.postDelayed(refreshScreenshotRunnable, duration); } public void refreshScreenshot() { refreshScreenshot(MIN_REFRESH_INTERVAL); } - public static ScreenshotProvider get() { - return SingleInstance.INSTANCE; - } - public void registerScreenshotRefreshedListener(OnScreenshotRefreshedListener listener) { register(); - this.mListener = listener; + this.refreshListener = listener; refreshScreenshot(); + + registry.executeData(EventFlutter.flutterCircle(true), EventFlutter.class, Void.class); } public void unregisterScreenshotRefreshedListener() { - this.mListener = null; + this.refreshListener = null; unRegister(); + + registry.executeData(EventFlutter.flutterCircle(false), EventFlutter.class, Void.class); } public interface OnScreenshotRefreshedListener { @@ -148,12 +161,13 @@ public interface OnScreenshotRefreshedListener { private void sendScreenshotRefreshed(String screenshotBase64, float scale) { lastSendTime = System.currentTimeMillis(); - CircleScreenshot.Builder builder = new CircleScreenshot.Builder() + CircleScreenshot.Builder builder = new CircleScreenshot + .Builder(deviceInfoProvider.getScreenWidth(), deviceInfoProvider.getScreenHeight()) .setScale(scale) .setScreenshot(screenshotBase64) .setSnapshotKey(mSnapshotKey++); - builder.build(new Callback() { + builder.buildWithNodes(viewNodeProvider, new Callback() { @Override public void onSuccess(CircleScreenshot result) { Logger.d(TAG, "Create circle screenshot successfully"); @@ -168,17 +182,17 @@ public void onFailed() { } public void sendScreenshot(CircleScreenshot result) { - if (result != null && mListener != null) { - this.mListener.onScreenshotRefreshed(result); + if (result != null && refreshListener != null) { + this.refreshListener.onScreenshotRefreshed(result); } } public void generateCircleData(Circler.CirclerData data) { - if (mListener == null) return; - mHandler.removeMessages(0); - Message message = Message.obtain(mHandler, new CircleDataThread(data)); + if (refreshListener == null) return; + screenshotHandler.removeMessages(0); + Message message = Message.obtain(screenshotHandler, new CircleDataThread(data)); message.what = 0; - mHandler.sendMessageDelayed(message, 100L); + screenshotHandler.sendMessageDelayed(message, 100L); } private class CircleDataThread implements Runnable { @@ -192,11 +206,8 @@ private class CircleDataThread implements Runnable { public void run() { lastSendTime = System.currentTimeMillis(); - - CircleScreenshot.Builder builder = new CircleScreenshot.Builder() + CircleScreenshot.Builder builder = new CircleScreenshot.Builder((int) circlerData.getWidth(), (int) circlerData.getHeight()) .setScale((float) circlerData.getScale()) - .setScreenWidth((int) circlerData.getWidth()) - .setScreenHeight((int) circlerData.getHeight()) .setSnapshotKey(mSnapshotKey++); for (int i = 0; i < circlerData.getElements().size(); i++) { @@ -218,7 +229,7 @@ public void run() { if (circlerData.getScreenshot() == null) { try { - ScreenshotUtil.getScreenshotBitmap(mScale, bitmap -> { + ScreenshotUtil.getScreenshotBitmap(scale, bitmap -> { try { String screenshotBase64 = ScreenshotUtil.getScreenshotBase64(bitmap); builder.setScreenshot(screenshotBase64); @@ -238,7 +249,7 @@ public void run() { } private void sendScreenshotWithBuilder(CircleScreenshot.Builder builder) { - builder.build(new Callback() { + builder.buildWithNodes(viewNodeProvider, new Callback() { @Override public void onSuccess(CircleScreenshot result) { Logger.d(TAG, "Create circle screenshot successfully"); @@ -252,4 +263,90 @@ public void onFailed() { }); } + @Override + public void onActivityLifecycle(ActivityLifecycleEvent event) { + if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_RESUMED) { + safeTipView.show(event.getActivity()); + } else if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_PAUSED) { + safeTipView.removeOnly(); + } + } + + void enableTipViewShow() { + safeTipView.enableShow(); + } + + void disableTipView() { + safeTipView.dismiss(); + } + + void readyTipView(ThreadSafeTipView.OnExitListener listener) { + safeTipView.onReady(listener); + } + + void setTipViewMessage(int message) { + safeTipView.setErrorMessage(message); + } + + void showQuitDialog(ThreadSafeTipView.OnExitListener listener) { + safeTipView.showQuitedDialog(listener); + } + + static final String MSG_READY_TYPE = "ready"; + static final String MSG_QUIT_TYPE = "quit"; + private static final String MSG_OS = "Android"; + private static final String MSG_CLIENT_TYPE = "client_info"; + + String buildReadyMessage() { + CoreConfiguration core = configurationProvider.core(); + JSONObject json = new JSONObject(); + try { + json.put("projectId", core.getProjectId()); + json.put("msgType", MSG_READY_TYPE); + json.put("timestamp", System.currentTimeMillis()); + json.put("domain", appInfoProvider.getPackageName()); + json.put("sdkVersion", SDKConfig.SDK_VERSION); + json.put("sdkVersionCode", SDKConfig.SDK_VERSION_CODE); + json.put("os", MSG_OS); + json.put("screenWidth", deviceInfoProvider.getScreenWidth()); + json.put("screenHeight", deviceInfoProvider.getScreenHeight()); + json.put("urlScheme", core.getUrlScheme()); + } catch (JSONException ignored) { + } + return json.toString(); + } + + String buildQuitMessage() { + JSONObject json = new JSONObject(); + try { + json.put("msgType", MSG_QUIT_TYPE); + } catch (JSONException ignored) { + } + return json.toString(); + } + + String buildClientInfoMessage() { + CoreConfiguration core = configurationProvider.core(); + JSONObject json = new JSONObject(); + try { + json.put("msgType", MSG_CLIENT_TYPE); + json.put("sdkVersion", SDKConfig.SDK_VERSION); + + JSONObject info = new JSONObject(); + info.put("os", MSG_OS); + info.put("appVersion", appInfoProvider.getAppVersion()); + info.put("appChannel", core.getChannel()); + info.put("osVersion", deviceInfoProvider.getOperatingSystemVersion()); + info.put("deviceType", deviceInfoProvider.getDeviceType()); + info.put("deviceBrand", deviceInfoProvider.getDeviceBrand()); + info.put("deviceModel", deviceInfoProvider.getDeviceModel()); + + json.put("data", info); + + } catch (JSONException ignored) { + } + + return json.toString(); + } + } \ No newline at end of file diff --git a/growingio-webservice/circler/src/main/java/com/growingio/android/circler/ThreadSafeTipView.java b/growingio-webservice/circler/src/main/java/com/growingio/android/circler/ThreadSafeTipView.java index 71d498eb..a737e21e 100644 --- a/growingio-webservice/circler/src/main/java/com/growingio/android/circler/ThreadSafeTipView.java +++ b/growingio-webservice/circler/src/main/java/com/growingio/android/circler/ThreadSafeTipView.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.circler; import android.app.Activity; @@ -22,11 +21,10 @@ import android.content.DialogInterface; import com.growingio.android.sdk.track.SDKConfig; +import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.providers.ActivityStateProvider; -import com.growingio.android.sdk.track.providers.AppInfoProvider; -import com.growingio.android.sdk.track.utils.ThreadUtils; -import com.growingio.android.sdk.track.webservices.widget.TipView; +import com.growingio.android.sdk.track.view.TipView; /** *

@@ -39,6 +37,9 @@ public class ThreadSafeTipView { private TipView tipView; private final Context context; + private final String appVersion; + private final ActivityStateProvider activityStateProvider; + private void initView() { if (tipView == null) { tipView = new TipView(context); @@ -46,9 +47,11 @@ private void initView() { } } - public ThreadSafeTipView(Context context) { + public ThreadSafeTipView(Context context, ActivityStateProvider activityStateProvider, String appVersion) { + this.appVersion = appVersion; this.context = context; - runOnUiThread(() -> initView()); + this.activityStateProvider = activityStateProvider; + runOnUiThread(this::initView); } public void enableShow() { @@ -97,31 +100,26 @@ public void show(Activity activity) { } protected void showExitDialog(OnExitListener listener) { - Activity activity = ActivityStateProvider.get().getForegroundActivity(); + Activity activity = activityStateProvider.getForegroundActivity(); if (activity == null) { Logger.e(TAG, "showExitDialog: ForegroundActivity is NULL"); return; } String message = activity.getString(R.string.growing_circler_app_version) - + AppInfoProvider.get().getAppVersion() + + appVersion + activity.getString(R.string.growing_circler_sdk_version) + SDKConfig.SDK_VERSION; new AlertDialog.Builder(activity) .setTitle(R.string.growing_circler_progress) .setMessage(message) - .setPositiveButton(R.string.growing_circler_exit, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - listener.onExitDebugger(); - } - }) + .setPositiveButton(R.string.growing_circler_exit, (dialog, which) -> listener.onExitCircler()) .setNegativeButton(R.string.growing_circler_continue, null) .create() .show(); } - public void showQuitedDialog(OnExitListener listener) { - Activity activity = ActivityStateProvider.get().getForegroundActivity(); + public void showQuitedDialog(OnExitListener listener) { + Activity activity = activityStateProvider.getForegroundActivity(); if (activity == null) { Logger.e(TAG, "showQuitedDialog: ForegroundActivity is NULL"); return; @@ -132,13 +130,13 @@ public void showQuitedDialog(OnExitListener listener) { .setPositiveButton(R.string.growing_circler_exit, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - listener.onExitDebugger(); + listener.onExitCircler(); } }) .setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { - listener.onExitDebugger(); + listener.onExitCircler(); } }) .setCancelable(false) @@ -147,11 +145,11 @@ public void onDismiss(DialogInterface dialog) { } private void runOnUiThread(Runnable r) { - ThreadUtils.runOnUiThread(r); + TrackMainThread.trackMain().runOnUiThread(r); } public interface OnExitListener { - void onExitDebugger(); + void onExitCircler(); } } diff --git a/growingio-webservice/circler/src/main/java/com/growingio/android/circler/WebSocketHandler.java b/growingio-webservice/circler/src/main/java/com/growingio/android/circler/WebSocketHandler.java index 3bc09238..010fa3fd 100644 --- a/growingio-webservice/circler/src/main/java/com/growingio/android/circler/WebSocketHandler.java +++ b/growingio-webservice/circler/src/main/java/com/growingio/android/circler/WebSocketHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,15 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.circler; import android.text.TextUtils; +import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.utils.ThreadUtils; -import com.growingio.android.sdk.track.webservices.message.QuitMessage; -import com.growingio.android.sdk.track.webservices.message.ReadyMessage; import org.json.JSONException; import org.json.JSONObject; @@ -41,8 +38,10 @@ class WebSocketHandler extends WebSocketListener { private final OnWebSocketListener webSocketListener; private WebSocket webSocket; private static final String TAG = "WebSocketHandler"; + private final ScreenshotProvider screenshotProvider; - WebSocketHandler(OnWebSocketListener webSocketListener) { + WebSocketHandler(OnWebSocketListener webSocketListener, ScreenshotProvider screenshotProvider) { + this.screenshotProvider = screenshotProvider; this.webSocketListener = webSocketListener; } @@ -54,19 +53,14 @@ public void sendMessage(String msg) { @Override public void onOpen(WebSocket webSocket, Response response) { - String readyMessage = ReadyMessage.createMessage().toJSONObject().toString(); + String readyMessage = screenshotProvider.buildReadyMessage(); Logger.d(TAG, "Prepare send open message: " + readyMessage); - if (webSocket.send(ReadyMessage.createMessage().toJSONObject().toString())) { + if (webSocket.send(readyMessage)) { this.webSocket = webSocket; Logger.d(TAG, "send ready message success"); } else { Logger.e(TAG, "send ready message failed"); - ThreadUtils.runOnUiThread(new Runnable() { - @Override - public void run() { - webSocketListener.onFailed(); - } - }); + TrackMainThread.trackMain().runOnUiThread(webSocketListener::onFailed); } } @@ -79,25 +73,25 @@ public void onMessage(WebSocket webSocket, String text) { Logger.d(TAG, "Received message is " + text); if (text.contains("had disconnected")) { - ThreadUtils.runOnUiThread(() -> webSocketListener.onQuited()); + TrackMainThread.trackMain().runOnUiThread(() -> webSocketListener.onQuited()); return; } try { JSONObject message = new JSONObject(text); String msgType = message.optString("msgType"); - if (ReadyMessage.MSG_TYPE.equals(msgType)) { + if (ScreenshotProvider.MSG_READY_TYPE.equals(msgType)) { Logger.d(TAG, "Web is ready"); - ThreadUtils.runOnUiThread(new Runnable() { + TrackMainThread.trackMain().runOnUiThread(new Runnable() { @Override public void run() { webSocketListener.onReady(); } }); return; - } else if (QuitMessage.MSG_TYPE.equals(msgType)) { + } else if (ScreenshotProvider.MSG_QUIT_TYPE.equals(msgType)) { Logger.d(TAG, "Web is quited"); - ThreadUtils.runOnUiThread(new Runnable() { + TrackMainThread.trackMain().runOnUiThread(new Runnable() { @Override public void run() { webSocketListener.onQuited(); @@ -108,34 +102,19 @@ public void run() { } catch (JSONException e) { Logger.e(TAG, e); } - ThreadUtils.runOnUiThread(new Runnable() { - @Override - public void run() { - webSocketListener.onMessage(text); - } - }); + TrackMainThread.trackMain().runOnUiThread(() -> webSocketListener.onMessage(text)); } @Override public void onClosed(WebSocket webSocket, int code, String reason) { Logger.e(TAG, "webSocket on onClosed, reason: $reason"); - ThreadUtils.runOnUiThread(new Runnable() { - @Override - public void run() { - webSocketListener.onQuited(); - } - }); + TrackMainThread.trackMain().runOnUiThread(webSocketListener::onQuited); } @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { Logger.e(TAG, t, "webSocket on onFailure, reason: "); - ThreadUtils.runOnUiThread(new Runnable() { - @Override - public void run() { - webSocketListener.onFailed(); - } - }); + TrackMainThread.trackMain().runOnUiThread(webSocketListener::onFailed); } public WebSocket getWebSocket() { diff --git a/growingio-webservice/circler/src/test/java/com/growingio/android/circler/CirclerTest.java b/growingio-webservice/circler/src/test/java/com/growingio/android/circler/CirclerTest.java index c504cd35..1942f125 100644 --- a/growingio-webservice/circler/src/test/java/com/growingio/android/circler/CirclerTest.java +++ b/growingio-webservice/circler/src/test/java/com/growingio/android/circler/CirclerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ */ package com.growingio.android.circler; -import android.app.Activity; import android.app.Application; import android.os.Looper; @@ -24,24 +23,18 @@ import com.google.common.truth.Truth; import com.google.common.util.concurrent.Uninterruptibles; import com.growingio.android.circler.shadow.ShadowThreadUtils; +import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.TrackerContext; -import com.growingio.android.sdk.track.listener.event.ActivityLifecycleEvent; import com.growingio.android.sdk.track.modelloader.DataFetcher; import com.growingio.android.sdk.track.modelloader.ModelLoader; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; -import com.growingio.android.sdk.track.utils.ThreadUtils; -import com.growingio.android.sdk.track.webservices.Circler; -import com.growingio.android.sdk.track.webservices.WebService; -import com.growingio.android.sdk.track.webservices.message.QuitMessage; -import com.growingio.android.sdk.track.webservices.message.ReadyMessage; +import com.growingio.android.sdk.track.middleware.webservice.Circler; +import com.growingio.android.sdk.track.middleware.webservice.WebService; import org.json.JSONException; import org.json.JSONObject; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; @@ -64,7 +57,10 @@ public class CirclerTest implements WebSocketHandler.OnWebSocketListener { private WebSocketHandler webSocketHandler; private OkHttpClient client; private MockWebServer mockWebServer; - private final Application context = ApplicationProvider.getApplicationContext(); + private final Application application = ApplicationProvider.getApplicationContext(); + + private TrackerContext context; + private ScreenshotProvider screenshotProvider; protected String getWsUrl() { String hostName = mockWebServer.getHostName(); @@ -82,7 +78,12 @@ private void sendMessage(WebSocket webSocket, String message) { @Before public void setup() { - webSocketHandler = new WebSocketHandler(this); + Tracker tracker = new Tracker(application); + CirclerLibraryGioModule dModule = new CirclerLibraryGioModule(); + tracker.registerComponent(dModule); + context = tracker.getContext(); + screenshotProvider = context.getProvider(ScreenshotProvider.class); + webSocketHandler = new WebSocketHandler(this,screenshotProvider); mockWebServer = new MockWebServer(); mockWebServer.setDispatcher(new Dispatcher() { @Override @@ -100,11 +101,11 @@ public void onMessage(WebSocket webSocket, String text) { JSONObject message = new JSONObject(text); String msgType = message.optString("msgType"); switch (msgType) { - case ReadyMessage.MSG_TYPE: - sendMessage(webSocket, ReadyMessage.MSG_TYPE); + case ScreenshotProvider.MSG_READY_TYPE: + sendMessage(webSocket,ScreenshotProvider.MSG_READY_TYPE); break; - case QuitMessage.MSG_TYPE: - sendMessage(webSocket, QuitMessage.MSG_TYPE); + case ScreenshotProvider.MSG_QUIT_TYPE: + sendMessage(webSocket, ScreenshotProvider.MSG_QUIT_TYPE); break; default: webSocket.send("error data"); @@ -116,7 +117,6 @@ public void onMessage(WebSocket webSocket, String text) { }); } }); - TrackerContext.init(context); client = new OkHttpClient.Builder() .connectTimeout(5, TimeUnit.SECONDS) .readTimeout(5, TimeUnit.SECONDS) @@ -125,7 +125,6 @@ public void onMessage(WebSocket webSocket, String text) { try { Looper.prepare(); System.out.println("[intercept]:" + Looper.myLooper()); - ThreadUtils.setUiThread(Looper.myLooper()); } catch (Exception ignored) { } return chain.proceed(chain.request()); @@ -165,10 +164,7 @@ public void onMessage(String msg) { @Test public void circlerModelTest() { - CirclerLibraryGioModule module = new CirclerLibraryGioModule(); - TrackerRegistry registry = new TrackerRegistry(); - module.registerComponents(context, registry); - ModelLoader dataLoader = registry.getModelLoader(Circler.class, WebService.class); + ModelLoader dataLoader = context.getRegistry().getModelLoader(Circler.class, WebService.class); HashMap param = new HashMap<>(); param.put("wsUrl", getWsUrl()); @@ -177,20 +173,6 @@ public void circlerModelTest() { dataFetcher.executeData(); Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - if (dataLoader instanceof CirclerDataLoader) { - serviceTest(((CirclerDataLoader) dataLoader).getCirclerService()); - } } - public void serviceTest(CirclerService circlerService) { - Activity activity = Robolectric.buildActivity(RobolectricActivity.class).create().resume().get(); - ActivityStateProvider.get().onActivityResumed(activity); - circlerService.sendMessage("test"); - circlerService.socketState.getAndSet(1); - circlerService.onScreenshotRefreshed(null); - circlerService.onActivityLifecycle(ActivityLifecycleEvent.createOnStartedEvent(activity)); - circlerService.onFailed(); - circlerService.exitCircler(); - circlerService.onQuited(); - } } diff --git a/growingio-webservice/circler/src/test/java/com/growingio/android/circler/RobolectricActivity.java b/growingio-webservice/circler/src/test/java/com/growingio/android/circler/RobolectricActivity.java index 4e7d62f4..39e9b17e 100644 --- a/growingio-webservice/circler/src/test/java/com/growingio/android/circler/RobolectricActivity.java +++ b/growingio-webservice/circler/src/test/java/com/growingio/android/circler/RobolectricActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,7 +49,7 @@ public void onClick(View v) { }); ImageView imageView = new ImageView(this); - imageView.setTag(com.growingio.android.sdk.autotrack.R.id.growing_tracker_view_page, new ActivityPage(this)); + imageView.setTag(R.id.growing_tracker_view_page, new ActivityPage(this)); WebView webView = new WebView(this); webView.loadUrl("https://www.growingio.com/"); diff --git a/growingio-webservice/circler/src/test/java/com/growingio/android/circler/ScreenShotTest.java b/growingio-webservice/circler/src/test/java/com/growingio/android/circler/ScreenShotTest.java index e5126467..1abece47 100644 --- a/growingio-webservice/circler/src/test/java/com/growingio/android/circler/ScreenShotTest.java +++ b/growingio-webservice/circler/src/test/java/com/growingio/android/circler/ScreenShotTest.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.circler; import android.app.Activity; @@ -25,14 +24,13 @@ import androidx.test.core.app.ApplicationProvider; import com.google.common.truth.Truth; -import com.growingio.android.circler.screenshot.CircleScreenshot; -import com.growingio.android.circler.screenshot.ScreenshotProvider; import com.growingio.android.circler.shadow.ShadowHandler; import com.growingio.android.circler.shadow.ShadowWH; import com.growingio.android.hybrid.HybridLibraryGioModule; +import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.TrackerContext; -import com.growingio.android.sdk.track.async.Callback; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; +import com.growingio.android.sdk.autotrack.view.ViewNodeProvider; +import com.growingio.android.sdk.track.listener.Callback; import com.growingio.android.sdk.track.view.DecorView; import org.junit.Before; @@ -50,33 +48,37 @@ public class ScreenShotTest { Application application = ApplicationProvider.getApplicationContext(); + private TrackerContext context; @Before public void setup() { - TrackerContext.init(application); + Tracker tracker = new Tracker(application); + context = tracker.getContext(); HybridLibraryGioModule module = new HybridLibraryGioModule(); - module.registerComponents(application, TrackerContext.get().getRegistry()); + tracker.registerComponent(module); + + CirclerLibraryGioModule cModule = new CirclerLibraryGioModule(); + tracker.registerComponent(cModule); } @Test public void dispatchTest() { + ScreenshotProvider screenshotProvider = context.getProvider(ScreenshotProvider.class); Activity activity = Robolectric.buildActivity(RobolectricActivity.class).create().resume().get(); - ActivityStateProvider.get().onActivityResumed(activity); ShadowWH.activity = activity; ScreenshotProvider.OnScreenshotRefreshedListener dispatchListener = screenshot -> { - System.out.println(screenshot.toJSONObject()); Truth.assertThat(screenshot).isNotNull(); }; - ScreenshotProvider.get().registerScreenshotRefreshedListener(dispatchListener); - ScreenshotProvider.get().refreshScreenshot(); + screenshotProvider.registerScreenshotRefreshedListener(dispatchListener); - ScreenshotProvider.get().unregisterScreenshotRefreshedListener(); + screenshotProvider.unregisterScreenshotRefreshedListener(); } @Test public void circlerScreenShotTest() { + ScreenshotProvider screenshotProvider = context.getProvider(ScreenshotProvider.class); + ViewNodeProvider viewNodeProvider = context.getProvider(ViewNodeProvider.class); Activity activity = Robolectric.buildActivity(RobolectricActivity.class).create().resume().get(); - ActivityStateProvider.get().onActivityResumed(activity); ShadowWH.activity = activity; ScreenshotProvider.OnScreenshotRefreshedListener baseListener = screenshot -> { @@ -84,16 +86,16 @@ public void circlerScreenShotTest() { "{\"screenWidth\":320,\"screenHeight\":470,\"scale\":100,\"screenshot\":\"this is test base64\",\"msgType\":\"refreshScreenshot\",\"snapshotKey\":0,\"elements\":[],\"pages\":[]}" ); }; - ScreenshotProvider.get().registerScreenshotRefreshedListener(baseListener); + screenshotProvider.registerScreenshotRefreshedListener(baseListener); - new CircleScreenshot.Builder() + new CircleScreenshot.Builder(1920, 1680) .setScale(100) .setScreenshot("this is test base64") .setSnapshotKey(0) - .build(getAllWindowDecorViews(), new Callback() { + .build(getAllWindowDecorViews(), viewNodeProvider, new Callback() { @Override public void onSuccess(CircleScreenshot result) { - ScreenshotProvider.get().sendScreenshot(result); + screenshotProvider.sendScreenshot(result); } @Override @@ -101,13 +103,11 @@ public void onFailed() { } }); - ScreenshotProvider.get().unregisterScreenshotRefreshedListener(); + screenshotProvider.unregisterScreenshotRefreshedListener(); } public List getAllWindowDecorViews() { Activity activity = Robolectric.buildActivity(RobolectricActivity.class).create().resume().get(); - ActivityStateProvider.get().onActivityCreated(activity, null); - ActivityStateProvider.get().onActivityResumed(activity); View view = activity.getWindow().getDecorView(); List decorViews = new ArrayList<>(); diff --git a/growingio-webservice/circler/src/test/java/com/growingio/android/circler/shadow/ShadowHandler.java b/growingio-webservice/circler/src/test/java/com/growingio/android/circler/shadow/ShadowHandler.java index 37522b9d..43b2f4ea 100644 --- a/growingio-webservice/circler/src/test/java/com/growingio/android/circler/shadow/ShadowHandler.java +++ b/growingio-webservice/circler/src/test/java/com/growingio/android/circler/shadow/ShadowHandler.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.circler.shadow; diff --git a/growingio-webservice/circler/src/test/java/com/growingio/android/circler/shadow/ShadowThreadUtils.java b/growingio-webservice/circler/src/test/java/com/growingio/android/circler/shadow/ShadowThreadUtils.java index c454082d..20e1a62f 100644 --- a/growingio-webservice/circler/src/test/java/com/growingio/android/circler/shadow/ShadowThreadUtils.java +++ b/growingio-webservice/circler/src/test/java/com/growingio/android/circler/shadow/ShadowThreadUtils.java @@ -1,28 +1,27 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.circler.shadow; -import com.growingio.android.sdk.track.utils.ThreadUtils; +import com.growingio.android.sdk.track.TrackMainThread; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; -@Implements(ThreadUtils.class) +@Implements(TrackMainThread.class) public class ShadowThreadUtils { public void doSomeThing() { diff --git a/growingio-webservice/circler/src/test/java/com/growingio/android/circler/shadow/ShadowWH.java b/growingio-webservice/circler/src/test/java/com/growingio/android/circler/shadow/ShadowWH.java index 2f70ff53..a34f202b 100644 --- a/growingio-webservice/circler/src/test/java/com/growingio/android/circler/shadow/ShadowWH.java +++ b/growingio-webservice/circler/src/test/java/com/growingio/android/circler/shadow/ShadowWH.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.circler.shadow; diff --git a/growingio-webservice/debugger/build.gradle b/growingio-webservice/debugger/build.gradle index 3f0347f6..19b40265 100644 --- a/growingio-webservice/debugger/build.gradle +++ b/growingio-webservice/debugger/build.gradle @@ -1,56 +1,56 @@ plugins { - id 'com.android.library' + id 'com.android.library' } android { - namespace 'com.growingio.android.debugger' - compileSdkVersion buildConfiguration.compileVersion - defaultConfig { - minSdkVersion buildConfiguration.minSdkVersion - targetSdkVersion buildConfiguration.targetSdkVersion - versionName libs.versions.growingio.get() - versionCode libs.versions.growingioCode.get().toInteger() - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility buildConfiguration.sourceCompatibility - targetCompatibility buildConfiguration.targetCompatibility - } - - testOptions { - unitTests.all { - jacoco { - includeNoLocationClasses = true - excludes = ['jdk.internal.*'] - } - } - unitTests { - returnDefaultValues = true - includeAndroidResources = true - } - } + namespace 'com.growingio.android.debugger' + compileSdkVersion buildConfiguration.compileVersion + defaultConfig { + minSdkVersion buildConfiguration.minSdkVersion + targetSdkVersion buildConfiguration.targetSdkVersion + versionName libs.versions.growingio.get() + versionCode libs.versions.growingioCode.get().toInteger() + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + sourceCompatibility buildConfiguration.sourceCompatibility + targetCompatibility buildConfiguration.targetCompatibility + } + + testOptions { + unitTests.all { + jacoco { + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } + } + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } } dependencies { - testImplementation libs.bundles.test - testImplementation libs.okhttp3.mockwebserver - testImplementation libs.androidx.test.core + testImplementation libs.bundles.test + testImplementation libs.okhttp3.mockwebserver + testImplementation libs.androidx.test.core - testImplementation project(':growingio-hybrid') + testImplementation project(':growingio-hybrid') - implementation project(':growingio-tracker-core') - implementation libs.okhttp3 + implementation project(':growingio-tracker-core') + implementation libs.okhttp3 - implementation project(":growingio-annotation") - annotationProcessor project(":growingio-annotation:compiler") + implementation project(":growingio-annotation") + annotationProcessor project(":growingio-annotation:compiler") } apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" apply from: "${rootProject.projectDir}/gradle/jacocoModule.gradle" \ No newline at end of file diff --git a/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerDataLoader.java b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerDataLoader.java index a8296ae1..2c46cbc5 100644 --- a/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerDataLoader.java +++ b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerDataLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.debugger; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.modelloader.ModelLoader; import com.growingio.android.sdk.track.modelloader.ModelLoaderFactory; -import com.growingio.android.sdk.track.webservices.Debugger; -import com.growingio.android.sdk.track.webservices.WebService; +import com.growingio.android.sdk.track.middleware.webservice.Debugger; +import com.growingio.android.sdk.track.middleware.webservice.WebService; import java.util.concurrent.TimeUnit; @@ -34,8 +34,8 @@ public class DebuggerDataLoader implements ModelLoader { private final DebuggerService debuggerService; - public DebuggerDataLoader(OkHttpClient client) { - debuggerService = new DebuggerService(client); + public DebuggerDataLoader(OkHttpClient client, TrackerContext context) { + debuggerService = new DebuggerService(client, context); } @Override @@ -64,14 +64,15 @@ private static OkHttpClient getsInternalClient() { return sInternalClient; } - public Factory() { - // in order to cache app start event,like:visit,first page event - DebuggerEventWrapper.get().observeEventBuild(); + private final TrackerContext context; + + public Factory(TrackerContext context) { + this.context = context; } @Override public ModelLoader build() { - return new DebuggerDataLoader(getsInternalClient()); + return new DebuggerDataLoader(getsInternalClient(), context); } } } diff --git a/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerEventWrapper.java b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerEventProvider.java similarity index 65% rename from growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerEventWrapper.java rename to growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerEventProvider.java index 423c0c47..bb41841f 100644 --- a/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerEventWrapper.java +++ b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerEventProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,16 +15,16 @@ */ package com.growingio.android.debugger; +import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.SDKConfig; -import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.events.EventBuildInterceptor; import com.growingio.android.sdk.track.events.base.BaseEvent; import com.growingio.android.sdk.track.log.CircularFifoQueue; import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.middleware.GEvent; import com.growingio.android.sdk.track.providers.ConfigurationProvider; -import com.growingio.android.sdk.track.providers.EventStateProvider; -import com.growingio.android.sdk.track.webservices.log.WsLogger; +import com.growingio.android.sdk.track.providers.EventBuilderProvider; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; import org.json.JSONException; import org.json.JSONObject; @@ -37,47 +37,49 @@ * * @author cpacm 2021/2/24 */ -public class DebuggerEventWrapper implements EventBuildInterceptor, ScreenshotProvider.OnScreenshotRefreshedListener { - private static final String TAG = "DebuggerEventWrapper"; +public class DebuggerEventProvider implements EventBuildInterceptor, TrackerLifecycleProvider { + private static final String TAG = "DebuggerEventProvider"; public static final String SERVICE_LOGGER_OPEN = "logger_open"; public static final String SERVICE_LOGGER_CLOSE = "logger_close"; public static final String SERVICE_DEBUGGER_TYPE = "debugger_data"; - private OnDebuggerEventListener mOnDebuggerEventListener; + private OnDebuggerEventListener debuggerEventListener; + private EventBuilderProvider eventBuilderProvider; + private ConfigurationProvider configurationProvider; - private static class SingleInstance { - private static final DebuggerEventWrapper INSTANCE = new DebuggerEventWrapper(); - } - public static DebuggerEventWrapper get() { - return DebuggerEventWrapper.SingleInstance.INSTANCE; + DebuggerEventProvider() { } - public void registerDebuggerEventListener(OnDebuggerEventListener listener) { - this.mOnDebuggerEventListener = listener; + @Override + public void setup(TrackerContext context) { + configurationProvider = context.getConfigurationProvider(); + eventBuilderProvider = context.getProvider(EventBuilderProvider.class); + // before debugger start, cache events. + eventBuilderProvider.addEventBuildInterceptor(this); } - private DebuggerEventWrapper() { + @Override + public void shutdown() { + eventBuilderProvider.removeEventBuildInterceptor(this); } - public void observeEventBuild() { - TrackMainThread.trackMain().addEventBuildInterceptor(this); + public void registerDebuggerEventListener(OnDebuggerEventListener listener) { + this.debuggerEventListener = listener; } public void ready() { mIsConnected = true; - observeEventBuild(); - ScreenshotProvider.get().registerScreenshotRefreshedListener(this); + eventBuilderProvider.addEventBuildInterceptor(this); sendCacheMessage(); } public void end() { mIsConnected = false; - ScreenshotProvider.get().unregisterScreenshotRefreshedListener(); - TrackMainThread.trackMain().removeEventBuildInterceptor(this); + eventBuilderProvider.removeEventBuildInterceptor(this); closeLogger(); - mOnDebuggerEventListener = null; + debuggerEventListener = null; } /***************** Base Event *******************/ @@ -86,8 +88,8 @@ public void end() { private void sendCacheMessage() { for (String message : mCollectionMessage) { - if (mOnDebuggerEventListener != null) { - mOnDebuggerEventListener.onDebuggerMessage(message); + if (debuggerEventListener != null) { + debuggerEventListener.onDebuggerMessage(message); } } mCollectionMessage.clear(); @@ -102,15 +104,15 @@ public void eventDidBuild(GEvent event) { if (event instanceof BaseEvent) { BaseEvent baseEvent = (BaseEvent) event; try { - JSONObject eventJson = EventStateProvider.get().toJson(baseEvent); + JSONObject eventJson = EventBuilderProvider.toJson(baseEvent); //添加额外的url,以便debugger显示请求地址 eventJson.put("url", getUrl()); JSONObject json = new JSONObject(); json.put("msgType", SERVICE_DEBUGGER_TYPE); json.put("sdkVersion", SDKConfig.SDK_VERSION); json.put("data", eventJson); - if (mIsConnected && mOnDebuggerEventListener != null) { - mOnDebuggerEventListener.onDebuggerMessage(json.toString()); + if (mIsConnected && debuggerEventListener != null) { + debuggerEventListener.onDebuggerMessage(json.toString()); } else { mCollectionMessage.add(json.toString()); } @@ -122,12 +124,12 @@ public void eventDidBuild(GEvent event) { } private String getUrl() { - StringBuilder url = new StringBuilder(ConfigurationProvider.core().getDataCollectionServerHost()); + StringBuilder url = new StringBuilder(configurationProvider.core().getDataCollectionServerHost()); if (url.length() > 0 && url.charAt(url.length() - 1) != '/') { url.append("/"); } url.append("v3/projects/"); - String projectId = ConfigurationProvider.core().getProjectId(); + String projectId = configurationProvider.core().getProjectId(); url.append(projectId); url.append("/collect?stm="); url.append(System.currentTimeMillis()); @@ -149,8 +151,8 @@ public void openLogger() { mWsLogger.openLog(); } mWsLogger.setCallback(logMessage -> { - if (mOnDebuggerEventListener != null) { - mOnDebuggerEventListener.onDebuggerMessage(logMessage); + if (debuggerEventListener != null) { + debuggerEventListener.onDebuggerMessage(logMessage); } }); } @@ -164,15 +166,6 @@ public void closeLogger() { mWsLogger = null; } - /***************** ScreenShot *******************/ - @Override - public void onScreenshotRefreshed(DebuggerScreenshot screenshot) { - if (mOnDebuggerEventListener != null) { - mOnDebuggerEventListener.onDebuggerMessage(screenshot.toJSONObject().toString()); - } - printLog(); - } - public interface OnDebuggerEventListener { void onDebuggerMessage(String message); } diff --git a/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerLibraryGioModule.java b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerLibraryGioModule.java index d93a9ae8..177241d4 100644 --- a/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerLibraryGioModule.java +++ b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerLibraryGioModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,14 +15,15 @@ */ package com.growingio.android.debugger; -import android.content.Context; - import com.growingio.android.sdk.LibraryGioModule; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; -import com.growingio.android.sdk.track.webservices.Debugger; -import com.growingio.android.sdk.track.webservices.WebService; +import com.growingio.android.sdk.TrackerContext; +import com.growingio.android.sdk.track.middleware.webservice.Debugger; +import com.growingio.android.sdk.track.middleware.webservice.WebService; +import com.growingio.android.sdk.track.providers.TrackerLifecycleProvider; import com.growingio.sdk.annotation.GIOLibraryModule; +import java.util.Map; + /** *

* @@ -31,7 +32,13 @@ @GIOLibraryModule public class DebuggerLibraryGioModule extends LibraryGioModule { @Override - public void registerComponents(Context context, TrackerRegistry registry) { - registry.register(Debugger.class, WebService.class, new DebuggerDataLoader.Factory()); + public void registerComponents(TrackerContext context) { + context.getRegistry().register(Debugger.class, WebService.class, new DebuggerDataLoader.Factory(context)); + } + + @Override + protected void setupProviders(Map, TrackerLifecycleProvider> providerStore) { + providerStore.put(ScreenshotProvider.class, new ScreenshotProvider()); + providerStore.put(DebuggerEventProvider.class, new DebuggerEventProvider()); } } diff --git a/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerScreenshot.java b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerScreenshot.java index c3be844f..e3ee9da4 100644 --- a/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerScreenshot.java +++ b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerScreenshot.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,11 +15,7 @@ */ package com.growingio.android.debugger; -import android.util.DisplayMetrics; - -import com.growingio.android.sdk.TrackerContext; -import com.growingio.android.sdk.track.async.Callback; -import com.growingio.android.sdk.track.utils.DeviceUtil; +import com.growingio.android.sdk.track.listener.Callback; import org.json.JSONException; import org.json.JSONObject; @@ -68,6 +64,11 @@ public static final class Builder { private long mSnapshotKey; private Callback mScreenshotResultCallback; + Builder(int width, int height) { + this.mScreenWidth = width; + this.mScreenHeight = height; + } + public Builder setScale(float scale) { mScale = scale; return this; @@ -88,10 +89,6 @@ public void build(Callback callback) { return; } mScreenshotResultCallback = callback; - DisplayMetrics displayMetrics = DeviceUtil.getDisplayMetrics(TrackerContext.get().getApplicationContext()); - mScreenWidth = displayMetrics.widthPixels; - mScreenHeight = displayMetrics.heightPixels; - callResultOnSuccess(); } diff --git a/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerService.java b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerService.java index ff17cc51..d6003b8e 100644 --- a/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerService.java +++ b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/DebuggerService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,17 +18,11 @@ import android.util.Base64; import com.growingio.android.sdk.TrackerContext; -import com.growingio.android.sdk.track.listener.IActivityLifecycle; -import com.growingio.android.sdk.track.listener.event.ActivityLifecycleEvent; +import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.log.Logger; -import com.growingio.android.sdk.track.middleware.EventFlutter; import com.growingio.android.sdk.track.modelloader.LoadDataFetcher; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; -import com.growingio.android.sdk.track.utils.ThreadUtils; -import com.growingio.android.sdk.track.webservices.Debugger; -import com.growingio.android.sdk.track.webservices.WebService; -import com.growingio.android.sdk.track.webservices.message.ClientInfoMessage; -import com.growingio.android.sdk.track.webservices.message.QuitMessage; +import com.growingio.android.sdk.track.middleware.webservice.Debugger; +import com.growingio.android.sdk.track.middleware.webservice.WebService; import org.json.JSONException; import org.json.JSONObject; @@ -44,8 +38,8 @@ * * @author cpacm 5/19/21 */ -public class DebuggerService implements LoadDataFetcher, IActivityLifecycle, - WebSocketHandler.OnWebSocketListener { +public class DebuggerService implements LoadDataFetcher, + WebSocketHandler.OnWebSocketListener, ScreenshotProvider.OnScreenshotRefreshedListener { private static final String TAG = "DebuggerService"; private static final String WS_URL = "wsUrl"; @@ -54,13 +48,15 @@ public class DebuggerService implements LoadDataFetcher, IActivityLi private static final int SOCKET_STATE_CLOSED = 2; private final OkHttpClient client; - private final ThreadSafeTipView safeTipView; private final WebSocketHandler webSocketHandler; private Map params; private int debuggerDataType; protected final AtomicInteger socketState = new AtomicInteger(SOCKET_STATE_INITIALIZE); + private final ScreenshotProvider screenshotProvider; + private final DebuggerEventProvider debuggerEventProvider; + void sendDebuggerData(Debugger debugger) { debuggerDataType = debugger.debuggerDataType; if (debuggerDataType == Debugger.DEBUGGER_INIT) { @@ -69,22 +65,16 @@ void sendDebuggerData(Debugger debugger) { byte[] screenshot = debugger.getScreenshot(); if (screenshot != null) { String screenshotBase64 = "data:image/jpeg;base64," + Base64.encodeToString(screenshot, Base64.DEFAULT); - ScreenshotProvider.get().generateDebuggerData(screenshotBase64); + screenshotProvider.generateDebuggerData(screenshotBase64); } } } - public DebuggerService(OkHttpClient client) { - DebuggerEventWrapper.get().registerDebuggerEventListener(new DebuggerEventWrapper.OnDebuggerEventListener() { - @Override - public void onDebuggerMessage(String message) { - sendMessage(message); - } - }); + public DebuggerService(OkHttpClient client, TrackerContext context) { this.client = client; - ActivityStateProvider.get().registerActivityLifecycleListener(this); - safeTipView = new ThreadSafeTipView(TrackerContext.get().getApplicationContext()); - webSocketHandler = new WebSocketHandler(this); + screenshotProvider = context.getProvider(ScreenshotProvider.class); + debuggerEventProvider = context.getProvider(DebuggerEventProvider.class); + webSocketHandler = new WebSocketHandler(this, screenshotProvider); } @Override @@ -116,17 +106,13 @@ public void loadData(DataCallback callback) { client.newWebSocket(request, webSocketHandler); //client.dispatcher().executorService().shutdown() - ActivityStateProvider.get().registerActivityLifecycleListener(this); - safeTipView.enableShow(); - - ThreadUtils.postOnUiThreadDelayed( - new Runnable() { - @Override - public void run() { - if (socketState.get() < SOCKET_STATE_READIED) { - Logger.e(TAG, "start WebSocketService timeout"); - onFailed(); - } + screenshotProvider.enableTipViewShow(); + + TrackMainThread.trackMain().postOnUiThreadDelayed( + () -> { + if (socketState.get() < SOCKET_STATE_READIED) { + Logger.e(TAG, "start WebSocketService timeout"); + onFailed(); } }, 10000); } @@ -142,6 +128,7 @@ public WebService executeData() { } public void cleanup() { + sendMessage(screenshotProvider.buildQuitMessage()); cancel(); } @@ -151,9 +138,9 @@ public void cancel() { if (webSocketHandler.getWebSocket() != null) { webSocketHandler.getWebSocket().close(1000, "exit"); } - DebuggerEventWrapper.get().end(); - safeTipView.dismiss(); - ActivityStateProvider.get().unregisterActivityLifecycleListener(this); + debuggerEventProvider.end(); + screenshotProvider.disableTipView(); + screenshotProvider.unregisterScreenshotRefreshedListener(); } @Override @@ -165,13 +152,12 @@ public Class getDataClass() { /************************** WebSocket Handler ************************/ @Override public void onReady() { - sendMessage(ClientInfoMessage.createMessage().toJSONObject().toString()); - DebuggerEventWrapper.get().registerDebuggerEventListener(this::sendMessage); + sendMessage(screenshotProvider.buildClientInfoMessage()); + debuggerEventProvider.registerDebuggerEventListener(this::sendMessage); socketState.set(SOCKET_STATE_READIED); - DebuggerEventWrapper.get().ready(); - safeTipView.onReady(this::exitDebugger); - - TrackerContext.get().executeData(EventFlutter.flutterDebugger(true), EventFlutter.class, Void.class); + debuggerEventProvider.ready(); + screenshotProvider.registerScreenshotRefreshedListener(this); + screenshotProvider.readyTipView(this::cleanup); } @Override @@ -179,10 +165,10 @@ public void onMessage(String msg) { try { JSONObject message = new JSONObject(msg); String msgType = message.optString("msgType"); - if (DebuggerEventWrapper.SERVICE_LOGGER_OPEN.equals(msgType)) { - DebuggerEventWrapper.get().openLogger(); - } else if (DebuggerEventWrapper.SERVICE_LOGGER_CLOSE.equals(msgType)) { - DebuggerEventWrapper.get().closeLogger(); + if (DebuggerEventProvider.SERVICE_LOGGER_OPEN.equals(msgType)) { + debuggerEventProvider.openLogger(); + } else if (DebuggerEventProvider.SERVICE_LOGGER_CLOSE.equals(msgType)) { + debuggerEventProvider.closeLogger(); } } catch (JSONException e) { Logger.e(TAG, e); @@ -190,8 +176,6 @@ public void onMessage(String msg) { } protected void exitDebugger() { - TrackerContext.get().executeData(EventFlutter.flutterDebugger(false), EventFlutter.class, Void.class); - sendMessage(new QuitMessage().toJSONObject().toString()); cleanup(); } @@ -201,9 +185,10 @@ public void onFailed() { return; } socketState.set(SOCKET_STATE_CLOSED); - safeTipView.setErrorMessage(R.string.growing_debugger_connected_to_web_failed); + Logger.e(TAG, "Start CirclerService Failed"); - safeTipView.showQuitedDialog(this::exitDebugger); + screenshotProvider.setTipViewMessage(R.string.growing_debugger_connected_to_web_failed); + screenshotProvider.showQuitDialog(this::cleanup); } @Override @@ -213,21 +198,14 @@ public void onQuited() { } cancel(); socketState.set(SOCKET_STATE_CLOSED); - safeTipView.showQuitedDialog(this::exitDebugger); - } - - public AtomicInteger getSocketState() { - return socketState; + screenshotProvider.showQuitDialog(this::cleanup); } /************************** Activity Lifecycle ************************/ - @Override - public void onActivityLifecycle(ActivityLifecycleEvent event) { - if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_RESUMED) { - safeTipView.show(event.getActivity()); - } else if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_PAUSED) { - safeTipView.removeOnly(); + public void onScreenshotRefreshed(DebuggerScreenshot screenshot) { + if (screenshot != null) { + sendMessage(screenshot.toJSONObject().toString()); } } } diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/log/LoggerDataMessage.java b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/LoggerDataMessage.java similarity index 74% rename from growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/log/LoggerDataMessage.java rename to growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/LoggerDataMessage.java index 0b3df8e6..d87b9dd7 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/log/LoggerDataMessage.java +++ b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/LoggerDataMessage.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.growingio.android.sdk.track.webservices.log; +package com.growingio.android.debugger; import android.util.Log; import com.growingio.android.sdk.track.SDKConfig; +import com.growingio.android.sdk.track.log.LogItem; import org.json.JSONArray; import org.json.JSONException; @@ -31,19 +31,19 @@ public class LoggerDataMessage { public static final String MSG_TYPE = "logger_data"; - private List mLogs; + private final List mLogs; private final String mMsgType; - private LoggerDataMessage(List logs) { + private LoggerDataMessage(List logs) { mMsgType = MSG_TYPE; mLogs = logs; } - public static LoggerDataMessage createTrackMessage(List logs) { - List list = new ArrayList<>(logs.size()); + public static LoggerDataMessage createTrackMessage(List logs) { + List list = new ArrayList<>(logs.size()); for (int i = 0; i < logs.size(); i++) { - com.growingio.android.sdk.track.log.LogItem logItem = logs.get(i); - list.add(LogItem.create(priorityToState(logItem.getPriority()), "subType", logItem.getMessage(), logItem.getTimeStamp())); + LogItem logItem = logs.get(i); + list.add(DebuggerLogItem.create(priorityToState(logItem.getPriority()), "subType", logItem.getMessage(), logItem.getTimeStamp())); } return new LoggerDataMessage(list); } @@ -54,7 +54,7 @@ public JSONObject toJSONObject() { json.put("msgType", mMsgType); json.put("sdkVersion", SDKConfig.SDK_VERSION); JSONArray logs = new JSONArray(); - for (LogItem logItem : mLogs) { + for (DebuggerLogItem logItem : mLogs) { logs.put(logItem.toJSONObject()); } json.put("data", logs); @@ -82,22 +82,22 @@ private static String priorityToState(int priority) { } } - public static class LogItem { + public static class DebuggerLogItem { private final String mType; private final String mSubType; private final String mMessage; private final long mTime; - public LogItem(String type, String subType, String message, long time) { + public DebuggerLogItem(String type, String subType, String message, long time) { mType = type; mSubType = subType; mMessage = message; mTime = time; } - public static LogItem create(String type, String subType, String message, long time) { - return new LogItem(type, subType, message, time); + public static DebuggerLogItem create(String type, String subType, String message, long time) { + return new DebuggerLogItem(type, subType, message, time); } public JSONObject toJSONObject() { diff --git a/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/ScreenshotProvider.java b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/ScreenshotProvider.java index 3193ce7e..9b054c42 100644 --- a/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/ScreenshotProvider.java +++ b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/ScreenshotProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.growingio.android.debugger; import android.app.Activity; @@ -22,18 +21,28 @@ import android.os.Message; import android.util.DisplayMetrics; +import com.growingio.android.sdk.CoreConfiguration; import com.growingio.android.sdk.TrackerContext; -import com.growingio.android.sdk.track.async.Callback; +import com.growingio.android.sdk.track.SDKConfig; +import com.growingio.android.sdk.track.listener.Callback; +import com.growingio.android.sdk.track.listener.event.ActivityLifecycleEvent; import com.growingio.android.sdk.track.log.Logger; +import com.growingio.android.sdk.track.middleware.EventFlutter; import com.growingio.android.sdk.track.modelloader.ModelLoader; import com.growingio.android.sdk.track.middleware.hybrid.HybridDom; import com.growingio.android.sdk.track.middleware.hybrid.HybridJson; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; +import com.growingio.android.sdk.track.modelloader.TrackerRegistry; +import com.growingio.android.sdk.track.providers.AppInfoProvider; +import com.growingio.android.sdk.track.providers.ConfigurationProvider; +import com.growingio.android.sdk.track.providers.DeviceInfoProvider; import com.growingio.android.sdk.track.utils.DeviceUtil; import com.growingio.android.sdk.track.view.ScreenshotUtil; import com.growingio.android.sdk.track.view.ViewStateChangedEvent; import com.growingio.android.sdk.track.view.ViewTreeStatusListener; +import org.json.JSONException; +import org.json.JSONObject; + import java.io.IOException; public class ScreenshotProvider extends ViewTreeStatusListener { @@ -45,33 +54,49 @@ public class ScreenshotProvider extends ViewTreeStatusListener { private static final long MAX_REFRESH_INTERVAL = 3000L; private long lastSendTime = 0L; // 记录上次发送的事件,用来避免当界面刷新频率过快时一直无法发送圈选事件。 - private final float mScale; - private final Handler mHandler; - private final Runnable mRefreshScreenshotRunnable = this::dispatchScreenshot; + private float scale; + private final Handler screenshotHandler; + private final Runnable refreshScreenshotRunnable = this::dispatchScreenshot; - private OnScreenshotRefreshedListener mListener; + private OnScreenshotRefreshedListener refreshListener; + private ThreadSafeTipView safeTipView; - private static class SingleInstance { - private static final ScreenshotProvider INSTANCE = new ScreenshotProvider(); - } - - private ScreenshotProvider() { - DisplayMetrics metrics = DeviceUtil.getDisplayMetrics(TrackerContext.get().getApplicationContext()); - mScale = SCREENSHOT_STANDARD_WIDTH / Math.min(metrics.widthPixels, metrics.heightPixels); + private ConfigurationProvider configurationProvider; + private AppInfoProvider appInfoProvider; + private DeviceInfoProvider deviceInfoProvider; + private TrackerRegistry registry; + ScreenshotProvider() { HandlerThread mHandlerThread = new HandlerThread("ScreenshotProvider"); mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); + screenshotHandler = new Handler(mHandlerThread.getLooper()); + } + + @Override + public void setup(TrackerContext context) { + super.setup(context); + configurationProvider = context.getConfigurationProvider(); + appInfoProvider = context.getProvider(AppInfoProvider.class); + deviceInfoProvider = context.getDeviceInfoProvider(); + registry = context.getRegistry(); - getHybridModelLoader(); + DisplayMetrics metrics = DeviceUtil.getDisplayMetrics(context.getBaseContext()); + scale = SCREENSHOT_STANDARD_WIDTH / Math.min(metrics.widthPixels, metrics.heightPixels); + + safeTipView = new ThreadSafeTipView(context.getBaseContext(), activityStateProvider, appInfoProvider.getAppVersion()); + + ModelLoader modelLoader = context.getRegistry().getModelLoader(HybridDom.class, HybridJson.class); + if (modelLoader != null) { + modelLoader.buildLoadData(new HybridDom(this::refreshScreenshot)).fetcher.executeData(); + } } @Override public void onViewStateChanged(ViewStateChangedEvent changedEvent) { if (System.currentTimeMillis() - lastSendTime >= MAX_REFRESH_INTERVAL) { lastSendTime = System.currentTimeMillis(); - mHandler.post(this::dispatchScreenshot); + screenshotHandler.post(this::dispatchScreenshot); } else { if (changedEvent.getStateType() == ViewStateChangedEvent.StateType.MANUAL_CHANGED) { refreshScreenshot(EVENT_REFRESH_INTERVAL); @@ -81,23 +106,16 @@ public void onViewStateChanged(ViewStateChangedEvent changedEvent) { } } - private void getHybridModelLoader() { - ModelLoader modelLoader = TrackerContext.get().getRegistry().getModelLoader(HybridDom.class, HybridJson.class); - if (modelLoader != null) { - modelLoader.buildLoadData(new HybridDom(this::refreshScreenshot)).fetcher.executeData(); - } - } - private void dispatchScreenshot() { - if (mListener == null) return; - Activity activity = ActivityStateProvider.get().getForegroundActivity(); + if (refreshListener == null) return; + Activity activity = activityStateProvider.getForegroundActivity(); if (activity == null) return; try { - ScreenshotUtil.getScreenshotBitmap(mScale, bitmap -> { + ScreenshotUtil.getScreenshotBitmap(scale, bitmap -> { try { String screenshotBase64 = ScreenshotUtil.getScreenshotBase64(bitmap); - sendScreenshotRefreshed(screenshotBase64, mScale); + sendScreenshotRefreshed(screenshotBase64, scale); } catch (IOException e) { Logger.e(TAG, "base64 screenshot failed:" + e.getMessage()); } @@ -108,8 +126,8 @@ private void dispatchScreenshot() { } private void refreshScreenshot(long duration) { - mHandler.removeCallbacks(mRefreshScreenshotRunnable); - mHandler.postDelayed(mRefreshScreenshotRunnable, duration); + screenshotHandler.removeCallbacks(refreshScreenshotRunnable); + screenshotHandler.postDelayed(refreshScreenshotRunnable, duration); } private void refreshScreenshot() { @@ -117,31 +135,31 @@ private void refreshScreenshot() { } public void generateDebuggerData(String screenshotBase64) { - if (mListener == null) return; - mHandler.removeMessages(0); - Message message = Message.obtain(mHandler, new Runnable() { + if (refreshListener == null) return; + screenshotHandler.removeMessages(0); + Message message = Message.obtain(screenshotHandler, new Runnable() { @Override public void run() { - sendScreenshotRefreshed(screenshotBase64, mScale); + sendScreenshotRefreshed(screenshotBase64, scale); } }); message.what = 0; - mHandler.sendMessageDelayed(message, MIN_REFRESH_INTERVAL); - } - - public static ScreenshotProvider get() { - return SingleInstance.INSTANCE; + screenshotHandler.sendMessageDelayed(message, MIN_REFRESH_INTERVAL); } public void registerScreenshotRefreshedListener(OnScreenshotRefreshedListener listener) { register(); - mListener = listener; + refreshListener = listener; refreshScreenshot(); + + registry.executeData(EventFlutter.flutterCircle(true), EventFlutter.class, Void.class); } public void unregisterScreenshotRefreshedListener() { - mListener = null; + refreshListener = null; unRegister(); + + registry.executeData(EventFlutter.flutterCircle(false), EventFlutter.class, Void.class); } public interface OnScreenshotRefreshedListener { @@ -154,14 +172,16 @@ public void sendScreenshotRefreshed(String screenshotBase64, float scale) { lastSendTime = System.currentTimeMillis(); - DebuggerScreenshot.Builder builder = new DebuggerScreenshot.Builder() + DebuggerScreenshot.Builder builder = new DebuggerScreenshot + .Builder(deviceInfoProvider.getScreenWidth(), deviceInfoProvider.getScreenHeight()) .setScale(scale) .setScreenshot(screenshotBase64) .setSnapshotKey(mSnapshotKey++); builder.build(new Callback() { @Override public void onSuccess(DebuggerScreenshot result) { - if (result != null && mListener != null) mListener.onScreenshotRefreshed(result); + if (result != null && refreshListener != null) + refreshListener.onScreenshotRefreshed(result); } @Override @@ -170,4 +190,90 @@ public void onFailed() { } }); } + + @Override + public void onActivityLifecycle(ActivityLifecycleEvent event) { + if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_RESUMED) { + safeTipView.show(event.getActivity()); + } else if (event.eventType == ActivityLifecycleEvent.EVENT_TYPE.ON_PAUSED) { + safeTipView.removeOnly(); + } + } + + void enableTipViewShow() { + safeTipView.enableShow(); + } + + void disableTipView() { + safeTipView.dismiss(); + } + + void readyTipView(ThreadSafeTipView.OnExitListener listener) { + safeTipView.onReady(listener); + } + + void setTipViewMessage(int message) { + safeTipView.setErrorMessage(message); + } + + void showQuitDialog(ThreadSafeTipView.OnExitListener listener) { + safeTipView.showQuitedDialog(listener); + } + + static final String MSG_READY_TYPE = "ready"; + static final String MSG_QUIT_TYPE = "quit"; + private static final String MSG_OS = "Android"; + static final String MSG_CLIENT_TYPE = "client_info"; + + String buildReadyMessage() { + CoreConfiguration core = configurationProvider.core(); + JSONObject json = new JSONObject(); + try { + json.put("projectId", core.getProjectId()); + json.put("msgType", MSG_READY_TYPE); + json.put("timestamp", System.currentTimeMillis()); + json.put("domain", appInfoProvider.getPackageName()); + json.put("sdkVersion", SDKConfig.SDK_VERSION); + json.put("sdkVersionCode", SDKConfig.SDK_VERSION_CODE); + json.put("os", MSG_OS); + json.put("screenWidth", deviceInfoProvider.getScreenWidth()); + json.put("screenHeight", deviceInfoProvider.getScreenHeight()); + json.put("urlScheme", core.getUrlScheme()); + } catch (JSONException ignored) { + } + return json.toString(); + } + + String buildQuitMessage() { + JSONObject json = new JSONObject(); + try { + json.put("msgType", MSG_QUIT_TYPE); + } catch (JSONException ignored) { + } + return json.toString(); + } + + String buildClientInfoMessage() { + CoreConfiguration core = configurationProvider.core(); + JSONObject json = new JSONObject(); + try { + json.put("msgType", MSG_CLIENT_TYPE); + json.put("sdkVersion", SDKConfig.SDK_VERSION); + + JSONObject info = new JSONObject(); + info.put("os", MSG_OS); + info.put("appVersion", appInfoProvider.getAppVersion()); + info.put("appChannel", core.getChannel()); + info.put("osVersion", deviceInfoProvider.getOperatingSystemVersion()); + info.put("deviceType", deviceInfoProvider.getDeviceType()); + info.put("deviceBrand", deviceInfoProvider.getDeviceBrand()); + info.put("deviceModel", deviceInfoProvider.getDeviceModel()); + + json.put("data", info); + + } catch (JSONException ignored) { + } + + return json.toString(); + } } \ No newline at end of file diff --git a/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/ThreadSafeTipView.java b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/ThreadSafeTipView.java index e60dab59..492e733d 100644 --- a/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/ThreadSafeTipView.java +++ b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/ThreadSafeTipView.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.debugger; import android.app.Activity; @@ -22,11 +21,10 @@ import android.content.DialogInterface; import com.growingio.android.sdk.track.SDKConfig; +import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.providers.ActivityStateProvider; -import com.growingio.android.sdk.track.providers.AppInfoProvider; -import com.growingio.android.sdk.track.utils.ThreadUtils; -import com.growingio.android.sdk.track.webservices.widget.TipView; +import com.growingio.android.sdk.track.view.TipView; /** *

@@ -39,6 +37,9 @@ public class ThreadSafeTipView { private TipView tipView; private final Context context; + private final String appVersion; + private final ActivityStateProvider activityStateProvider; + private void initView() { if (tipView == null) { tipView = new TipView(context); @@ -46,9 +47,11 @@ private void initView() { } } - public ThreadSafeTipView(Context context) { + public ThreadSafeTipView(Context context, ActivityStateProvider activityStateProvider, String appVersion) { + this.appVersion = appVersion; this.context = context; - runOnUiThread(() -> initView()); + this.activityStateProvider = activityStateProvider; + runOnUiThread(this::initView); } public void enableShow() { @@ -96,32 +99,27 @@ public void show(Activity activity) { }); } - private void showExitDialog(OnExitListener listener) { - Activity activity = ActivityStateProvider.get().getForegroundActivity(); + protected void showExitDialog(OnExitListener listener) { + Activity activity = activityStateProvider.getForegroundActivity(); if (activity == null) { Logger.e(TAG, "showExitDialog: ForegroundActivity is NULL"); return; } String message = activity.getString(R.string.growing_debugger_app_version) - + AppInfoProvider.get().getAppVersion() + + appVersion + activity.getString(R.string.growing_debugger_sdk_version) + SDKConfig.SDK_VERSION; new AlertDialog.Builder(activity) .setTitle(R.string.growing_debugger_progress) .setMessage(message) - .setPositiveButton(R.string.growing_debugger_exit, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - listener.onExitDebugger(); - } - }) + .setPositiveButton(R.string.growing_debugger_exit, (dialog, which) -> listener.onExitDebugger()) .setNegativeButton(R.string.growing_debugger_continue, null) .create() .show(); } public void showQuitedDialog(OnExitListener listener) { - Activity activity = ActivityStateProvider.get().getForegroundActivity(); + Activity activity = activityStateProvider.getForegroundActivity(); if (activity == null) { Logger.e(TAG, "showQuitedDialog: ForegroundActivity is NULL"); return; @@ -147,7 +145,7 @@ public void onDismiss(DialogInterface dialog) { } private void runOnUiThread(Runnable r) { - ThreadUtils.runOnUiThread(r); + TrackMainThread.trackMain().runOnUiThread(r); } public interface OnExitListener { diff --git a/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/WebSocketHandler.java b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/WebSocketHandler.java index 05cf91b6..fc1d4280 100644 --- a/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/WebSocketHandler.java +++ b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/WebSocketHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,9 +18,7 @@ import android.text.TextUtils; import android.util.Log; -import com.growingio.android.sdk.track.utils.ThreadUtils; -import com.growingio.android.sdk.track.webservices.message.QuitMessage; -import com.growingio.android.sdk.track.webservices.message.ReadyMessage; +import com.growingio.android.sdk.track.TrackMainThread; import org.json.JSONException; import org.json.JSONObject; @@ -41,7 +39,10 @@ class WebSocketHandler extends WebSocketListener { private WebSocket webSocket; private static final String TAG = "WebSocketHandler"; - WebSocketHandler(OnWebSocketListener webSocketListener) { + private final ScreenshotProvider screenshotProvider; + + WebSocketHandler(OnWebSocketListener webSocketListener, ScreenshotProvider screenshotProvider) { + this.screenshotProvider = screenshotProvider; this.webSocketListener = webSocketListener; } @@ -54,11 +55,11 @@ public void sendMessage(String msg) { @Override public void onOpen(WebSocket webSocket, Response response) { - if (webSocket.send(ReadyMessage.createMessage().toJSONObject().toString())) { + if (webSocket.send(screenshotProvider.buildReadyMessage())) { this.webSocket = webSocket; } else { Log.e(TAG, "send ready message failed"); - ThreadUtils.runOnUiThread(new Runnable() { + TrackMainThread.trackMain().runOnUiThread(new Runnable() { @Override public void run() { webSocketListener.onFailed(); @@ -75,24 +76,24 @@ public void onMessage(WebSocket webSocket, String text) { Log.d(TAG, "Received message is " + text); if (text.contains("had disconnected")) { - ThreadUtils.runOnUiThread(() -> webSocketListener.onQuited()); + TrackMainThread.trackMain().runOnUiThread(() -> webSocketListener.onQuited()); return; } try { JSONObject message = new JSONObject(text); String msgType = message.optString("msgType"); - if (ReadyMessage.MSG_TYPE.equals(msgType)) { + if (ScreenshotProvider.MSG_READY_TYPE.equals(msgType)) { Log.d(TAG, "Web is ready"); - ThreadUtils.runOnUiThread(new Runnable() { + TrackMainThread.trackMain().runOnUiThread(new Runnable() { @Override public void run() { webSocketListener.onReady(); } }); return; - } else if (QuitMessage.MSG_TYPE.equals(msgType)) { + } else if (ScreenshotProvider.MSG_QUIT_TYPE.equals(msgType)) { Log.d(TAG, "Web is quited"); - ThreadUtils.runOnUiThread(new Runnable() { + TrackMainThread.trackMain().runOnUiThread(new Runnable() { @Override public void run() { webSocketListener.onQuited(); @@ -103,7 +104,7 @@ public void run() { } catch (JSONException e) { Log.e(TAG, e.getMessage()); } - ThreadUtils.runOnUiThread(new Runnable() { + TrackMainThread.trackMain().runOnUiThread(new Runnable() { @Override public void run() { webSocketListener.onMessage(text); @@ -114,7 +115,7 @@ public void run() { @Override public void onClosed(WebSocket webSocket, int code, String reason) { Log.e(TAG, "webSocket on onClosed, reason: " + reason); - ThreadUtils.runOnUiThread(new Runnable() { + TrackMainThread.trackMain().runOnUiThread(new Runnable() { @Override public void run() { webSocketListener.onQuited(); @@ -125,7 +126,7 @@ public void run() { @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { Log.e(TAG, "webSocket on onFailure, reason: " + t.getMessage()); - ThreadUtils.runOnUiThread(new Runnable() { + TrackMainThread.trackMain().runOnUiThread(new Runnable() { @Override public void run() { webSocketListener.onFailed(); diff --git a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/log/WsLogger.java b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/WsLogger.java similarity index 96% rename from growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/log/WsLogger.java rename to growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/WsLogger.java index 06ff0e31..6a06232f 100644 --- a/growingio-tracker-core/src/main/java/com/growingio/android/sdk/track/webservices/log/WsLogger.java +++ b/growingio-webservice/debugger/src/main/java/com/growingio/android/debugger/WsLogger.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,8 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.growingio.android.sdk.track.webservices.log; +package com.growingio.android.debugger; import android.os.Handler; import android.os.HandlerThread; diff --git a/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/DebuggerTest.java b/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/DebuggerTest.java index e0d05b57..0557638c 100644 --- a/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/DebuggerTest.java +++ b/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/DebuggerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,9 @@ */ package com.growingio.android.debugger; +import static com.google.common.truth.Truth.assertThat; + +import android.annotation.SuppressLint; import android.app.Activity; import android.app.Application; import android.os.Looper; @@ -24,19 +27,15 @@ import com.google.common.truth.Truth; import com.google.common.util.concurrent.Uninterruptibles; import com.growingio.android.debugger.shadow.ShadowThreadUtils; +import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.TrackerContext; import com.growingio.android.sdk.track.TrackMainThread; import com.growingio.android.sdk.track.events.CustomEvent; -import com.growingio.android.sdk.track.listener.event.ActivityLifecycleEvent; +import com.growingio.android.sdk.track.log.Logger; import com.growingio.android.sdk.track.modelloader.DataFetcher; import com.growingio.android.sdk.track.modelloader.ModelLoader; -import com.growingio.android.sdk.track.modelloader.TrackerRegistry; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; -import com.growingio.android.sdk.track.utils.ThreadUtils; -import com.growingio.android.sdk.track.webservices.Debugger; -import com.growingio.android.sdk.track.webservices.WebService; -import com.growingio.android.sdk.track.webservices.message.QuitMessage; -import com.growingio.android.sdk.track.webservices.message.ReadyMessage; +import com.growingio.android.sdk.track.middleware.webservice.Debugger; +import com.growingio.android.sdk.track.middleware.webservice.WebService; import org.json.JSONException; import org.json.JSONObject; @@ -69,9 +68,12 @@ public class DebuggerTest implements WebSocketHandler.OnWebSocketListener { private WebSocketHandler webSocketHandler; private OkHttpClient client; private MockWebServer mockWebServer; - private final Application context = ApplicationProvider.getApplicationContext(); + private final Application application = ApplicationProvider.getApplicationContext(); private int round = 0; + private TrackerContext context; + private ScreenshotProvider screenshotProvider; + protected String getWsUrl() { String hostName = mockWebServer.getHostName(); int port = mockWebServer.getPort(); @@ -88,7 +90,12 @@ private void sendMessage(WebSocket webSocket, String message) { @Before public void setup() { - webSocketHandler = new WebSocketHandler(this); + Tracker tracker = new Tracker(application); + DebuggerLibraryGioModule dModule = new DebuggerLibraryGioModule(); + tracker.registerComponent(dModule); + context = tracker.getContext(); + screenshotProvider = context.getProvider(ScreenshotProvider.class); + webSocketHandler = new WebSocketHandler(this, screenshotProvider); mockWebServer = new MockWebServer(); mockWebServer.setDispatcher(new Dispatcher() { @Override @@ -106,25 +113,25 @@ public void onMessage(WebSocket webSocket, String text) { JSONObject message = new JSONObject(text); String msgType = message.optString("msgType"); switch (msgType) { - case ReadyMessage.MSG_TYPE: + case ScreenshotProvider.MSG_READY_TYPE: round = 1; - sendMessage(webSocket, ReadyMessage.MSG_TYPE); + sendMessage(webSocket, ScreenshotProvider.MSG_READY_TYPE); break; - case DebuggerEventWrapper.SERVICE_LOGGER_OPEN: + case DebuggerEventProvider.SERVICE_LOGGER_OPEN: round = 2; - sendMessage(webSocket, DebuggerEventWrapper.SERVICE_LOGGER_OPEN); + sendMessage(webSocket, DebuggerEventProvider.SERVICE_LOGGER_OPEN); break; - case DebuggerEventWrapper.SERVICE_LOGGER_CLOSE: + case DebuggerEventProvider.SERVICE_LOGGER_CLOSE: round = 3; - sendMessage(webSocket, DebuggerEventWrapper.SERVICE_LOGGER_CLOSE); + sendMessage(webSocket, DebuggerEventProvider.SERVICE_LOGGER_CLOSE); break; - case DebuggerEventWrapper.SERVICE_DEBUGGER_TYPE: + case DebuggerEventProvider.SERVICE_DEBUGGER_TYPE: round = 4; - sendMessage(webSocket, DebuggerEventWrapper.SERVICE_DEBUGGER_TYPE); + sendMessage(webSocket, DebuggerEventProvider.SERVICE_DEBUGGER_TYPE); break; - case QuitMessage.MSG_TYPE: + case ScreenshotProvider.MSG_QUIT_TYPE: round = 5; - sendMessage(webSocket, QuitMessage.MSG_TYPE); + sendMessage(webSocket, ScreenshotProvider.MSG_QUIT_TYPE); break; default: webSocket.send("error data"); @@ -136,7 +143,6 @@ public void onMessage(WebSocket webSocket, String text) { }); } }); - TrackerContext.init(context); client = new OkHttpClient.Builder() .connectTimeout(5, TimeUnit.SECONDS) .readTimeout(5, TimeUnit.SECONDS) @@ -145,7 +151,6 @@ public void onMessage(WebSocket webSocket, String text) { try { Looper.prepare(); System.out.println("[intercept]:" + Looper.myLooper()); - ThreadUtils.setUiThread(Looper.myLooper()); } catch (Exception ignored) { } return chain.proceed(chain.request()); @@ -165,7 +170,7 @@ public void socketHandlerTest() { public void onReady() { System.out.println("[client]:onReady"); Truth.assertThat(round).isEqualTo(1); - sendMessage(webSocketHandler.getWebSocket(), DebuggerEventWrapper.SERVICE_LOGGER_OPEN); + sendMessage(webSocketHandler.getWebSocket(), DebuggerEventProvider.SERVICE_LOGGER_OPEN); } @Override @@ -187,15 +192,15 @@ public void onMessage(String msg) { try { JSONObject message = new JSONObject(msg); String msgType = message.optString("msgType"); - if (msgType.equals(DebuggerEventWrapper.SERVICE_LOGGER_OPEN)) { + if (msgType.equals(DebuggerEventProvider.SERVICE_LOGGER_OPEN)) { Truth.assertThat(round).isEqualTo(2); - sendMessage(webSocketHandler.getWebSocket(), DebuggerEventWrapper.SERVICE_LOGGER_CLOSE); - } else if (msgType.equals(DebuggerEventWrapper.SERVICE_LOGGER_CLOSE)) { + sendMessage(webSocketHandler.getWebSocket(), DebuggerEventProvider.SERVICE_LOGGER_CLOSE); + } else if (msgType.equals(DebuggerEventProvider.SERVICE_LOGGER_CLOSE)) { Truth.assertThat(round).isEqualTo(3); - sendMessage(webSocketHandler.getWebSocket(), DebuggerEventWrapper.SERVICE_DEBUGGER_TYPE); - } else if (msgType.equals(DebuggerEventWrapper.SERVICE_DEBUGGER_TYPE)) { + sendMessage(webSocketHandler.getWebSocket(), DebuggerEventProvider.SERVICE_DEBUGGER_TYPE); + } else if (msgType.equals(DebuggerEventProvider.SERVICE_DEBUGGER_TYPE)) { Truth.assertThat(round).isEqualTo(4); - sendMessage(webSocketHandler.getWebSocket(), QuitMessage.MSG_TYPE); + sendMessage(webSocketHandler.getWebSocket(), ScreenshotProvider.MSG_QUIT_TYPE); } } catch (JSONException e) { System.out.println("[client]:error json data"); @@ -206,13 +211,13 @@ public void onMessage(String msg) { @Test public void debuggerModelTest() throws JSONException { DebuggerLibraryGioModule module = new DebuggerLibraryGioModule(); - TrackerRegistry registry = new TrackerRegistry(); - module.registerComponents(context, registry); - ModelLoader dataLoader = registry.getModelLoader(Debugger.class, WebService.class); + module.registerComponents(context); + ModelLoader dataLoader = context.getRegistry().getModelLoader(Debugger.class, WebService.class); HashMap param = new HashMap<>(); param.put("wsUrl", getWsUrl()); - DataFetcher dataFetcher = dataLoader.buildLoadData(new Debugger(param)).fetcher; + DataFetcher dataFetcher = + dataLoader.buildLoadData(new Debugger(param)).fetcher; Truth.assertThat(dataFetcher.getDataClass()).isAssignableTo(WebService.class); dataFetcher.executeData(); @@ -223,11 +228,9 @@ public void debuggerModelTest() throws JSONException { public void serviceTest(DebuggerService service) throws JSONException { Activity activity = Robolectric.buildActivity(RobolectricActivity.class).create().resume().get(); - ActivityStateProvider.get().onActivityResumed(activity); service.sendMessage("test"); service.socketState.getAndSet(1); - service.onMessage(new JSONObject().put("msgType", DebuggerEventWrapper.SERVICE_LOGGER_OPEN).toString()); - service.onActivityLifecycle(ActivityLifecycleEvent.createOnStartedEvent(activity)); + service.onMessage(new JSONObject().put("msgType", DebuggerEventProvider.SERVICE_LOGGER_OPEN).toString()); service.onFailed(); service.exitDebugger(); service.onQuited(); @@ -235,14 +238,15 @@ public void serviceTest(DebuggerService service) throws JSONException { @Test public void debuggerEventTest() { - DebuggerEventWrapper.get().ready(); - DebuggerEventWrapper.get().registerDebuggerEventListener(new DebuggerEventWrapper.OnDebuggerEventListener() { + DebuggerEventProvider debuggerEventProvider = context.getProvider(DebuggerEventProvider.class); + debuggerEventProvider.ready(); + debuggerEventProvider.registerDebuggerEventListener(new DebuggerEventProvider.OnDebuggerEventListener() { @Override public void onDebuggerMessage(String message) { try { System.out.println(message); JSONObject jsonObject = new JSONObject(message); - if (Objects.equals(jsonObject.opt("msgType"), DebuggerEventWrapper.SERVICE_DEBUGGER_TYPE)) { + if (Objects.equals(jsonObject.opt("msgType"), DebuggerEventProvider.SERVICE_DEBUGGER_TYPE)) { Truth.assertThat(jsonObject.getJSONObject("data").opt("eventName")).isEqualTo("custom"); } } catch (JSONException e) { @@ -256,4 +260,22 @@ public void onDebuggerMessage(String message) { Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); } + @Test + public void logTest() { + WsLogger wsLogger = new WsLogger(); + wsLogger.setCallback(new WsLogger.Callback() { + @SuppressLint("CheckResult") + @Override + public void disposeLog(String logMessage) { + assertThat(logMessage.contains("this is test log")); + } + }); + wsLogger.openLog(); + Logger.v("test", "this is test log"); + wsLogger.closeLog(); + wsLogger.printOut(); + wsLogger.setCallback(null); + + } + } diff --git a/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/RobolectricActivity.java b/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/RobolectricActivity.java index 8170f1ca..ffdaf01a 100644 --- a/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/RobolectricActivity.java +++ b/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/RobolectricActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/ScreenShotTest.java b/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/ScreenShotTest.java index 2e0be0f4..69cb37fa 100644 --- a/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/ScreenShotTest.java +++ b/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/ScreenShotTest.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.debugger; import android.app.Activity; @@ -25,8 +24,8 @@ import com.growingio.android.debugger.shadow.ShadowHandler; import com.growingio.android.debugger.shadow.ShadowWH; import com.growingio.android.hybrid.HybridLibraryGioModule; +import com.growingio.android.sdk.Tracker; import com.growingio.android.sdk.TrackerContext; -import com.growingio.android.sdk.track.providers.ActivityStateProvider; import org.junit.Before; import org.junit.Test; @@ -40,38 +39,44 @@ public class ScreenShotTest { Application application = ApplicationProvider.getApplicationContext(); + private TrackerContext context; @Before public void setup() { - TrackerContext.init(application); + Tracker tracker = new Tracker(application); HybridLibraryGioModule module = new HybridLibraryGioModule(); - module.registerComponents(application, TrackerContext.get().getRegistry()); + tracker.registerComponent(module); + + DebuggerLibraryGioModule dModule = new DebuggerLibraryGioModule(); + tracker.registerComponent(dModule); + + context = tracker.getContext(); } @Test public void dispatchTest() { + ScreenshotProvider screenshotProvider = context.getProvider(ScreenshotProvider.class); Activity activity = Robolectric.buildActivity(RobolectricActivity.class).create().resume().get(); - ActivityStateProvider.get().onActivityResumed(activity); ShadowWH.activity = activity; ScreenshotProvider.OnScreenshotRefreshedListener dispatchListener = screenshot -> { - System.out.println(screenshot.toJSONObject()); Truth.assertThat(screenshot).isNotNull(); }; - ScreenshotProvider.get().registerScreenshotRefreshedListener(dispatchListener); + screenshotProvider.registerScreenshotRefreshedListener(dispatchListener); - ScreenshotProvider.get().unregisterScreenshotRefreshedListener(); + screenshotProvider.unregisterScreenshotRefreshedListener(); } @Test public void debuggerScreenShotTest() { - ScreenshotProvider.get().registerScreenshotRefreshedListener(new ScreenshotProvider.OnScreenshotRefreshedListener() { + ScreenshotProvider screenshotProvider = context.getProvider(ScreenshotProvider.class); + screenshotProvider.registerScreenshotRefreshedListener(new ScreenshotProvider.OnScreenshotRefreshedListener() { @Override public void onScreenshotRefreshed(DebuggerScreenshot screenshot) { Truth.assertThat(screenshot.toJSONObject().toString()).isEqualTo( "{\"screenWidth\":320,\"screenHeight\":470,\"scale\":100,\"screenshot\":\"this test base64\",\"msgType\":\"refreshScreenshot\",\"snapshotKey\":0}"); } }); - ScreenshotProvider.get().sendScreenshotRefreshed("this test base64", 100); + screenshotProvider.sendScreenshotRefreshed("this test base64", 100); } } diff --git a/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/shadow/ShadowHandler.java b/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/shadow/ShadowHandler.java index 51db22dd..5f62edc3 100644 --- a/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/shadow/ShadowHandler.java +++ b/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/shadow/ShadowHandler.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.debugger.shadow; diff --git a/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/shadow/ShadowThreadUtils.java b/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/shadow/ShadowThreadUtils.java index 2f2b9ee9..a7505c76 100644 --- a/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/shadow/ShadowThreadUtils.java +++ b/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/shadow/ShadowThreadUtils.java @@ -1,28 +1,27 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.debugger.shadow; -import com.growingio.android.sdk.track.utils.ThreadUtils; +import com.growingio.android.sdk.track.TrackMainThread; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; -@Implements(ThreadUtils.class) +@Implements(TrackMainThread.class) public class ShadowThreadUtils { public void doSomeThing() { diff --git a/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/shadow/ShadowWH.java b/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/shadow/ShadowWH.java index 274c6069..1d0c55cd 100644 --- a/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/shadow/ShadowWH.java +++ b/growingio-webservice/debugger/src/test/java/com/growingio/android/debugger/shadow/ShadowWH.java @@ -1,19 +1,18 @@ /* - * Copyright (C) 2020 Beijing Yishu Technology Co., Ltd. + * Copyright (C) 2023 Beijing Yishu Technology Co., Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.growingio.android.debugger.shadow;