001package com.gigya.android.sdk.account; 002 003import com.gigya.android.sdk.Config; 004import com.gigya.android.sdk.account.models.GigyaAccount; 005import com.gigya.android.sdk.utils.ObjectUtils; 006import com.google.gson.Gson; 007import com.google.gson.reflect.TypeToken; 008 009import java.util.HashMap; 010import java.util.Map; 011import java.util.concurrent.TimeUnit; 012 013/** 014 * Account handling service responsible for holding a cached copy of the current account instance. 015 * 016 * @param <A> Typed account instance (extends GigyaAccount). 017 */ 018public class accountCacheService<A extends GigyaAccount> implements IAccountService<A> { 019 020 final private Config _config; 021 022 /* 023 Cached generic account object. 024 */ 025 private String _cachedAccount; 026 027 @SuppressWarnings("unchecked") 028 private Class<A> _accountScheme; 029 030 /* 031 Invalidation timestamp for cached account. 032 */ 033 private long _accountInvalidationTimestamp = 0L; 034 /* 035 Override account caching flag. Set to TRUE to override caching policy. This will result in consecutive getAccount HTTP requests firing 036 every time the user will request "getAccount" from the SDK. 037 */ 038 private boolean _accountOverrideCache = false; 039 040 041 /* 042 Saving reference to the last "include" call parameter. 043 */ 044 private String _lastRequestedInclude; 045 046 /* 047 Saving reference to the last "profileExtraFields" call parameter. 048 */ 049 private String _lastRequestedProfileExtraFields; 050 051 052 public accountCacheService(Config config, GigyaAccountClass<A> accountClazz) { 053 _config = config; 054 _accountScheme = accountClazz.getAccountClass(); 055 } 056 057 @Override 058 public void setAccountScheme(Class<A> scheme) { 059 _accountScheme = scheme; 060 } 061 062 @Override 063 public Class<A> getAccountSchema() { 064 return _accountScheme; 065 } 066 067 @Override 068 public void setAccount(String json) { 069 _cachedAccount = json; 070 nextAccountInvalidationTimestamp(); 071 } 072 073 @Override 074 public void invalidateAccount() { 075 _cachedAccount = null; 076 } 077 078 @Override 079 public A getAccount() { 080 return new Gson().fromJson(_cachedAccount, _accountScheme); 081 } 082 083 @Override 084 public boolean isCachedAccount(String include, String profileExtraFields) { 085 if (!(!_accountOverrideCache && _cachedAccount != null && System.currentTimeMillis() < _accountInvalidationTimestamp)) { 086 return false; 087 } 088 if (!ObjectUtils.nullAllowedEquals(include, _lastRequestedInclude)) { 089 return false; 090 } 091 return ObjectUtils.nullAllowedEquals(profileExtraFields, _lastRequestedProfileExtraFields); 092 } 093 094 @Override 095 public void nextAccountInvalidationTimestamp() { 096 if (_cachedAccount == null) { 097 return; 098 } 099 final int accountCacheTime = _config.getAccountCacheTime(); 100 if (!_accountOverrideCache && accountCacheTime > 0) { 101 _accountInvalidationTimestamp = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(accountCacheTime); 102 } 103 } 104 105 @Override 106 public long getNextInvalidationTimestamp() { 107 return _accountInvalidationTimestamp; 108 } 109 110 @Override 111 public void setAccountOverrideCache(boolean accountOverrideCache) { 112 _accountOverrideCache = accountOverrideCache; 113 } 114 115 @Override 116 public void updateExtendedParametersRequest(String include, String profileExtraFields) { 117 // Null fields are viable. 118 _lastRequestedInclude = include; 119 _lastRequestedProfileExtraFields = profileExtraFields; 120 } 121 122 //region ACCOUNT SPECIFIC LOGIC 123 124 /** 125 * Get account object objectDifference. 126 */ 127 @SuppressWarnings({"ConstantConditions"}) 128 @Override 129 public Map<String, Object> calculateDiff(Gson gson, A cachedAccount, A updatedAccount) { 130 /* Map updated account object to JSON -> Map. */ 131 final String updatedJson = gson.toJson(updatedAccount); 132 Map<String, Object> updatedMap = gson.fromJson(updatedJson, new TypeToken<HashMap<String, Object>>() { 133 }.getType()); 134 135 /* Map original account object to JSON -> Map. */ 136 final String originalJson = gson.toJson(cachedAccount); 137 Map<String, Object> originalMap = gson.fromJson(originalJson, new TypeToken<HashMap<String, Object>>() { 138 }.getType()); 139 140 /* Calculate objectDifference. */ 141 Map<String, Object> diff = ObjectUtils.objectDifference(originalMap, updatedMap); 142 /* Must have UID or regToken. */ 143 if (updatedMap.containsKey("UID")) { 144 diff.put("UID", updatedMap.get("UID")); 145 } else if (updatedMap.containsKey("regToken")) { 146 diff.put("regToken", updatedMap.get("regToken")); 147 } 148 serializeObjectFields(gson, diff); 149 return diff; 150 } 151 152 /** 153 * Object represented field values must be set as JSON Objects. So we are reverting each one to 154 * its JSON String representation. 155 */ 156 public static void serializeObjectFields(Gson gson, Map<String, Object> map) { 157 for (Map.Entry<String, Object> entry : map.entrySet()) { 158 if (entry.getValue() instanceof Map) { 159 map.put(entry.getKey(), gson.toJson(entry.getValue())); 160 } 161 } 162 } 163 164 //endregion 165}