Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Java] Complete the feedback widget module #76

Merged
merged 65 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
ce1ff84
fix: usage of callbacks
arifBurakDemiray Sep 25, 2023
d38cb1a
feat: immediate request
arifBurakDemiray Sep 25, 2023
2fd128b
feat: irGenerator to cnfig
arifBurakDemiray Sep 25, 2023
ea10142
feat: immediate req test
arifBurakDemiray Sep 25, 2023
3c2fa8e
fix: stop countly
arifBurakDemiray Sep 25, 2023
3659d47
fix: directory creatioon
arifBurakDemiray Sep 25, 2023
9e39ad9
fix: folder checks
arifBurakDemiray Sep 25, 2023
2bed30f
feat: add feedback to sdk core and countly
arifBurakDemiray Sep 26, 2023
2664bb2
doc: comments for new ir
arifBurakDemiray Sep 26, 2023
993c291
doc: add changelog entry for feedback call
arifBurakDemiray Sep 26, 2023
9a76029
feat: get widgets
arifBurakDemiray Sep 26, 2023
a372d79
feat: get widget url
arifBurakDemiray Sep 26, 2023
07a9151
doc: remake comments
arifBurakDemiray Sep 26, 2023
f080523
feat: get feedback data
arifBurakDemiray Sep 26, 2023
8dfa5ec
feat: manual widget reporting
arifBurakDemiray Sep 26, 2023
ed66c18
feat: register module mapping of feedbacks
arifBurakDemiray Sep 26, 2023
ec16c09
feat: add missing mark
arifBurakDemiray Sep 26, 2023
ba97edd
feat: add missing mark
arifBurakDemiray Sep 26, 2023
8f80fae
feat: feedback module test
arifBurakDemiray Sep 27, 2023
739473d
Merge pull request #78 from Countly/feedback_tests
arifBurakDemiray Sep 27, 2023
b18f99f
fix: add is init check
arifBurakDemiray Sep 27, 2023
36fb170
feat: usage of timed req
arifBurakDemiray Sep 27, 2023
2603f37
fix: transport ref
arifBurakDemiray Sep 27, 2023
a8b01e1
revert: test
arifBurakDemiray Sep 27, 2023
c0548eb
feat: add garbage json test
arifBurakDemiray Sep 27, 2023
de86ef2
feat: usage of generic callback interface
arifBurakDemiray Sep 27, 2023
0411543
feat: validate required params
arifBurakDemiray Sep 27, 2023
4948d82
feat: construct feedback url test
arifBurakDemiray Sep 27, 2023
ee05b67
feat: get feedback data tests
arifBurakDemiray Sep 27, 2023
238b90b
feat: report manually init test
arifBurakDemiray Sep 27, 2023
1b04598
Merge branch 'staging' into create_feedback_module
ArtursKadikis Sep 27, 2023
23dbe87
Merge branch 'staging' into create_feedback_module
ArtursKadikis Sep 28, 2023
e91d0a8
Fixing failed merge
ArtursKadikis Sep 28, 2023
3887b99
Some test changes
ArtursKadikis Sep 28, 2023
d0fbf40
feat: remove unnecessary
arifBurakDemiray Sep 28, 2023
5574758
feat: get widget data success
arifBurakDemiray Sep 28, 2023
45154ff
feat: usage of new way of platform and event recording
arifBurakDemiray Sep 28, 2023
b7a1bca
feat: os check for widgets
arifBurakDemiray Sep 28, 2023
1aad3e4
refactor: event size queue check
arifBurakDemiray Sep 28, 2023
d4bf4d8
revert: once
arifBurakDemiray Sep 28, 2023
f365c9b
Merge pull request #86 from Countly/refactor_event_queue_size_check
arifBurakDemiray Sep 28, 2023
1f913d4
fix: get os
arifBurakDemiray Sep 28, 2023
e4795f1
feat: report widget manually
arifBurakDemiray Sep 28, 2023
8b88c79
feat: add report manually feedback widget
arifBurakDemiray Sep 28, 2023
ae003d0
doc: widget data
arifBurakDemiray Sep 28, 2023
ff8f6d3
fix: enable test
arifBurakDemiray Sep 28, 2023
a521d63
feat: test for checking from rq
arifBurakDemiray Sep 29, 2023
1f1a9f3
Merge branch 'staging' into create_feedback_module
ArtursKadikis Sep 29, 2023
e59c0f0
refactor: remove unnecessary await
arifBurakDemiray Sep 29, 2023
9549038
code styling
ArtursKadikis Sep 29, 2023
1faaa37
some test tweaks
ArtursKadikis Sep 29, 2023
d40667e
feat: feedback widgets to example app
arifBurakDemiray Sep 29, 2023
6518f58
Merge branch 'create_feedback_module' of https://github.com/Countly/c…
arifBurakDemiray Sep 29, 2023
0a4dd9b
fix: jsn array check
arifBurakDemiray Sep 29, 2023
e4410f7
fix: url encode platform and http req check
arifBurakDemiray Sep 29, 2023
9cfa100
fix: add exit prompt to example
arifBurakDemiray Sep 29, 2023
6d42e97
fix: tear shutdown in module events
arifBurakDemiray Sep 29, 2023
a57d915
Merge branch 'staging' of https://github.com/Countly/countly-sdk-java…
ArtursKadikis Oct 2, 2023
bd43f3e
Cleaning up the tests
ArtursKadikis Oct 2, 2023
281dcc1
fix: get app version
arifBurakDemiray Oct 2, 2023
7cdee6c
Moving the file cleaner to the before block
ArtursKadikis Oct 3, 2023
5b85d1f
Reworking a couple of tests and changing the widget URL creation func…
ArtursKadikis Oct 3, 2023
c810728
Refactoring tests
ArtursKadikis Oct 3, 2023
53351c1
Typo
ArtursKadikis Oct 3, 2023
b89a3cc
changelog entries
ArtursKadikis Oct 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* Deprecated call "resetDeviceId" is removed
* Deprecated the init time configuration of 'setEventsBufferSize(eventsBufferSize)'. Introduced replacement 'setEventQueueSizeToSend(eventsQueueSize)'
* Deprecated the init time configuration of 'setSendUpdateEachSeconds(sendUpdateEachSeconds)'. Introduced replacement 'setUpdateSessionTimerDelay(delay)'
* Added feedback widgets and manual reporting. Added consent for it "Config.Feature.Feedback".
* Feedback module is accessible through "Countly::instance()::feedback()" call.

