diff --git a/.github/workflows/unit-and-instrumented-tests-action.yml b/.github/workflows/unit-and-instrumented-tests-action.yml index a1fafa6af..6907c2cf4 100644 --- a/.github/workflows/unit-and-instrumented-tests-action.yml +++ b/.github/workflows/unit-and-instrumented-tests-action.yml @@ -30,6 +30,7 @@ jobs: with: name: unit-test-api-level-$API_MIN path: ./Branch-SDK/build/ + instrumented-test-api-level-min: name: instrumented-test-api-level-$API_MIN runs-on: macos-latest @@ -66,6 +67,44 @@ jobs: with: name: instrumented-test-api-level-$API_MIN path: ./Branch-SDK/build/ + + jacoco-test-coverage-api-level-min: + name: jacoco-test-coverage-api-level-$API_MIN + if: ${{ always() && contains(join(needs.*.result, ','), 'success') }} + needs: [unit-test-api-level-min, instrumented-test-api-level-min] + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + # create an emulator with google apis, runs on java 8 + - name: Create Android emulator + run: | + echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --licenses + echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install "system-images;android-"$API_MIN";google_apis;x86_64" + echo "no" | $ANDROID_HOME/tools/bin/avdmanager --verbose create avd --force --name test --package "system-images;android-"$API_MIN";google_apis;x86_64" + # boots and waits for the emulator to be ready + - name: Launch Emulator + run: | + echo "Starting emulator and waiting for boot to complete." + nohup $ANDROID_HOME/tools/emulator -avd test -no-audio -no-boot-anim -camera-back none -camera-front none -qemu -m 2048 2>&1 & + $ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do echo "waiting..."; sleep 1; done; input keyevent 82' + echo "Emulator has finished booting" + # repo's gradle is configured to run on java 17 + - name: Setup java 17 for gradle + uses: actions/setup-java@v3 + with: + distribution: ${{ env.JAVA_DISTRIBUTION }} + java-version: ${{ env.JAVA_VERSION }} + - name: Run Coverage + run: | + ./gradlew :Branch-SDK:jacocoTestReport --info + - name: Upload Coverage Test Report + if: success() + uses: codecov/codecov-action@v3 + with: + file: ./Branch-SDK/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml + fail_ci_if_error: true + unit-test-api-level-current: name: unit-test-api-level-$API_CURRENT runs-on: macos-latest diff --git a/Branch-SDK-TestBed/src/main/java/io/branch/branchandroidtestbed/SettingsActivity.java b/Branch-SDK-TestBed/src/main/java/io/branch/branchandroidtestbed/SettingsActivity.java index 8ae21e68a..d6c4de0ec 100644 --- a/Branch-SDK-TestBed/src/main/java/io/branch/branchandroidtestbed/SettingsActivity.java +++ b/Branch-SDK-TestBed/src/main/java/io/branch/branchandroidtestbed/SettingsActivity.java @@ -3,7 +3,6 @@ import android.app.Activity; import android.os.Bundle; -import android.view.KeyEvent; import android.view.View; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; @@ -26,6 +25,7 @@ protected void onCreate(Bundle savedInstanceState) { setupDisableAdNetworkCalloutsSwitch(); setupPrepHelperView(); setupRetryEditText(); + setupApiUrlText(); } void setupRetryEditText() { @@ -35,21 +35,40 @@ void setupRetryEditText() { retryEditText.setText(Integer.toString(currentRetries)); - retryEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { - @Override - public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) { - if (i == EditorInfo.IME_ACTION_DONE) { - int retries = Integer.valueOf(textView.getText().toString()); - PrefHelper.getInstance(SettingsActivity.this).setRetryCount(retries); - - InputMethodManager imm = (InputMethodManager)getSystemService(SettingsActivity.this.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(retryEditText.getWindowToken(), 0); - - Toast.makeText(getApplicationContext(), "Set Network Retries to " + retries, Toast.LENGTH_SHORT).show(); - return true; - } - return false; + retryEditText.setOnEditorActionListener((textView, i, keyEvent) -> { + if (i == EditorInfo.IME_ACTION_DONE) { + int retries = Integer.parseInt(textView.getText().toString()); + PrefHelper.getInstance(SettingsActivity.this).setRetryCount(retries); + + InputMethodManager imm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(retryEditText.getWindowToken(), 0); + + Toast.makeText(getApplicationContext(), "Set Network Retries to " + retries, Toast.LENGTH_SHORT).show(); + return true; + } + return false; + }); + } + + void setupApiUrlText() { + final EditText apiUrlText = findViewById(R.id.api_url_text); + final PrefHelper prefHelper = PrefHelper.getInstance(this); + String currentApiUrl = prefHelper.getAPIBaseUrl(); + + apiUrlText.setText(currentApiUrl); + + apiUrlText.setOnEditorActionListener((textView, i, keyEvent) -> { + if (i == EditorInfo.IME_ACTION_DONE) { + String newApiUrl = textView.getText().toString(); + Branch.getInstance().setAPIUrl(newApiUrl); + + InputMethodManager imm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(apiUrlText.getWindowToken(), 0); + + Toast.makeText(getApplicationContext(), "Set API Base URL to " + newApiUrl, Toast.LENGTH_SHORT).show(); + return true; } + return false; }); } diff --git a/Branch-SDK-TestBed/src/main/res/layout/activity_settings.xml b/Branch-SDK-TestBed/src/main/res/layout/activity_settings.xml index fb644e2dd..496b07714 100644 --- a/Branch-SDK-TestBed/src/main/res/layout/activity_settings.xml +++ b/Branch-SDK-TestBed/src/main/res/layout/activity_settings.xml @@ -56,6 +56,28 @@ + + + + + + + + { + isIncludeNoLocationClasses = true + excludes = listOf("jdk.internal.*") + } } } + +tasks.create("jacocoTestReport") { + group = "Reporting" + description = "Generate Jacoco code coverage reports after running tests." + dependsOn("testDebugUnitTest","createDebugCoverageReport") + reports { + xml.required.set(true) + html.required.set(true) + } + sourceDirectories.setFrom("${project.projectDir}/src/main/java") + classDirectories.setFrom("${project.buildDir}/intermediates/javac/debug/classes") + executionData.setFrom( + fileTree(project.buildDir) { + include( + "jacoco/testDebugUnitTest.exec", + "outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec", + "outputs/code_coverage/debugAndroidTest/connected/**/*.ec", + ) + } + ) +} diff --git a/Branch-SDK/src/androidTest/java/io/branch/referral/PrefHelperTest.java b/Branch-SDK/src/androidTest/java/io/branch/referral/PrefHelperTest.java index 81275f345..2c9a9d55c 100644 --- a/Branch-SDK/src/androidTest/java/io/branch/referral/PrefHelperTest.java +++ b/Branch-SDK/src/androidTest/java/io/branch/referral/PrefHelperTest.java @@ -49,26 +49,26 @@ public void testGetAPIBaseUrl() { @Test public void testSetAPIUrl_Example() { - PrefHelper.setAPIUrl("https://www.example.com/"); + prefHelper.setAPIUrl("https://www.example.com/"); String actual = prefHelper.getAPIBaseUrl(); Assert.assertEquals("https://www.example.com/", actual); } @Test public void testSetAPIUrl_InvalidHttp() { - PrefHelper.setAPIUrl("http://www.example.com/"); + prefHelper.setAPIUrl("http://www.example.com/"); assertDefaultURL(); } @Test public void testSetAPIUrl_InvalidNull() { - PrefHelper.setAPIUrl(null); + prefHelper.setAPIUrl(null); assertDefaultURL(); } @Test public void testSetAPIUrl_InvalidEmpty() { - PrefHelper.setAPIUrl(""); + prefHelper.setAPIUrl(""); assertDefaultURL(); } diff --git a/Branch-SDK/src/main/java/io/branch/referral/Branch.java b/Branch-SDK/src/main/java/io/branch/referral/Branch.java index a76b685db..f1361105a 100644 --- a/Branch-SDK/src/main/java/io/branch/referral/Branch.java +++ b/Branch-SDK/src/main/java/io/branch/referral/Branch.java @@ -508,10 +508,11 @@ public static void expectDelayedSessionInitialization(boolean expectDelayedInit) *

