diff --git a/app/app-dependencies.gradle b/app/app-dependencies.gradle
index 45ba4a62..68cd5a83 100644
--- a/app/app-dependencies.gradle
+++ b/app/app-dependencies.gradle
@@ -19,17 +19,17 @@ ext {
// the version number in one location for those artifacts
demoAppSharedVersions = [
okhttp3Version : '3.2.0',
- supportLibraryVersion : '25.0.1',
+ supportLibraryVersion : '25.2.0',
leakCanaryVersion : '1.5',
bvSdkVersion : VERSION_NAME,
stethoVersion : '1.3.1',
butterknifeVersion : '8.5.0',
daggerVersion: '2.8',
- playServicesVersion: '9.8.0'
+ playServicesVersion: '11.0.0'
]
demoAppBuildDep = [
- androidPlugin : 'com.android.tools.build:gradle:2.3.2',
+ androidPlugin : 'com.android.tools.build:gradle:2.3.3',
crashlyticsPlugin : 'io.fabric.tools:gradle:1.21.6',
]
@@ -62,6 +62,7 @@ ext {
processPhoenix : "com.jakewharton:process-phoenix:2.0.0",
bvSdkAnalytics : "com.bazaarvoice.bvandroidsdk:analytics:$demoAppSharedVersions.bvSdkVersion",
bvSdkAds : "com.bazaarvoice.bvandroidsdk:advertising:$demoAppSharedVersions.bvSdkVersion",
+ bvSdkAuthIovation : "com.bazaarvoice.bvandroidsdk:auth-iovation:$demoAppSharedVersions.bvSdkVersion",
bvSdkConversations : "com.bazaarvoice.bvandroidsdk:conversations:$demoAppSharedVersions.bvSdkVersion",
bvSdkCurations : "com.bazaarvoice.bvandroidsdk:curations:$demoAppSharedVersions.bvSdkVersion",
bvSdkRecommendations: "com.bazaarvoice.bvandroidsdk:recommendations:$demoAppSharedVersions.bvSdkVersion",
diff --git a/app/build.gradle b/app/build.gradle
index 2bfcfb6c..fb10d40a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -4,11 +4,6 @@ buildscript {
classpath demoAppBuildDep.crashlyticsPlugin
// classpath demoAppBuildDep.androidAptPlugin
}
- repositories {
- flatDir {
- dirs 'libs'
- }
- }
}
repositories {
@@ -103,6 +98,7 @@ dependencies {
if (useLocalSdk) {
compile project(':bvanalytics')
compile project(':bvadvertising')
+ compile project(':bvauthiovation')
compile project(':bvrecommendations')
compile project(':bvconversations')
compile project(':bvcurations')
@@ -114,6 +110,8 @@ dependencies {
} else {
compile demoAppDep.bvSdkAnalytics
compile demoAppDep.bvSdkAds
+ compile demoAppDep.bvSdkAuthIovation
+ compile('com.iovation.deviceprint.lib.DevicePrint:deviceprint-lib-release-2.3.3:2.3.3@aar')
compile demoAppDep.bvSdkRecommendations
compile demoAppDep.bvSdkConversations
compile demoAppDep.bvSdkCurations
@@ -145,14 +143,6 @@ dependencies {
compile demoAppDep.butterknife
compile demoAppDep.processPhoenix
annotationProcessor demoAppDep.butterknifeCompiler
- // iovation aar dependency
- // Uncomment the following lines to add iovation support
- // Make sure the iovation arr is in the app/libs folder
- // dependencies {
- // compile fileTree(dir: 'libs', include: ['*.jar'])
- // compile(name:'deviceprint-release-2.3.0', ext:'aar')
- // compile(group:'com.android.support', name:'support-v4', version:'23.0.1')
- // }
releaseCompile demoAppDep.leakCanaryNoOp
debugCompile demoAppDep.leakCanary
testCompile demoAppTestDep.junit
diff --git a/app/libs/deviceprint-lib-release-2.3.3.aar b/app/libs/deviceprint-lib-release-2.3.3.aar
new file mode 100644
index 00000000..227a0939
Binary files /dev/null and b/app/libs/deviceprint-lib-release-2.3.3.aar differ
diff --git a/app/src/main/java/com/bazaarvoice/bvsdkdemoandroid/DemoBvModule.java b/app/src/main/java/com/bazaarvoice/bvsdkdemoandroid/DemoBvModule.java
index 6a0e7d49..c30fa6d6 100644
--- a/app/src/main/java/com/bazaarvoice/bvsdkdemoandroid/DemoBvModule.java
+++ b/app/src/main/java/com/bazaarvoice/bvsdkdemoandroid/DemoBvModule.java
@@ -27,6 +27,7 @@
import com.bazaarvoice.bvandroidsdk.BVSDK;
import com.bazaarvoice.bvandroidsdk.BazaarEnvironment;
import com.bazaarvoice.bvandroidsdk.CurationsImageLoader;
+import com.bazaarvoice.bvandroidsdk.IovationFingerprint;
import com.bazaarvoice.bvandroidsdk.PinClient;
import com.bazaarvoice.bvsdkdemoandroid.configs.DemoClientConfigUtils;
import com.bazaarvoice.bvsdkdemoandroid.conversations.DemoConvResponseHandler;
@@ -58,7 +59,9 @@ BVSDK provideBvSdk(
@Provides @DemoAppScope
BVConversationsClient provideConversationsClient(BVSDK bvsdk) {
- return new BVConversationsClient();
+ return new BVConversationsClient.Builder(bvsdk)
+ .fingerprintProvider(new IovationFingerprint(bvsdk)) // For North America authentication
+ .build();
}
@Provides @DemoAppScope
diff --git a/app/src/main/java/com/bazaarvoice/bvsdkdemoandroid/conversations/DemoConvApiPresenter.java b/app/src/main/java/com/bazaarvoice/bvsdkdemoandroid/conversations/DemoConvApiPresenter.java
index df8a6e73..c8b5cc2c 100644
--- a/app/src/main/java/com/bazaarvoice/bvsdkdemoandroid/conversations/DemoConvApiPresenter.java
+++ b/app/src/main/java/com/bazaarvoice/bvsdkdemoandroid/conversations/DemoConvApiPresenter.java
@@ -174,15 +174,11 @@ private boolean readyForDemo() {
}
private void submitReviewWithPhoto(String productId) {
- // For clients non-EU clients, iovation SDK is required!
- // String blackbox = getBlackbox(getContext().getApplicationContext());
-
view.showProgressWithTitle("Submitting Review...");
File localImageFile = demoAssetsUtil.parseImageFileFromAssets("puppy_thumbnail.jpg");
ReviewSubmissionRequest submission = new ReviewSubmissionRequest.Builder(submitAction, productId)
-// .fingerPrint(blackbox) // uncomment me when using iovation SDK
.userNickname("shazbat")
.userEmail("foo@bar.com")
.userId("shazbatuser" + Math.random()) // Creating a random user id to avoid duplicated -- FOR TESTING ONLY!!!
diff --git a/bvadvertising/src/main/res/values/strings.xml b/bvadvertising/src/main/res/values/strings.xml
deleted file mode 100644
index dbd6707d..00000000
--- a/bvadvertising/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- BVSDK Advertising
-
\ No newline at end of file
diff --git a/bvanalytics/src/main/res/values/strings.xml b/bvanalytics/src/main/res/values/strings.xml
deleted file mode 100644
index e18e5cbb..00000000
--- a/bvanalytics/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- bvanalytics
-
diff --git a/bvauthiovation/.gitignore b/bvauthiovation/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/bvauthiovation/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/bvauthiovation/build.gradle b/bvauthiovation/build.gradle
new file mode 100644
index 00000000..379f36d8
--- /dev/null
+++ b/bvauthiovation/build.gradle
@@ -0,0 +1,30 @@
+buildscript {
+ dependencies {
+ classpath sdkBuildDep.androidPlugin
+ classpath sdkBuildDep.credentialsPlugin
+ classpath sdkBuildDep.jacocoPlugin
+ }
+}
+
+repositories {
+ flatDir {
+ dir 'libs'
+ }
+}
+
+apply from: rootProject.file('gradle/bvsdk-module-android.gradle')
+
+dependencies {
+ compile('com.iovation.deviceprint.lib.DevicePrint:deviceprint-lib-release-2.3.3:2.3.3@aar')
+ compile project(':bvconversations')
+ provided sdkDep.okhttp3
+
+ // Dependencies for local unit tests
+ testCompile sdkTestDep.junit
+ testCompile sdkTestDep.mockito
+ testCompile sdkTestDep.mockWebServer
+ testCompile sdkTestDep.robolectric
+}
+
+apply from: rootProject.file('gradle/setup-signing.gradle')
+apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
\ No newline at end of file
diff --git a/bvauthiovation/gradle.properties b/bvauthiovation/gradle.properties
new file mode 100644
index 00000000..d990ef32
--- /dev/null
+++ b/bvauthiovation/gradle.properties
@@ -0,0 +1,3 @@
+POM_NAME=Bazaarvoice Authentication Iovation Library
+POM_ARTIFACT_ID=auth-iovation
+POM_PACKAGING=aar
diff --git a/bvauthiovation/libs/deviceprint-lib-release-2.3.3.aar b/bvauthiovation/libs/deviceprint-lib-release-2.3.3.aar
new file mode 100644
index 00000000..227a0939
Binary files /dev/null and b/bvauthiovation/libs/deviceprint-lib-release-2.3.3.aar differ
diff --git a/bvauthiovation/proguard-rules.pro b/bvauthiovation/proguard-rules.pro
new file mode 100644
index 00000000..e107ef53
--- /dev/null
+++ b/bvauthiovation/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/casey.kulm/Library/Android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/bvauthiovation/src/main/AndroidManifest.xml b/bvauthiovation/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..081853c9
--- /dev/null
+++ b/bvauthiovation/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/bvauthiovation/src/main/java/com/bazaarvoice/bvandroidsdk/IovationFingerprint.java b/bvauthiovation/src/main/java/com/bazaarvoice/bvandroidsdk/IovationFingerprint.java
new file mode 100644
index 00000000..4fa78013
--- /dev/null
+++ b/bvauthiovation/src/main/java/com/bazaarvoice/bvandroidsdk/IovationFingerprint.java
@@ -0,0 +1,25 @@
+package com.bazaarvoice.bvandroidsdk;
+
+import android.content.Context;
+
+import com.iovation.mobile.android.DevicePrint;
+
+public final class IovationFingerprint implements FingerprintProvider {
+ private final Context appContext;
+ private String fingerPrint;
+ private boolean started = false;
+
+ public IovationFingerprint(BVSDK bvsdk) {
+ this.appContext = bvsdk.getBvUserProvidedData().getAppContext();
+ }
+
+ @Override
+ public String getFingerprint() throws VerifyError {
+ if (!started) {
+ DevicePrint.start(appContext);
+ fingerPrint = DevicePrint.getBlackbox(appContext);
+ started = true;
+ }
+ return fingerPrint;
+ }
+}
\ No newline at end of file
diff --git a/bvcommon/src/main/res/values/strings.xml b/bvcommon/src/main/res/values/strings.xml
index 4a7502b7..10ff95aa 100644
--- a/bvcommon/src/main/res/values/strings.xml
+++ b/bvcommon/src/main/res/values/strings.xml
@@ -1,5 +1,4 @@
- BVSDK Common
seen_product_onscreen_with_id_set
\ No newline at end of file
diff --git a/bvcommon/src/sharedSdkTest/java/com/bazaarvoice/bvandroidsdk/BVBaseTest.java b/bvcommon/src/sharedSdkTest/java/com/bazaarvoice/bvandroidsdk/BVBaseTest.java
index 8c924f74..054188fb 100644
--- a/bvcommon/src/sharedSdkTest/java/com/bazaarvoice/bvandroidsdk/BVBaseTest.java
+++ b/bvcommon/src/sharedSdkTest/java/com/bazaarvoice/bvandroidsdk/BVBaseTest.java
@@ -117,7 +117,7 @@ protected void modifyPropertiesToInitSDK() {}
protected void afterInitSdk(BVSDK bvsdk) {}
- String jsonFileAsString(String fileName) throws IOException {
+ static String jsonFileAsString(String fileName) throws IOException {
File file = new File("src/test/resources/" + fileName);
return readFile(file);
}
diff --git a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/BVConversationsClient.java b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/BVConversationsClient.java
index b139d29b..367087ec 100644
--- a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/BVConversationsClient.java
+++ b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/BVConversationsClient.java
@@ -17,7 +17,9 @@
package com.bazaarvoice.bvandroidsdk;
-import java.util.List;
+import android.os.Looper;
+
+import com.google.gson.Gson;
import okhttp3.OkHttpClient;
import okhttp3.Request;
@@ -27,13 +29,13 @@
* BVConversationsClient as a single instance in your app.
*/
public final class BVConversationsClient {
- private static final OkHttpClient okHttpClient = BVSDK.getInstance().getBvWorkerData().getOkHttpClient();
- private static final String CONVERSATIONS_RELATIVE_URL = "data/";
- static final String conversationsBaseUrl = BVSDK.getInstance().getBvWorkerData().getRootApiUrls().getBazaarvoiceApiRootUrl() + CONVERSATIONS_RELATIVE_URL;
-
private final RequestFactory requestFactory;
private final BVConfig bvConfig;
private final ConversationsAnalyticsManager conversationsAnalyticsManager;
+ private final Looper bgLooper;
+ private final Looper uiLooper;
+ private final OkHttpClient okHttpClient;
+ private final Gson gson;
/**
* @deprecated Now use the {@link Builder} which explicitly requires the {@link BVSDK} singleton instance.
@@ -45,12 +47,20 @@ public BVConversationsClient() {
bvConfig = client.bvConfig;
requestFactory = client.requestFactory;
conversationsAnalyticsManager = client.conversationsAnalyticsManager;
+ bgLooper = client.bgLooper;
+ uiLooper = client.uiLooper;
+ okHttpClient = client.okHttpClient;
+ gson = client.gson;
}
private BVConversationsClient(Builder builder) {
bvConfig = builder.bvConfig;
requestFactory = builder.requestFactory;
conversationsAnalyticsManager = builder.conversationsAnalyticsManager;
+ bgLooper = builder.bgLooper;
+ uiLooper = builder.uiLooper;
+ okHttpClient = builder.okHttpClient;
+ gson = builder.gson;
}
/**
@@ -58,7 +68,7 @@ private BVConversationsClient(Builder builder) {
* @return LoadCall object with the request ready to be sent
*/
public LoadCallDisplay prepareCall(QuestionAndAnswerRequest request) {
- return factoryCreateDisplayCall(QuestionAndAnswerResponse.class, request, requestFactory, conversationsAnalyticsManager);
+ return factoryCreateDisplayCall(QuestionAndAnswerResponse.class, request);
}
/**
@@ -66,11 +76,11 @@ public LoadCallDisplay prep
* @return LoadCall object with the request ready to be sent
*/
public LoadCallDisplay prepareCall(ProductDisplayPageRequest request) {
- return factoryCreateDisplayCall(ProductDisplayPageResponse.class, request, requestFactory, conversationsAnalyticsManager);
+ return factoryCreateDisplayCall(ProductDisplayPageResponse.class, request);
}
public LoadCallDisplay prepareCall(BulkProductRequest request) {
- return factoryCreateDisplayCall(BulkProductResponse.class, request, requestFactory, conversationsAnalyticsManager);
+ return factoryCreateDisplayCall(BulkProductResponse.class, request);
}
/**
@@ -78,7 +88,7 @@ public LoadCallDisplay prepareCall(Bulk
* @return LoadCall object with the request ready to be sent
*/
public LoadCallDisplay prepareCall(BulkRatingsRequest request) {
- return factoryCreateDisplayCall(BulkRatingsResponse.class, request, requestFactory, conversationsAnalyticsManager);
+ return factoryCreateDisplayCall(BulkRatingsResponse.class, request);
}
/**
@@ -86,7 +96,7 @@ public LoadCallDisplay prepareCall(Bulk
* @return LoadCall object with the request ready to be sent
*/
public LoadCallDisplay prepareCall(BulkStoreRequest request) {
- return factoryCreateDisplayCall(BulkStoreResponse.class, request, requestFactory, conversationsAnalyticsManager);
+ return factoryCreateDisplayCall(BulkStoreResponse.class, request);
}
/**
@@ -94,19 +104,19 @@ public LoadCallDisplay prepareCall(BulkStor
* @return LoadCall object with the request ready to be sent
*/
public LoadCallDisplay prepareCall(ReviewsRequest request) {
- return factoryCreateDisplayCall(ReviewResponse.class, request, requestFactory, conversationsAnalyticsManager);
+ return factoryCreateDisplayCall(ReviewResponse.class, request);
}
public LoadCallDisplay prepareCall(StoreReviewsRequest request) {
- return factoryCreateDisplayCall(StoreReviewResponse.class, request, requestFactory, conversationsAnalyticsManager);
+ return factoryCreateDisplayCall(StoreReviewResponse.class, request);
}
public LoadCallDisplay prepareCall(AuthorsRequest request) {
- return factoryCreateDisplayCall(AuthorsResponse.class, request, requestFactory, conversationsAnalyticsManager);
+ return factoryCreateDisplayCall(AuthorsResponse.class, request);
}
public LoadCallDisplay prepareCall(CommentsRequest request) {
- return factoryCreateDisplayCall(CommentsResponse.class, request, requestFactory, conversationsAnalyticsManager);
+ return factoryCreateDisplayCall(CommentsResponse.class, request);
}
public interface DisplayLoader {
@@ -114,59 +124,43 @@ public interface DisplayLoader prepareCall(AnswerSubmissionRequest submission) {
- return factoryCreateSubmissionCall(AnswerSubmissionResponse.class, submission, requestFactory, conversationsAnalyticsManager, bvConfig.getApiKeyConversations());
+ return factoryCreateSubmissionCall(AnswerSubmissionResponse.class, submission);
}
public LoadCallSubmission prepareCall(ReviewSubmissionRequest submission) {
- return factoryCreateSubmissionCall(ReviewSubmissionResponse.class, submission, requestFactory, conversationsAnalyticsManager, bvConfig.getApiKeyConversations());
+ return factoryCreateSubmissionCall(ReviewSubmissionResponse.class, submission);
}
public LoadCallSubmission prepareCall(StoreReviewSubmissionRequest submission) {
- return factoryCreateSubmissionCall(StoreReviewSubmissionResponse.class, submission, requestFactory, conversationsAnalyticsManager, bvConfig.getApiKeyConversationsStores());
+ return factoryCreateSubmissionCall(StoreReviewSubmissionResponse.class, submission);
}
public LoadCallSubmission prepareCall(QuestionSubmissionRequest submission) {
- return factoryCreateSubmissionCall(QuestionSubmissionResponse.class, submission, requestFactory, conversationsAnalyticsManager, bvConfig.getApiKeyConversations());
+ return factoryCreateSubmissionCall(QuestionSubmissionResponse.class, submission);
}
public LoadCallSubmission prepareCall(FeedbackSubmissionRequest submission) {
- return factoryCreateSubmissionCall(FeedbackSubmissionResponse.class, submission, requestFactory, conversationsAnalyticsManager, bvConfig.getApiKeyConversations());
+ return factoryCreateSubmissionCall(FeedbackSubmissionResponse.class, submission);
}
public LoadCallSubmission prepareCall(CommentSubmissionRequest submission) {
- return factoryCreateSubmissionCall(CommentSubmissionResponse.class, submission, requestFactory, conversationsAnalyticsManager, bvConfig.getApiKeyConversations());
+ return factoryCreateSubmissionCall(CommentSubmissionResponse.class, submission);
}
- private static LoadCallDisplay factoryCreateDisplayCall(Class responseTypeClass, RequestType request, RequestFactory requestFactory, ConversationsAnalyticsManager conversationsAnalyticsManager) {
+ private LoadCallDisplay factoryCreateDisplayCall(Class responseTypeClass, RequestType request) {
final Request okRequest = requestFactory.create(request);
- return new LoadCallDisplay(request, responseTypeClass, okHttpClient.newCall(okRequest), conversationsAnalyticsManager);
+ return new LoadCallDisplay(request, responseTypeClass, okHttpClient.newCall(okRequest), conversationsAnalyticsManager, okHttpClient, gson);
}
- private static LoadCallSubmission factoryCreateSubmissionCall(Class responseTypeClass, RequestType request, RequestFactory requestFactory, ConversationsAnalyticsManager conversationsAnalyticsManager, String apiKey) {
+ private LoadCallSubmission factoryCreateSubmissionCall(Class responseTypeClass, RequestType request) {
if (request.getAction() == Action.Submit) {
request.setForcePreview(true);
}
- return loadCallFromSubmission(responseTypeClass, request, requestFactory, conversationsAnalyticsManager, apiKey);
+ return loadCallFromSubmission(responseTypeClass, request);
}
- static LoadCallSubmission loadCallFromSubmission(Class responseTypeClass, RequestType request, RequestFactory requestFactory, ConversationsAnalyticsManager conversationsAnalyticsManager, String apiKey) {
- final Request okRequest = requestFactory.create(request);
- return new LoadCallSubmission(request, responseTypeClass, okHttpClient.newCall(okRequest), conversationsAnalyticsManager, requestFactory, apiKey);
- }
-
- // TODO: Remove as part of photo request refactor
- static LoadCall reCreateCallWithPhotos(Class responseTypeClass, RequestType submission, List photos, RequestFactory requestFactory, ConversationsAnalyticsManager conversationsAnalyticsManager, String apiKey) {
- submission.setPhotos(photos);
- submission.getBuilder().photoUploads.clear();
- submission.setForcePreview(false);
-
- return loadCallFromSubmission(responseTypeClass, submission, requestFactory, conversationsAnalyticsManager, apiKey);
- }
-
- // TODO: Remove as part of photo request refactor
- static LoadCall reCreateCallNoPreview(Class responseTypeClass, RequestType submission, RequestFactory requestFactory, ConversationsAnalyticsManager conversationsAnalyticsManager, String apiKey) {
- submission.setForcePreview(false);
- return loadCallFromSubmission(responseTypeClass, submission, requestFactory, conversationsAnalyticsManager, apiKey);
+ private LoadCallSubmission loadCallFromSubmission(Class responseTypeClass, RequestType request) {
+ return new LoadCallSubmission(request, responseTypeClass, conversationsAnalyticsManager, requestFactory, bgLooper, uiLooper, okHttpClient, gson);
}
/**
@@ -175,12 +169,22 @@ static Request create(RequestType request) {
+ if (request instanceof ReviewsRequest) {
+ return createFromReviewRequest((ReviewsRequest) request);
+ } else if (request instanceof QuestionAndAnswerRequest) {
+ return createFromQuestionAndAnswerRequest((QuestionAndAnswerRequest) request);
+ } else if (request instanceof CommentsRequest) {
+ return createFromCommentsRequest((CommentsRequest) request);
+ } else if (request instanceof AuthorsRequest) {
+ return createFromAuthorsRequest((AuthorsRequest) request);
+ } else if (request instanceof BulkStoreRequest) {
+ return createFromBulkStoreRequest((BulkStoreRequest) request);
+ } else if (request instanceof BulkRatingsRequest) {
+ return createFromBulkRatingsRequest((BulkRatingsRequest) request);
+ } else if (request instanceof StoreReviewsRequest) {
+ return createFromStoreReviewsRequest((StoreReviewsRequest) request);
+ } else if (request instanceof BulkProductRequest) {
+ return createFromBulkProductRequest((BulkProductRequest) request);
+ } else if (request instanceof ProductDisplayPageRequest) {
+ return createFromProductDisplayPageRequest((ProductDisplayPageRequest) request);
+ } else if (request instanceof FeedbackSubmissionRequest) {
+ return createFromFeedbackSubmissionRequest((FeedbackSubmissionRequest) request);
+ } else if (request instanceof ReviewSubmissionRequest) {
+ return createFromReviewSubmissionRequest((ReviewSubmissionRequest) request);
+ } else if (request instanceof StoreReviewSubmissionRequest) {
+ return createFromStoreReviewSubmissionRequest((StoreReviewSubmissionRequest) request);
+ } else if (request instanceof QuestionSubmissionRequest) {
+ return createFromQuestionSubmissionRequest((QuestionSubmissionRequest) request);
+ } else if (request instanceof AnswerSubmissionRequest) {
+ return createFromAnswerSubmissionRequest((AnswerSubmissionRequest) request);
+ } else if (request instanceof CommentSubmissionRequest) {
+ return createFromCommentSubmissionRequest((CommentSubmissionRequest) request);
+ } else if (request instanceof PhotoUploadRequest) {
+ return createFromPhotoUploadRequest((PhotoUploadRequest) request);
+ }
+ throw new IllegalStateException("Unknown request type: " + request.getClass().getCanonicalName());
+ }
+
+ // endregion
+
+ // region Mapping Implementation
+ private Request createFromReviewRequest(ReviewsRequest request) {
+ Request.Builder okRequestBuilder = new Request.Builder();
+
+ HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
+ .newBuilder()
+ .addPathSegments(REVIEWS_ENDPOINT);
+
+ addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo);
+ addCommonDisplayQueryParams(httpUrlBuilder, request);
+ addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset());
+
+ if (!request.getReviewIncludeTypes().isEmpty()) {
+ if (request.getReviewIncludeTypes().contains(ReviewIncludeType.PRODUCTS)) {
+ // "Stats=Reviews must be used in conjunction with Include=Products"
+ // - https://developer.bazaarvoice.com/conversations-api/reference/v5.4/reviews/review-display#requesting-all-reviews-for-a-particular-product-with-review-statistics-(inc.-average-rating)
+ httpUrlBuilder.addQueryParameter(KEY_STATS, STATS_REVIEWS);
+ }
+ String includeStr = StringUtils.componentsSeparatedBy(request.getReviewIncludeTypes(), ",");
+ httpUrlBuilder.addQueryParameter(kINCLUDE, includeStr);
+ }
+
+ if (!request.getSorts().isEmpty()) {
+ httpUrlBuilder
+ .addQueryParameter(kSORT, StringUtils.componentsSeparatedBy(request.getSorts(), ","));
+ }
+
+ if (request.getSearchPhrase() != null) {
+ httpUrlBuilder
+ .addQueryParameter(kSEARCH, request.getSearchPhrase());
+ }
+
+ HttpUrl httpUrl = httpUrlBuilder.build();
+
+ Headers.Builder headersBuilder = new Headers.Builder();
+ addCommonHeaders(headersBuilder, bvSdkUserAgent);
+ Headers headers = headersBuilder.build();
+
+ return okRequestBuilder
+ .url(httpUrl)
+ .headers(headers)
+ .build();
+ }
+
+ private Request createFromQuestionAndAnswerRequest(QuestionAndAnswerRequest request) {
+ Request.Builder okRequestBuilder = new Request.Builder();
+
+ HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
+ .newBuilder()
+ .addPathSegments(QUESTIONS_AND_ANSWERS_ENDPOINT);
+
+ addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo);
+ addCommonDisplayQueryParams(httpUrlBuilder, request);
+ addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset());
+
+ httpUrlBuilder.addQueryParameter(kINCLUDE, INCLUDE_ANSWERS);
+
+ if (!request.getQuestionSorts().isEmpty()){
+ httpUrlBuilder.addQueryParameter(kSORT, StringUtils.componentsSeparatedBy(request.getQuestionSorts(), ","));
+ }
+
+ if (!request.getAnswerSorts().isEmpty()){
+ httpUrlBuilder.addQueryParameter(kSORT_ANSWERS, StringUtils.componentsSeparatedBy(request.getAnswerSorts(), ","));
+ }
+
+ if (request.getSearchPhrase() != null) {
+ httpUrlBuilder.addQueryParameter(kSEARCH, request.getSearchPhrase());
+ }
+
+ HttpUrl httpUrl = httpUrlBuilder.build();
+
+ Headers.Builder headersBuilder = new Headers.Builder();
+ addCommonHeaders(headersBuilder, bvSdkUserAgent);
+ Headers headers = headersBuilder.build();
+
+ return okRequestBuilder
+ .url(httpUrl)
+ .headers(headers)
+ .build();
+ }
+
+ private Request createFromCommentsRequest(CommentsRequest request) {
+ Request.Builder okRequestBuilder = new Request.Builder();
+
+ HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
+ .newBuilder()
+ .addPathSegments(REVIEW_COMMENTS_ENDPOINT);
+
+ addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo);
+ addCommonDisplayQueryParams(httpUrlBuilder, request);
+ addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset());
+
+ if (!request.getIncludeTypes().isEmpty()) {
+ httpUrlBuilder.addQueryParameter(kINCLUDE, StringUtils.componentsSeparatedBy(request.getIncludeTypes(), ","));
+ }
+
+ if (!request.getIncludeTypeLimitMap().isEmpty()) {
+ for (CommentIncludeType commentIncludeType : request.getIncludeTypeLimitMap().keySet()) {
+ String formattedKey = String.format("Limit_%s", commentIncludeType.toString());
+ int limit = request.getIncludeTypeLimitMap().get(commentIncludeType);
+ String formattedValue = String.valueOf(limit);
+ httpUrlBuilder.addEncodedQueryParameter(formattedKey, formattedValue);
+ }
+ }
+
+ if (!request.getSorts().isEmpty()) {
+ httpUrlBuilder
+ .addQueryParameter(kSORT, StringUtils.componentsSeparatedBy(request.getSorts(), ","));
+ }
+
+ HttpUrl httpUrl = httpUrlBuilder.build();
+
+ Headers.Builder headersBuilder = new Headers.Builder();
+ addCommonHeaders(headersBuilder, bvSdkUserAgent);
+ Headers headers = headersBuilder.build();
+
+ return okRequestBuilder
+ .url(httpUrl)
+ .headers(headers)
+ .build();
+ }
+
+ private Request createFromAuthorsRequest(AuthorsRequest request) {
+ Request.Builder okRequestBuilder = new Request.Builder();
+
+ HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
+ .newBuilder()
+ .addPathSegments(AUTHORS_ENDPOINT);
+
+ addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo);
+ addCommonDisplayQueryParams(httpUrlBuilder, request);
+
+ if (!request.getReviewSorts().isEmpty()){
+ httpUrlBuilder.addQueryParameter(kSORT_REVIEW, StringUtils.componentsSeparatedBy(request.getReviewSorts(), ","));
+ }
+
+ if (!request.getQuestionSorts().isEmpty()){
+ httpUrlBuilder.addQueryParameter(kSORT_QUESTIONS, StringUtils.componentsSeparatedBy(request.getQuestionSorts(), ","));
+ }
+
+ if (!request.getAnswerSorts().isEmpty()){
+ httpUrlBuilder.addQueryParameter(kSORT_ANSWERS, StringUtils.componentsSeparatedBy(request.getAnswerSorts(), ","));
+ }
+
+ if (!request.getIncludes().isEmpty()) {
+ httpUrlBuilder.addQueryParameter(kINCLUDE, StringUtils.componentsSeparatedBy(request.getIncludes(), ","));
+ }
+
+ for (Include include : request.getIncludes()) {
+ httpUrlBuilder.addQueryParameter(include.getLimitParamKey(), String.valueOf(include.getLimit()));
+ }
+
+ if (!request.getStatistics().isEmpty()) {
+ httpUrlBuilder.addQueryParameter(kSTATS, StringUtils.componentsSeparatedBy(request.getStatistics(), ","));
+ }
+
+ HttpUrl httpUrl = httpUrlBuilder.build();
+
+ Headers.Builder headersBuilder = new Headers.Builder();
+ addCommonHeaders(headersBuilder, bvSdkUserAgent);
+ Headers headers = headersBuilder.build();
+
+ return okRequestBuilder
+ .url(httpUrl)
+ .headers(headers)
+ .build();
+ }
+
+ private Request createFromBulkStoreRequest(BulkStoreRequest request) {
+ Request.Builder okRequestBuilder = new Request.Builder();
+
+ HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
+ .newBuilder()
+ .addPathSegments(PRODUCTS_ENDPOINT);
+
+ addCommonQueryParams(httpUrlBuilder, storeApiKey, bvMobileInfo);
+ addCommonDisplayQueryParams(httpUrlBuilder, request);
+ addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset());
+
+ httpUrlBuilder.addQueryParameter(kSTATS, request.getStatsType().getKey());
+
+ HttpUrl httpUrl = httpUrlBuilder.build();
+
+ Headers.Builder headersBuilder = new Headers.Builder();
+ addCommonHeaders(headersBuilder, bvSdkUserAgent);
+ Headers headers = headersBuilder.build();
+
+ return okRequestBuilder
+ .url(httpUrl)
+ .headers(headers)
+ .build();
+ }
+
+ private Request createFromBulkRatingsRequest(BulkRatingsRequest request) {
+ Request.Builder okRequestBuilder = new Request.Builder();
+
+ HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
+ .newBuilder()
+ .addPathSegments(REVIEWS_ENDPOINT);
+
+ addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo);
+ addCommonDisplayQueryParams(httpUrlBuilder, request);
+
+ httpUrlBuilder.addQueryParameter(kSTATS, request.getStatsType().getKey());
+
+ HttpUrl httpUrl = httpUrlBuilder.build();
+
+ Headers.Builder headersBuilder = new Headers.Builder();
+ addCommonHeaders(headersBuilder, bvSdkUserAgent);
+ Headers headers = headersBuilder.build();
+
+ return okRequestBuilder
+ .url(httpUrl)
+ .headers(headers)
+ .build();
+ }
+
+ private Request createFromStoreReviewsRequest(StoreReviewsRequest request) {
+ Request.Builder okRequestBuilder = new Request.Builder();
+
+ HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
+ .newBuilder()
+ .addPathSegments(REVIEWS_ENDPOINT);
+
+ addCommonQueryParams(httpUrlBuilder, storeApiKey, bvMobileInfo);
+ addCommonDisplayQueryParams(httpUrlBuilder, request);
+ addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset());
+
+ if (!request.getReviewIncludeTypes().isEmpty()) {
+ if (request.getReviewIncludeTypes().contains(ReviewIncludeType.PRODUCTS)) {
+ // "Stats=Reviews must be used in conjunction with Include=Products"
+ // - https://developer.bazaarvoice.com/conversations-api/reference/v5.4/reviews/review-display#requesting-all-reviews-for-a-particular-product-with-review-statistics-(inc.-average-rating)
+ httpUrlBuilder.addQueryParameter(KEY_STATS, STATS_REVIEWS);
+ }
+ String includeStr = StringUtils.componentsSeparatedBy(request.getReviewIncludeTypes(), ",");
+ httpUrlBuilder.addQueryParameter(kINCLUDE, includeStr);
+ }
+
+ if (!request.getSorts().isEmpty()){
+ httpUrlBuilder.addQueryParameter(kSORT, StringUtils.componentsSeparatedBy(request.getSorts(), ","));
+ }
+
+ // TODO: All of these repeated query params are begging for some better composability. Composite or Decorator pattern?
+ if (request.getSearchPhrase() != null) {
+ httpUrlBuilder.addQueryParameter(kSEARCH, request.getSearchPhrase());
+ }
+
+ HttpUrl httpUrl = httpUrlBuilder.build();
+
+ Headers.Builder headersBuilder = new Headers.Builder();
+ addCommonHeaders(headersBuilder, bvSdkUserAgent);
+ Headers headers = headersBuilder.build();
+
+ return okRequestBuilder
+ .url(httpUrl)
+ .headers(headers)
+ .build();
+ }
+
+ private Request createFromBulkProductRequest(BulkProductRequest request) {
+ Request.Builder okRequestBuilder = new Request.Builder();
+
+ HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
+ .newBuilder()
+ .addPathSegments(PRODUCTS_ENDPOINT);
+
+ addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo);
+ addCommonDisplayQueryParams(httpUrlBuilder, request);
+ addSortableProductParams(httpUrlBuilder, request);
+
+ HttpUrl httpUrl = httpUrlBuilder.build();
+
+ Headers.Builder headersBuilder = new Headers.Builder();
+ addCommonHeaders(headersBuilder, bvSdkUserAgent);
+ Headers headers = headersBuilder.build();
+
+ return okRequestBuilder
+ .url(httpUrl)
+ .headers(headers)
+ .build();
+ }
+
+ private Request createFromProductDisplayPageRequest(ProductDisplayPageRequest request) {
+ Request.Builder okRequestBuilder = new Request.Builder();
+
+ HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
+ .newBuilder()
+ .addPathSegments(PRODUCTS_ENDPOINT);
+
+ addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo);
+ addCommonDisplayQueryParams(httpUrlBuilder, request);
+ addSortableProductParams(httpUrlBuilder, request);
+
+ HttpUrl httpUrl = httpUrlBuilder.build();
+
+ Headers.Builder headersBuilder = new Headers.Builder();
+ addCommonHeaders(headersBuilder, bvSdkUserAgent);
+ Headers headers = headersBuilder.build();
+
+ return okRequestBuilder
+ .url(httpUrl)
+ .headers(headers)
+ .build();
+ }
+
+ private Request createFromFeedbackSubmissionRequest(FeedbackSubmissionRequest request) {
+ Request.Builder okRequestBuilder = new Request.Builder();
+
+ HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
+ .newBuilder()
+ .addPathSegments(FEEDBACK_SUBMIT_ENDPOINT);
+
+ FormBody.Builder formBodyBuilder = new FormBody.Builder();
+
+ addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo, fingerprintProvider);
+
+ formPutSafe(formBodyBuilder, kCONTENT_ID, request.getContentId());
+ formPutSafe(formBodyBuilder, kCONTENT_TYPE, request.getContentType());
+ formPutSafe(formBodyBuilder, kFEEDBACK_TYPE, request.getFeedbackType());
+ formPutSafe(formBodyBuilder, kVOTE, request.getFeedbackVote());
+ formPutSafe(formBodyBuilder, kREASON_TEXT, request.getReasonFlaggedText());
+
+ RequestBody requestBody = formBodyBuilder.build();
+ HttpUrl httpUrl = httpUrlBuilder.build();
+
+ Headers.Builder headersBuilder = new Headers.Builder();
+ addCommonHeaders(headersBuilder, bvSdkUserAgent);
+ Headers headers = headersBuilder.build();
+
+ return okRequestBuilder
+ .url(httpUrl)
+ .headers(headers)
+ .post(requestBody)
+ .build();
+ }
+
+ private Request createFromReviewSubmissionRequest(ReviewSubmissionRequest request) {
+ Request.Builder okRequestBuilder = new Request.Builder();
+
+ HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
+ .newBuilder()
+ .addPathSegments(REVIEW_SUBMIT_ENDPOINT);
+
+ FormBody.Builder formBodyBuilder = new FormBody.Builder();
+
+ addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo, fingerprintProvider);
+ addCommonReviewSubmissionFormParams(formBodyBuilder, request);
+
+ RequestBody requestBody = formBodyBuilder.build();
+ HttpUrl httpUrl = httpUrlBuilder.build();
+
+ Headers.Builder headersBuilder = new Headers.Builder();
+ addCommonHeaders(headersBuilder, bvSdkUserAgent);
+ Headers headers = headersBuilder.build();
+
+ return okRequestBuilder
+ .url(httpUrl)
+ .headers(headers)
+ .post(requestBody)
+ .build();
+ }
+
+ private Request createFromStoreReviewSubmissionRequest(StoreReviewSubmissionRequest request) {
+ Request.Builder okRequestBuilder = new Request.Builder();
+
+ HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
+ .newBuilder()
+ .addPathSegments(REVIEW_SUBMIT_ENDPOINT);
+
+ FormBody.Builder formBodyBuilder = new FormBody.Builder();
+
+ addCommonSubmissionFormParams(formBodyBuilder, request, storeApiKey, bvMobileInfo, fingerprintProvider);
+ addCommonReviewSubmissionFormParams(formBodyBuilder, request);
+
+ RequestBody requestBody = formBodyBuilder.build();
+ HttpUrl httpUrl = httpUrlBuilder.build();
+
+ Headers.Builder headersBuilder = new Headers.Builder();
+ addCommonHeaders(headersBuilder, bvSdkUserAgent);
+ Headers headers = headersBuilder.build();
+
+ return okRequestBuilder
+ .url(httpUrl)
+ .headers(headers)
+ .post(requestBody)
+ .build();
+ }
+
+ private Request createFromQuestionSubmissionRequest(QuestionSubmissionRequest request) {
+ Request.Builder okRequestBuilder = new Request.Builder();
+
+ HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
+ .newBuilder()
+ .addPathSegments(QUESTION_SUBMIT_ENDPOINT);
+
+ FormBody.Builder formBodyBuilder = new FormBody.Builder();
+
+ addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo, fingerprintProvider);
+ formPutSafe(formBodyBuilder, kPRODUCT_ID, request.getProductId());
+ formPutSafe(formBodyBuilder, kQUESTION_SUMMARY, request.getQuestionSummary());
+ formPutSafe(formBodyBuilder, kQUESTION_DETAILS, request.getQuestionDetails());
+ formPutSafe(formBodyBuilder, kIS_ANONUSER, request.getUserAnonymous());
+ formPutSafe(formBodyBuilder, kSEND_EMAIL_ANSWERED, request.getSendEmailAlertWhenAnswered());
+
+ RequestBody requestBody = formBodyBuilder.build();
+ HttpUrl httpUrl = httpUrlBuilder.build();
+
+ Headers.Builder headersBuilder = new Headers.Builder();
+ addCommonHeaders(headersBuilder, bvSdkUserAgent);
+ Headers headers = headersBuilder.build();
+
+ return okRequestBuilder
+ .url(httpUrl)
+ .headers(headers)
+ .post(requestBody)
+ .build();
+ }
+
+ private Request createFromAnswerSubmissionRequest(AnswerSubmissionRequest request) {
+ Request.Builder okRequestBuilder = new Request.Builder();
+
+ HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
+ .newBuilder()
+ .addPathSegments(ANSWER_SUBMIT_ENDPOINT);
+
+ FormBody.Builder formBodyBuilder = new FormBody.Builder();
+
+ addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo, fingerprintProvider);
+ formPutSafe(formBodyBuilder, kQUESTIONID, request.getQuestionId());
+ formPutSafe(formBodyBuilder, kANSWERTEXT, request.getAnswerText());
+
+ RequestBody requestBody = formBodyBuilder.build();
+ HttpUrl httpUrl = httpUrlBuilder.build();
+
+ Headers.Builder headersBuilder = new Headers.Builder();
+ addCommonHeaders(headersBuilder, bvSdkUserAgent);
+ Headers headers = headersBuilder.build();
+
+ return okRequestBuilder
+ .url(httpUrl)
+ .headers(headers)
+ .post(requestBody)
+ .build();
+ }
+
+ private Request createFromCommentSubmissionRequest(CommentSubmissionRequest request) {
+ Request.Builder okRequestBuilder = new Request.Builder();
+
+ HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
+ .newBuilder()
+ .addPathSegments(COMMENT_SUBMIT_ENDPOINT);
+
+ FormBody.Builder formBodyBuilder = new FormBody.Builder();
+
+ addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo, fingerprintProvider);
+ formPutSafe(formBodyBuilder, KEY_REVIEW_ID, request.getReviewId());
+ formPutSafe(formBodyBuilder, KEY_COMMENT_TEXT, request.getCommentText());
+ formPutSafe(formBodyBuilder, KEY_TITLE, request.getTitle());
+
+ RequestBody requestBody = formBodyBuilder.build();
+ HttpUrl httpUrl = httpUrlBuilder.build();
+
+ Headers.Builder headersBuilder = new Headers.Builder();
+ addCommonHeaders(headersBuilder, bvSdkUserAgent);
+ Headers headers = headersBuilder.build();
+
+ return okRequestBuilder
+ .url(httpUrl)
+ .headers(headers)
+ .post(requestBody)
+ .build();
+ }
+
+ private Request createFromPhotoUploadRequest(PhotoUploadRequest request) {
+ Request.Builder okRequestBuilder = new Request.Builder();
+
+ HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
+ .newBuilder()
+ .addPathSegments(PHOTO_SUBMIT_ENDPOINT);
+
+ MultipartBody.Builder multiPartBuilder = new MultipartBody.Builder();
+
+ PhotoUpload upload = request.getPhotoUpload();
+
+ multiPartBuilder
+ .setType(MultipartBody.FORM)
+ .addFormDataPart(ConversationsRequest.kAPI_VERSION, API_VERSION)
+ .addFormDataPart(ConversationsRequest.kPASS_KEY, convApiKey)
+ .addFormDataPart(PhotoUpload.kCONTENT_TYPE, upload.getContentType().getKey())
+ .addFormDataPart("photo", "photo.png", RequestBody.create(MEDIA_TYPE_PNG, upload.getPhotoFile()));
+
+ RequestBody requestBody = multiPartBuilder.build();
+ HttpUrl httpUrl = httpUrlBuilder.build();
+
+ Headers.Builder headersBuilder = new Headers.Builder();
+ addCommonHeaders(headersBuilder, bvSdkUserAgent);
+ Headers headers = headersBuilder.build();
+
+ return okRequestBuilder
+ .url(httpUrl)
+ .headers(headers)
+ .post(requestBody)
+ .build();
+ }
+ // endregion
+
+ // region Static Helpers
+ private static void addCommonQueryParams(HttpUrl.Builder httpUrlBuilder, String apiKey, BVMobileInfo bvMobileInfo) {
+ httpUrlBuilder.addQueryParameter(kAPI_VERSION, API_VERSION)
+ .addQueryParameter(kPASS_KEY, apiKey)
+ .addQueryParameter(kAPP_ID, bvMobileInfo.getMobileAppIdentifier())
+ .addQueryParameter(kAPP_VERSION, bvMobileInfo.getMobileAppVersion())
+ .addQueryParameter(kBUILD_NUM, bvMobileInfo.getMobileAppCode())
+ .addQueryParameter(kSDK_VERSION, bvMobileInfo.getBvSdkVersion());
+ }
+
+ private static void addCommonPagingQueryParams(HttpUrl.Builder httpUrlBuilder, int limit, int offset) {
+ httpUrlBuilder.addQueryParameter(kLIMIT, String.valueOf(limit));
+ httpUrlBuilder.addQueryParameter(kOFFSET, String.valueOf(offset));
+ }
+
+ private static void addCommonDisplayQueryParams(HttpUrl.Builder httpUrlBuilder, ConversationsDisplayRequest request) {
+ addFilterQueryParams(httpUrlBuilder, request.getFilters());
+ addExtraQueryParams(httpUrlBuilder, request.getExtraParams());
+ }
+
+ private static void addFilterQueryParams(HttpUrl.Builder httpUrlBuilder, List filters) {
+ for (Filter filter : filters) {
+ httpUrlBuilder
+ .addEncodedQueryParameter(kFILTER, filter.toString());
+ }
+ }
+
+ private static void addExtraQueryParams(HttpUrl.Builder httpUrlBuilder, List extraParams) {
+ for (ConversationsDisplayRequest.QueryPair queryPair : extraParams) {
+ if (queryPair.getKey() == null || queryPair.getValue() == null) {
+ continue;
+ }
+ httpUrlBuilder
+ .addQueryParameter(queryPair.getKey(), queryPair.getValue());
+ }
+ }
+
+ private static void addCommonHeaders(Headers.Builder headersBuilder, String bvSdkUserAgent) {
+ headersBuilder.add(KEY_USER_AGENT, bvSdkUserAgent);
+ }
+
+ private static void addSortableProductParams(HttpUrl.Builder httpUrlBuilder, SortableProductRequest request) {
+ if (!request.getReviewSorts().isEmpty()){
+ httpUrlBuilder.addQueryParameter(kSORT_REVIEW, StringUtils.componentsSeparatedBy(request.getReviewSorts(), ","));
+ }
+
+ if (!request.getQuestionSorts().isEmpty()){
+ httpUrlBuilder.addQueryParameter(kSORT_QUESTIONS, StringUtils.componentsSeparatedBy(request.getQuestionSorts(), ","));
+ }
+
+ if (!request.getAnswerSorts().isEmpty()){
+ httpUrlBuilder.addQueryParameter(kSORT_ANSWERS, StringUtils.componentsSeparatedBy(request.getAnswerSorts(), ","));
+ }
+
+ if (!request.getIncludes().isEmpty()) {
+ httpUrlBuilder.addQueryParameter(kINCLUDE, StringUtils.componentsSeparatedBy(request.getIncludes(), ","));
+ }
+
+ for (Include include : request.getIncludes()) {
+ httpUrlBuilder.addQueryParameter(include.getLimitParamKey(), String.valueOf(include.getLimit()));
+ }
+
+ if (!request.getStatistics().isEmpty()) {
+ httpUrlBuilder.addQueryParameter(kSTATS, StringUtils.componentsSeparatedBy(request.getStatistics(), ","));
+ }
+ }
+
+ private static void formPutSafe(FormBody.Builder formBodyBuilder, @NonNull String key, @Nullable Object value) {
+ if (value != null && !value.toString().isEmpty()) {
+ String valueStr = String.valueOf(value);
+ formBodyBuilder.add(key, valueStr);
+ }
+ }
+
+ private static void addCommonSubmissionFormParams(FormBody.Builder formBodyBuilder, ConversationsSubmissionRequest request, String apiKey, BVMobileInfo bvMobileInfo, FingerprintProvider fingerprintProvider) {
+ formPutSafe(formBodyBuilder, kAPI_VERSION, API_VERSION);
+ formPutSafe(formBodyBuilder, kPASS_KEY, apiKey);
+ formPutSafe(formBodyBuilder, kAPP_ID, bvMobileInfo.getMobileAppIdentifier());
+ formPutSafe(formBodyBuilder, kAPP_VERSION, bvMobileInfo.getMobileAppVersion());
+ formPutSafe(formBodyBuilder, kBUILD_NUM, bvMobileInfo.getMobileAppCode());
+ formPutSafe(formBodyBuilder, kSDK_VERSION, bvMobileInfo.getBvSdkVersion());
+ formPutSafe(formBodyBuilder, kCAMPAIGN_ID, request.getCampaignId());
+ formPutSafe(formBodyBuilder, kFINGER_PRINT, fingerprintProvider.getFingerprint());
+ formPutSafe(formBodyBuilder, kHOSTED_AUTH_EMAIL, request.getHostedAuthenticationEmail());
+ formPutSafe(formBodyBuilder, kHOST_AUTH_CALLBACK, request.getHostedAuthenticationCallback());
+ formPutSafe(formBodyBuilder, kLOCALE, request.getLocale());
+ formPutSafe(formBodyBuilder, kUSER, request.getUser());
+ formPutSafe(formBodyBuilder, kUSER_EMAIL, request.getUserEmail());
+ formPutSafe(formBodyBuilder, kUSER_ID, request.getUserId());
+ formPutSafe(formBodyBuilder, kUSER_LOCATION, request.getUserLocation());
+ formPutSafe(formBodyBuilder, kUSER_NICKNAME, request.getUserNickname());
+ formPutSafe(formBodyBuilder, kSEND_EMAIL_PUBLISHED, request.getSendEmailAlertWhenPublished());
+ formPutSafe(formBodyBuilder, kAGREE_TERMS, request.getAgreedToTermsAndConditions());
+ formPutSafe(formBodyBuilder, kACTION, getAction(request).getKey());
+
+ addSubmissionPhotosFormParams(formBodyBuilder, request);
+ }
+
+ private static void addSubmissionPhotosFormParams(FormBody.Builder formBodyBuilder, ConversationsSubmissionRequest request) {
+ final List photos = request.getPhotos();
+ if (photos != null) {
+ int idx = 0;
+ for (Photo photo : photos) {
+ final String keyPhotoUrl = String.format(Locale.US, KEY_PHOTO_URL_TEMPLATE, idx);
+ final String keyPhotoCaption = String.format(Locale.US, KEY_PHOTO_CAPTION_TEMPLATE, idx);
+ formPutSafe(formBodyBuilder, keyPhotoUrl, photo.getContent().getNormalUrl());
+ formPutSafe(formBodyBuilder, keyPhotoCaption, photo.getCaption());
+ idx++;
+ }
+ }
+ }
+
+ private static void addCommonReviewSubmissionFormParams(FormBody.Builder formBodyBuilder, BaseReviewSubmissionRequest request) {
+ formPutSafe(formBodyBuilder, kPRODUCT_ID, request.getProductId());
+ formPutSafe(formBodyBuilder, kIS_RECOMMENDED, request.getRecommended());
+ formPutSafe(formBodyBuilder, kSEND_EMAIL_COMMENTED, request.getSendEmailAlertWhenCommented());
+ formPutSafe(formBodyBuilder, kRATING, request.getRating());
+ formPutSafe(formBodyBuilder, kNET_PROMOTER_SCORE, request.getNetPromoterScore());
+ formPutSafe(formBodyBuilder, kNET_PROMOTER_COMMENT, request.getNetPromoterComment());
+ formPutSafe(formBodyBuilder, kTITLE, request.getTitle());
+ formPutSafe(formBodyBuilder, kREVIEW_TEXT, request.getReviewText());
+
+ final List predefinedTags = request.getPredefinedTags();
+ for (int i=0; i freeFormTags = request.getFreeFormTags();
+ final List freeFormTagKeys = new ArrayList<>(freeFormTags.keySet());
+ for (int i=0; i ratingSliderKeys = request.getRatingSliders().keySet();
+ for (String key : ratingSliderKeys) {
+ final String keyRating = String.format(Locale.US, KEY_RATING_TEMPLATE, key);
+ formPutSafe(formBodyBuilder, keyRating, request.getRatingSliders().get(key));
+ }
+
+ final Set ratingQuestionsKeys = request.getRatingQuestions().keySet();
+ for (String key : ratingQuestionsKeys) {
+ final String keyRating = String.format(Locale.US, KEY_RATING_TEMPLATE, key);
+ formPutSafe(formBodyBuilder, keyRating, request.getRatingQuestions().get(key));
+ }
+
+ final Set contextDataValueKeys = request.getContextDataValues().keySet();
+ for (String key : contextDataValueKeys) {
+ final String keyRating = String.format(Locale.US, KEY_CDV_TEMPLATE, key);
+ formPutSafe(formBodyBuilder, keyRating, request.getContextDataValues().get(key));
+ }
+
+ final Map additionalFields = request.getAdditionalFields();
+ for (String key : additionalFields.keySet()) {
+ final String value = additionalFields.get(key);
+ final String keyAdditionalField = String.format(Locale.US, KEY_ADDITIONAL_PARAM_TEMPLATE, key);
+ formPutSafe(formBodyBuilder, keyAdditionalField, value);
+ }
+
+ final List videoDataList = request.getVideoSubmissionData();
+ for (int i=0; i {
- final Call call;
+ Call call;
final Class responseTypeClass;
- final OkHttpClient okHttpClient = BVSDK.getInstance().getBvWorkerData().getOkHttpClient();
- final Gson gson = BVSDK.getInstance().getBvWorkerData().getGson();
+ final OkHttpClient okHttpClient;
+ final Gson gson;
final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
- LoadCall(Class responseTypeClass, Call call) {
- this.call = call;
+ LoadCall(Class responseTypeClass, OkHttpClient okHttpClient, Gson gson) {
this.responseTypeClass = responseTypeClass;
+ this.okHttpClient = okHttpClient;
+ this.gson = gson;
}
/**
@@ -60,7 +61,9 @@ abstract class LoadCall conversationsCallback);
public void cancel() {
- call.cancel();
+ if (call != null && !call.isCanceled()) {
+ call.cancel();
+ }
}
ResponseType deserializeAndCloseResponse(Response response) throws BazaarException {
@@ -70,7 +73,6 @@ ResponseType deserializeAndCloseResponse(Response response) throws BazaarExcepti
try {
Reader jsonReader = response.body().charStream();
conversationResponse = (ResponseType) gson.fromJson(jsonReader, responseTypeClass);
-
} catch (JsonSyntaxException | JsonIOException e) {
error = new BazaarException("Unable to parse JSON " + response);
} finally {
diff --git a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/LoadCallDisplay.java b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/LoadCallDisplay.java
index e7cd0ec9..33daf20c 100644
--- a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/LoadCallDisplay.java
+++ b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/LoadCallDisplay.java
@@ -17,10 +17,13 @@
package com.bazaarvoice.bvandroidsdk;
+import com.google.gson.Gson;
+
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
+import okhttp3.OkHttpClient;
import okhttp3.Response;
import static com.bazaarvoice.bvandroidsdk.internal.Utils.checkMain;
@@ -85,8 +88,9 @@ public void onResponse(Call call, Response response) throws IOException {
}
}
- LoadCallDisplay(RequestType request, Class responseTypeClass, Call call, ConversationsAnalyticsManager conversationsAnalyticsManager) {
- super(responseTypeClass, call);
+ LoadCallDisplay(RequestType request, Class responseTypeClass, Call call, ConversationsAnalyticsManager conversationsAnalyticsManager, OkHttpClient okHttpClient, Gson gson) {
+ super(responseTypeClass, okHttpClient, gson);
+ this.call = call;
this.request = request;
this.conversationsAnalyticsManager = conversationsAnalyticsManager;
}
diff --git a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/LoadCallSubmission.java b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/LoadCallSubmission.java
index f86f314d..f340b916 100644
--- a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/LoadCallSubmission.java
+++ b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/LoadCallSubmission.java
@@ -17,20 +17,27 @@
package com.bazaarvoice.bvandroidsdk;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.support.annotation.AnyThread;
+import android.support.annotation.MainThread;
+import android.support.annotation.WorkerThread;
+
+import com.google.gson.Gson;
+
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import okhttp3.Call;
-import okhttp3.Callback;
-import okhttp3.MultipartBody;
+import okhttp3.OkHttpClient;
import okhttp3.Request;
-import okhttp3.RequestBody;
import okhttp3.Response;
import static android.util.Log.d;
+import static com.bazaarvoice.bvandroidsdk.internal.Utils.checkNotMain;
/**
* Wrapper for a {@link Call} for a Conversations request to
@@ -43,129 +50,153 @@ public final class LoadCallSubmission implements Callback {
- private final ConversationsCallback conversationsCallback;
- private final LoadCallSubmission loadCallSubmission;
- private final RequestType submissionRequest;
- private final ConversationsAnalyticsManager conversationsAnalyticsManager;
- private final Class responseTypeClass;
- private final RequestFactory requestFactory;
- private final String apiKey;
-
- public SubmissionDelegateCallback(final ConversationsCallback conversationsCallback, final LoadCallSubmission loadCallDisplay, final RequestType submissionRequest, final Class responseTypeClass, ConversationsAnalyticsManager conversationsAnalyticsManager, RequestFactory requestFactory, String apiKey) {
- this.conversationsCallback = conversationsCallback;
- this.loadCallSubmission = loadCallDisplay;
- this.submissionRequest = submissionRequest;
- this.responseTypeClass = responseTypeClass;
- this.conversationsAnalyticsManager = conversationsAnalyticsManager;
- this.requestFactory = requestFactory;
- this.apiKey = apiKey;
- }
-
- @Override
- public void onFailure(Call call, IOException e) {
- BazaarException bazaarException = new BazaarException("Submission Request Failed", e);
- loadCallSubmission.errorOnMainThread(conversationsCallback, bazaarException);
- }
-
- @Override
- public void onResponse(Call call, Response response) throws IOException {
- ConversationsResponse conversationResponse = null;
- BazaarException error = null;
- try {
- if (!response.isSuccessful()) {
- error = new BazaarException("Unsuccessful response for Conversations with error code: " + response.code());
- } else {
- try {
- conversationResponse = loadCallSubmission.deserializeAndCloseResponse(response);
- } catch (BazaarException t) {
- error = t;
- }
- }
-
- boolean isPreview = submissionRequest.getAction() == Action.Preview;
- if (error != null) {
- loadCallSubmission.errorOnMainThread(conversationsCallback, error);
- } else {
- //if User intended to only Preview or if a Submit has already been previewed we are done and can callback
- boolean readyToDeliverResult = isPreview || !submissionRequest.getForcePreview();
- if (readyToDeliverResult) {
- conversationsAnalyticsManager.sendSuccessfulConversationsSubmitResponse(submissionRequest);
- loadCallSubmission.successOnMainThread(conversationsCallback, conversationResponse);
- }
- //We know that a Submit was succesfully previewed so now we Submit for real
- else {
- LoadCall newCall = BVConversationsClient.reCreateCallNoPreview(responseTypeClass, submissionRequest, requestFactory, conversationsAnalyticsManager, apiKey);
- newCall.loadAsync(conversationsCallback);
- }
- }
- } finally {
- if (response != null) {
- response.body().close();
- }
- }
- }
- }
-
- LoadCallSubmission(RequestType submissionRequest, Class responseTypeClass, Call call, ConversationsAnalyticsManager conversationsAnalyticsManager, RequestFactory requestFactory, String apiKey) {
- super(responseTypeClass, call);
+ private final SubmitUiHandler submitUiHandler;
+ private final SubmitWorkerHandler submitWorkerHandler;
+ private ConversationsCallback submitCallback;
+
+ LoadCallSubmission(
+ RequestType submissionRequest,
+ Class responseTypeClass,
+ ConversationsAnalyticsManager conversationsAnalyticsManager,
+ RequestFactory requestFactory,
+ Looper bgLooper,
+ Looper uiLooper,
+ OkHttpClient okHttpClient,
+ Gson gson) {
+ super(responseTypeClass, okHttpClient, gson);
this.submissionRequest = submissionRequest;
this.conversationsAnalyticsManager = conversationsAnalyticsManager;
this.requestFactory = requestFactory;
- this.apiKey = apiKey;
+ this.submitUiHandler = new SubmitUiHandler<>(uiLooper, this);
+ this.submitWorkerHandler = new SubmitWorkerHandler<>(bgLooper, this);
}
+ @WorkerThread
@Override
public ResponseType loadSync() throws BazaarException {
- ConversationsResponse conversationResponse = null;
+ checkNotMain();
+ return submitFlow(true);
+ }
+
+ /**
+ * @deprecated To be replaced
+ *
+ * This is a container for the old load async behavior which is not ideal
+ * in that it doesn't route 100% of errors to the error block, therefore
+ * requiring complicated logic to know when success has happened. This is
+ * being maintained to avoid breaking 6.x clients, but will be replaced by
+ * the more expected {@link #loadSync()} behavior which routes all errors
+ * to the error block.
+ *
+ * @return Response with expected type
+ * @throws BazaarException
+ */
+ private ResponseType legacyLoadAsyncBehavior() throws BazaarException {
+ return submitFlow(false);
+ }
+
+ /**
+ * Handles the logic of Preview vs Submit, as well as Photo Submission.
+ *
+ * If it is Preview mode, then it will simply run, and return. This can be seen
+ * as a Form preview request.
+ *
+ * If it is Submit mode, then it will first run in Preview mode to validate
+ * the form data. If the form data is invalid it will return or throw depending
+ * on {@code routeFormErrorsToFailure}. If the form data is valid, then it will
+ * submit each of the photos, collecting the metadata associated with each. If any
+ * photo submission failed, then an exception will be thrown. If all photo submit
+ * requests succeeded, it will add any photo metadata to the request, and submit
+ * in Submit mode.
+ *
+ * @param routeFormErrorsToFailure Behavior that each {@link #loadSync()} and {@link #loadAsync(ConversationsCallback)} were
+ * doing differently. Will go away in a future release, and all errors will route to failure.
+ * @return Server JSON response mapped to the corresponding POJO type
+ * @throws BazaarException Exception if the request failed to happen, or if the JSON deserialization failed
+ */
+ private ResponseType submitFlow(boolean routeFormErrorsToFailure) throws BazaarException {
BazaarException error = submissionRequest.getError();
if (error != null) {
+ // If request contains known errors
throw error;
}
- try {
- List photoUploads = submissionRequest.getBuilder().photoUploads;
- if (photoUploads != null && photoUploads.size() > 0 && !submissionRequest.getForcePreview() && submissionRequest.getAction() == Action.Submit) {
+ boolean isPreview = submissionRequest.getAction() == Action.Preview;
+ if (isPreview) {
+ // If the user chose preview, simply submit request
+ return submit();
+ } else {
+ // Send preview request for user incase we need to see error values
+ submissionRequest.setForcePreview(true);
+ ResponseType previewResponse = submit();
+ if (previewResponse.getHasErrors()) {
+ if (routeFormErrorsToFailure) {
+ throw new BazaarException(gson.toJson(previewResponse.getErrors()));
+ } else {
+ return previewResponse;
+ }
+ }
- return postPhotosAndSubmissionSync(photoUploads, submissionRequest);
+ // If it is a valid request, follow through with a full submit
+ if (submissionRequest.getPhotoUploads() != null && submissionRequest.getPhotoUploads().size() > 0) {
+ // If the user wants to submit photos, submit each of them, collect the metadata,
+ // and associate it with the submission request
+ List photos = postPhotosAndSubmissionSync(submissionRequest.getPhotoUploads());
+ submissionRequest.setPhotos(photos);
+ }
+ // Toggle back to no be force preview anymore
+ submissionRequest.setForcePreview(false);
+ return submit();
+ }
+ }
+ /**
+ * @return Server JSON response mapped to the corresponding POJO type
+ * @throws BazaarException Exception if the request failed to happen, or if the JSON deserialization failed
+ */
+ private ResponseType submit() throws BazaarException {
+ ResponseType conversationResponse = null;
+ BazaarException error = null;
+ final Request okRequest = requestFactory.create(submissionRequest);
+ call = okHttpClient.newCall(okRequest);
+ Response response = null;
+ try {
+ response = call.execute();
+ if (!response.isSuccessful()) {
+ error = new BazaarException("Unsuccessful response for Conversations with error code: " + response.code());
} else {
- Response response = null;
try {
- response = call.execute();
conversationResponse = deserializeAndCloseResponse(response);
- if (conversationResponse.getHasErrors()) {
- throw new BazaarException(gson.toJson(conversationResponse.getErrors()));
- }
- if (submissionRequest.getForcePreview()) {
- LoadCall loadCall = BVConversationsClient.reCreateCallNoPreview(responseTypeClass, submissionRequest, requestFactory, conversationsAnalyticsManager, apiKey);
- ResponseType finalResponse = (ResponseType) loadCall.loadSync();
- conversationsAnalyticsManager.sendSuccessfulConversationsSubmitResponse(submissionRequest);
- return finalResponse;
- }
- } finally {
- if (response != null) {
- response.body().close();
- }
+ } catch (BazaarException t) {
+ error = t;
}
}
- } catch (Throwable t) {
- throw new BazaarException(t.getMessage());
+ if (error != null) {
+ throw error;
+ } else {
+ conversationsAnalyticsManager.sendSuccessfulConversationsSubmitResponse(submissionRequest);
+ return conversationResponse;
+ }
+ } catch (IOException e) {
+ throw new BazaarException("Network request failed", e);
+ } finally {
+ if (response != null) {
+ response.body().close();
+ }
}
- return (ResponseType) conversationResponse;
}
- private ResponseType postPhotosAndSubmissionSync(List photoUploads, ConversationsSubmissionRequest submission) throws BazaarException {
+ private List postPhotosAndSubmissionSync(List photoUploads) throws BazaarException {
d("Submission", String.format("Preparing to submit %d photos", photoUploads.size()));
final List photos = new ArrayList<>();
try {
for (PhotoUpload upload : photoUploads) {
- Call photoCall = makePhotoCall(upload, submission);
+ final PhotoUploadRequest uploadRequest = new PhotoUploadRequest.Builder(upload).build();
+ final Request okRequest = requestFactory.create(uploadRequest);
+ Call photoCall = okHttpClient.newCall(okRequest);
Response response = null;
try {
response = photoCall.execute();
@@ -182,48 +213,7 @@ private ResponseType postPhotosAndSubmissionSync(List photoUploads,
throw new BazaarException(e.getMessage());
}
- LoadCall loadCall = BVConversationsClient.reCreateCallWithPhotos(responseTypeClass, submission, photos, requestFactory, conversationsAnalyticsManager, apiKey);
- return (ResponseType) loadCall.loadSync();
- }
-
- private void postPhotosAndSubmissionAsync(final ConversationsCallback conversationsCallback, final List photoUploads, final ConversationsSubmissionRequest submission) {
- final BVLogger bvLogger = BVSDK.getInstance().getBvLogger();
- bvLogger.d("Submission", String.format("Preparing to submit %d photos", photoUploads.size()));
- final List photos = Collections.synchronizedList(new ArrayList());
-
- for (final PhotoUpload upload : photoUploads) {
- Call photoCall = makePhotoCall(upload, submission);
- //async upload all photos
- photoCall.enqueue(new Callback() {
- @Override
- public void onFailure(Call call, IOException e) {
- errorOnMainThread(conversationsCallback, new BazaarException(e.getMessage()));
- }
-
- @Override
- public void onResponse(Call call, Response response) throws IOException {
- try {
- Photo photo = deserializePhotoResponse(response);
- photo.setCaption(upload.getCaption());
- photos.add(photo);
- conversationsAnalyticsManager.sendSuccessfulConversationsPhotoUpload(submission);
- //whenever we have received successful responses for every expect photo upload we can
- // reconstruct the submissionRequest request with the photo upload response details
- if (photos.size() == photoUploads.size()) {
- LoadCall loadCall = BVConversationsClient.reCreateCallWithPhotos(responseTypeClass, submission, photos, requestFactory, conversationsAnalyticsManager, apiKey);
- loadCall.loadAsync(conversationsCallback);
- }
- } catch (BazaarException e) {
- errorOnMainThread(conversationsCallback, e);
- } finally {
- if (response != null) {
- response.body().close();
- }
- }
- }
- });
- }
-
+ return photos;
}
private Photo deserializePhotoResponse(Response response) throws BazaarException {
@@ -238,43 +228,97 @@ private Photo deserializePhotoResponse(Response response) throws BazaarException
@Override
public void loadAsync(final ConversationsCallback conversationsCallback) {
- BazaarException error = submissionRequest.getError();
+ submitCallback = conversationsCallback;
+ dispatchSubmit();
+ }
- if (error != null) {
- errorOnMainThread(conversationsCallback, error);
- return;
+ private static class SubmitUiHandler extends Handler {
+ private static final int CB_SUCCESS = 1;
+ private static final int CB_FAILURE = 2;
+
+ private final LoadCallSubmission loadCallSubmission;
+
+ public SubmitUiHandler(Looper looper, LoadCallSubmission loadCallSubmission) {
+ super(looper);
+ this.loadCallSubmission = loadCallSubmission;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case CB_SUCCESS: {
+ ResponseType response = (ResponseType) msg.obj;
+ loadCallSubmission.completeWithSuccess(response);
+ break;
+ }
+ case CB_FAILURE: {
+ BazaarException bazaarException = (BazaarException) msg.obj;
+ loadCallSubmission.completeWithFailure(bazaarException);
+ break;
+ }
+ }
}
+ }
- List photoUploads = submissionRequest.getBuilder().photoUploads;
- //At this point we assume know that since we are submitting and not being forced to preview that it has been previewed already.
- // The request is ready to have its photos uploaded then submitted.
- if (photoUploads != null && photoUploads.size() > 0 && !submissionRequest.getForcePreview() && submissionRequest.getAction() == Action.Submit) {
+ private static class SubmitWorkerHandler extends Handler {
+ private static final int SUBMIT = 1;
+ private final LoadCallSubmission loadCallSubmission;
- postPhotosAndSubmissionAsync(conversationsCallback, photoUploads, submissionRequest);
+ public SubmitWorkerHandler(Looper looper, LoadCallSubmission loadCallSubmission) {
+ super(looper);
+ this.loadCallSubmission = loadCallSubmission;
+ }
- } else {
- //At this point we know that the submissionRequest needs to be previewed
- //Either by user asking for preview or being forced to preview before Submit
- //Or it is ready for full submissionRequest (it's had it's photo's uploaded or never had any)
- this.call.enqueue(new SubmissionDelegateCallback<>(conversationsCallback, this, submissionRequest, responseTypeClass, conversationsAnalyticsManager, requestFactory, apiKey));
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case SUBMIT: {
+ try {
+ ResponseType response = loadCallSubmission.legacyLoadAsyncBehavior();
+ loadCallSubmission.dispatchCompleteWithSuccess(response);
+ } catch (BazaarException e) {
+ loadCallSubmission.dispatchCompleteWithFailure(e);
+ }
+ break;
+ }
+ }
}
}
- // TODO: Clean up the photo submission process to be it's own internal Request object that we send synchronously on our BVSDK HandlerThread
- private Call makePhotoCall(PhotoUpload upload, ConversationsSubmissionRequest conversationsSubmitRequest) {
- String fullUrl = String.format("%s%s", BVConversationsClient.conversationsBaseUrl, upload.getEndPoint());
- RequestBody req = new MultipartBody.Builder().setType(MultipartBody.FORM)
- .addFormDataPart(ConversationsRequest.kAPI_VERSION, ConversationsRequest.API_VERSION)
- .addFormDataPart(ConversationsRequest.kPASS_KEY, apiKey)
- .addFormDataPart(PhotoUpload.kCONTENT_TYPE, upload.getContentType().getKey())
- .addFormDataPart("photo", "photo.png", RequestBody.create(MEDIA_TYPE_PNG, upload.getPhotoFile())).build();
-
- Request request = new Request.Builder()
- .addHeader("User-Agent", BVSDK.getInstance().getBvWorkerData().getBvSdkUserAgent())
- .url(fullUrl)
- .post(req)
- .build();
-
- return okHttpClient.newCall(request);
+ @AnyThread
+ private void dispatchSubmit() {
+ submitWorkerHandler.sendMessage(submitWorkerHandler.obtainMessage(SubmitWorkerHandler.SUBMIT));
+ }
+
+ @WorkerThread
+ private void dispatchCompleteWithFailure(BazaarException e) {
+ submitUiHandler.sendMessage(submitUiHandler.obtainMessage(SubmitUiHandler.CB_FAILURE, e));
+ }
+
+ @WorkerThread
+ private void dispatchCompleteWithSuccess(ResponseType response) {
+ submitUiHandler.sendMessage(submitUiHandler.obtainMessage(SubmitUiHandler.CB_SUCCESS, response));
+ }
+
+ @MainThread
+ private void completeWithFailure(BazaarException e) {
+ if (submitCallback != null) {
+ submitCallback.onFailure(e);
+ submitCallback = null;
+ }
+ }
+
+ @MainThread
+ private void completeWithSuccess(ResponseType response) {
+ if (submitCallback != null) {
+ submitCallback.onSuccess(response);
+ submitCallback = null;
+ }
+ }
+
+ @Override
+ public void cancel() {
+ super.cancel();
+ submitCallback = null;
}
}
diff --git a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/PhotoUploadRequest.java b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/PhotoUploadRequest.java
new file mode 100644
index 00000000..19e3ab0d
--- /dev/null
+++ b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/PhotoUploadRequest.java
@@ -0,0 +1,33 @@
+package com.bazaarvoice.bvandroidsdk;
+
+/**
+ * Internal class for uploading photos associate with CGC submission
+ */
+class PhotoUploadRequest extends ConversationsRequest {
+ private final PhotoUpload photoUpload;
+
+ private PhotoUploadRequest(Builder builder) {
+ photoUpload = builder.photoUpload;
+ }
+
+ @Override
+ BazaarException getError() {
+ return null;
+ }
+
+ public PhotoUpload getPhotoUpload() {
+ return photoUpload;
+ }
+
+ public static class Builder {
+ private final PhotoUpload photoUpload;
+
+ public Builder(PhotoUpload photoUpload) {
+ this.photoUpload = photoUpload;
+ }
+
+ public PhotoUploadRequest build() {
+ return new PhotoUploadRequest(this);
+ }
+ }
+}
diff --git a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/RequestFactory.java b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/RequestFactory.java
index 03a6a290..4ef2ea80 100644
--- a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/RequestFactory.java
+++ b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/RequestFactory.java
@@ -1,858 +1,7 @@
package com.bazaarvoice.bvandroidsdk;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-import okhttp3.FormBody;
-import okhttp3.Headers;
-import okhttp3.HttpUrl;
import okhttp3.Request;
-import okhttp3.RequestBody;
-
-/**
- * Converts user created Request Objects into OkHttp Request Objects.
- * Each user created Request Object will only have the information that
- * the user entered. This Factory will manage adding anything else
- * required including Headers and Query Parameters, as well as taking
- * user inputted data, and formatting it correctly such as POST body
- * media content.
- */
-class RequestFactory {
- // region Generic Request Keys
- private static final String kAPI_VERSION = "apiversion";
- private static final String kPASS_KEY = "passkey";
- private static final String kAPP_ID = "_appId";
- private static final String kAPP_VERSION = "_appVersion";
- private static final String kBUILD_NUM = "_buildNumber";
- private static final String kSDK_VERSION = "_bvAndroidSdkVersion";
- private static final String API_VERSION = "5.4";
- private static final String KEY_USER_AGENT = "User-Agent";
- // endregion
-
- // region Generic Display Request Keys
- private static final String kFILTER = "Filter";
- private static final String kSORT= "Sort";
- private static final String kSORT_REVIEW = "Sort_Reviews";
- private static final String kSORT_QUESTIONS = "Sort_Questions";
- private static final String kSORT_ANSWERS= "Sort_Answers";
- private static final String kINCLUDE = "Include";
- private static final String kSTATS = "Stats";
- private static final String kLIMIT = "Limit";
- private static final String kOFFSET = "Offset";
- private static final String kSEARCH = "Search";
- // endregion
-
- // region Display Reviews Request Keys
- private static final String INCLUDE_ANSWERS = "Answers";
- private static final String REVIEWS_ENDPOINT = "data/reviews.json";
- private static final String KEY_STATS = "Stats";
- private static final String STATS_REVIEWS = "Reviews";
- // endregion
-
- // region Display Q&A Request Keys
- private static final String QUESTIONS_AND_ANSWERS_ENDPOINT = "data/questions.json";
- // endregion
-
- // region Display Comments Request Keys
- private static final String REVIEW_COMMENTS_ENDPOINT = "data/reviewcomments.json";
- // endregion
-
- // region Display Authors Request Keys
- private static final String AUTHORS_ENDPOINT = "data/authors.json";
- // endregion
-
- // region Display Products Request Keys
- private static final String PRODUCTS_ENDPOINT = "data/products.json";
- // endregion
-
- // region Generic Submit Request Keys
- private static final String kCAMPAIGN_ID = "campaignid";
- private static final String kFINGER_PRINT = "fp";
- private static final String kHOSTED_AUTH_EMAIL = "hostedauthentication_authenticationemail";
- private static final String kHOST_AUTH_CALLBACK = "hostedauthentication_callbackurl";
- private static final String kLOCALE = "locale";
- private static final String kUSER = "User";
- private static final String kUSER_EMAIL = "UserEmail";
- private static final String kUSER_LOCATION = "UserLocation";
- private static final String kUSER_NICKNAME = "UserNickname";
- private static final String kSEND_EMAIL_PUBLISHED = "sendemailalertwhenpublished";
- private static final String kAGREE_TERMS = "agreedToTermsAndConditions";
- private static final String kACTION = "action";
- private static final String KEY_PHOTO_URL_TEMPLATE = "photourl_%d";
- private static final String KEY_PHOTO_CAPTION_TEMPLATE = "photocaption_%d";
- private static final String kPRODUCT_ID = "ProductId";
- // endregion
-
- // region Submit Feedback Request Keys
- private static final String FEEDBACK_SUBMIT_ENDPOINT = "data/submitfeedback.json";
- private static final String kCONTENT_ID = "ContentId";
- private static final String kCONTENT_TYPE = "ContentType";
- private static final String kFEEDBACK_TYPE = "FeedbackType";
- private static final String kUSER_ID = "UserId";
- private static final String kVOTE = "Vote";
- private static final String kREASON_TEXT = "ReasonText";
- // endregion
-
- // region Submit Review Request Keys
- private static final String REVIEW_SUBMIT_ENDPOINT = "data/submitreview.json";
- private static final String KEY_ADDITIONAL_PARAM_TEMPLATE = "additionalfield_%s";
- private static final String kIS_RECOMMENDED = "IsRecommended";
- private static final String kSEND_EMAIL_COMMENTED = "SendEmailAlertWhenCommented";
- private static final String kRATING = "Rating";
- private static final String kNET_PROMOTER_SCORE = "NetPromoterScore";
- private static final String kTITLE = "Title";
- private static final String kREVIEW_TEXT = "ReviewText";
- private static final String kNET_PROMOTER_COMMENT = "NetPromoterComment";
- private static final String VIDEO_URL_TEMPLATE = "VideoUrl_%d";
- private static final String VIDEO_CAPTION_TEMPLATE = "VideoCaption_%d";
- private static final String KEY_RATING_TEMPLATE = "rating_%s";
- private static final String KEY_CDV_TEMPLATE = "contextdatavalue_%s";
- private static final String KEY_FREEFORM_TAG_TEMPLATE = "tag_%s_%d";
- // endregion
-
- // region Submit Question Request Keys
- private static final String QUESTION_SUBMIT_ENDPOINT = "data/submitquestion.json";
- private static final String kQUESTION_SUMMARY = "QuestionSummary";
- private static final String kQUESTION_DETAILS = "QuestionDetails";
- private static final String kIS_ANONUSER = "IsUserAnonymous";
- private static final String kSEND_EMAIL_ANSWERED = "SendEmailAlertWhenAnswered";
- // endregion
-
- // region Submit Answer Request Keys
- private static final String ANSWER_SUBMIT_ENDPOINT = "data/submitanswer.json";
- private static final String kQUESTIONID = "QuestionId";
- private static final String kANSWERTEXT = "AnswerText";
- // endregion
-
- // region Submit Comment Request Keys
- private static final String COMMENT_SUBMIT_ENDPOINT = "data/submitreviewcomment.json";
- private static final String KEY_REVIEW_ID = "ReviewId";
- private static final String KEY_COMMENT_TEXT = "CommentText";
- private static final String KEY_TITLE = "Title";
- // endregion
-
- // region Instance Fields
- private final BVMobileInfo bvMobileInfo;
- private final String bvRootApiUrl;
- private final String convApiKey;
- private final String storeApiKey;
- private final String bvSdkUserAgent;
- // endregion
-
- // region Constructor
- RequestFactory(BVMobileInfo bvMobileInfo, BVRootApiUrls bvRootApiUrls, BVConfig bvConfig, String bvSdkUserAgent) {
- this.bvMobileInfo = bvMobileInfo;
- this.bvRootApiUrl = bvRootApiUrls.getBazaarvoiceApiRootUrl();
- this.convApiKey = bvConfig.getApiKeyConversations();
- this.storeApiKey = bvConfig.getApiKeyConversationsStores();
- this.bvSdkUserAgent = bvSdkUserAgent;
- }
- // endregion
-
- // region API
- Request create(RequestType request) {
- if (request instanceof ReviewsRequest) {
- return createFromReviewRequest((ReviewsRequest) request);
- } else if (request instanceof QuestionAndAnswerRequest) {
- return createFromQuestionAndAnswerRequest((QuestionAndAnswerRequest) request);
- } else if (request instanceof CommentsRequest) {
- return createFromCommentsRequest((CommentsRequest) request);
- } else if (request instanceof AuthorsRequest) {
- return createFromAuthorsRequest((AuthorsRequest) request);
- } else if (request instanceof BulkStoreRequest) {
- return createFromBulkStoreRequest((BulkStoreRequest) request);
- } else if (request instanceof BulkRatingsRequest) {
- return createFromBulkRatingsRequest((BulkRatingsRequest) request);
- } else if (request instanceof StoreReviewsRequest) {
- return createFromStoreReviewsRequest((StoreReviewsRequest) request);
- } else if (request instanceof BulkProductRequest) {
- return createFromBulkProductRequest((BulkProductRequest) request);
- } else if (request instanceof ProductDisplayPageRequest) {
- return createFromProductDisplayPageRequest((ProductDisplayPageRequest) request);
- } else if (request instanceof FeedbackSubmissionRequest) {
- return createFromFeedbackSubmissionRequest((FeedbackSubmissionRequest) request);
- } else if (request instanceof ReviewSubmissionRequest) {
- return createFromReviewSubmissionRequest((ReviewSubmissionRequest) request);
- } else if (request instanceof StoreReviewSubmissionRequest) {
- return createFromStoreReviewSubmissionRequest((StoreReviewSubmissionRequest) request);
- } else if (request instanceof QuestionSubmissionRequest) {
- return createFromQuestionSubmissionRequest((QuestionSubmissionRequest) request);
- } else if (request instanceof AnswerSubmissionRequest) {
- return createFromAnswerSubmissionRequest((AnswerSubmissionRequest) request);
- } else if (request instanceof CommentSubmissionRequest) {
- return createFromCommentSubmissionRequest((CommentSubmissionRequest) request);
- }
- throw new IllegalStateException("Unknown request type: " + request.getClass().getCanonicalName());
- }
-
- // endregion
-
- // region Mapping Implementation
- private Request createFromReviewRequest(ReviewsRequest request) {
- Request.Builder okRequestBuilder = new Request.Builder();
-
- HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
- .newBuilder()
- .addPathSegments(REVIEWS_ENDPOINT);
-
- addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo);
- addCommonDisplayQueryParams(httpUrlBuilder, request);
- addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset());
-
- if (!request.getReviewIncludeTypes().isEmpty()) {
- if (request.getReviewIncludeTypes().contains(ReviewIncludeType.PRODUCTS)) {
- // "Stats=Reviews must be used in conjunction with Include=Products"
- // - https://developer.bazaarvoice.com/conversations-api/reference/v5.4/reviews/review-display#requesting-all-reviews-for-a-particular-product-with-review-statistics-(inc.-average-rating)
- httpUrlBuilder.addQueryParameter(KEY_STATS, STATS_REVIEWS);
- }
- String includeStr = StringUtils.componentsSeparatedBy(request.getReviewIncludeTypes(), ",");
- httpUrlBuilder.addQueryParameter(kINCLUDE, includeStr);
- }
-
- if (!request.getSorts().isEmpty()) {
- httpUrlBuilder
- .addQueryParameter(kSORT, StringUtils.componentsSeparatedBy(request.getSorts(), ","));
- }
-
- if (request.getSearchPhrase() != null) {
- httpUrlBuilder
- .addQueryParameter(kSEARCH, request.getSearchPhrase());
- }
-
- HttpUrl httpUrl = httpUrlBuilder.build();
-
- Headers.Builder headersBuilder = new Headers.Builder();
- addCommonHeaders(headersBuilder, bvSdkUserAgent);
- Headers headers = headersBuilder.build();
-
- return okRequestBuilder
- .url(httpUrl)
- .headers(headers)
- .build();
- }
-
- private Request createFromQuestionAndAnswerRequest(QuestionAndAnswerRequest request) {
- Request.Builder okRequestBuilder = new Request.Builder();
-
- HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
- .newBuilder()
- .addPathSegments(QUESTIONS_AND_ANSWERS_ENDPOINT);
-
- addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo);
- addCommonDisplayQueryParams(httpUrlBuilder, request);
- addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset());
-
- httpUrlBuilder.addQueryParameter(kINCLUDE, INCLUDE_ANSWERS);
-
- if (!request.getQuestionSorts().isEmpty()){
- httpUrlBuilder.addQueryParameter(kSORT, StringUtils.componentsSeparatedBy(request.getQuestionSorts(), ","));
- }
-
- if (!request.getAnswerSorts().isEmpty()){
- httpUrlBuilder.addQueryParameter(kSORT_ANSWERS, StringUtils.componentsSeparatedBy(request.getAnswerSorts(), ","));
- }
-
- if (request.getSearchPhrase() != null) {
- httpUrlBuilder.addQueryParameter(kSEARCH, request.getSearchPhrase());
- }
-
- HttpUrl httpUrl = httpUrlBuilder.build();
-
- Headers.Builder headersBuilder = new Headers.Builder();
- addCommonHeaders(headersBuilder, bvSdkUserAgent);
- Headers headers = headersBuilder.build();
-
- return okRequestBuilder
- .url(httpUrl)
- .headers(headers)
- .build();
- }
-
- private Request createFromCommentsRequest(CommentsRequest request) {
- Request.Builder okRequestBuilder = new Request.Builder();
-
- HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
- .newBuilder()
- .addPathSegments(REVIEW_COMMENTS_ENDPOINT);
-
- addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo);
- addCommonDisplayQueryParams(httpUrlBuilder, request);
- addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset());
-
- if (!request.getIncludeTypes().isEmpty()) {
- httpUrlBuilder.addQueryParameter(kINCLUDE, StringUtils.componentsSeparatedBy(request.getIncludeTypes(), ","));
- }
-
- if (!request.getIncludeTypeLimitMap().isEmpty()) {
- for (CommentIncludeType commentIncludeType : request.getIncludeTypeLimitMap().keySet()) {
- String formattedKey = String.format("Limit_%s", commentIncludeType.toString());
- int limit = request.getIncludeTypeLimitMap().get(commentIncludeType);
- String formattedValue = String.valueOf(limit);
- httpUrlBuilder.addEncodedQueryParameter(formattedKey, formattedValue);
- }
- }
-
- if (!request.getSorts().isEmpty()) {
- httpUrlBuilder
- .addQueryParameter(kSORT, StringUtils.componentsSeparatedBy(request.getSorts(), ","));
- }
-
- HttpUrl httpUrl = httpUrlBuilder.build();
-
- Headers.Builder headersBuilder = new Headers.Builder();
- addCommonHeaders(headersBuilder, bvSdkUserAgent);
- Headers headers = headersBuilder.build();
-
- return okRequestBuilder
- .url(httpUrl)
- .headers(headers)
- .build();
- }
-
- private Request createFromAuthorsRequest(AuthorsRequest request) {
- Request.Builder okRequestBuilder = new Request.Builder();
-
- HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
- .newBuilder()
- .addPathSegments(AUTHORS_ENDPOINT);
-
- addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo);
- addCommonDisplayQueryParams(httpUrlBuilder, request);
-
- if (!request.getReviewSorts().isEmpty()){
- httpUrlBuilder.addQueryParameter(kSORT_REVIEW, StringUtils.componentsSeparatedBy(request.getReviewSorts(), ","));
- }
-
- if (!request.getQuestionSorts().isEmpty()){
- httpUrlBuilder.addQueryParameter(kSORT_QUESTIONS, StringUtils.componentsSeparatedBy(request.getQuestionSorts(), ","));
- }
-
- if (!request.getAnswerSorts().isEmpty()){
- httpUrlBuilder.addQueryParameter(kSORT_ANSWERS, StringUtils.componentsSeparatedBy(request.getAnswerSorts(), ","));
- }
-
- if (!request.getIncludes().isEmpty()) {
- httpUrlBuilder.addQueryParameter(kINCLUDE, StringUtils.componentsSeparatedBy(request.getIncludes(), ","));
- }
-
- for (Include include : request.getIncludes()) {
- httpUrlBuilder.addQueryParameter(include.getLimitParamKey(), String.valueOf(include.getLimit()));
- }
-
- if (!request.getStatistics().isEmpty()) {
- httpUrlBuilder.addQueryParameter(kSTATS, StringUtils.componentsSeparatedBy(request.getStatistics(), ","));
- }
-
- HttpUrl httpUrl = httpUrlBuilder.build();
-
- Headers.Builder headersBuilder = new Headers.Builder();
- addCommonHeaders(headersBuilder, bvSdkUserAgent);
- Headers headers = headersBuilder.build();
-
- return okRequestBuilder
- .url(httpUrl)
- .headers(headers)
- .build();
- }
-
- private Request createFromBulkStoreRequest(BulkStoreRequest request) {
- Request.Builder okRequestBuilder = new Request.Builder();
-
- HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
- .newBuilder()
- .addPathSegments(PRODUCTS_ENDPOINT);
-
- addCommonQueryParams(httpUrlBuilder, storeApiKey, bvMobileInfo);
- addCommonDisplayQueryParams(httpUrlBuilder, request);
- addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset());
-
- httpUrlBuilder.addQueryParameter(kSTATS, request.getStatsType().getKey());
-
- HttpUrl httpUrl = httpUrlBuilder.build();
-
- Headers.Builder headersBuilder = new Headers.Builder();
- addCommonHeaders(headersBuilder, bvSdkUserAgent);
- Headers headers = headersBuilder.build();
-
- return okRequestBuilder
- .url(httpUrl)
- .headers(headers)
- .build();
- }
-
- private Request createFromBulkRatingsRequest(BulkRatingsRequest request) {
- Request.Builder okRequestBuilder = new Request.Builder();
-
- HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
- .newBuilder()
- .addPathSegments(REVIEWS_ENDPOINT);
-
- addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo);
- addCommonDisplayQueryParams(httpUrlBuilder, request);
-
- httpUrlBuilder.addQueryParameter(kSTATS, request.getStatsType().getKey());
-
- HttpUrl httpUrl = httpUrlBuilder.build();
-
- Headers.Builder headersBuilder = new Headers.Builder();
- addCommonHeaders(headersBuilder, bvSdkUserAgent);
- Headers headers = headersBuilder.build();
-
- return okRequestBuilder
- .url(httpUrl)
- .headers(headers)
- .build();
- }
-
- private Request createFromStoreReviewsRequest(StoreReviewsRequest request) {
- Request.Builder okRequestBuilder = new Request.Builder();
-
- HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
- .newBuilder()
- .addPathSegments(REVIEWS_ENDPOINT);
-
- addCommonQueryParams(httpUrlBuilder, storeApiKey, bvMobileInfo);
- addCommonDisplayQueryParams(httpUrlBuilder, request);
- addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset());
-
- if (!request.getReviewIncludeTypes().isEmpty()) {
- if (request.getReviewIncludeTypes().contains(ReviewIncludeType.PRODUCTS)) {
- // "Stats=Reviews must be used in conjunction with Include=Products"
- // - https://developer.bazaarvoice.com/conversations-api/reference/v5.4/reviews/review-display#requesting-all-reviews-for-a-particular-product-with-review-statistics-(inc.-average-rating)
- httpUrlBuilder.addQueryParameter(KEY_STATS, STATS_REVIEWS);
- }
- String includeStr = StringUtils.componentsSeparatedBy(request.getReviewIncludeTypes(), ",");
- httpUrlBuilder.addQueryParameter(kINCLUDE, includeStr);
- }
-
- if (!request.getSorts().isEmpty()){
- httpUrlBuilder.addQueryParameter(kSORT, StringUtils.componentsSeparatedBy(request.getSorts(), ","));
- }
-
- // TODO: All of these repeated query params are begging for some better composability. Composite or Decorator pattern?
- if (request.getSearchPhrase() != null) {
- httpUrlBuilder.addQueryParameter(kSEARCH, request.getSearchPhrase());
- }
-
- HttpUrl httpUrl = httpUrlBuilder.build();
-
- Headers.Builder headersBuilder = new Headers.Builder();
- addCommonHeaders(headersBuilder, bvSdkUserAgent);
- Headers headers = headersBuilder.build();
-
- return okRequestBuilder
- .url(httpUrl)
- .headers(headers)
- .build();
- }
-
- private Request createFromBulkProductRequest(BulkProductRequest request) {
- Request.Builder okRequestBuilder = new Request.Builder();
-
- HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
- .newBuilder()
- .addPathSegments(PRODUCTS_ENDPOINT);
-
- addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo);
- addCommonDisplayQueryParams(httpUrlBuilder, request);
- addSortableProductParams(httpUrlBuilder, request);
-
- HttpUrl httpUrl = httpUrlBuilder.build();
-
- Headers.Builder headersBuilder = new Headers.Builder();
- addCommonHeaders(headersBuilder, bvSdkUserAgent);
- Headers headers = headersBuilder.build();
-
- return okRequestBuilder
- .url(httpUrl)
- .headers(headers)
- .build();
- }
-
- private Request createFromProductDisplayPageRequest(ProductDisplayPageRequest request) {
- Request.Builder okRequestBuilder = new Request.Builder();
-
- HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
- .newBuilder()
- .addPathSegments(PRODUCTS_ENDPOINT);
-
- addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo);
- addCommonDisplayQueryParams(httpUrlBuilder, request);
- addSortableProductParams(httpUrlBuilder, request);
-
- HttpUrl httpUrl = httpUrlBuilder.build();
-
- Headers.Builder headersBuilder = new Headers.Builder();
- addCommonHeaders(headersBuilder, bvSdkUserAgent);
- Headers headers = headersBuilder.build();
-
- return okRequestBuilder
- .url(httpUrl)
- .headers(headers)
- .build();
- }
-
- private Request createFromFeedbackSubmissionRequest(FeedbackSubmissionRequest request) {
- Request.Builder okRequestBuilder = new Request.Builder();
-
- HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
- .newBuilder()
- .addPathSegments(FEEDBACK_SUBMIT_ENDPOINT);
-
- FormBody.Builder formBodyBuilder = new FormBody.Builder();
-
- addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo);
-
- formPutSafe(formBodyBuilder, kCONTENT_ID, request.getContentId());
- formPutSafe(formBodyBuilder, kCONTENT_TYPE, request.getContentType());
- formPutSafe(formBodyBuilder, kFEEDBACK_TYPE, request.getFeedbackType());
- formPutSafe(formBodyBuilder, kVOTE, request.getFeedbackVote());
- formPutSafe(formBodyBuilder, kREASON_TEXT, request.getReasonFlaggedText());
-
- RequestBody requestBody = formBodyBuilder.build();
- HttpUrl httpUrl = httpUrlBuilder.build();
-
- Headers.Builder headersBuilder = new Headers.Builder();
- addCommonHeaders(headersBuilder, bvSdkUserAgent);
- Headers headers = headersBuilder.build();
-
- return okRequestBuilder
- .url(httpUrl)
- .headers(headers)
- .post(requestBody)
- .build();
- }
-
- private Request createFromReviewSubmissionRequest(ReviewSubmissionRequest request) {
- Request.Builder okRequestBuilder = new Request.Builder();
-
- HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
- .newBuilder()
- .addPathSegments(REVIEW_SUBMIT_ENDPOINT);
-
- FormBody.Builder formBodyBuilder = new FormBody.Builder();
-
- addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo);
- addCommonReviewSubmissionFormParams(formBodyBuilder, request);
-
- RequestBody requestBody = formBodyBuilder.build();
- HttpUrl httpUrl = httpUrlBuilder.build();
-
- Headers.Builder headersBuilder = new Headers.Builder();
- addCommonHeaders(headersBuilder, bvSdkUserAgent);
- Headers headers = headersBuilder.build();
-
- return okRequestBuilder
- .url(httpUrl)
- .headers(headers)
- .post(requestBody)
- .build();
- }
-
- private Request createFromStoreReviewSubmissionRequest(StoreReviewSubmissionRequest request) {
- Request.Builder okRequestBuilder = new Request.Builder();
-
- HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
- .newBuilder()
- .addPathSegments(REVIEW_SUBMIT_ENDPOINT);
-
- FormBody.Builder formBodyBuilder = new FormBody.Builder();
-
- addCommonSubmissionFormParams(formBodyBuilder, request, storeApiKey, bvMobileInfo);
- addCommonReviewSubmissionFormParams(formBodyBuilder, request);
-
- RequestBody requestBody = formBodyBuilder.build();
- HttpUrl httpUrl = httpUrlBuilder.build();
-
- Headers.Builder headersBuilder = new Headers.Builder();
- addCommonHeaders(headersBuilder, bvSdkUserAgent);
- Headers headers = headersBuilder.build();
-
- return okRequestBuilder
- .url(httpUrl)
- .headers(headers)
- .post(requestBody)
- .build();
- }
-
- private Request createFromQuestionSubmissionRequest(QuestionSubmissionRequest request) {
- Request.Builder okRequestBuilder = new Request.Builder();
-
- HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
- .newBuilder()
- .addPathSegments(QUESTION_SUBMIT_ENDPOINT);
-
- FormBody.Builder formBodyBuilder = new FormBody.Builder();
-
- addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo);
- formPutSafe(formBodyBuilder, kPRODUCT_ID, request.getProductId());
- formPutSafe(formBodyBuilder, kQUESTION_SUMMARY, request.getQuestionSummary());
- formPutSafe(formBodyBuilder, kQUESTION_DETAILS, request.getQuestionDetails());
- formPutSafe(formBodyBuilder, kIS_ANONUSER, request.getUserAnonymous());
- formPutSafe(formBodyBuilder, kSEND_EMAIL_ANSWERED, request.getSendEmailAlertWhenAnswered());
-
- RequestBody requestBody = formBodyBuilder.build();
- HttpUrl httpUrl = httpUrlBuilder.build();
-
- Headers.Builder headersBuilder = new Headers.Builder();
- addCommonHeaders(headersBuilder, bvSdkUserAgent);
- Headers headers = headersBuilder.build();
-
- return okRequestBuilder
- .url(httpUrl)
- .headers(headers)
- .post(requestBody)
- .build();
- }
-
- private Request createFromAnswerSubmissionRequest(AnswerSubmissionRequest request) {
- Request.Builder okRequestBuilder = new Request.Builder();
-
- HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
- .newBuilder()
- .addPathSegments(ANSWER_SUBMIT_ENDPOINT);
-
- FormBody.Builder formBodyBuilder = new FormBody.Builder();
-
- addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo);
- formPutSafe(formBodyBuilder, kQUESTIONID, request.getQuestionId());
- formPutSafe(formBodyBuilder, kANSWERTEXT, request.getAnswerText());
-
- RequestBody requestBody = formBodyBuilder.build();
- HttpUrl httpUrl = httpUrlBuilder.build();
-
- Headers.Builder headersBuilder = new Headers.Builder();
- addCommonHeaders(headersBuilder, bvSdkUserAgent);
- Headers headers = headersBuilder.build();
-
- return okRequestBuilder
- .url(httpUrl)
- .headers(headers)
- .post(requestBody)
- .build();
- }
-
- private Request createFromCommentSubmissionRequest(CommentSubmissionRequest request) {
- Request.Builder okRequestBuilder = new Request.Builder();
-
- HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl)
- .newBuilder()
- .addPathSegments(COMMENT_SUBMIT_ENDPOINT);
-
- FormBody.Builder formBodyBuilder = new FormBody.Builder();
-
- addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo);
- formPutSafe(formBodyBuilder, KEY_REVIEW_ID, request.getReviewId());
- formPutSafe(formBodyBuilder, KEY_COMMENT_TEXT, request.getCommentText());
- formPutSafe(formBodyBuilder, KEY_TITLE, request.getTitle());
-
- RequestBody requestBody = formBodyBuilder.build();
- HttpUrl httpUrl = httpUrlBuilder.build();
-
- Headers.Builder headersBuilder = new Headers.Builder();
- addCommonHeaders(headersBuilder, bvSdkUserAgent);
- Headers headers = headersBuilder.build();
-
- return okRequestBuilder
- .url(httpUrl)
- .headers(headers)
- .post(requestBody)
- .build();
- }
- // endregion
-
- // region Static Helpers
- private static void addCommonQueryParams(HttpUrl.Builder httpUrlBuilder, String apiKey, BVMobileInfo bvMobileInfo) {
- httpUrlBuilder.addQueryParameter(kAPI_VERSION, API_VERSION)
- .addQueryParameter(kPASS_KEY, apiKey)
- .addQueryParameter(kAPP_ID, bvMobileInfo.getMobileAppIdentifier())
- .addQueryParameter(kAPP_VERSION, bvMobileInfo.getMobileAppVersion())
- .addQueryParameter(kBUILD_NUM, bvMobileInfo.getMobileAppCode())
- .addQueryParameter(kSDK_VERSION, bvMobileInfo.getBvSdkVersion());
- }
-
- private static void addCommonPagingQueryParams(HttpUrl.Builder httpUrlBuilder, int limit, int offset) {
- httpUrlBuilder.addQueryParameter(kLIMIT, String.valueOf(limit));
- httpUrlBuilder.addQueryParameter(kOFFSET, String.valueOf(offset));
- }
-
- private static void addCommonDisplayQueryParams(HttpUrl.Builder httpUrlBuilder, ConversationsDisplayRequest request) {
- addFilterQueryParams(httpUrlBuilder, request.getFilters());
- addExtraQueryParams(httpUrlBuilder, request.getExtraParams());
- }
-
- private static void addFilterQueryParams(HttpUrl.Builder httpUrlBuilder, List filters) {
- for (Filter filter : filters) {
- httpUrlBuilder
- .addEncodedQueryParameter(kFILTER, filter.toString());
- }
- }
-
- private static void addExtraQueryParams(HttpUrl.Builder httpUrlBuilder, List extraParams) {
- for (ConversationsDisplayRequest.QueryPair queryPair : extraParams) {
- if (queryPair.getKey() == null || queryPair.getValue() == null) {
- continue;
- }
- httpUrlBuilder
- .addQueryParameter(queryPair.getKey(), queryPair.getValue());
- }
- }
-
- private static void addCommonHeaders(Headers.Builder headersBuilder, String bvSdkUserAgent) {
- headersBuilder.add(KEY_USER_AGENT, bvSdkUserAgent);
- }
-
- private static void addSortableProductParams(HttpUrl.Builder httpUrlBuilder, SortableProductRequest request) {
- if (!request.getReviewSorts().isEmpty()){
- httpUrlBuilder.addQueryParameter(kSORT_REVIEW, StringUtils.componentsSeparatedBy(request.getReviewSorts(), ","));
- }
-
- if (!request.getQuestionSorts().isEmpty()){
- httpUrlBuilder.addQueryParameter(kSORT_QUESTIONS, StringUtils.componentsSeparatedBy(request.getQuestionSorts(), ","));
- }
-
- if (!request.getAnswerSorts().isEmpty()){
- httpUrlBuilder.addQueryParameter(kSORT_ANSWERS, StringUtils.componentsSeparatedBy(request.getAnswerSorts(), ","));
- }
-
- if (!request.getIncludes().isEmpty()) {
- httpUrlBuilder.addQueryParameter(kINCLUDE, StringUtils.componentsSeparatedBy(request.getIncludes(), ","));
- }
-
- for (Include include : request.getIncludes()) {
- httpUrlBuilder.addQueryParameter(include.getLimitParamKey(), String.valueOf(include.getLimit()));
- }
-
- if (!request.getStatistics().isEmpty()) {
- httpUrlBuilder.addQueryParameter(kSTATS, StringUtils.componentsSeparatedBy(request.getStatistics(), ","));
- }
- }
-
- private static void formPutSafe(FormBody.Builder formBodyBuilder, @NonNull String key, @Nullable Object value) {
- if (value != null) {
- String valueStr = String.valueOf(value);
- formBodyBuilder.add(key, valueStr);
- }
- }
-
- private static void addCommonSubmissionFormParams(FormBody.Builder formBodyBuilder, ConversationsSubmissionRequest request, String apiKey, BVMobileInfo bvMobileInfo) {
- formPutSafe(formBodyBuilder, kAPI_VERSION, API_VERSION);
- formPutSafe(formBodyBuilder, kPASS_KEY, apiKey);
- formPutSafe(formBodyBuilder, kAPP_ID, bvMobileInfo.getMobileAppIdentifier());
- formPutSafe(formBodyBuilder, kAPP_VERSION, bvMobileInfo.getMobileAppVersion());
- formPutSafe(formBodyBuilder, kBUILD_NUM, bvMobileInfo.getMobileAppCode());
- formPutSafe(formBodyBuilder, kSDK_VERSION, bvMobileInfo.getBvSdkVersion());
- formPutSafe(formBodyBuilder, kCAMPAIGN_ID, request.getCampaignId());
- formPutSafe(formBodyBuilder, kFINGER_PRINT, request.getFingerPrint());
- formPutSafe(formBodyBuilder, kHOSTED_AUTH_EMAIL, request.getHostedAuthenticationEmail());
- formPutSafe(formBodyBuilder, kHOST_AUTH_CALLBACK, request.getHostedAuthenticationCallback());
- formPutSafe(formBodyBuilder, kLOCALE, request.getLocale());
- formPutSafe(formBodyBuilder, kUSER, request.getUser());
- formPutSafe(formBodyBuilder, kUSER_EMAIL, request.getUserEmail());
- formPutSafe(formBodyBuilder, kUSER_ID, request.getUserId());
- formPutSafe(formBodyBuilder, kUSER_LOCATION, request.getUserLocation());
- formPutSafe(formBodyBuilder, kUSER_NICKNAME, request.getUserNickname());
- formPutSafe(formBodyBuilder, kSEND_EMAIL_PUBLISHED, request.getSendEmailAlertWhenPublished());
- formPutSafe(formBodyBuilder, kAGREE_TERMS, request.getAgreedToTermsAndConditions());
- formPutSafe(formBodyBuilder, kACTION, getAction(request).getKey());
-
- addSubmissionPhotosFormParams(formBodyBuilder, request);
- }
-
- private static void addSubmissionPhotosFormParams(FormBody.Builder formBodyBuilder, ConversationsSubmissionRequest request) {
- final List photos = request.getPhotos();
- if (photos != null) {
- int idx = 0;
- for (Photo photo : photos) {
- final String keyPhotoUrl = String.format(Locale.US, KEY_PHOTO_URL_TEMPLATE, idx);
- final String keyPhotoCaption = String.format(Locale.US, KEY_PHOTO_CAPTION_TEMPLATE, idx);
- formPutSafe(formBodyBuilder, keyPhotoUrl, photo.getContent().getNormalUrl());
- formPutSafe(formBodyBuilder, keyPhotoCaption, photo.getCaption());
- idx++;
- }
- }
- }
-
- private static void addCommonReviewSubmissionFormParams(FormBody.Builder formBodyBuilder, BaseReviewSubmissionRequest request) {
- formPutSafe(formBodyBuilder, kPRODUCT_ID, request.getProductId());
- formPutSafe(formBodyBuilder, kIS_RECOMMENDED, request.getRecommended());
- formPutSafe(formBodyBuilder, kSEND_EMAIL_COMMENTED, request.getSendEmailAlertWhenCommented());
- formPutSafe(formBodyBuilder, kRATING, request.getRating());
- formPutSafe(formBodyBuilder, kNET_PROMOTER_SCORE, request.getNetPromoterScore());
- formPutSafe(formBodyBuilder, kNET_PROMOTER_COMMENT, request.getNetPromoterComment());
- formPutSafe(formBodyBuilder, kTITLE, request.getTitle());
- formPutSafe(formBodyBuilder, kREVIEW_TEXT, request.getReviewText());
-
- final List predefinedTags = request.getPredefinedTags();
- for (int i=0; i freeFormTags = request.getFreeFormTags();
- final List freeFormTagKeys = new ArrayList<>(freeFormTags.keySet());
- for (int i=0; i ratingSliderKeys = request.getRatingSliders().keySet();
- for (String key : ratingSliderKeys) {
- final String keyRating = String.format(Locale.US, KEY_RATING_TEMPLATE, key);
- formPutSafe(formBodyBuilder, keyRating, request.getRatingSliders().get(key));
- }
-
- final Set ratingQuestionsKeys = request.getRatingQuestions().keySet();
- for (String key : ratingQuestionsKeys) {
- final String keyRating = String.format(Locale.US, KEY_RATING_TEMPLATE, key);
- formPutSafe(formBodyBuilder, keyRating, request.getRatingQuestions().get(key));
- }
-
- final Set contextDataValueKeys = request.getContextDataValues().keySet();
- for (String key : contextDataValueKeys) {
- final String keyRating = String.format(Locale.US, KEY_CDV_TEMPLATE, key);
- formPutSafe(formBodyBuilder, keyRating, request.getContextDataValues().get(key));
- }
-
- final Map additionalFields = request.getAdditionalFields();
- for (String key : additionalFields.keySet()) {
- final String value = additionalFields.get(key);
- final String keyAdditionalField = String.format(Locale.US, KEY_ADDITIONAL_PARAM_TEMPLATE, key);
- formPutSafe(formBodyBuilder, keyAdditionalField, value);
- }
-
- final List videoDataList = request.getVideoSubmissionData();
- for (int i=0; i Request create(RequestType request);
}
diff --git a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/ReviewSubmissionRequest.java b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/ReviewSubmissionRequest.java
index 632d0590..b2ea9d3a 100644
--- a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/ReviewSubmissionRequest.java
+++ b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/ReviewSubmissionRequest.java
@@ -25,6 +25,12 @@ public class ReviewSubmissionRequest extends BaseReviewSubmissionRequest {
super(builder);
}
+ public ReviewSubmissionRequest.Builder newBuilder() {
+ ReviewSubmissionRequest.Builder builder = new ReviewSubmissionRequest.Builder(getAction(), getProductId());
+
+ return builder;
+ }
+
public static class Builder extends BaseReviewBuilder {
public Builder(Action action, String productId) {
super(action, productId);
diff --git a/bvconversations/src/res/values/strings.xml b/bvconversations/src/res/values/strings.xml
deleted file mode 100644
index a048f379..00000000
--- a/bvconversations/src/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- BVSDK Conversations
-
\ No newline at end of file
diff --git a/bvconversations/src/test/java/com/bazaarvoice/bvandroidsdk/RequestFactoryTest.java b/bvconversations/src/test/java/com/bazaarvoice/bvandroidsdk/BasicRequestFactoryTest.java
similarity index 96%
rename from bvconversations/src/test/java/com/bazaarvoice/bvandroidsdk/RequestFactoryTest.java
rename to bvconversations/src/test/java/com/bazaarvoice/bvandroidsdk/BasicRequestFactoryTest.java
index c4808c94..f8a111a9 100644
--- a/bvconversations/src/test/java/com/bazaarvoice/bvandroidsdk/RequestFactoryTest.java
+++ b/bvconversations/src/test/java/com/bazaarvoice/bvandroidsdk/BasicRequestFactoryTest.java
@@ -25,8 +25,8 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-public class RequestFactoryTest {
- private RequestFactory requestFactory;
+public class BasicRequestFactoryTest {
+ private BasicRequestFactory requestFactory;
private GenericFormBodyParams genericFormBodyParams;
@Before
@@ -351,6 +351,12 @@ public void reviewSubmissionShouldHaveOwnFormParams() throws Exception {
assertFormBodyContainsKeyValEncoded(okRequest, "VideoCaption_1", "Totes%20cool%20100%25%21");
}
+ @Test
+ public void reviewSubmissionShouldHaveFingerprintProvided() throws Exception {
+ final Request okRequest = createFullReviewSubmissionRequest();
+ assertFormBodyContainsKeyValEncoded(okRequest, "fp", "test_fp");
+ }
+
@Test
public void submissionShouldNotAllowNullFormParams() throws Exception {
final ReviewSubmissionRequest request = new ReviewSubmissionRequest.Builder(Action.Preview, "prod1")
@@ -401,6 +407,12 @@ public void storeReviewSubmissionShouldHaveOwnFormParams() throws Exception {
assertFormBodyContainsKeyValEncoded(okRequest, "VideoCaption_1", "Totes%20cool%20100%25%21");
}
+ @Test
+ public void storeReviewSubmissionShouldHaveFingerprintProvided() throws Exception {
+ final Request okRequest = createFullStoreReviewSubmissionRequest();
+ assertFormBodyContainsKeyValEncoded(okRequest, "fp", "test_fp");
+ }
+
@Test
public void questionSubmissionShouldHaveBVSDKFormParams() throws Exception {
final Request okRequest = createFullQuestionSubmissionRequest();
@@ -431,6 +443,12 @@ public void questionSubmissionShouldHaveOwnFormParams() throws Exception {
assertFormBodyContainsKeyValEncoded(okRequest, "SendEmailAlertWhenAnswered", "true");
}
+ @Test
+ public void questionSubmissionShouldHaveFingerprintProvided() throws Exception {
+ final Request okRequest = createFullQuestionSubmissionRequest();
+ assertFormBodyContainsKeyValEncoded(okRequest, "fp", "test_fp");
+ }
+
@Test
public void answerSubmissionShouldHaveBVSDKFormParams() throws Exception {
final Request okRequest = createFullAnswerSubmissionRequest();
@@ -450,6 +468,12 @@ public void answerSubmissionShouldHaveOwnFormParams() throws Exception {
assertFormBodyContainsKeyValEncoded(okRequest, "AnswerText", "Some%20great%20answer%20%24%25%20here");
}
+ @Test
+ public void answerSubmissionShouldHaveFingerprintProvided() throws Exception {
+ final Request okRequest = createFullAnswerSubmissionRequest();
+ assertFormBodyContainsKeyValEncoded(okRequest, "fp", "test_fp");
+ }
+
@Test
public void commentSubmissionShouldHaveBVSDKFormParams() throws Exception {
final Request okRequest = createFullCommentSubmissionRequest();
@@ -476,6 +500,12 @@ public void commentSubmissionShouldHaveOwnFormParams() throws Exception {
assertFormBodyContainsKeyValEncoded(okRequest, "Title", "Something%20**great**%21");
}
+ @Test
+ public void commentSubmissionShouldHaveFingerprintProvided() throws Exception {
+ final Request okRequest = createFullCommentSubmissionRequest() ;
+ assertFormBodyContainsKeyValEncoded(okRequest, "fp", "test_fp");
+ }
+
// region Helper Assertions
private void assertFormBodyContainsKeyValEncoded(Request okRequest, String encodedKey, String encodedVal) throws Exception {
@@ -577,7 +607,6 @@ private void assertContainsBVMobileInfoFormParams(Request okRequest) throws Exce
private void assertContainsConversationsSubmissionRequestFormParams(Request okRequest) throws Exception {
assertFormBodyContainsKeyValEncoded(okRequest, "UserId", genericFormBodyParams.getUserId());
assertFormBodyContainsKeyValEncoded(okRequest, "UserNickname", genericFormBodyParams.getUserNickname());
- assertFormBodyContainsKeyValEncoded(okRequest, "fp", genericFormBodyParams.getFingerprint());
assertFormBodyContainsKeyValEncoded(okRequest, "hostedauthentication_authenticationemail", "email%40email.com");
assertFormBodyContainsKeyValEncoded(okRequest, "hostedauthentication_callbackurl", "https%3A%2F%2Fwww.callback.url");
assertFormBodyContainsKeyValEncoded(okRequest, "campaignid", genericFormBodyParams.getCampaignId());
@@ -591,7 +620,7 @@ private void assertContainsConversationsSubmissionRequestFormParams(Request okRe
// endregion
// region Stub Data
- private RequestFactory createTestRequestFactory() {
+ private BasicRequestFactory createTestRequestFactory() {
final BVMobileInfo bvMobileInfo = mock(BVMobileInfo.class);
when(bvMobileInfo.getBvSdkVersion()).thenReturn("bvsdk7");
when(bvMobileInfo.getMobileAppCode()).thenReturn("1");
@@ -612,7 +641,13 @@ private RequestFactory createTestRequestFactory() {
.apiKeyConversationsStores(storeApiKey)
.clientId("yoyomakers")
.build();
- return new RequestFactory(bvMobileInfo, bvRootApiUrls, bvConfig, bvSdkUserAgent);
+ final FingerprintProvider fingerprintProvider = new FingerprintProvider() {
+ @Override
+ public String getFingerprint() {
+ return "test_fp";
+ }
+ };
+ return new BasicRequestFactory(bvMobileInfo, bvRootApiUrls, bvConfig, bvSdkUserAgent, fingerprintProvider);
}
private List createPhotoList(PhotoUpload.ContentType contentType) {
diff --git a/bvcurations/src/main/res/values/strings.xml b/bvcurations/src/main/res/values/strings.xml
deleted file mode 100644
index 79bae69f..00000000
--- a/bvcurations/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- BVSDK Curations
-
\ No newline at end of file
diff --git a/bvcurationsui/src/main/java/com/bazaarvoice/bvandroidsdk/CurationsAspectRatioImageView.java b/bvcurationsui/src/main/java/com/bazaarvoice/bvandroidsdk/CurationsAspectRatioImageView.java
index 55b3215a..30e9c3de 100644
--- a/bvcurationsui/src/main/java/com/bazaarvoice/bvandroidsdk/CurationsAspectRatioImageView.java
+++ b/bvcurationsui/src/main/java/com/bazaarvoice/bvandroidsdk/CurationsAspectRatioImageView.java
@@ -10,7 +10,7 @@
import static android.view.View.MeasureSpec.EXACTLY;
-final class CurationsAspectRatioImageView extends AppCompatImageView {
+public final class CurationsAspectRatioImageView extends AppCompatImageView {
private static final String TAG = "AspRatioImageView";
private int widthRatio;
private int heightRatio;
diff --git a/bvcurationsui/src/main/res/values/curationsui_strings.xml b/bvcurationsui/src/main/res/values/curationsui_strings.xml
deleted file mode 100644
index e49fb209..00000000
--- a/bvcurationsui/src/main/res/values/curationsui_strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- bvcurationsui
-
diff --git a/bvlocation/build.gradle b/bvlocation/build.gradle
index 59aa9082..347dafda 100644
--- a/bvlocation/build.gradle
+++ b/bvlocation/build.gradle
@@ -14,6 +14,7 @@ dependencies {
compile sdkDep.gimbalSLF4J
compile sdkDep.slf4jApi
compile sdkDep.appCompatV7
+ provided sdkDep.gson
// Dependencies for local unit tests
testCompile sdkTestDep.junit
diff --git a/bvlocation/src/main/res/values/strings.xml b/bvlocation/src/main/res/values/strings.xml
deleted file mode 100644
index 9f7ee18f..00000000
--- a/bvlocation/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- BVSDK Location
-
diff --git a/bvnotifications/src/main/res/values/strings.xml b/bvnotifications/src/main/res/values/strings.xml
deleted file mode 100644
index ab9c2442..00000000
--- a/bvnotifications/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- bvandroidsdknotifications
-
diff --git a/bvpin/src/main/res/values/strings.xml b/bvpin/src/main/res/values/strings.xml
deleted file mode 100644
index adbddfce..00000000
--- a/bvpin/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- bvandroidsdkpin
-
diff --git a/bvpinnotifications/src/main/res/values/strings.xml b/bvpinnotifications/src/main/res/values/strings.xml
deleted file mode 100644
index 149de739..00000000
--- a/bvpinnotifications/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- bvpinnotifications
-
diff --git a/bvrecommendations/build.gradle b/bvrecommendations/build.gradle
index 33b4529a..fe7f836a 100644
--- a/bvrecommendations/build.gradle
+++ b/bvrecommendations/build.gradle
@@ -12,6 +12,7 @@ dependencies {
compile project(':bvcommon')
provided sdkDep.recyclerview
provided sdkDep.gson
+ provided sdkDep.okhttp3
// Dependencies for local unit tests
testCompile sdkTestDep.junit
diff --git a/bvrecommendations/src/main/res/values/strings.xml b/bvrecommendations/src/main/res/values/strings.xml
deleted file mode 100644
index 46807bae..00000000
--- a/bvrecommendations/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- BVSDK Recommendations
-
\ No newline at end of file
diff --git a/bvstorenotifications/build.gradle b/bvstorenotifications/build.gradle
index bb5a2b2a..8e54d4fe 100644
--- a/bvstorenotifications/build.gradle
+++ b/bvstorenotifications/build.gradle
@@ -16,6 +16,7 @@ dependencies {
compile sdkDep.gimbalSLF4J
compile sdkDep.slf4jApi
compile sdkDep.appCompatV7
+ provided sdkDep.gson
// Dependencies for local unit tests
testCompile sdkTestDep.junit
diff --git a/bvstorenotifications/src/main/res/values/strings.xml b/bvstorenotifications/src/main/res/values/strings.xml
deleted file mode 100644
index 6326c352..00000000
--- a/bvstorenotifications/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- bvstorenotifications
-
diff --git a/dependencies.gradle b/dependencies.gradle
index 946069b1..8c39af81 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -16,14 +16,14 @@ ext {
// the version number in one location for those artifacts
sdkSharedVersions = [
okhttp3Version : '3.2.0',
- supportLibraryVersion : '25.0.0',
+ supportLibraryVersion : '25.2.0',
gimbalVersion : '2.46',
- playServicesVersion : '9.0.0',
+ playServicesVersion : '11.0.0',
robolectric: '3.2.2'
]
sdkBuildDep = [
- androidPlugin : 'com.android.tools.build:gradle:2.3.2',
+ androidPlugin : 'com.android.tools.build:gradle:2.3.3',
credentialsPlugin : 'nu.studer:gradle-credentials-plugin:1.0.1',
playServicesPlugin : 'com.google.gms:google-services:3.0.0',
jacocoPlugin : 'com.dicedmelon.gradle:jacoco-android:0.1.1'
diff --git a/gradle.properties b/gradle.properties
index 8f0665c0..392ad5a8 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,4 @@
-VERSION_NAME=6.8.1
+VERSION_NAME=6.9.0-RC2-BV-SNAPSHOT
GROUP=com.bazaarvoice.bvandroidsdk
POM_DESCRIPTION=Bazaarvoice Android SDK
diff --git a/settings.gradle b/settings.gradle
index 26da5b21..c431360a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,4 +1,5 @@
include ':app',
+ ':bvauthiovation',
':bvanalytics',
':bvcommon',
':bvrecommendations',
@@ -10,4 +11,4 @@ include ':app',
':bvnotifications',
':bvstorenotifications',
':bvpinnotifications',
- ':bvcurationsui'
+ ':bvcurationsui'
\ No newline at end of file