Gate Android polling behind realtime health
This commit is contained in:
@@ -34,6 +34,7 @@ final class BossRealtimeClient {
|
||||
private final BossApiClient apiClient;
|
||||
private final Listener listener;
|
||||
private volatile boolean running;
|
||||
private volatile boolean connected;
|
||||
private @Nullable Thread workerThread;
|
||||
private @Nullable HttpURLConnection activeConnection;
|
||||
|
||||
@@ -53,6 +54,7 @@ final class BossRealtimeClient {
|
||||
|
||||
synchronized void stop() {
|
||||
running = false;
|
||||
connected = false;
|
||||
if (activeConnection != null) {
|
||||
activeConnection.disconnect();
|
||||
activeConnection = null;
|
||||
@@ -63,6 +65,10 @@ final class BossRealtimeClient {
|
||||
}
|
||||
}
|
||||
|
||||
boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
private void runLoop() {
|
||||
long backoffMs = INITIAL_BACKOFF_MS;
|
||||
while (running) {
|
||||
@@ -123,6 +129,7 @@ final class BossRealtimeClient {
|
||||
if (statusCode < 200 || statusCode >= 300) {
|
||||
throw new IOException("REALTIME_STREAM_HTTP_" + statusCode);
|
||||
}
|
||||
connected = true;
|
||||
Log.i(TAG, "Realtime stream connected");
|
||||
|
||||
try (InputStream inputStream = connection.getInputStream();
|
||||
@@ -145,6 +152,7 @@ final class BossRealtimeClient {
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
connected = false;
|
||||
activeConnection = null;
|
||||
connection.disconnect();
|
||||
}
|
||||
|
||||
@@ -114,10 +114,10 @@ public class MainActivity extends AppCompatActivity {
|
||||
@Override
|
||||
public void run() {
|
||||
conversationAutoRefreshArmed = false;
|
||||
if (!shouldAutoRefreshConversations()) {
|
||||
if (!shouldMaintainConversationAutoRefresh()) {
|
||||
return;
|
||||
}
|
||||
if (!screenRefresh.isRefreshing()) {
|
||||
if (!screenRefresh.isRefreshing() && shouldAutoRefreshConversations()) {
|
||||
refreshCurrentTab();
|
||||
}
|
||||
armConversationAutoRefresh();
|
||||
@@ -1688,14 +1688,24 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private boolean shouldAutoRefreshConversations() {
|
||||
return shouldMaintainConversationAutoRefresh() && !isRealtimeConnected();
|
||||
}
|
||||
|
||||
private boolean shouldMaintainConversationAutoRefresh() {
|
||||
return conversationAutoRefreshEnabled
|
||||
&& contentPanel != null
|
||||
&& contentPanel.getVisibility() == View.VISIBLE
|
||||
&& "conversations".equals(activeTab);
|
||||
&& "conversations".equals(activeTab)
|
||||
&& apiClient != null
|
||||
&& apiClient.hasSessionHints();
|
||||
}
|
||||
|
||||
private boolean isRealtimeConnected() {
|
||||
return realtimeClient != null && realtimeClient.isConnected();
|
||||
}
|
||||
|
||||
private void updateConversationAutoRefresh() {
|
||||
if (shouldAutoRefreshConversations()) {
|
||||
if (shouldMaintainConversationAutoRefresh()) {
|
||||
armConversationAutoRefresh();
|
||||
} else {
|
||||
cancelConversationAutoRefresh();
|
||||
|
||||
@@ -8,6 +8,8 @@ import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
@@ -41,6 +43,7 @@ import java.util.concurrent.Executors;
|
||||
public class ProjectDetailActivity extends BossScreenActivity {
|
||||
public static final String EXTRA_PROJECT_ID = "project_id";
|
||||
public static final String EXTRA_PROJECT_NAME = "project_name";
|
||||
private static final long CONVERSATION_AUTO_REFRESH_MS = 8_000L;
|
||||
private static final long REPLY_WAIT_TIMEOUT_MS = 55_000L;
|
||||
private static final long REPLY_WAIT_POLL_INTERVAL_MS = 1_500L;
|
||||
private static final long REALTIME_RELOAD_THROTTLE_MS = 900L;
|
||||
@@ -86,9 +89,25 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
||||
private final ExecutorService replyWaitExecutor = Executors.newSingleThreadExecutor();
|
||||
private @Nullable BossRealtimeClient realtimeClient;
|
||||
private final java.util.HashMap<String, Long> recentRealtimeEventTimestamps = new java.util.HashMap<>();
|
||||
private final Handler uiHandler = new Handler(Looper.getMainLooper());
|
||||
private boolean conversationAutoRefreshArmed;
|
||||
private boolean conversationAutoRefreshEnabled;
|
||||
private boolean reloadInFlight;
|
||||
private boolean pendingReload;
|
||||
private boolean pendingReloadForcedScrollToBottom;
|
||||
private final Runnable conversationAutoRefreshRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
conversationAutoRefreshArmed = false;
|
||||
if (!shouldMaintainConversationAutoRefresh()) {
|
||||
return;
|
||||
}
|
||||
if (!reloadInFlight && !refreshLayout.isRefreshing() && shouldAutoRefreshConversation()) {
|
||||
reload(false);
|
||||
}
|
||||
armConversationAutoRefresh();
|
||||
}
|
||||
};
|
||||
|
||||
static final class ChromeBindings {
|
||||
final boolean multiSelecting;
|
||||
@@ -258,6 +277,7 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
cancelConversationAutoRefresh();
|
||||
stopRealtimeUpdates();
|
||||
replyWaitExecutor.shutdownNow();
|
||||
super.onDestroy();
|
||||
@@ -266,11 +286,15 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
conversationAutoRefreshEnabled = true;
|
||||
updateConversationAutoRefresh();
|
||||
updateRealtimeSubscription();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
conversationAutoRefreshEnabled = false;
|
||||
cancelConversationAutoRefresh();
|
||||
stopRealtimeUpdates();
|
||||
super.onPause();
|
||||
}
|
||||
@@ -410,6 +434,9 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
||||
}
|
||||
updateComposerSendButtonState();
|
||||
updateSelectionUi();
|
||||
if (!refreshing) {
|
||||
updateConversationAutoRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
private void renderProject(
|
||||
@@ -498,6 +525,42 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldMaintainConversationAutoRefresh() {
|
||||
return conversationAutoRefreshEnabled
|
||||
&& apiClient != null
|
||||
&& apiClient.hasSessionHints()
|
||||
&& !TextUtils.isEmpty(projectId);
|
||||
}
|
||||
|
||||
private boolean shouldAutoRefreshConversation() {
|
||||
return shouldMaintainConversationAutoRefresh() && !isRealtimeConnected();
|
||||
}
|
||||
|
||||
private boolean isRealtimeConnected() {
|
||||
return realtimeClient != null && realtimeClient.isConnected();
|
||||
}
|
||||
|
||||
private void updateConversationAutoRefresh() {
|
||||
if (shouldMaintainConversationAutoRefresh()) {
|
||||
armConversationAutoRefresh();
|
||||
} else {
|
||||
cancelConversationAutoRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
private void armConversationAutoRefresh() {
|
||||
if (conversationAutoRefreshArmed) {
|
||||
return;
|
||||
}
|
||||
uiHandler.postDelayed(conversationAutoRefreshRunnable, CONVERSATION_AUTO_REFRESH_MS);
|
||||
conversationAutoRefreshArmed = true;
|
||||
}
|
||||
|
||||
private void cancelConversationAutoRefresh() {
|
||||
uiHandler.removeCallbacks(conversationAutoRefreshRunnable);
|
||||
conversationAutoRefreshArmed = false;
|
||||
}
|
||||
|
||||
private void renderQuickActions() {
|
||||
if (quickActionsLayout == null) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user