-
Notifications
You must be signed in to change notification settings - Fork 615
Add first-open time to Remote Config fetch request. #3653
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
745829f
351f5c4
aa5957e
085c946
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,6 +52,7 @@ | |
import java.util.Map; | ||
import java.util.Random; | ||
import java.util.concurrent.Executor; | ||
import org.joda.time.Instant; | ||
|
||
/** | ||
* A handler for fetch requests to the Firebase Remote Config backend. | ||
|
@@ -79,6 +80,9 @@ public class ConfigFetchHandler { | |
*/ | ||
@VisibleForTesting static final int HTTP_TOO_MANY_REQUESTS = 429; | ||
|
||
/** First Open time key in GA user-properties. */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might add to the comment that this is a reserved property set by GA (I think that's the case?) per https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it is an internal property. Updated the comment to say that it's collected by GA as part of of the predefined "user-dimensions". I called it "user-dimensions" following the recent GA documentation (https://support.google.com/firebase/answer/9268042?hl=en&ref_topic=6317484) but on second thought, maybe something simpler is better: "internal user-property automatically collected by GA" ? |
||
@VisibleForTesting static final String FIRST_OPEN_TIME_KEY = "_fot"; | ||
|
||
private final FirebaseInstallationsApi firebaseInstallations; | ||
private final Provider<AnalyticsConnector> analyticsConnector; | ||
|
||
|
@@ -110,7 +114,6 @@ public ConfigFetchHandler( | |
this.fetchedConfigsCache = fetchedConfigsCache; | ||
this.frcBackendApiClient = frcBackendApiClient; | ||
this.frcMetadata = frcMetadata; | ||
|
||
this.customHttpHeaders = customHttpHeaders; | ||
} | ||
|
||
|
@@ -311,6 +314,7 @@ private FetchResponse fetchFromBackend( | |
getUserProperties(), | ||
frcMetadata.getLastFetchETag(), | ||
customHttpHeaders, | ||
getFirstOpenTime(), | ||
currentTime); | ||
|
||
if (response.getLastFetchETag() != null) { | ||
|
@@ -492,8 +496,8 @@ private void updateLastFetchStatusAndTime( | |
} | ||
|
||
/** | ||
* Returns the list of user properties in Analytics. If the Analytics SDK is not available, | ||
* returns an empty list. | ||
* Returns the list of custom user properties in Analytics. If the Analytics SDK is not available, | ||
* this method returns an empty list. | ||
*/ | ||
@WorkerThread | ||
private Map<String, String> getUserProperties() { | ||
|
@@ -510,6 +514,23 @@ private Map<String, String> getUserProperties() { | |
return userPropertiesMap; | ||
} | ||
|
||
/** | ||
* Returns first-open time from Analytics. If the Analytics SDK is not available, or if Analytics | ||
* does not have first-open time for the app, this method returns null. | ||
*/ | ||
@WorkerThread | ||
private Instant getFirstOpenTime() { | ||
AnalyticsConnector connector = this.analyticsConnector.get(); | ||
if (connector == null) { | ||
return null; | ||
} | ||
Map<String, Object> userPropertiesMap = connector.getUserProperties(/*includeInternal=*/ true); | ||
|
||
return userPropertiesMap.containsKey(FIRST_OPEN_TIME_KEY) | ||
? Instant.ofEpochMilli((long) userPropertiesMap.get(FIRST_OPEN_TIME_KEY)) | ||
: null; | ||
} | ||
|
||
/** Used to verify that the fetch handler is getting Analytics as expected. */ | ||
@VisibleForTesting | ||
public Provider<AnalyticsConnector> getAnalyticsConnector() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ | |
import static com.google.firebase.remoteconfig.RemoteConfigConstants.RequestFieldKey.APP_ID; | ||
import static com.google.firebase.remoteconfig.RemoteConfigConstants.RequestFieldKey.APP_VERSION; | ||
import static com.google.firebase.remoteconfig.RemoteConfigConstants.RequestFieldKey.COUNTRY_CODE; | ||
import static com.google.firebase.remoteconfig.RemoteConfigConstants.RequestFieldKey.FIRST_OPEN_TIME; | ||
import static com.google.firebase.remoteconfig.RemoteConfigConstants.RequestFieldKey.INSTANCE_ID; | ||
import static com.google.firebase.remoteconfig.RemoteConfigConstants.RequestFieldKey.INSTANCE_ID_TOKEN; | ||
import static com.google.firebase.remoteconfig.RemoteConfigConstants.RequestFieldKey.LANGUAGE_CODE; | ||
|
@@ -65,6 +66,7 @@ | |
import java.util.TimeZone; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
import org.joda.time.Instant; | ||
import org.json.JSONArray; | ||
import org.json.JSONException; | ||
import org.json.JSONObject; | ||
|
@@ -163,6 +165,7 @@ HttpURLConnection createHttpURLConnection() throws FirebaseRemoteConfigException | |
* server uses this ETag to determine if there has been a change in the response body since | ||
* the last fetch. | ||
* @param customHeaders custom HTTP headers that will be sent to the FRC server. | ||
* @param firstOpenTime first time the app was opened. This value comes from Google Analytics. | ||
* @param currentTime the current time on the device that is performing the fetch. | ||
*/ | ||
// TODO(issues/263): Set custom headers in ConfigFetchHttpClient's constructor. | ||
|
@@ -174,6 +177,7 @@ FetchResponse fetch( | |
Map<String, String> analyticsUserProperties, | ||
String lastFetchETag, | ||
Map<String, String> customHeaders, | ||
Instant firstOpenTime, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thinking aloud: it doesn't make sense to merge this in with the other There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, and the reason we want to send it in its own request param is because analyticsUserProperties in fetch traditionally contains custom user-defined properties only. And we want to keep, and highlight, that distinction. It also lets us explicitly call out the information that we are sending up to the server. |
||
Date currentTime) | ||
throws FirebaseRemoteConfigException { | ||
setUpUrlConnection(urlConnection, lastFetchETag, installationAuthToken, customHeaders); | ||
|
@@ -182,7 +186,8 @@ FetchResponse fetch( | |
JSONObject fetchResponse; | ||
try { | ||
byte[] requestBody = | ||
createFetchRequestBody(installationId, installationAuthToken, analyticsUserProperties) | ||
createFetchRequestBody( | ||
installationId, installationAuthToken, analyticsUserProperties, firstOpenTime) | ||
.toString() | ||
.getBytes("utf-8"); | ||
setFetchRequestBody(urlConnection, requestBody); | ||
|
@@ -292,7 +297,8 @@ private String getFingerprintHashForPackage() { | |
private JSONObject createFetchRequestBody( | ||
String installationId, | ||
String installationAuthToken, | ||
Map<String, String> analyticsUserProperties) | ||
Map<String, String> analyticsUserProperties, | ||
Instant firstOpenTime) | ||
throws FirebaseRemoteConfigClientException { | ||
Map<String, Object> requestBodyMap = new HashMap<>(); | ||
|
||
|
@@ -315,7 +321,7 @@ private JSONObject createFetchRequestBody( | |
: locale.toString(); | ||
requestBodyMap.put(LANGUAGE_CODE, languageCode); | ||
|
||
requestBodyMap.put(PLATFORM_VERSION, Integer.toString(android.os.Build.VERSION.SDK_INT)); | ||
requestBodyMap.put(PLATFORM_VERSION, Integer.toString(Build.VERSION.SDK_INT)); | ||
|
||
requestBodyMap.put(TIME_ZONE, TimeZone.getDefault().getID()); | ||
|
||
|
@@ -336,6 +342,10 @@ private JSONObject createFetchRequestBody( | |
|
||
requestBodyMap.put(ANALYTICS_USER_PROPERTIES, new JSONObject(analyticsUserProperties)); | ||
|
||
if (firstOpenTime != null) { | ||
requestBodyMap.put(FIRST_OPEN_TIME, firstOpenTime.toString()); | ||
} | ||
|
||
return new JSONObject(requestBodyMap); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a pretty heavy dependency, especially given that you don't seem to do any arithmetic on it but just rely on its toString() method, I suggest to use the long directly and use SimpleDateFormat to convert it to an iso string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the dependency and replaced it with SimpleDataFormat. Thanks, I definitely struggled a bit with this and was looking for inputs!