I have the below defined:
I first launch the app with the device's language (from Settings) set to English and it loads the lang-EN json.
If I go in the Settings of the device, change the language to French and restart the app, .fetch() will still return the lang-EN value. If I clear cache the app and restart it, it then loads the lang-FR value.
Doesn't .fetch() get ALL the remote config params and locally decide which to show the user? From the functionality i'm getting it seems that .fetch() only gets the parameter values only pertaining to the device at the moment of the call. Either that or there's a bug in the firebase code ... OR ... and i'm hoping this is the case, i'm doing something wrong.
This is how i'm initializing the singleton. Perhaps i'm missing something?
public void initialize(Context applicationContext) {
FirebaseRemoteConfigSettings configSettings = new FirebaseRemoteConfigSettings.Builder()
.setMinimumFetchIntervalInSeconds(5 * 60 * 60)//5 hours
.build();
getConfig().setConfigSettingsAsync(configSettings);
}
And yes, i have the latest version of the remote config sdk in the dependencies 19.2.0
fetch() is not enough.
To fetch parameter values from the Remote Config backend, call the fetch() method. Any values that you set in the backend are fetched and stored in the Remote Config object.
To make fetched parameter values available to your app, call the activate() method.
For cases where you want to fetch and activate values in one call, you can use a fetchAndActivate() request to fetch values from the Remote Config backend and make them available to the app:
mFirebaseRemoteConfig.fetchAndActivate()
.addOnCompleteListener(this, new OnCompleteListener<Boolean>() {
#Override
public void onComplete(#NonNull Task<Boolean> task) {
if (task.isSuccessful()) {
boolean updated = task.getResult();
Log.d(TAG, "Config params updated: " + updated);
Toast.makeText(MainActivity.this, "Fetch and activate succeeded",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "Fetch failed",
Toast.LENGTH_SHORT).show();
}
displayWelcomeMessage();
}
});
Because these updated parameter values affect the behavior and appearance of your app, you should activate the fetched values at a time that ensures a smooth experience for your user, such as the next time that the user opens your app
Related
I am using Firebase on both Android and iOS and after I register a user, I pass the username I took from the user, to my server to update the displayname. This works, but it's not visible on the app until the user closes the app or logs out.
I have tried using the reload method which states
Manually refreshes the data of the current user (for example, attached providers, display name, and so on).
But this doesn't do anything.
Is there another way of refreshing/reloading the data, or maybe clearing the cache?
It's possible that I could add the displayName in the app itself, but as I limit the displayname based on availability of names in my server, it makes sense to do it there.
UPDATE: (have added some code examples)
I update the displayname in a java app using the admin sdk like this
private void updateUsernameInFirebase(String uniqueId, String username){
UserRecord.UpdateRequest request = new UserRecord.UpdateRequest(uniqueId).setDisplayName(username);
try {
UserRecord userRecord = FirebaseAuth.getInstance().updateUser(request);
} catch (FirebaseAuthException e) {
e.printStackTrace();
}
}
and then try to refresh the data in my Android client app like this
FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
if(firebaseUser != null) {
Task<Void> reload = firebaseUser.reload();
}
I have an issue with AdMob's new 'Consent SDK'; the integration guide says to put the following into onCreate...
public class MainActivity extends Activity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
...
ConsentInformation consentInformation = ConsentInformation.getInstance(context);
String[] publisherIds = {"pub-xxxxxxxxxxxxxxxx"};
consentInformation.requestConsentInfoUpdate(publisherIds, new ConsentInfoUpdateListener() {
#Override
public void onConsentInfoUpdated(ConsentStatus consentStatus) {
// User's consent status successfully updated.
}
#Override
public void onFailedToUpdateConsentInfo(String errorDescription) {
// User's consent status failed to update.
}
});
...
}
...
}
And then make a call to:
ConsentInformation.getInstance(context).isRequestLocationInEeaOrUnknown()
The problem I'm having is that when the app is first installed/launched, isRequestLocationInEeaOrUnknown() always returns 'false' (I am in the EEA by the way).
If I then exit the app and re-launch it, it returns 'true' - this is correct. If I then go into my device settings and perform a 'clear data' on my app and re-launch it, once again it returns 'false'.
Obviously this is worrying as I am showing my own custom consent dialog to EEA/Swizerland users than I am the ROW. And this needs to happen on first launch.
Interestingly, I tried putting the call to isRequestLocationInEeaOrUnknown() in my AsyncTask' doInBackground method (I kick this ASync off in onCreate) and then it does return 'true' on first-launch as do calls to it made in the ASync's 'onPostExecute' method. It's just that calls made to it in onCreate do not (before or after the Async starts).
I know it's early days, but has anyone stumbled upon similar issues with this?
You must call isRequestLocationInEeaOrUnknown() after onConsentInfoUpdated callback is called.
This value is retrieved asynchronously by requestConsentInfoUpdate(), so it is not correct at first launch, but it is then cached so on second launch you have correct value.
Since i faced the same issue, and the docks are a bit confusing I will try to explain how it works.
add this for testing only remove them in production and make sure you add them before you request consent
ConsentInformation.getInstance(this#MainActivity).addTestDevice("DEVICE_ID") // add your device id for testing
ConsentInformation.getInstance(this#MainActivity).debugGeography = DebugGeography.DEBUG_GEOGRAPHY_EEA // test if user in EEA
ConsentInformation.getInstance(this#MainActivity).consentStatus = ConsentStatus.UNKNOWN // this sets the state to unknown, useful to reset the consent state in between tests careful to remove this if you want to see the flow for a returning user
Request consent status like this:
val consentInformation = ConsentInformation.getInstance(this#MainActivity)
val publisherIds = arrayOf(ADMOB_PUBLISHER_ID)
consentInformation.requestConsentInfoUpdate(publisherIds, object: ConsentInfoUpdateListener {
override fun onFailedToUpdateConsentInfo(reason: String?) {
// consent request failed so probably you sould display non personalized ads
log("MAIN ACTIVITY", "FAILED TO UPDATE CONSENT SHOW NOT PERSONALIZED")
initializeAds(NON_PERSONALIZED)
// YOU COULD RETRY HERE OR IT WILL RETRY ON NEXT SESSION
}
override fun onConsentInfoUpdated(consentStatus: ConsentStatus?) {
when (consentStatus) {
ConsentStatus.PERSONALIZED -> {
log("MAIN ACTIVITY", "USER OPTED FOR PERSONALIZED")
// USER ALREADY GAVE HIS CONSENT FOR YOUR PUBLISHER ID SO YOU CAN DISPLAY PERSONALIZED ADS
initializeAds(PERSONALIZED)
}
ConsentStatus.NON_PERSONALIZED -> {
log("MAIN ACTIVITY", "USER OPTED FOR NON_PERSONALIZED")
// USER OPTED FOR NON_PERSONALIZED ADS SO INCLUDE THAT IN YOUR ADD REQUEST
initializeAds(NON_PERSONALIZED)
}
ConsentStatus.UNKNOWN -> {
log("MAIN ACTIVITY", "USER CONSENT STATUS IS UNKNOWN ")
// USER WAS NEVER PROMPTED TO GIVE HIS CONSENT (DEFAULT STATE FOR ALL USERS)
if (consentInformation.isRequestLocationInEeaOrUnknown) {
log("MAIN ACTIVITY", "USER IS IN EEA REQUEST CONSENT ")
// USER IS IN THE EEA AREA SO WE NEED TO REQUEST HIS CONSENT (WE SHOW THE PROMPT HERE) YOU SHOULD UPDATE CONSENT WITH HIS OPTION SO THIS FUNCTION WILL NEVER GET CALLED AGAIN
requestConsentFromUser()
} else {
log("MAIN ACTIVITY", "USER NOT IN EEA INITIALIZE ADS ")
// USER IS NOT IN EEA SO WE ARE NOT REQUIRED TO REQUEST CONSENT (YOU COULD STILL REQUEST IT IF YOU LIKE)
initializeAds(PERSONALIZED)
}
}
}
}
})
I face the same, some countries are correct, some aren't.
I tried "https://adservice.google.com/getconfig/pubvendors" too
I'm trying to set up Firebase Remote Config for my project.
I added Firebase via the Assistant. I added values to the server values on Google Cloud Console:
I've created default values xml in res/xml
<defaultsMap>
<!-- Strings-->
<entry >
<key>textView_send_text</key>
<value >your phrase goes here.</value>
</entry>
</defaultsMap>
Thats my MainActivity:
final private FirebaseRemoteConfig mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
protected void onCreate(Bundle savedInstanceState) {
//..code..
//fetch from Firebase
fetchAll();
}
private void fetchAll(){
final FirebaseRemoteConfig mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
FirebaseRemoteConfigSettings configSettings = new FirebaseRemoteConfigSettings.Builder()
.setDeveloperModeEnabled(BuildConfig.DEBUG)
.build();
mFirebaseRemoteConfig.setConfigSettings(configSettings);
mFirebaseRemoteConfig.setDefaults(R.xml.defaults);
mFirebaseRemoteConfig.fetch()
.addOnCompleteListener(this, new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if(task.isSuccessful()){
Toast.makeText(MainActivity.this, "Fetch Succeeded",
Toast.LENGTH_SHORT).show();
mFirebaseRemoteConfig.activateFetched();
}else{
Toast.makeText(MainActivity.this, "Fetch Failed",
Toast.LENGTH_SHORT).show();
}
displayWelcomeMessage();
}
});
}
private void displayWelcomeMessage(){
String welcomeMessage = mFirebaseRemoteConfig.getString("textView_send_text");
Toast.makeText(this, welcomeMessage,
Toast.LENGTH_SHORT).show();
}
Toast output:
So Toast gets the value from xml/defaults not from the Cloud.
It'd be much appreciated if somebody found where I made a mistake.
For development testing, specify a cache expiration time of zero to force an immediate fetch:
mFirebaseRemoteConfig.fetch(0) // <- add the zero
.addOnCompleteListener(this, new OnCompleteListener<Void>() {
...
});
Some tips the helped to me:
Don't forget to click "publish changes" in Firebase console after each value update
Uninstall and install the App before checking (Firebase may not fetch immediately)
Use mFirebaseRemoteConfig.fetch(0)
For what it worth I found that our firebase remote configs weren't downloading no matter what we tried - we are usually debugging while connected to a proxy (like Charles Proxy) and that was interrupting the firebase cloud update.
Once we connected to a non-proxied wifi connection we got the update.
You can also set your config to developer mode if running a debug build which will refresh values more often - but the proxy was our root problem.
FirebaseRemoteConfigSettings configSettings = new FirebaseRemoteConfigSettings.Builder()
.setDeveloperModeEnabled(BuildConfig.DEBUG)
.build();
In Flutter, I just changed the value of minimumFetchInterval to const Duration(hours: 0). I manually added an message to firebase and here I just get it. Here is my code;
await Firebase.initializeApp();
RemoteConfig remoteConfig = RemoteConfig.instance;
await remoteConfig.setConfigSettings(RemoteConfigSettings(
fetchTimeout: const Duration(seconds: 10),
minimumFetchInterval: const Duration(hours: 0),
));
RemoteConfigValue(null, ValueSource.valueStatic);
bool updated = await remoteConfig.fetchAndActivate();
if (updated) {
print("it is updated");
} else {
print("it is not updated");
}
print('my_message ${remoteConfig.getString('my_message')}');
Make sure your mFirebaseRemoteConfig.fetch() called only once. It gets throttled if you call it multiple times.
There is a default minimum time duration Intervals for firebase remote config fetching. According to the Firebase documentation, it is now 12 hours. During that default time interval gap, if you change the keys from Firebase remote config and if you don't uninstall the app, you don't get updated data. you will get updated data after passing default time intervals. If you need more frequent data changes, you can override fetch intervals from client side.
According to Firebase cloud messaging documentation, for subscribing a user to a topic I need to call
FirebaseMessaging.getInstance().subscribeToTopic("news");
In my application, I need all users to be subscribed to my cloud
messaging topic. Since return value is void, the question is how
can I understand that subscription was successful?
Is it a bad practice to call subscribeToTopic each time my
application starts?
1. How can I understand that subscription was successful?
Edit:
You could now check if subscription is successful by adding addOnSuccessListener()
FirebaseMessaging.getInstance().subscribeToTopic("news").addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(getApplicationContext(),"Success",Toast.LENGTH_LONG).show();
}
});
Original:
There is nothing explicitly mentioned in the docs about a response received when the subscription is successful.
However, if you need to mandate all of your users to be subscribed to a specific topic, you should call the subscribeToTopic on your app's first install. This will most likely make sure that there is a connection to the internet (since it's probably been downloaded and installed via the Play Store) and the subscription successful.
However, if you want to make sure, you can also handle he checking via your own App Server. As mentioned in the docs:
You can take advantage of Instance ID APIs to perform basic topic management tasks from the server side. Given the registration token(s) of client app instances, you can do the following:
Find out details about a client app instance's subscriptions, including each topic name and subscribe date. See Get information about app instances.
Check through the registration tokens, if they haven't been successfully subsribed to your topic, send a notification to it where it will trigger your client app to call subscribeToTopic.
2. Is it a bad practice to call subscribeToTopic each time my application starts?
Edit: Adding it in from the comments section: Subscribing on app start should be fine.
Thank you #FrankvanPuffelen for verifying. :)
I have written this function and tested. May be helpful.
private void subscribeToMessaging(){
SharedPreferences prefs = getSharedPreferences(SETTINGS_TITLE, MODE_PRIVATE);
// Getting value from shared preferences
boolean isSubscriptionEnable = prefs.getBoolean(SETTING_NOTIFICATION, true);
// if "isSubscriptionEnable" is true then check whether its already subscribed or not
if (isSubscriptionEnable){
boolean alreadySubscribed = prefs.getBoolean(SETTING_ALREADY_SUBSCRIBED, false);
// if not already subscribed then subscribe to topic and save value to shared preferences
if (!alreadySubscribed){
FirebaseMessaging.getInstance().subscribeToTopic("global").addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(getApplicationContext(),"Success",Toast.LENGTH_LONG).show();
}
});
SharedPreferences.Editor editor = getSharedPreferences(SETTINGS_TITLE, MODE_PRIVATE).edit();
editor.putBoolean(SETTING_ALREADY_SUBSCRIBED, true);
editor.apply();
Toast.makeText(this, "Subscribed", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "Already subscribed", Toast.LENGTH_LONG).show();
}
}
}
Don't forget to write these lines above onCreate()
public static final String SETTINGS_TITLE = "settings";
public static final String SETTING_NOTIFICATION = "notification_state";
public static final String SETTING_ALREADY_SUBSCRIBED = "already_subscribed";
I'm trying to have a remote config parameter using the new Remote Config feature of Firebase, and I'm having an issue.
Here's my Remote Config console:
I'm doing a fetch and update in my Application's onCreate():
final FirebaseRemoteConfig remoteConfig = FirebaseRemoteConfig.getInstance();
remoteConfig.fetch().addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
remoteConfig.activateFetched();
}
}
});
And here's how I'm reading it:
FirebaseRemoteConfig remoteConfig = FirebaseRemoteConfig.getInstance();
String value = remoteConfig.getString("active_subscriptions");
Value is returning null.
If I call remoteConfig.getInfo().getLastFetchStatus(), it returns LAST_FETCH_STATUS_SUCCESS, so it seems the fetch is going through successfully.
Any idea why my value is blank?
Workaround found! See below
I'm running into the "silent completion" thing - I call "fetch" but onComplete, onSuccess, or onFailure listeners never fire. I tried moving it to an activity onCreate, and still nothing happened, and therefore, the config items never get loaded from the server. I've got Developer Mode enabled, and am calling fetch with a cache value of 0.
I was able to (once) put a breakpoint on the line "public void onComplete(#NonNull Task task) {", which got hit, and then I was able to step through and the onComplete fired. I was then unable to reproduce this same result any other way, including doing the same thing (I think) a second time.
Seems like a timing or concurrency issue, but that makes little sense, given this is an asynchronous call.
Workaround
If you fetch from Activity#onResume (or, I presume, Activity#onStart), it works perfectly. Calling fetch from Activity#onCreate or Application#onCreate results in a call that seemingly never gets handled, and in fact, performance of the app degrades noticeably after the fetch begins, so I think there's a looper running or something.*
Workaround #2
If you really want this to run from Application#onCreate (which I do), this seems to work as well:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// Run mFirebaseRemoteConfig.fetch(timeout) here, and it works
}
}, 0);
You're likely hitting the caching in Remote Config. The way it works is that Config will cache incoming items locally, and return them. So your last (cached) fetch status was probably before the value was defined, and we get a cached blank value.
You can control the cache expiry, but if you fetch too often you risk getting throttled.
Because this is a common development problem though, there is a developer mode that lets you request more rapidly (for small groups of users):
FirebaseRemoteConfigSettings configSettings =
new FirebaseRemoteConfigSettings.Builder()
.setDeveloperModeEnabled(BuildConfig.DEBUG)
.build();
FirebaseRemoteConfig.getInstance().setConfigSettings(configSettings);
When you call fetch you can then pass a short cache expiration time
long cacheExpiration = 3600;
FirebaseRemoteConfig mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
if (mFirebaseRemoteConfig.getInfo().getConfigSettings().isDeveloperModeEnabled()) {
cacheExpiration = 0;
}
mFirebaseRemoteConfig.fetch(cacheExpiration)
.addOnCompleteListener(new OnCompleteListener<Void>() {
// ...
});
That's how its done in the quickstart sample if you want a full reference.
Found the problem.
After adding some logging, I found that the fetch job's onComplete() was never being called. I moved the fetch from my Application's onCreate to a fragment's, and now it works properly!
(Ian Barber, this might be something to look into or clarify, as the logs indicated that Firebase was initialized without an issue when it was in the Application, and the fetches were silent failures.)
I also encountered this problem. Turns out I hadn't seen the 'Publish' button in the the Firebase console. :facepalm:
I had the same problem and no workarounds were helpful in my case. The problem was in the testing device. I used emulator without installing Google Mobile Services, because of this the Complete event was not fired. I tried my phone with GMS and everything worked great. Good luck.
First thing in such case is check if you have the correct firebase config and you are connected to firebase .If you have android studio 2.2 got to Tools->Firebase->RemoteConfig - Connect to Firebase and see if you get a notification saying connected.Once Connected do the following in your code:
mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
/** NOTE: At this point, your app can use in-app default parameter values.To use in-app
* default values,skip the next section. You can deploy your app without setting
* parameter values on the server,and then later set values on the server to
* override the default behavior and appearance of your app.
*/
mFirebaseRemoteConfig.setDefaults(R.xml.remote_config_defaults);
FirebaseRemoteConfigSettings configSettings = new FirebaseRemoteConfigSettings.Builder()
.setDeveloperModeEnabled(true)
.build();
mFirebaseRemoteConfig.setConfigSettings(configSettings);
And then for fetching config do the following
long cacheExpiration = 2000; // Can increase this usually 12hrs is what is recommended
/** If in developer mode cacheExpiration is set to 0 so each fetch will retrieve values from
* the server.*/
if (mFirebaseRemoteConfig.getInfo().getConfigSettings().isDeveloperModeEnabled()) {
cacheExpiration = 0;
}
/** cacheExpirationSeconds is set to cacheExpiration here, indicating that any previously
* fetched and cached config would be considered expired because it would have been fetched
* more than cacheExpiration seconds ago. Thus the next fetch would go to the server unless
* throttling is in progress. The default expiration duration is 43200 (12 hours).
*/
mFirebaseRemoteConfig.fetch(cacheExpiration)//TODO Bring this from a config file
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "Firebase Remote config Fetch Succeeded");
// Once the config is successfully fetched it must be activated before newly fetched
// values are returned.
mFirebaseRemoteConfig.activateFetched();
} else {
Log.d(TAG, "Firebase Remote config Fetch failed");
}
showRemoteConfig();
}
});
Run your App and check in logs " Firebase Remote config Fetch Succeeded ". If you see the same your remote configs are loaded and activated.
I've used a similar code like #Ian Barber (copy):
FirebaseRemoteConfigSettings configSettings =
new FirebaseRemoteConfigSettings.Builder()
.setDeveloperModeEnabled(BuildConfig.DEBUG)
.build();
FirebaseRemoteConfig.getInstance().setConfigSettings(configSettings);
My problem was the "BuildConfig.DEBUG", it returns false. So it takes the value 1h in cache until it was fetched again!
I had a problem that Firebase Remote Config didn't fire OnCompleteListener with fetch(0), but with fetch() did.
Looking at FirebaseRemoteConfig.fetch() does not trigger OnCompleteListener every time, I found that the first answer was working sometimes even with fetch(0). Then I again set 3600 seconds for interval, as errors continued to appear:
override fun onPostResume() {
super.onPostResume()
// Initialize FirebaseRemoteConfig here.
...
firebaseRemoteConfig.fetch(3600).addOnCompleteListener { task ->
if (task.isSuccessful) {
firebaseRemoteConfig.activateFetched()
//calling function to check if new version is available or not
checkForUpdate(currentVersionCode, firebaseRemoteConfig.getString(VERSION_CODE_KEY))
} else
Toast.makeText(this#MainActivity, "Someting went wrong please try again",
Toast.LENGTH_SHORT).show()
}
}
Well in my case, I am able to receive control in addOnCompleteListener for fetch method but I have fetched values firebaseRemoteConfig just after I called firebaseRemoteConfig.activate(), so when I have tried to get the values from firebaseRemoteConfig it returns me previously saved values because firebaseRemoteConfig.activate() runs asynchronously and new values didn't saved before I am getting them from firebaseRemoteConfig, so I have added complete listener for activate() method also, Here:
firebaseRemoteConfig.fetch()
.addOnCompleteListener(activity, OnCompleteListener {
if (it.isSuccessful)
{
Log.d("task","success")
firebaseRemoteConfig.activate().addOnCompleteListener { // here I have added a listener
val base_url=firebaseRemoteConfig.getString("base_url")
Log.d("base url",base_url)
Toast.makeText(activity, "Base url: $base_url",Toast.LENGTH_SHORT).show()
}
}
else
{
Log.d("task","failure")
}
})
Does Android Studio's Build Variant match your intended Firebase project?
I work on a big project and the problem was buried in an unexpected place.
Long story short: the firebase application id(normally set through google-services.json) was changed through code:
FirebaseOptions.Builder builder = new FirebaseOptions.Builder();
builder.setApplicationId(applicationId);
builder.setApiKey(apiKey);
FirebaseOptions options = builder.build();
FirebaseApp.initializeApp(context, options);
The solution was to remove that code and let firebase use the info from "google-services.json".
Use fetchAndActivate instead of fetch
I was facing the same problem. After fetching, no listener get call for first time only. I try fetchAndActivate in single line and it works for me. Use below code
mFirebaseRemoteConfig.fetchAndActivate()
.addOnCompleteListener(this, new OnCompleteListener<Boolean>() {
#Override
public void onComplete(#NonNull Task<Boolean> task) {
if (task.isSuccessful()) {
boolean updated = task.getResult();
Log.d(TAG, "Config params updated: " + updated);
Toast.makeText(MainActivity.this, "Fetch and activate succeeded",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "Fetch failed",
Toast.LENGTH_SHORT).show();
}
displayWelcomeMessage();
}
});
It will fetch and activate immediately. You can find this way in official documentation here