diff --git a/android/app/src/main/java/com/hyzq/boss/MainActivity.java b/android/app/src/main/java/com/hyzq/boss/MainActivity.java index d2f07b8..ea548fb 100644 --- a/android/app/src/main/java/com/hyzq/boss/MainActivity.java +++ b/android/app/src/main/java/com/hyzq/boss/MainActivity.java @@ -2,6 +2,8 @@ package com.hyzq.boss; import android.content.Intent; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.text.Editable; import android.text.TextWatcher; import android.view.View; @@ -28,8 +30,10 @@ public class MainActivity extends AppCompatActivity { private static final String UI_PREFS = "boss_native_client"; private static final String KEY_LAST_ROOT_TAB = "last_root_tab"; private static final long ROOT_BACK_EXIT_WINDOW_MS = 1_500L; + private static final long CONVERSATION_AUTO_REFRESH_MS = 12_000L; private final ExecutorService executor = Executors.newSingleThreadExecutor(); + private final Handler uiHandler = new Handler(Looper.getMainLooper()); private BossApiClient apiClient; @@ -65,6 +69,21 @@ public class MainActivity extends AppCompatActivity { private @Nullable String boundDeviceName; private String conversationSearchQuery = ""; private boolean pinnedConversationsCollapsed = false; + private boolean conversationAutoRefreshArmed = false; + private boolean conversationAutoRefreshEnabled = false; + private final Runnable conversationAutoRefreshRunnable = new Runnable() { + @Override + public void run() { + conversationAutoRefreshArmed = false; + if (!shouldAutoRefreshConversations()) { + return; + } + if (!screenRefresh.isRefreshing()) { + refreshCurrentTab(); + } + armConversationAutoRefresh(); + } + }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -110,10 +129,25 @@ public class MainActivity extends AppCompatActivity { @Override protected void onDestroy() { + cancelConversationAutoRefresh(); executor.shutdownNow(); super.onDestroy(); } + @Override + protected void onResume() { + super.onResume(); + conversationAutoRefreshEnabled = true; + updateConversationAutoRefresh(); + } + + @Override + protected void onPause() { + conversationAutoRefreshEnabled = false; + cancelConversationAutoRefresh(); + super.onPause(); + } + private void bindViews() { loginPanel = findViewById(R.id.login_panel); contentPanel = findViewById(R.id.content_panel); @@ -355,6 +389,7 @@ public class MainActivity extends AppCompatActivity { loginPanel.setVisibility(View.GONE); contentPanel.setVisibility(View.VISIBLE); setActiveTab(activeTab, false); + updateConversationAutoRefresh(); } private void setLoginLoading(boolean loading, String hint) { @@ -373,6 +408,7 @@ public class MainActivity extends AppCompatActivity { lastRootBackPressedAt = 0L; updateTabStyles(); renderCurrentTab(); + updateConversationAutoRefresh(); } private void maybeApplyPreferredEntry() { @@ -699,6 +735,37 @@ public class MainActivity extends AppCompatActivity { private void startRefreshing(boolean refreshing) { screenRefresh.setRefreshing(refreshing); syncTopActionVisualState(refreshing); + if (!refreshing) { + updateConversationAutoRefresh(); + } + } + + private boolean shouldAutoRefreshConversations() { + return conversationAutoRefreshEnabled + && contentPanel != null + && contentPanel.getVisibility() == View.VISIBLE + && "conversations".equals(activeTab); + } + + private void updateConversationAutoRefresh() { + if (shouldAutoRefreshConversations()) { + 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 showMessage(String text) { diff --git a/android/app/src/test/java/com/hyzq/boss/MainActivityConversationAutoRefreshTest.java b/android/app/src/test/java/com/hyzq/boss/MainActivityConversationAutoRefreshTest.java new file mode 100644 index 0000000..7bb3df7 --- /dev/null +++ b/android/app/src/test/java/com/hyzq/boss/MainActivityConversationAutoRefreshTest.java @@ -0,0 +1,38 @@ +package com.hyzq.boss; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +@RunWith(RobolectricTestRunner.class) +@Config(sdk = 34) +public class MainActivityConversationAutoRefreshTest { + @Test + public void conversationAutoRefresh_onlyArmsOnVisibleConversationTab() { + org.robolectric.android.controller.ActivityController controller = + Robolectric.buildActivity(MainActivity.class).setup().resume(); + MainActivity activity = controller.get(); + ReflectionHelpers.callInstanceMethod(activity, "showContent"); + + assertTrue(ReflectionHelpers.getField(activity, "conversationAutoRefreshArmed")); + + ReflectionHelpers.callInstanceMethod(activity, "setActiveTab", + ReflectionHelpers.ClassParameter.from(String.class, "devices"), + ReflectionHelpers.ClassParameter.from(boolean.class, false)); + assertFalse(ReflectionHelpers.getField(activity, "conversationAutoRefreshArmed")); + + ReflectionHelpers.callInstanceMethod(activity, "setActiveTab", + ReflectionHelpers.ClassParameter.from(String.class, "conversations"), + ReflectionHelpers.ClassParameter.from(boolean.class, false)); + assertTrue(ReflectionHelpers.getField(activity, "conversationAutoRefreshArmed")); + + controller.pause(); + assertFalse(ReflectionHelpers.getField(activity, "conversationAutoRefreshArmed")); + } +}