Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,10 @@ public void doWork(String requestData, String customEndpoint, ConnectionProcesso
}
};
}

@Override public ImmediateRequestI CreatePreflightRequestMaker() {
return null;
}
};
}

Expand Down Expand Up @@ -1162,6 +1166,10 @@ private int[] setupTest_allFeatures(JSONObject serverConfig) {
}
};
}

@Override public ImmediateRequestI CreatePreflightRequestMaker() {
return null;
}
};
countlyConfig.metricProviderOverride = new MockedMetricProvider();
Countly.sharedInstance().init(countlyConfig);
Expand Down
57 changes: 57 additions & 0 deletions sdk/src/main/java/ly/count/android/sdk/ConnectionProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,63 @@ private enum RequestResult {
return conn;
}

synchronized public @NonNull URLConnection urlConnectionForPreflightRequest(@NonNull String preflightData) throws IOException {
long approxSize = preflightData.length();
URL url = new URL(preflightData);

long tOpen = pcc != null ? UtilsTime.getNanoTime() : 0;
HttpURLConnection conn = (HttpURLConnection) url.openConnection();

if (conn instanceof HttpsURLConnection && (Countly.publicKeyPinCertificates != null || Countly.certificatePinCertificates != null)) {
((HttpsURLConnection) conn).setSSLSocketFactory(sslContext_.getSocketFactory());
}

if (pcc != null) {
pcc.TrackCounterTimeNs("ConnectionProcessorUrlConnectionForPreflightRequest_01_OpenURLConnection", UtilsTime.getNanoTime() - tOpen);
tOpen = UtilsTime.getNanoTime();
}

conn.setRequestMethod("HEAD");
conn.setConnectTimeout(CONNECT_TIMEOUT_IN_MILLISECONDS);
conn.setReadTimeout(READ_TIMEOUT_IN_MILLISECONDS);
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setDoOutput(false);
conn.setInstanceFollowRedirects(true);

if (requestHeaderCustomValues_ != null) {
L.v("[ConnectionProcessor] Adding [" + requestHeaderCustomValues_.size() + "] custom header fields");
for (Map.Entry<String, String> e : requestHeaderCustomValues_.entrySet()) {
if (e.getKey() != null && e.getValue() != null && !e.getKey().isEmpty()) {
conn.addRequestProperty(e.getKey(), e.getValue());
}
}
}

if (pcc != null) {
pcc.TrackCounterTimeNs("ConnectionProcessorUrlConnectionForPreflightRequest_02_ConfigureConnection", UtilsTime.getNanoTime() - tOpen);
tOpen = UtilsTime.getNanoTime();
}

try {
for (int i = 0; ; i++) {
String key = conn.getHeaderFieldKey(i);
if (key == null) break;
String value = conn.getHeaderField(i);
approxSize += key.getBytes(StandardCharsets.US_ASCII).length + value.getBytes(StandardCharsets.US_ASCII).length + 2L;
}
} catch (Exception e) {
L.e("[ConnectionProcessor] urlConnectionForPreflightRequest, exception while calculating header field size: " + e);
}

if (pcc != null) {
pcc.TrackCounterTimeNs("ConnectionProcessorUrlConnectionForPreflightRequest_03_HeaderFieldSize", UtilsTime.getNanoTime() - tOpen);
}

L.v("[ConnectionProcessor] urlConnectionForPreflightRequest, Approx data size: [" + approxSize + " B]");
return conn;
}

