package org.telegram.messenger; import android.util.Pair; import java.util.ArrayList; import java.util.HashMap; public abstract class Fetcher { protected void getRemote(int currentAccount, Args arguments, long hash, Utilities.Callback3 onResult) { // Implement this function } // Not specifying getLocal and setLocal would mean that data is cached only in RAM protected void getLocal(int currentAccount, Args arguments, Utilities.Callback2 onResult) { // Implement this function onResult.run(0L, null); } protected void setLocal(int currentAccount, Args arguments, R data, long hash) { // Implement this function } private long requestRemotelyTimeout = 4 * 60 * 1000; private HashMap, R> cachedResults; private HashMap, ArrayList>> loadingCallbacks; private HashMap, Long> lastRequestedRemotely; public void fetch(int currentAccount, Args arguments, Utilities.Callback onResult) { final Pair key = new Pair<>(currentAccount, arguments); if (isLoading(key)) { saveCallback(key, onResult); return; } R cached = getCachedResult(key); if (cached != null && !shouldRequest(key)) { if (onResult != null) { onResult.run(cached); } return; } saveCallback(key, onResult); getLocal(currentAccount, arguments, (hash, data) -> { if (shouldRequest(key)) { saveLastRequested(key); getRemote(currentAccount, arguments, hash, (notModified, remoteData, newHash) -> { if (notModified) { cacheResult(key, data); callCallbacks(key, data); } else { if (remoteData != null) { setLocal(currentAccount, arguments, remoteData, newHash); cacheResult(key, remoteData); } callCallbacks(key, remoteData); } }); } else { cacheResult(key, data); callCallbacks(key, data); } }); } private R getCachedResult(Pair key) { if (cachedResults == null) { return null; } return cachedResults.get(key); } private void cacheResult(Pair key, R result) { if (cachedResults == null) { cachedResults = new HashMap<>(); } cachedResults.put(key, result); } private void saveLastRequested(Pair key) { if (lastRequestedRemotely == null) { lastRequestedRemotely = new HashMap<>(); } lastRequestedRemotely.put(key, System.currentTimeMillis()); } private boolean shouldRequest(Pair key) { Long lastRequested = lastRequestedRemotely != null ? lastRequestedRemotely.get(key) : null; return lastRequested == null || System.currentTimeMillis() - lastRequested >= requestRemotelyTimeout; } private boolean isLoading(Pair key) { return loadingCallbacks != null && loadingCallbacks.get(key) != null; } private void saveCallback(Pair key, Utilities.Callback callback) { if (callback == null) { return; } if (loadingCallbacks == null) { loadingCallbacks = new HashMap<>(); } ArrayList> callbacks = loadingCallbacks.get(key); if (callbacks == null) { loadingCallbacks.put(key, callbacks = new ArrayList<>()); } callbacks.add(callback); } private void callCallbacks(Pair key, R result) { if (loadingCallbacks == null) { return; } final ArrayList> callbacks = loadingCallbacks.get(key); if (callbacks == null) { return; } AndroidUtilities.runOnUIThread(() -> { for (Utilities.Callback callback: callbacks) { callback.run(result); } callbacks.clear(); }); loadingCallbacks.remove(key); } }