* !! Major breaking change !! The following methods and their functionality are deprecated from the "UserEditor" interface and will not function anymore:
* "addToCohort(key)"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package ly.count.java.demo;

import ly.count.sdk.java.Config;
import ly.count.sdk.java.Countly;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
import ly.count.sdk.java.Config;
import ly.count.sdk.java.Countly;

public class BackendModeExample {
final static String DEVICE_ID = "device-id";
Expand Down Expand Up @@ -314,8 +313,10 @@ public static void main(String[] args) throws Exception {
String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" };
File sdkStorageRootDirectory = new File(String.join(File.separator, sdkStorageRootPath));

if(sdkStorageRootDirectory.mkdirs()){
System.out.println("Directory creation failed");
if (!(sdkStorageRootDirectory.exists() && sdkStorageRootDirectory.isDirectory())) {
if (!sdkStorageRootDirectory.mkdirs()) {
System.out.println("Directory creation failed");
}
}

// Main initialization call, SDK can be used after this one is done
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package ly.count.java.demo;

import ly.count.sdk.java.Config;
import ly.count.sdk.java.Countly;
import ly.count.sdk.java.internal.Device;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import ly.count.sdk.java.Config;
import ly.count.sdk.java.Countly;
import ly.count.sdk.java.internal.Device;

public class BackendModePerformanceTests {
final static String DEVICE_ID = "device-id";
Expand All @@ -27,8 +26,10 @@ private static void initSDK(int eventQueueSize, int requestQueueSize) {
String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" };
File sdkStorageRootDirectory = new File(String.join(File.separator, sdkStorageRootPath));

if(sdkStorageRootDirectory.mkdirs()){
System.out.println("Directory creation failed");
if (!(sdkStorageRootDirectory.exists() && sdkStorageRootDirectory.isDirectory())) {
if (!sdkStorageRootDirectory.mkdirs()) {
System.out.println("Directory creation failed");
}
}

// Main initialization call, SDK can be used after this one is done
Expand Down
7 changes: 4 additions & 3 deletions app-java/src/main/java/ly/count/java/demo/Example.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

import ly.count.sdk.java.Config;
import ly.count.sdk.java.Countly;
import ly.count.sdk.java.internal.LogCallback;
Expand Down Expand Up @@ -125,8 +124,10 @@ public static void main(String[] args) throws Exception {
String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" };
File sdkStorageRootDirectory = new File(String.join(File.separator, sdkStorageRootPath));

if(sdkStorageRootDirectory.mkdirs()){
System.out.println("Directory creation failed");
if (!(sdkStorageRootDirectory.exists() && sdkStorageRootDirectory.isDirectory())) {
if (!sdkStorageRootDirectory.mkdirs()) {
System.out.println("Directory creation failed");
}
}

Config config = new Config(COUNTLY_SERVER_URL, COUNTLY_APP_KEY, sdkStorageRootDirectory)
Expand Down
16 changes: 13 additions & 3 deletions sdk-java/src/main/java/ly/count/sdk/java/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import ly.count.sdk.java.internal.*;
import ly.count.sdk.java.internal.Byteable;
import ly.count.sdk.java.internal.CoreFeature;
import ly.count.sdk.java.internal.ImmediateRequestGenerator;
import ly.count.sdk.java.internal.Log;
import ly.count.sdk.java.internal.LogCallback;
import ly.count.sdk.java.internal.ModuleBase;
import ly.count.sdk.java.internal.Utils;

/**
* Countly configuration object.
Expand Down Expand Up @@ -76,7 +81,8 @@ public enum Feature {
Views(CoreFeature.Views.getIndex()),
CrashReporting(CoreFeature.CrashReporting.getIndex()),
Location(CoreFeature.Location.getIndex()),
UserProfiles(CoreFeature.UserProfiles.getIndex());
UserProfiles(CoreFeature.UserProfiles.getIndex()),
Feedback(CoreFeature.Feedback.getIndex());
// StarRating(1 << 12),
// RemoteConfig(1 << 13),
// PerformanceMonitoring(1 << 14);
Expand Down Expand Up @@ -110,6 +116,8 @@ public static Config.Feature byIndex(int index) {
// return RemoteConfig;
// } else if (index == PerformanceMonitoring.index) {
// return PerformanceMonitoring;
} else if (index == Feedback.index) {
return Feedback;
} else {
return null;
}
Expand Down Expand Up @@ -462,6 +470,8 @@ public boolean restore(byte[] data, Log L) {
*/
File sdkStorageRootDirectory = null;

protected ImmediateRequestGenerator immediateRequestGenerator = null;

// /**
// * Maximum size of all string keys
// */
Expand Down
19 changes: 17 additions & 2 deletions sdk-java/src/main/java/ly/count/sdk/java/Countly.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

import java.io.File;
import java.util.Map;

import ly.count.sdk.java.internal.*;
import ly.count.sdk.java.internal.CtxCore;
import ly.count.sdk.java.internal.Device;
import ly.count.sdk.java.internal.InternalConfig;
import ly.count.sdk.java.internal.Log;
import ly.count.sdk.java.internal.ModuleBackendMode;
import ly.count.sdk.java.internal.ModuleFeedback;
import ly.count.sdk.java.internal.SDKCore;

/**
* Main Countly SDK API class.
Expand Down Expand Up @@ -311,6 +316,16 @@ public static void onConsentRemoval(Config.Feature... features) {
}
}

/**
* <code>Feedback</code> interface to use feedback widgets nps,rating and forms.
* And do manual reporting of feedbacks.
*
* @return {@link ModuleFeedback.Feedback} instance.
*/
public ModuleFeedback.Feedback feedback() {
return sdk.feedback();
}

@Override
public Event event(String key) {
L.d("[Countly] event: key = " + key);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ly.count.sdk.java.internal;

import java.util.Arrays;

public class CountlyFeedbackWidget {
public String widgetId;
public FeedbackWidgetType type;
public String name;
public String[] tags;

@Override
public boolean equals(Object o) {
if (!(o instanceof CountlyFeedbackWidget)) {
return false;
}
CountlyFeedbackWidget gonnaCompare = (CountlyFeedbackWidget) o;

String str = widgetId + type + name + Arrays.toString(tags);
String str2 = gonnaCompare.widgetId + gonnaCompare.type + gonnaCompare.name + Arrays.toString(gonnaCompare.tags);
return str.equals(str2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,9 @@ public void stop(CtxCore ctx) {
shutdown = true;
tasks.shutdown();
}

@Override
public Transport getTransport() {
return transport;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ly.count.sdk.java.internal;

public enum FeedbackWidgetType {
survey("[CLY]_survey"),
nps("[CLY]_nps"),
rating("[CLY]_star_rating");

final String eventKey;

FeedbackWidgetType(String eventKey) {
this.eventKey = eventKey;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ly.count.sdk.java.internal;

/**
* Interface for creating ImmediateRequestMaker
* a basic factory pattern implementation
*
* @see ImmediateRequestI
*/
public interface ImmediateRequestGenerator {
/**
* Create a new instance of ImmediateRequestI
* and override when needed
*
* @return new instance of ImmediateRequestI
*/
ImmediateRequestI createImmediateRequestMaker();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ly.count.sdk.java.internal;

public interface ImmediateRequestI {

/**
* Do the work of making the request directly without waiting for the queue.
*
* @param requestData query parameters
* @param customEndpoint custom endpoint to use instead of the default one
* @param cp transport to use when making the request
* @param requestShouldBeDelayed whether the request should be delayed or not
* @param networkingIsEnabled whether networking is enabled or not
* @param callback callback to call when the request is done
* @param log logger to use
*/
void doWork(String requestData, String customEndpoint, Transport cp, boolean requestShouldBeDelayed, boolean networkingIsEnabled, ImmediateRequestMaker.InternalImmediateRequestCallback callback, Log log);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package ly.count.sdk.java.internal;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.concurrent.CompletableFuture;
import org.json.JSONObject;

/**
* Async task for making immediate server requests
*/
class ImmediateRequestMaker implements ImmediateRequestI {

@Override
public void doWork(String requestData, String customEndpoint, Transport cp, boolean requestShouldBeDelayed, boolean networkingIsEnabled, InternalImmediateRequestCallback callback, Log log) {
CompletableFuture.supplyAsync(() -> doInBackground(requestData, customEndpoint, cp, requestShouldBeDelayed, networkingIsEnabled, callback, log))
.thenAcceptAsync(this::onFinished);
}

/**
* Used for callback from async task
*/
protected interface InternalImmediateRequestCallback {
void callback(JSONObject checkResponse);
}

private InternalImmediateRequestCallback callback;
private Log L;

/**
* params fields:
* 0 - requestData
* 1 - custom endpoint
* 2 - connection processor
* 3 - requestShouldBeDelayed
* 4 - networkingIsEnabled
* 5 - callback
* 6 - log module
*/
private JSONObject doInBackground(String requestData, String customEndpoint, Transport cp, boolean requestShouldBeDelayed, boolean networkingIsEnabled, InternalImmediateRequestCallback callback, Log log) {
this.callback = callback;
L = log;
if (!networkingIsEnabled) {
L.w("[ImmediateRequestMaker] ImmediateRequestMaker, Networking config is disabled, request cancelled. Endpoint[" + customEndpoint + "] request[" + requestData + "]");

return null;
}

L.v("[ImmediateRequestMaker] Starting request");

HttpURLConnection connection = null;

try {
L.d("[ImmediateRequestMaker] delayed[" + requestShouldBeDelayed + "] hasCallback[" + (callback != null) + "] endpoint[" + customEndpoint + "] request[" + requestData + "]");

if (requestShouldBeDelayed) {
//used in cases after something has to be done after a device id change
L.v("[ImmediateRequestMaker] request should be delayed, waiting for 0.5 seconds");

try {
Thread.sleep(500);
} catch (InterruptedException e) {
L.w("[ImmediateRequestMaker] While waiting for 0.5 seconds in ImmediateRequestMaker, sleep was interrupted");
}
}
Request request = new Request();
request.params.add(requestData);
request.endpoint(customEndpoint);
//getting connection ready
try {
connection = cp.connection(request);
} catch (IOException e) {
L.e("[ImmediateRequestMaker] IOException while preparing remote config update request :[" + e.toString() + "]");
return null;
}
//connecting
connection.connect();

int code = connection.getResponseCode();
String receivedBuffer = cp.response(connection);

if (receivedBuffer == null) {
L.e("[ImmediateRequestMaker] Encountered problem while making a immediate server request, received result was null");
return null;
}

char firstChar = receivedBuffer.trim().charAt(0);
if (code >= 200 && code < 300 && (firstChar == '[' || receivedBuffer.contains("result"))) {
L.d("[ImmediateRequestMaker] Received the following response, :[" + receivedBuffer + "]");

// we check if the result was a json array or json object and convert the array into an object if necessary
if (firstChar == '[') {
return new JSONObject("{\"jsonArray\":" + receivedBuffer + "}");
}
return new JSONObject(receivedBuffer);
} else {
L.e("[ImmediateRequestMaker] Encountered problem while making a immediate server request, :[" + receivedBuffer + "]");
return null;
}
} catch (Exception e) {
L.e("[ImmediateRequestMaker] Received exception while making a immediate server request " + e.getMessage());
} finally {
if (connection != null) {
connection.disconnect();
}
}
L.v("[ImmediateRequestMaker] Finished request");
return null;
}

private void onFinished(JSONObject result) {
L.v("[ImmediateRequestMaker] onPostExecute");
if (callback != null) {
callback.callback(result);
}
}
}
Loading