/**
* Return the size of the text multipart entry
*
Expand Down
4 changes: 4 additions & 0 deletions sdk/src/main/java/ly/count/android/sdk/Countly.java
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,10 @@ public synchronized Countly init(CountlyConfig config) {
@Override public ImmediateRequestI CreateImmediateRequestMaker() {
return (new ImmediateRequestMaker());
}

@Override public ImmediateRequestI CreatePreflightRequestMaker() {
return (new PreflightRequestMaker());
}
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@

interface ImmediateRequestGenerator {
ImmediateRequestI CreateImmediateRequestMaker();

ImmediateRequestI CreatePreflightRequestMaker();
}
53 changes: 31 additions & 22 deletions sdk/src/main/java/ly/count/android/sdk/ModuleContent.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,28 +76,37 @@ void fetchContentsInternal(@NonNull String[] categories) {
try {
if (validateResponse(checkResponse)) {
L.d("[ModuleContent] fetchContentsInternal, got new content data, showing it");
Map<Integer, TransparentActivityConfig> placementCoordinates = parseContent(checkResponse, displayMetrics);
if (placementCoordinates.isEmpty()) {
L.d("[ModuleContent] fetchContentsInternal, placement coordinates are empty, skipping");
return;
}

Intent intent = new Intent(_cly.context_, TransparentActivity.class);
intent.putExtra(TransparentActivity.CONFIGURATION_LANDSCAPE, placementCoordinates.get(Configuration.ORIENTATION_LANDSCAPE));
intent.putExtra(TransparentActivity.CONFIGURATION_PORTRAIT, placementCoordinates.get(Configuration.ORIENTATION_PORTRAIT));
intent.putExtra(TransparentActivity.ORIENTATION, _cly.context_.getResources().getConfiguration().orientation);

Long id = System.currentTimeMillis();
intent.putExtra(TransparentActivity.ID_CALLBACK, id);
if (globalContentCallback != null) {
TransparentActivity.contentCallbacks.put(id, globalContentCallback);
}

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
_cly.context_.startActivity(intent);

shouldFetchContents = false; // disable fetching contents until the next time, this will disable the timer fetching
isCurrentlyInContentZone = true;

iRGenerator.CreatePreflightRequestMaker().doWork(checkResponse.optString("html"), null, cp, false, true, preflightResponse -> {
if (preflightResponse == null) {
L.d("[ModuleContent] fetchContentsInternal, preflight check failed, skipping showing content");
return;
}

Map<Integer, TransparentActivityConfig> placementCoordinates = parseContent(checkResponse, displayMetrics);
if (placementCoordinates.isEmpty()) {
L.d("[ModuleContent] fetchContentsInternal, placement coordinates are empty, skipping");
return;
}

L.d("[ModuleContent] fetchContentsInternal, preflight check succeeded");
Intent intent = new Intent(_cly.context_, TransparentActivity.class);
intent.putExtra(TransparentActivity.CONFIGURATION_LANDSCAPE, placementCoordinates.get(Configuration.ORIENTATION_LANDSCAPE));
intent.putExtra(TransparentActivity.CONFIGURATION_PORTRAIT, placementCoordinates.get(Configuration.ORIENTATION_PORTRAIT));
intent.putExtra(TransparentActivity.ORIENTATION, _cly.context_.getResources().getConfiguration().orientation);

Long id = System.currentTimeMillis();
intent.putExtra(TransparentActivity.ID_CALLBACK, id);
if (globalContentCallback != null) {
TransparentActivity.contentCallbacks.put(id, globalContentCallback);
}

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
_cly.context_.startActivity(intent);

shouldFetchContents = false; // disable fetching contents until the next time, this will disable the timer fetching
isCurrentlyInContentZone = true;
}, L);
} else {
L.w("[ModuleContent] fetchContentsInternal, response is not valid, skipping");
}
Expand Down
58 changes: 34 additions & 24 deletions sdk/src/main/java/ly/count/android/sdk/ModuleFeedback.java
Original file line number Diff line number Diff line change
Expand Up @@ -268,35 +268,45 @@ void presentFeedbackWidgetInternal(@Nullable final CountlyFeedbackWidget widgetI
widgetListUrl.append(customObjectToSendWithTheWidget);

String preparedWidgetUrl = widgetListUrl.toString();

L.d("[ModuleFeedback] Using following url for widget:[" + preparedWidgetUrl + "]");
if (!Utils.isNullOrEmpty(widgetInfo.widgetVersion)) {
L.d("[ModuleFeedback] Will use transparent activity for displaying the widget");
showFeedbackWidget_newActivity(context, preparedWidgetUrl, widgetInfo, devCallback);
} else {
L.d("[ModuleFeedback] Will use dialog for displaying the widget");
//enable for chrome debugging
// WebView.setWebContentsDebuggingEnabled(true);
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
public void run() {
L.d("[ModuleFeedback] Calling on main thread");

try {
showFeedbackWidget(context, widgetInfo, closeButtonText, devCallback, preparedWidgetUrl);
iRGenerator.CreatePreflightRequestMaker().doWork(preparedWidgetUrl, null, requestQueueProvider.createConnectionProcessor(), false, true, preflightResponse -> {
if (preflightResponse == null) {
L.e("[ModuleFeedback] Failed to do preflight check for the widget url");
if (devCallback != null) {
devCallback.onFinished("Failed to do preflight check for the widget url");
}
return;
}

if (devCallback != null) {
devCallback.onFinished(null);
}
} catch (Exception ex) {
L.e("[ModuleFeedback] Failed at displaying feedback widget dialog, [" + ex.toString() + "]");
if (devCallback != null) {
devCallback.onFinished("Failed at displaying feedback widget dialog, [" + ex.toString() + "]");
if (!Utils.isNullOrEmpty(widgetInfo.widgetVersion)) {
L.d("[ModuleFeedback] Will use transparent activity for displaying the widget");
showFeedbackWidget_newActivity(context, preparedWidgetUrl, widgetInfo, devCallback);
} else {
L.d("[ModuleFeedback] Will use dialog for displaying the widget");
//enable for chrome debugging
// WebView.setWebContentsDebuggingEnabled(true);
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
public void run() {
L.d("[ModuleFeedback] Calling on main thread");

try {
showFeedbackWidget(context, widgetInfo, closeButtonText, devCallback, preparedWidgetUrl);

if (devCallback != null) {
devCallback.onFinished(null);
}
} catch (Exception ex) {
L.e("[ModuleFeedback] Failed at displaying feedback widget dialog, [" + ex.toString() + "]");
if (devCallback != null) {
devCallback.onFinished("Failed at displaying feedback widget dialog, [" + ex.toString() + "]");
}
}
}
}
});
}
});
}
}, L);
}

private void showFeedbackWidget(Context context, CountlyFeedbackWidget widgetInfo, String closeButtonText, FeedbackCallback devCallback, String url) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package ly.count.android.sdk;

import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.IOException;
import java.net.HttpURLConnection;
import org.json.JSONObject;

class PreflightRequestMaker extends AsyncTask<Object, Void, Boolean> implements ImmediateRequestI {

ImmediateRequestMaker.InternalImmediateRequestCallback callback;
ModuleLog L;

@Override
public void doWork(@NonNull String requestData, @Nullable String customEndpoint, @NonNull ConnectionProcessor cp, boolean requestShouldBeDelayed, boolean networkingIsEnabled, @NonNull ImmediateRequestMaker.InternalImmediateRequestCallback callback, @NonNull ModuleLog log) {
assert Utils.isNotNullOrEmpty(requestData);
assert cp != null;
assert log != null;
assert callback != null;

this.execute(requestData, customEndpoint, cp, requestShouldBeDelayed, networkingIsEnabled, callback, log);
}

/**
* params fields:
* 0 - requestData
* 1 - custom endpoint
* 2 - connection processor
* 3 - requestShouldBeDelayed
* 4 - networkingIsEnabled
* 5 - callback
* 6 - log module
*/
protected Boolean doInBackground(Object... params) {
final String urlRequest = (String) params[0];
final ConnectionProcessor cp = (ConnectionProcessor) params[2];
callback = (ImmediateRequestMaker.InternalImmediateRequestCallback) params[5];
L = (ModuleLog) params[6];

L.v("[PreflightRequestMaker] doPreflightRequest, Starting preflight request");

HttpURLConnection connection = null;

try {
//getting connection ready
try {
connection = (HttpURLConnection) cp.urlConnectionForPreflightRequest(urlRequest);
} catch (IOException e) {
L.e("[PreflightRequestMaker] doPreflightRequest, IOException while preparing preflight request :[" + e + "]");
return null;
}

int responseCode = connection.getResponseCode();

L.v("[PreflightRequestMaker] doPreflightRequest, Preflight request finished, response code: " + responseCode);
return responseCode >= 200 && responseCode < 400;
} catch (Exception e) {
L.e("[PreflightRequestMaker] doPreflightRequest, Received exception while making a immediate server request", e);
} finally {
if (connection != null) {
connection.disconnect();
}
}
L.v("[PreflightRequestMaker] doPreflightRequest, Finished request");
return false;
}

@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
L.v("[PreflightRequestMaker] onPostExecute");

if (callback != null) {
callback.callback(result ? new JSONObject() : null);
}
}
}
Loading