Sets a custom base URL for all calls to the Branch API. Requires https.

* @param url The {@link String} URL base URL that the Branch API uses. */ - public static void setAPIUrl(String url) { - PrefHelper.setAPIUrl(url); + public void setAPIUrl(String url) { + if (prefHelper_ != null && url.length() > 0) { + prefHelper_.setAPIUrl(url); + } } - /** *

Sets a custom CDN base URL.

* @param url The {@link String} base URL for CDN endpoints. @@ -1024,9 +1025,13 @@ public void logout() { * @param callback An instance of {@link io.branch.referral.Branch.LogoutStatusListener} to callback with the logout operation status. */ public void logout(LogoutStatusListener callback) { - ServerRequest req = new ServerRequestLogout(context_, callback); - if (!req.constructError_ && !req.handleErrors(context_)) { - requestQueue_.handleNewRequest(req); + prefHelper_.setIdentity(PrefHelper.NO_STRING_VALUE); + prefHelper_.clearUserValues(); + //On Logout clear the link cache and all pending requests + linkCache_.clear(); + requestQueue_.clear(); + if (callback != null) { + callback.onLogoutFinished(true, null); } } diff --git a/Branch-SDK/src/main/java/io/branch/referral/Defines.java b/Branch-SDK/src/main/java/io/branch/referral/Defines.java index 36d64bfb7..908342cfe 100644 --- a/Branch-SDK/src/main/java/io/branch/referral/Defines.java +++ b/Branch-SDK/src/main/java/io/branch/referral/Defines.java @@ -246,7 +246,6 @@ public enum RequestPath { RegisterInstall("v1/install"), RegisterOpen("v1/open"), IdentifyUser("v1/profile"), - Logout("v1/logout"), ContentEvent("v1/content-events"), TrackStandardEvent("v2/event/standard"), TrackCustomEvent("v2/event/custom"), diff --git a/Branch-SDK/src/main/java/io/branch/referral/PrefHelper.java b/Branch-SDK/src/main/java/io/branch/referral/PrefHelper.java index f86215451..e9eb604b5 100644 --- a/Branch-SDK/src/main/java/io/branch/referral/PrefHelper.java +++ b/Branch-SDK/src/main/java/io/branch/referral/PrefHelper.java @@ -121,6 +121,7 @@ public class PrefHelper { static final String KEY_REFERRING_URL_QUERY_PARAMETERS = "bnc_referringUrlQueryParameters"; static final String KEY_ANON_ID = "bnc_anon_id"; + private static final String KEY_BASE_URL = "bnc_base_url"; /** * Internal static variable of own type {@link PrefHelper}. This variable holds the single @@ -156,11 +157,6 @@ public class PrefHelper { */ private final JSONObject secondaryRequestMetadata = new JSONObject(); - /** - * Branch Custom server url. Used by clients that want to proxy all requests. - */ - private static String customServerURL_ = null; - /** * Branch Custom server url. Used by clients that want to proxy all CDN requests. */ @@ -207,7 +203,6 @@ static void shutDown() { // Reset all of the statics. enableLogging_ = false; prefHelper_ = null; - customServerURL_ = null; customCDNBaseURL_ = null; } @@ -215,8 +210,8 @@ static void shutDown() { *

Sets a custom base URL for all calls to the Branch API. Requires https.

* @param url The {@link String} URL base URL that the Branch API uses. */ - static void setAPIUrl(String url) { - customServerURL_ = url; + public void setAPIUrl(String url) { + setString(KEY_BASE_URL, url); } /** @@ -227,8 +222,8 @@ static void setAPIUrl(String url) { * API uses. */ public String getAPIBaseUrl() { - if (URLUtil.isHttpsUrl(customServerURL_)) { - return customServerURL_; + if (URLUtil.isHttpsUrl(getString(KEY_BASE_URL))) { + return getString(KEY_BASE_URL); } if (Build.VERSION.SDK_INT >= 20) { diff --git a/Branch-SDK/src/main/java/io/branch/referral/ServerRequest.java b/Branch-SDK/src/main/java/io/branch/referral/ServerRequest.java index 06837dbac..18dd6a0b8 100644 --- a/Branch-SDK/src/main/java/io/branch/referral/ServerRequest.java +++ b/Branch-SDK/src/main/java/io/branch/referral/ServerRequest.java @@ -372,8 +372,6 @@ private static ServerRequest getExtendedServerRequest(String requestPath, JSONOb extendedReq = new ServerRequestCreateUrl(Defines.RequestPath.GetURL, post, context); } else if (requestPath.equalsIgnoreCase(Defines.RequestPath.IdentifyUser.getPath())) { extendedReq = new ServerRequestIdentifyUserRequest(Defines.RequestPath.IdentifyUser, post, context); - } else if (requestPath.equalsIgnoreCase(Defines.RequestPath.Logout.getPath())) { - extendedReq = new ServerRequestLogout(Defines.RequestPath.Logout, post, context); } else if (requestPath.equalsIgnoreCase(Defines.RequestPath.RegisterInstall.getPath())) { extendedReq = new ServerRequestRegisterInstall(Defines.RequestPath.RegisterInstall, post, context, initiatedByClient); } else if (requestPath.equalsIgnoreCase(Defines.RequestPath.RegisterOpen.getPath())) { diff --git a/Branch-SDK/src/main/java/io/branch/referral/ServerRequestLogout.java b/Branch-SDK/src/main/java/io/branch/referral/ServerRequestLogout.java deleted file mode 100644 index e29797c85..000000000 --- a/Branch-SDK/src/main/java/io/branch/referral/ServerRequestLogout.java +++ /dev/null @@ -1,100 +0,0 @@ -package io.branch.referral; - -import android.app.Application; -import android.content.Context; - -import org.json.JSONException; -import org.json.JSONObject; - -/** - * *

- * The server request for logging out current user from Branch API. Handles request creation and execution. - *

- */ -class ServerRequestLogout extends ServerRequest { - - private Branch.LogoutStatusListener callback_; - - /** - *

Create an instance of {@link ServerRequestLogout} to signal when different person is about to use the app. For example, - * if you allow users to log out and let their friend use the app, you should call this to notify Branch - * to create a new user for this device. This will clear the first and latest params, as a new session is created.

- * - * @param context Current {@link Application} context - * @param callback An instance of {@link io.branch.referral.Branch.LogoutStatusListener} to callback with the logout operation status. - */ - public ServerRequestLogout(Context context, Branch.LogoutStatusListener callback) { - super(context, Defines.RequestPath.Logout); - callback_ = callback; - JSONObject post = new JSONObject(); - try { - post.put(Defines.Jsonkey.RandomizedBundleToken.getKey(), prefHelper_.getRandomizedBundleToken()); - post.put(Defines.Jsonkey.RandomizedDeviceToken.getKey(), prefHelper_.getRandomizedDeviceToken()); - post.put(Defines.Jsonkey.SessionID.getKey(), prefHelper_.getSessionID()); - if (!prefHelper_.getLinkClickID().equals(PrefHelper.NO_STRING_VALUE)) { - post.put(Defines.Jsonkey.LinkClickID.getKey(), prefHelper_.getLinkClickID()); - } - setPost(post); - } catch (JSONException ex) { - ex.printStackTrace(); - constructError_ = true; - } - } - - public ServerRequestLogout(Defines.RequestPath requestPath, JSONObject post, Context context) { - super(requestPath, post, context); - } - - @Override - public void onRequestSucceeded(ServerResponse resp, Branch branch) { - try { - prefHelper_.setSessionID(resp.getObject().getString(Defines.Jsonkey.SessionID.getKey())); - prefHelper_.setRandomizedBundleToken(resp.getObject().getString(Defines.Jsonkey.RandomizedBundleToken.getKey())); - prefHelper_.setUserURL(resp.getObject().getString(Defines.Jsonkey.Link.getKey())); - - prefHelper_.setInstallParams(PrefHelper.NO_STRING_VALUE); - prefHelper_.setSessionParams(PrefHelper.NO_STRING_VALUE); - prefHelper_.setIdentity(PrefHelper.NO_STRING_VALUE); - prefHelper_.clearUserValues(); - } catch (JSONException e) { - e.printStackTrace(); - } finally { - if (callback_ != null) { - callback_.onLogoutFinished(true, null); - } - } - } - - @Override - public void handleFailure(int statusCode, String causeMsg) { - if (callback_ != null) { - callback_.onLogoutFinished(false, new BranchError("Logout error. " + causeMsg, statusCode)); - } - } - - @Override - public boolean handleErrors(Context context) { - if (!super.doesAppHasInternetPermission(context)) { - if (callback_ != null) { - callback_.onLogoutFinished(false, new BranchError("Logout failed", BranchError.ERR_NO_INTERNET_PERMISSION)); - } - return true; - } - return false; - } - - @Override - public boolean isGetRequest() { - return false; - } - - @Override - public void clearCallbacks() { - callback_ = null; - } - - @Override - boolean isPersistable() { - return false; // No need to retrieve logout from previous session - } -} diff --git a/Branch-SDK/src/main/java/io/branch/referral/ServerRequestQueue.java b/Branch-SDK/src/main/java/io/branch/referral/ServerRequestQueue.java index 4d657cb3a..763fe1d36 100644 --- a/Branch-SDK/src/main/java/io/branch/referral/ServerRequestQueue.java +++ b/Branch-SDK/src/main/java/io/branch/referral/ServerRequestQueue.java @@ -465,11 +465,6 @@ public void handleNewRequest(ServerRequest req) { } //If not initialised put an open or install request in front of this request(only if this needs session) if (Branch.getInstance().initState_ != Branch.SESSION_STATE.INITIALISED && !(req instanceof ServerRequestInitSession)) { - if ((req instanceof ServerRequestLogout)) { - req.handleFailure(BranchError.ERR_NO_SESSION, ""); - BranchLogger.d("Branch is not initialized, cannot logout"); - return; - } if (requestNeedsSession(req)) { BranchLogger.d("handleNewRequest " + req + " needs a session"); req.addProcessWaitLock(ServerRequest.PROCESS_WAIT_LOCK.SDK_INIT_WAIT_LOCK); @@ -576,10 +571,6 @@ private void onRequestSuccess(ServerResponse serverResponse) { } catch (JSONException ex) { ex.printStackTrace(); } - } else if (thisReq_ instanceof ServerRequestLogout) { - //On Logout clear the link cache and all pending requests - Branch.getInstance().linkCache_.clear(); - ServerRequestQueue.this.clear(); }