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

Feature set language callbacks #1536

Merged
merged 9 commits into from
Mar 1, 2022
65 changes: 62 additions & 3 deletions OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,44 @@ public static class SendTagsError {
public String getMessage() { return message; }
}

static class OSDeviceInfoError {
private int errorCode;
private String message;

OSDeviceInfoError(int errorCode, String message) {
this.errorCode = errorCode;
this.message = message;
}

public int getCode() { return errorCode; }

public String getMessage() { return message; }
}

interface OSDeviceInfoCompletionHandler {
void onSuccess(String results);
void onFailure(OSDeviceInfoError error);
}

public static class OSLanguageError {
private int errorCode;
private String message;

OSLanguageError(int errorCode, String message) {
this.errorCode = errorCode;
this.message = message;
}

public int getCode() { return errorCode; }

public String getMessage() { return message; }
}

public interface OSSetLanguageCompletionHandler {
void onSuccess(String results);
void onFailure(OSLanguageError error);
}

public enum ExternalIdErrorType {
REQUIRES_EXTERNAL_ID_AUTH, INVALID_OPERATION, NETWORK
}
Expand Down Expand Up @@ -1471,7 +1509,7 @@ private static void registerUserTask() throws JSONException {
deviceInfo.put("carrier", osUtils.getCarrierName());
deviceInfo.put("rooted", RootToolsInternalMethods.isRooted());

OneSignalStateSynchronizer.updateDeviceInfo(deviceInfo);
OneSignalStateSynchronizer.updateDeviceInfo(deviceInfo, null);

JSONObject pushState = new JSONObject();
pushState.put("identifier", lastRegistrationId);
Expand Down Expand Up @@ -1697,19 +1735,40 @@ public void run() {
}

public static void setLanguage(@NonNull final String language) {
setLanguage(language, null);
}

public static void setLanguage(@NonNull final String language, @Nullable final OSSetLanguageCompletionHandler completionCallback) {
if (taskRemoteController.shouldQueueTaskForInit(OSTaskRemoteController.SET_LANGUAGE)) {
logger.error("Waiting for remote params. " +
"Moving " + OSTaskRemoteController.SET_LANGUAGE + " operation to a pending task queue.");
taskRemoteController.addTaskToQueue(new Runnable() {
@Override
public void run() {
logger.debug("Running " + OSTaskRemoteController.SET_LANGUAGE + " operation from pending task queue.");
setLanguage(language);
setLanguage(language, completionCallback);
}
});
return;
}

OSDeviceInfoCompletionHandler deviceInfoCompletionHandler = null;

if(completionCallback != null) {
deviceInfoCompletionHandler = new OSDeviceInfoCompletionHandler() {
@Override
public void onSuccess(String results) {
completionCallback.onSuccess(results);
}

@Override
public void onFailure(OSDeviceInfoError error) {
OSLanguageError languageError = new OSLanguageError(error.errorCode, error.message);
completionCallback.onFailure(languageError);
}
};
}

if (shouldLogUserPrivacyConsentErrorMessageForMethodName(OSTaskRemoteController.SET_LANGUAGE))
return;

Expand All @@ -1720,7 +1779,7 @@ public void run() {
try {
JSONObject deviceInfo = new JSONObject();
deviceInfo.put("language", languageContext.getLanguage());
OneSignalStateSynchronizer.updateDeviceInfo(deviceInfo);
OneSignalStateSynchronizer.updateDeviceInfo(deviceInfo, deviceInfoCompletionHandler);
} catch (JSONException exception) {
exception.printStackTrace();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ static void setSubscription(boolean enable) {
static boolean getUserSubscribePreference() {
return getPushStateSynchronizer().getUserSubscribePreference();
}

static String getLanguage() {
return getPushStateSynchronizer().getLanguage();
}

static void setPermission(boolean enable) {
getPushStateSynchronizer().setPermission(enable);
Expand Down Expand Up @@ -218,10 +222,10 @@ static void resetCurrentState() {
OneSignal.setLastSessionTime(-60 * 61);
}

static void updateDeviceInfo(JSONObject deviceInfo) {
getPushStateSynchronizer().updateDeviceInfo(deviceInfo);
getEmailStateSynchronizer().updateDeviceInfo(deviceInfo);
getSMSStateSynchronizer().updateDeviceInfo(deviceInfo);
static void updateDeviceInfo(JSONObject deviceInfo, OneSignal.OSDeviceInfoCompletionHandler handler) {
getPushStateSynchronizer().updateDeviceInfo(deviceInfo, handler);
getEmailStateSynchronizer().updateDeviceInfo(deviceInfo, handler);
getSMSStateSynchronizer().updateDeviceInfo(deviceInfo, handler);
}

static void updatePushState(JSONObject pushState) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ public boolean getUserSubscribePreference() {
return getToSyncUserState().getDependValues().optBoolean(USER_SUBSCRIBE_PREF, true);
}

public String getLanguage() {
return getToSyncUserState().getDependValues().optString(LANGUAGE, null);
}

@Override
public void setPermission(boolean enable) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import com.onesignal.OneSignal.ChangeTagsUpdateHandler;
import com.onesignal.OneSignal.SendTagsError;
import com.onesignal.OneSignalStateSynchronizer.UserStateSynchronizerType;
import com.onesignal.OneSignal.OSDeviceInfoCompletionHandler;
import com.onesignal.OneSignal.OSDeviceInfoError;

import org.json.JSONException;
import org.json.JSONObject;
Expand Down Expand Up @@ -37,6 +39,7 @@ abstract class UserStateSynchronizer {
protected static final String ANDROID_PERMISSION = "androidPermission";
protected static final String SUBSCRIBABLE_STATUS = "subscribableStatus";
protected static final String TAGS = "tags";
protected static final String LANGUAGE = "language";
protected static final String EXTERNAL_USER_ID = "external_user_id";
protected static final String EMAIL_KEY = "email";
protected static final String LOGOUT_EMAIL = "logoutEmail";
Expand Down Expand Up @@ -92,6 +95,7 @@ String getRegistrationId() {
// sendTags() multiple times it will call each callback
final private Queue<ChangeTagsUpdateHandler> sendTagsHandlers = new ConcurrentLinkedQueue<>();
final private Queue<OneSignal.OSInternalExternalUserIdUpdateCompletionHandler> externalUserIdUpdateHandlers = new ConcurrentLinkedQueue<>();
final private Queue<OneSignal.OSDeviceInfoCompletionHandler> deviceInfoCompletionHandler = new ConcurrentLinkedQueue<>();

boolean hasQueuedHandlers() {
return externalUserIdUpdateHandlers.size() > 0;
Expand Down Expand Up @@ -367,6 +371,9 @@ void onFailure(int statusCode, String response, Throwable throwable) {
OneSignal.onesignalLog(OneSignal.LOG_LEVEL.ERROR, "Error setting external user id for push with status code: " + statusCode + " and message: " + response);
externalUserIdUpdateHandlersPerformOnFailure();
}

if (jsonBody.has(LANGUAGE))
deviceInfoHandlersPerformOnFailure(new OSDeviceInfoError(statusCode, response));
}

@Override
Expand All @@ -381,6 +388,9 @@ void onSuccess(String response) {

if (jsonBody.has(EXTERNAL_USER_ID))
externalUserIdUpdateHandlersPerformOnSuccess();

if (jsonBody.has(LANGUAGE))
deviceInfoHandlersPerformOnSuccess();
}
});
}
Expand Down Expand Up @@ -505,7 +515,9 @@ protected UserState getUserStateForModification() {

abstract protected void scheduleSyncToServer();

void updateDeviceInfo(JSONObject deviceInfo) {
void updateDeviceInfo(JSONObject deviceInfo, @Nullable OSDeviceInfoCompletionHandler handler) {
if (handler != null)
this.deviceInfoCompletionHandler.add(handler);
getUserStateForModification().generateJsonDiffFromIntoSyncValued(deviceInfo, null);
}

Expand Down Expand Up @@ -612,4 +624,18 @@ private void externalUserIdUpdateHandlersPerformOnFailure() {
}
}

private void deviceInfoHandlersPerformOnSuccess() {
String language = OneSignalStateSynchronizer.getLanguage();
OneSignal.OSDeviceInfoCompletionHandler handler;
while ((handler = deviceInfoCompletionHandler.poll()) != null) {
handler.onSuccess(language);
}
}

private void deviceInfoHandlersPerformOnFailure(OSDeviceInfoError error) {
OneSignal.OSDeviceInfoCompletionHandler handler;
while((handler = deviceInfoCompletionHandler.poll()) != null) {
handler.onFailure(error);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
import com.onesignal.OSSubscriptionStateChanges;
import com.onesignal.OneSignal;
import com.onesignal.OneSignal.ChangeTagsUpdateHandler;
import com.onesignal.OneSignal.OSSetLanguageCompletionHandler;
import com.onesignal.OneSignal.OSLanguageError;
import com.onesignal.OneSignalPackagePrivateHelper;
import com.onesignal.OneSignalShadowPackageManager;
import com.onesignal.PermissionsActivity;
Expand Down Expand Up @@ -2775,6 +2777,68 @@ public void testSetLanguageOnSession() throws Exception {
assertEquals("fr", lastRequest.payload.getString("language"));
}

private static class TestSetLanguageHandler implements OSSetLanguageCompletionHandler {
private AtomicBoolean succeeded = new AtomicBoolean(false);
private AtomicBoolean failed = new AtomicBoolean(false);

@Override
public void onSuccess(String results) { succeeded.set(true); }

@Override
public void onFailure(OSLanguageError error) { failed.set(true); }

boolean getSucceeded() { return succeeded.get(); }

boolean getFailed() { return failed.get(); }
}

// Tests to make sure the onSuccess handler works
@Test
public void shouldSetLanguageWithResponse() throws Exception {
OneSignalInit();
threadAndTaskWait();

TestSetLanguageHandler handler = new TestSetLanguageHandler();

OneSignal.setLanguage("fr", handler);

threadAndTaskWait();

assertTrue(handler.getSucceeded());

// now test to make sure the handler still fires for a call to
// setLanguage() that doesn't modify existing language (no JSON delta)

handler = new TestSetLanguageHandler();

OneSignal.setLanguage("es", handler);

threadAndTaskWait();

assertTrue(handler.getSucceeded());
}

// Tests to make sure that the onFailure callback works
@Test
public void shouldFailToSetLanguageWithResponse() throws Exception {
TestSetLanguageHandler handler = new TestSetLanguageHandler();

OneSignalInit();
threadAndTaskWait();

ShadowOneSignalRestClient.failMethod = "players";
ShadowOneSignalRestClient.failHttpCode = 403;
ShadowOneSignalRestClient.setNextFailureJSONResponse(new JSONObject() {{
put("tags", "error");
}});

// Should fail because players call failed with tags
OneSignal.setLanguage("fr", handler);
threadAndTaskWait();

assertTrue(handler.getFailed());
}

/**
* Similar workflow to testLocationPermissionPromptWithPrivacyConsent()
* We want to provide consent but make sure that session time tracking works properly
Expand Down