From 6fa358661ee60474d6b304f9f4c332645bdc43b0 Mon Sep 17 00:00:00 2001 From: Muhammad Noman Date: Thu, 16 Mar 2023 23:27:32 +0500 Subject: [PATCH] [FSSDK-8952] chore: prepare 3.13.4 branch for release (#451) * fix(EventRescheduler): catch exception for event service restart (#446) (cherry picked from commit 585f0998f4dd169f9468ba2930f5725f920f42c3) * [FSSDK-8952] doc: Full Stack to Feature Experimentation Rename (#450) * [FSSDK-8952] doc: Full Stack to Feature Experimentation Rename --------- Co-authored-by: mnoman09 (cherry picked from commit 390c46451a6534b3078608177d4761d6169484c3) * changed java version to 3.10.3 * [FSSDK-8952] chore: prepare for release 3.13.4 (#452) * chore: prepare for release 3.13.4 * set java core version to 3.10.3 * removed comment * Revert "removed comment" This reverts commit a326808359adbf1d3f71b118aaa62591237edcd2. * Revert "set java core version to 3.10.3" This reverts commit fe586f65475af5d3df4be5806e3ca5d13509f993. --------- (cherry picked from commit cbcdd1e80897dce41976504659506d0b5410ed74) --------- Co-authored-by: Jae Kim <45045038+jaeopt@users.noreply.github.com> --- CHANGELOG.md | 8 ++ README.md | 48 +++++++++--- build.gradle | 4 +- .../event_handler/EventReschedulerTest.java | 59 +++++++++------ .../event_handler/EventRescheduler.java | 15 +++- .../DefaultEventHandlerTest.java | 74 ------------------- test-app/build.gradle | 2 +- 7 files changed, 95 insertions(+), 115 deletions(-) rename event-handler/src/{test => androidTest}/java/com/optimizely/ab/android/event_handler/EventReschedulerTest.java (58%) delete mode 100644 event-handler/src/test/java/com/optimizely/ab/android/event_handler/DefaultEventHandlerTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 95b6fcae..6ce5e656 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Optimizely Android X SDK Changelog +## 3.13.4 +March 15th, 2023 + +- Update README.md and other non-functional code to reflect that this SDK supports both Optimizely Feature Experimentation and Optimizely Full Stack. ([#450](https://github.com/optimizely/android-sdk/pull/450)). + +### Bug Fixes +* Catch exception from event service and restart the service. ([#446](https://github.com/optimizely/android-sdk/pull/446)). + ## 3.13.3 December 1st, 2022 diff --git a/README.md b/README.md index 5a3cd9ec..82cb1e6b 100644 --- a/README.md +++ b/README.md @@ -2,24 +2,20 @@ [![Apache 2.0](https://img.shields.io/github/license/nebula-plugins/gradle-extra-configurations-plugin.svg)](http://www.apache.org/licenses/LICENSE-2.0) [![Build Status](https://travis-ci.org/optimizely/android-sdk.svg?branch=master)](https://travis-ci.org/optimizely/android-sdk) -## Overview +This repository houses the Android SDK for use with Optimizely Feature Experimentation and Optimizely Full Stack (legacy). The Android SDK depends on the [Optimizely Java SDK](https://github.com/optimizely/java-sdk). -This repository houses the Android SDK for use with Optimizely Full Stack and Optimizely Rollouts. The Android SDK depends on the [Optimizely Java SDK](https://github.com/optimizely/java-sdk). +Optimizely Feature Experimentation is an A/B testing and feature management tool for product development teams, letting you experiment at every step. Using Optimizely Feature Experimentation allows for every feature on your roadmap to be an opportunity to discover hidden insights. Learn more at [Optimizely.com](https://www.optimizely.com/products/experiment/feature-experimentation/), or see the [developer documentation](https://docs.developers.optimizely.com/experimentation/v4.0.0-full-stack/docs/welcome). -Optimizely Full Stack is A/B testing and feature flag management for product development teams. Experiment in any application. Make every feature on your roadmap an opportunity to learn. Learn more at https://www.optimizely.com/platform/full-stack/, or see the [documentation](https://docs.developers.optimizely.com/experimentation/v4.0.0-full-stack/docs/welcome). +Optimizely Rollouts is [free feature flags](https://www.optimizely.com/free-feature-flagging/) for development teams. You can easily roll out and roll back features in any application without code deploys, mitigating risk for every feature on your roadmap. -Optimizely Rollouts is free feature flags for development teams. Easily roll out and roll back features in any application without code deploys. Mitigate risk for every feature on your roadmap. Learn more at https://www.optimizely.com/rollouts/, or see the [documentation](https://docs.developers.optimizely.com/experimentation/v3.1.0-full-stack/docs/introduction-to-rollouts). +## Get Started -## Getting Started - -### Using the SDK -See the [Android SDK developer documentation](https://docs.developers.optimizely.com/experimentation/v4.0.0-full-stack/docs/install-sdk-android) to learn how to set -up an Optimizely project and start using the SDK. +Refer to the [Android SDK's developer documentation](https://docs.developers.optimizely.com/experimentation/v4.0.0-full-stack/docs/android-sdk) for detailed instructions on getting started with using the SDK. ### Requirements * Android API 14 or higher -### Installing the SDK +### Install the SDK To add the android-sdk and all modules to your project, include the following in your app's `build.gradle`: --- @@ -37,7 +33,7 @@ repositories { } dependencies { - implementation 'com.optimizely.ab:android-sdk:3.13.3' + implementation 'com.optimizely.ab:android-sdk:3.13.4' } ``` @@ -60,6 +56,10 @@ optimizelyManager.initialize(this, null, (OptimizelyClient optimizely) -> { ``` +## Use the Android SDK + +See the Optimizely Feature Experimentation [developer documentation](https://docs.developers.optimizely.com/experimentation/v4.0-full-stack/docs/android-sdk) to learn how to set up your first Android project and use the SDK. + ## Architecture This project includes 5 library modules and a test app. @@ -82,7 +82,7 @@ This project includes 5 library modules and a test app. 6. Test App - A simple app showing how to use the Optimizely Android SDK -## Development +## SDK Development ### Command Line @@ -111,6 +111,7 @@ You can import this project into Android Studio by opening Android Studio and se Tests can be run by right clicking the file in the project pane or by clicking the method name in source and selecting run. You will be prompted to create an AVD or connect a device if one isn't connected. ### Contributing + Please see [CONTRIBUTING](CONTRIBUTING.md). ### Credits @@ -128,3 +129,26 @@ Additional credits from java-sdk:[https://github.com/optimizely/java-sdk/blob/ma **Android Logger** [https://github.com/noveogroup/android-logger](https://github.com/noveogroup/android-logger) License (Public Domain): [https://github.com/noveogroup/android-logger/blob/master/LICENSE.txt](https://github.com/noveogroup/android-logger/blob/master/LICENSE.txt) +### Other Optimzely SDKs + +- Agent - https://github.com/optimizely/agent + +- C# - https://github.com/optimizely/csharp-sdk + +- Flutter - https://github.com/optimizely/optimizely-flutter-sdk + +- Go - https://github.com/optimizely/go-sdk + +- Java - https://github.com/optimizely/java-sdk + +- JavaScript - https://github.com/optimizely/javascript-sdk + +- PHP - https://github.com/optimizely/php-sdk + +- Python - https://github.com/optimizely/python-sdk + +- React - https://github.com/optimizely/react-sdk + +- Ruby - https://github.com/optimizely/ruby-sdk + +- Swift - https://github.com/optimizely/swift-sdk diff --git a/build.gradle b/build.gradle index e471cd4b..2f7745d4 100644 --- a/build.gradle +++ b/build.gradle @@ -61,7 +61,7 @@ ext { build_tools_version = "30.0.3" min_sdk_version = 14 target_sdk_version = 33 - java_core_ver = "3.10.2" + java_core_ver = "3.10.3" android_logger_ver = "1.3.6" jacksonversion= "2.11.2" annotations_ver = "1.2.0" @@ -193,7 +193,7 @@ configure(publishedProjects) { customizePom(pom, docTitle) // "description" is required by MavenCentral but it does not work in customizePom(). added here explicitly. - pom.description = 'The Android SDK for Optimizely Full Stack (feature flag management for product development teams)' + pom.description = 'The Android SDK for Optimizely Feature Experimentation, Optimizely Full Stack (legacy), and Optimizely Rollouts' from components.release artifact releaseSourcesJar diff --git a/event-handler/src/test/java/com/optimizely/ab/android/event_handler/EventReschedulerTest.java b/event-handler/src/androidTest/java/com/optimizely/ab/android/event_handler/EventReschedulerTest.java similarity index 58% rename from event-handler/src/test/java/com/optimizely/ab/android/event_handler/EventReschedulerTest.java rename to event-handler/src/androidTest/java/com/optimizely/ab/android/event_handler/EventReschedulerTest.java index 28f5c904..18236dfa 100644 --- a/event-handler/src/test/java/com/optimizely/ab/android/event_handler/EventReschedulerTest.java +++ b/event-handler/src/androidTest/java/com/optimizely/ab/android/event_handler/EventReschedulerTest.java @@ -1,23 +1,22 @@ -/**************************************************************************** - * Copyright 2016,2021, Optimizely, Inc. and contributors * - * * - * 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. * - ***************************************************************************/ +// Copyright 2016,2021,2023, Optimizely, Inc. and contributors +// +// 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 +// +// https://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.optimizely.ab.android.event_handler; import android.content.Context; import android.content.Intent; +import android.net.NetworkInfo; import android.net.wifi.WifiManager; import org.junit.Before; @@ -26,15 +25,23 @@ import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnitRunner; import org.slf4j.Logger; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.matches; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import androidx.test.ext.junit.runners.AndroidJUnit4; + /** * Unit tests for {@link EventRescheduler} */ -@RunWith(MockitoJUnitRunner.class) -@Ignore +@RunWith(AndroidJUnit4.class) public class EventReschedulerTest { private Context context; @@ -47,8 +54,7 @@ public void setupEventRescheduler() { context = mock(Context.class); intent = mock(Intent.class); logger = mock(Logger.class); - rescheduler = mock(EventRescheduler.class); - rescheduler = new EventRescheduler(); + rescheduler = spy(new EventRescheduler()); rescheduler.logger = logger; } @@ -71,6 +77,13 @@ public void onReceiveInvalidAction() { verify(logger).warn("Received unsupported broadcast action to event rescheduler"); } + @Test + public void onReceiveWhenRescheduleWithException() { + when(intent.getAction()).thenThrow(new IllegalStateException()); + rescheduler.onReceive(context, intent); + verify(logger).warn(matches("WorkScheduler failed to reschedule an event service.*")); + } + @Test public void onReceiveValidBootComplete() { when(intent.getAction()).thenReturn(Intent.ACTION_BOOT_COMPLETED); @@ -88,10 +101,12 @@ public void onReceiveValidPackageReplaced() { @Test public void flushOnWifiConnectionIfScheduled() { final Intent eventServiceIntent = mock(Intent.class); - when(intent.getAction()).thenReturn(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); - when(intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false)).thenReturn(true); + when(intent.getAction()).thenReturn(WifiManager.WIFI_STATE_CHANGED_ACTION); + NetworkInfo info = mock(NetworkInfo.class); + when(info.isConnected()).thenReturn(true); + when(intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO)).thenReturn(info); + rescheduler.reschedule(context, intent); - verify(context).startService(eventServiceIntent); verify(logger).info("Preemptively flushing events since wifi became available"); } } diff --git a/event-handler/src/main/java/com/optimizely/ab/android/event_handler/EventRescheduler.java b/event-handler/src/main/java/com/optimizely/ab/android/event_handler/EventRescheduler.java index 65bad758..9393718e 100644 --- a/event-handler/src/main/java/com/optimizely/ab/android/event_handler/EventRescheduler.java +++ b/event-handler/src/main/java/com/optimizely/ab/android/event_handler/EventRescheduler.java @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright 2016-2021, Optimizely, Inc. and contributors * + * Copyright 2016-2021, 2023 Optimizely, Inc. and contributors * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * @@ -62,10 +62,17 @@ public class EventRescheduler extends BroadcastReceiver { */ @Override public void onReceive(Context context, Intent intent) { - if (context != null && intent != null) { - reschedule(context, intent); - } else { + if (context == null || intent == null) { logger.warn("Received invalid broadcast to event rescheduler"); + return; + } + + try { + reschedule(context, intent); + } catch (Exception e) { + // Rare exceptions (IllegalStateException: "WorkManager is not initialized properly...") with WorkerScheduler.startService(), probably related to a WorkManager start timing issue. + // Gracefully handled here, and it's safe for those rare cases since event-dispatch service will be scheduled again on next events. + logger.warn("WorkScheduler failed to reschedule an event service: " + e.getMessage()); } } diff --git a/event-handler/src/test/java/com/optimizely/ab/android/event_handler/DefaultEventHandlerTest.java b/event-handler/src/test/java/com/optimizely/ab/android/event_handler/DefaultEventHandlerTest.java deleted file mode 100644 index b3a68c73..00000000 --- a/event-handler/src/test/java/com/optimizely/ab/android/event_handler/DefaultEventHandlerTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** - * Copyright 2016, Optimizely, Inc. and contributors * - * * - * 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.optimizely.ab.android.event_handler; - -import android.content.Context; - -import com.optimizely.ab.event.LogEvent; -import com.optimizely.ab.event.internal.payload.EventBatch; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.runners.MockitoJUnitRunner; -import org.slf4j.Logger; - -import java.net.MalformedURLException; -import java.util.HashMap; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link DefaultEventHandler} - */ -@RunWith(MockitoJUnitRunner.class) -@Ignore -public class DefaultEventHandlerTest { - - private Context context; - private Logger logger; - private DefaultEventHandler eventHandler; - private String url = "http://www.foo.com"; - - @Before - public void setupEventHandler() { - context = mock(Context.class); - logger = mock(Logger.class); - eventHandler = DefaultEventHandler.getInstance(context); - eventHandler.logger = logger; - } - - @Test - public void dispatchEventSuccess() throws MalformedURLException { - eventHandler.dispatchEvent(new LogEvent(LogEvent.RequestMethod.POST, url, new HashMap(), new EventBatch())); - verify(logger).info("Sent url {} to the event handler service", "http://www.foo.com"); - } - - @Test - public void dispatchEmptyUrlString() { - eventHandler.dispatchEvent(new LogEvent(LogEvent.RequestMethod.POST, "", new HashMap(), new EventBatch())); - verify(logger).error("Event dispatcher received an empty url"); - } - - @Test - public void dispatchEmptyParams() { - eventHandler.dispatchEvent(new LogEvent(LogEvent.RequestMethod.POST, url, new HashMap(), new EventBatch())); - verify(logger).info("Sent url {} to the event handler service", "http://www.foo.com"); - } -} diff --git a/test-app/build.gradle b/test-app/build.gradle index ef448760..d83eb0dd 100644 --- a/test-app/build.gradle +++ b/test-app/build.gradle @@ -39,7 +39,7 @@ android { } dependencies { - // Includes the Optimizely X Full Stack Java SDK, event handler, and user profile + // Includes the Optimizely Feature Experimentation, Optimizely Full Stack (legacy), and Optimizely Rollouts Java SDK, event handler, and user profile //implementation "com.optimizely.ab:android-sdk:1.0.0" implementation (project(':android-sdk')) { exclude group: 'com.google.code.gson', module:'gson'