fix: keep chat scroll usable while sending
This commit is contained in:
@@ -19,6 +19,7 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageButton;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -61,7 +62,7 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
|||||||
private LinearLayout quickActionsLayout;
|
private LinearLayout quickActionsLayout;
|
||||||
private LinearLayout composerRow;
|
private LinearLayout composerRow;
|
||||||
private LinearLayout multiSelectActionsLayout;
|
private LinearLayout multiSelectActionsLayout;
|
||||||
private Button composerAttachmentButton;
|
private ImageButton composerAttachmentButton;
|
||||||
private EditText composerInput;
|
private EditText composerInput;
|
||||||
private Button composerSendButton;
|
private Button composerSendButton;
|
||||||
private Button multiSelectForwardButton;
|
private Button multiSelectForwardButton;
|
||||||
@@ -105,6 +106,7 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
|||||||
private boolean realtimeReloadRequiresFullSnapshot;
|
private boolean realtimeReloadRequiresFullSnapshot;
|
||||||
private boolean reloadInFlight;
|
private boolean reloadInFlight;
|
||||||
private boolean pendingReload;
|
private boolean pendingReload;
|
||||||
|
private boolean pendingReloadMessagesOnly;
|
||||||
private boolean pendingReloadForcedScrollToBottom;
|
private boolean pendingReloadForcedScrollToBottom;
|
||||||
private volatile boolean activityDestroyed;
|
private volatile boolean activityDestroyed;
|
||||||
private final Runnable conversationAutoRefreshRunnable = new Runnable() {
|
private final Runnable conversationAutoRefreshRunnable = new Runnable() {
|
||||||
@@ -114,7 +116,7 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
|||||||
if (!shouldMaintainConversationAutoRefresh()) {
|
if (!shouldMaintainConversationAutoRefresh()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!reloadInFlight && !refreshLayout.isRefreshing() && shouldAutoRefreshConversation()) {
|
if (!reloadInFlight && !isComposerBusy() && shouldAutoRefreshConversation()) {
|
||||||
reload(false);
|
reload(false);
|
||||||
}
|
}
|
||||||
armConversationAutoRefresh();
|
armConversationAutoRefresh();
|
||||||
@@ -280,6 +282,7 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
|||||||
|
|
||||||
BossWindowInsets.applyKeyboardAvoidingInset(composerRow);
|
BossWindowInsets.applyKeyboardAvoidingInset(composerRow);
|
||||||
BossWindowInsets.applyKeyboardAvoidingInset(multiSelectActionsLayout);
|
BossWindowInsets.applyKeyboardAvoidingInset(multiSelectActionsLayout);
|
||||||
|
bindChatRefreshScrollBridge();
|
||||||
|
|
||||||
updateProjectHeader(initialProjectName == null ? "项目详情" : initialProjectName, "正在同步项目详情...");
|
updateProjectHeader(initialProjectName == null ? "项目详情" : initialProjectName, "正在同步项目详情...");
|
||||||
if (composerAttachmentButton != null) {
|
if (composerAttachmentButton != null) {
|
||||||
@@ -352,6 +355,18 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
|||||||
reload(false);
|
reload(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void bindChatRefreshScrollBridge() {
|
||||||
|
if (refreshLayout == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
refreshLayout.setOnChildScrollUpCallback((parent, child) -> {
|
||||||
|
if (chatScrollView == null || chatScrollView.getVisibility() != View.VISIBLE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return chatScrollView.canScrollVertically(-1) || chatScrollView.getScrollY() > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void handleRealtimeEvent(BossRealtimeEvent event) {
|
void handleRealtimeEvent(BossRealtimeEvent event) {
|
||||||
if (event == null || event.eventName.isEmpty() || projectId == null || projectId.isEmpty()) {
|
if (event == null || event.eventName.isEmpty() || projectId == null || projectId.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
@@ -371,7 +386,7 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
|||||||
if (tryApplyRealtimeMessagesPatch(event)) {
|
if (tryApplyRealtimeMessagesPatch(event)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
runOnUiThread(() -> scheduleRealtimeReload(true));
|
runOnUiThread(() -> scheduleRealtimeReload(!"project.messages.updated".equals(event.eventName)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean tryApplyRealtimeMessagesPatch(BossRealtimeEvent event) {
|
private boolean tryApplyRealtimeMessagesPatch(BossRealtimeEvent event) {
|
||||||
@@ -562,6 +577,10 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (reloadInFlight) {
|
if (reloadInFlight) {
|
||||||
|
pendingReloadMessagesOnly = pendingReload && pendingReloadMessagesOnly && messagesOnly;
|
||||||
|
if (!pendingReload) {
|
||||||
|
pendingReloadMessagesOnly = messagesOnly;
|
||||||
|
}
|
||||||
pendingReload = true;
|
pendingReload = true;
|
||||||
pendingReloadForcedScrollToBottom = pendingReloadForcedScrollToBottom || forcedScrollToBottom;
|
pendingReloadForcedScrollToBottom = pendingReloadForcedScrollToBottom || forcedScrollToBottom;
|
||||||
return;
|
return;
|
||||||
@@ -641,20 +660,16 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean forcedScrollToBottom = pendingReloadForcedScrollToBottom;
|
boolean forcedScrollToBottom = pendingReloadForcedScrollToBottom;
|
||||||
|
boolean messagesOnly = pendingReloadMessagesOnly;
|
||||||
pendingReload = false;
|
pendingReload = false;
|
||||||
|
pendingReloadMessagesOnly = false;
|
||||||
pendingReloadForcedScrollToBottom = false;
|
pendingReloadForcedScrollToBottom = false;
|
||||||
reload(forcedScrollToBottom);
|
reloadSnapshot(forcedScrollToBottom, messagesOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setRefreshing(boolean refreshing) {
|
protected void setRefreshing(boolean refreshing) {
|
||||||
super.setRefreshing(refreshing);
|
super.setRefreshing(refreshing);
|
||||||
if (composerAttachmentButton != null) {
|
|
||||||
composerAttachmentButton.setEnabled(!refreshing && !composerSending);
|
|
||||||
}
|
|
||||||
if (composerInput != null) {
|
|
||||||
composerInput.setEnabled(!refreshing);
|
|
||||||
}
|
|
||||||
updateComposerSendButtonState();
|
updateComposerSendButtonState();
|
||||||
updateSelectionUi();
|
updateSelectionUi();
|
||||||
if (!refreshing) {
|
if (!refreshing) {
|
||||||
@@ -788,7 +803,7 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
|||||||
&& shouldMaintainConversationAutoRefresh()
|
&& shouldMaintainConversationAutoRefresh()
|
||||||
&& !reloadInFlight
|
&& !reloadInFlight
|
||||||
&& refreshLayout != null
|
&& refreshLayout != null
|
||||||
&& !refreshLayout.isRefreshing()) {
|
&& !isComposerBusy()) {
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
updateConversationAutoRefresh();
|
updateConversationAutoRefresh();
|
||||||
@@ -1017,7 +1032,6 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
|||||||
private void sendProjectMessage(String kind, String body) {
|
private void sendProjectMessage(String kind, String body) {
|
||||||
composerSending = true;
|
composerSending = true;
|
||||||
updateComposerSendButtonState();
|
updateComposerSendButtonState();
|
||||||
setRefreshing(true);
|
|
||||||
appendPendingOutgoingBubble(body);
|
appendPendingOutgoingBubble(body);
|
||||||
scrollChatToBottom();
|
scrollChatToBottom();
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
@@ -1220,7 +1234,6 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
|||||||
private void uploadAttachment(AttachmentComposerState.PendingAttachment attachment) {
|
private void uploadAttachment(AttachmentComposerState.PendingAttachment attachment) {
|
||||||
composerSending = true;
|
composerSending = true;
|
||||||
updateComposerSendButtonState();
|
updateComposerSendButtonState();
|
||||||
setRefreshing(true);
|
|
||||||
appendPendingOutgoingAttachment(attachment);
|
appendPendingOutgoingAttachment(attachment);
|
||||||
scrollChatToBottom();
|
scrollChatToBottom();
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
@@ -2361,8 +2374,13 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
|||||||
if (composerSendButton == null || composerInput == null) {
|
if (composerSendButton == null || composerInput == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
boolean composerBusy = isComposerBusy();
|
||||||
|
if (composerAttachmentButton != null) {
|
||||||
|
composerAttachmentButton.setEnabled(!composerBusy);
|
||||||
|
}
|
||||||
|
composerInput.setEnabled(!composerBusy);
|
||||||
String body = composerInput.getText() == null ? "" : composerInput.getText().toString();
|
String body = composerInput.getText() == null ? "" : composerInput.getText().toString();
|
||||||
composerSendButton.setEnabled(ProjectChatUiState.canSend(body, isComposerBusy()));
|
composerSendButton.setEnabled(ProjectChatUiState.canSend(body, composerBusy));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isComposerBusy() {
|
private boolean isComposerBusy() {
|
||||||
@@ -2698,7 +2716,6 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
|||||||
}
|
}
|
||||||
composerSending = true;
|
composerSending = true;
|
||||||
updateComposerSendButtonState();
|
updateComposerSendButtonState();
|
||||||
setRefreshing(true);
|
|
||||||
showMessage(waitingMessage);
|
showMessage(waitingMessage);
|
||||||
enqueueReplyWaitPoll(waitSpec.baselineMessageId, includeDispatchPlans);
|
enqueueReplyWaitPoll(waitSpec.baselineMessageId, includeDispatchPlans);
|
||||||
}
|
}
|
||||||
@@ -2781,7 +2798,6 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
|||||||
if (!hasReply && !isMasterAgentConversation()) {
|
if (!hasReply && !isMasterAgentConversation()) {
|
||||||
composerSending = true;
|
composerSending = true;
|
||||||
updateComposerSendButtonState();
|
updateComposerSendButtonState();
|
||||||
setRefreshing(true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
renderedInitialSnapshot = true;
|
renderedInitialSnapshot = true;
|
||||||
|
|||||||
@@ -141,18 +141,17 @@
|
|||||||
android:paddingRight="12dp"
|
android:paddingRight="12dp"
|
||||||
android:paddingBottom="12dp">
|
android:paddingBottom="12dp">
|
||||||
|
|
||||||
<Button
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/project_chat_attach"
|
android:id="@+id/project_chat_attach"
|
||||||
android:layout_width="44dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="44dp"
|
android:layout_height="48dp"
|
||||||
android:layout_marginRight="8dp"
|
android:layout_marginRight="8dp"
|
||||||
android:background="@drawable/bg_secondary_button"
|
android:background="@drawable/bg_secondary_button"
|
||||||
android:minWidth="0dp"
|
android:contentDescription="发送附件"
|
||||||
android:text="+"
|
android:padding="12dp"
|
||||||
android:textAllCaps="false"
|
android:scaleType="center"
|
||||||
android:textColor="@color/boss_text_primary"
|
android:src="@drawable/ic_boss_add"
|
||||||
android:textSize="20sp"
|
android:tint="@color/boss_text_primary" />
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/project_chat_input"
|
android:id="@+id/project_chat_input"
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ public class ProjectDetailActivityRealtimeTest {
|
|||||||
);
|
);
|
||||||
Shadows.shadowOf(activity.getMainLooper()).idle();
|
Shadows.shadowOf(activity.getMainLooper()).idle();
|
||||||
|
|
||||||
assertEquals(1, activity.reloadCount);
|
assertEquals(0, activity.reloadCount);
|
||||||
|
assertEquals(1, activity.messageReloadCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -131,7 +132,8 @@ public class ProjectDetailActivityRealtimeTest {
|
|||||||
);
|
);
|
||||||
Shadows.shadowOf(activity.getMainLooper()).idle();
|
Shadows.shadowOf(activity.getMainLooper()).idle();
|
||||||
|
|
||||||
assertEquals(2, activity.reloadCount);
|
assertEquals(1, activity.reloadCount);
|
||||||
|
assertEquals(1, activity.messageReloadCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -159,6 +161,7 @@ public class ProjectDetailActivityRealtimeTest {
|
|||||||
Shadows.shadowOf(activity.getMainLooper()).idle();
|
Shadows.shadowOf(activity.getMainLooper()).idle();
|
||||||
|
|
||||||
assertEquals(1, activity.reloadCount);
|
assertEquals(1, activity.reloadCount);
|
||||||
|
assertEquals(0, activity.messageReloadCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -196,7 +199,8 @@ public class ProjectDetailActivityRealtimeTest {
|
|||||||
);
|
);
|
||||||
Shadows.shadowOf(activity.getMainLooper()).idle();
|
Shadows.shadowOf(activity.getMainLooper()).idle();
|
||||||
|
|
||||||
assertEquals(1, activity.reloadCount);
|
assertEquals(0, activity.reloadCount);
|
||||||
|
assertEquals(1, activity.messageReloadCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -241,13 +245,15 @@ public class ProjectDetailActivityRealtimeTest {
|
|||||||
);
|
);
|
||||||
Shadows.shadowOf(activity.getMainLooper()).idle();
|
Shadows.shadowOf(activity.getMainLooper()).idle();
|
||||||
|
|
||||||
assertEquals(1, activity.loadCallCount);
|
assertEquals(0, activity.loadCallCount);
|
||||||
|
assertEquals(1, activity.messageLoadCallCount);
|
||||||
assertEquals(0, activity.renderCount);
|
assertEquals(0, activity.renderCount);
|
||||||
|
|
||||||
activity.releaseFirstLoad();
|
activity.releaseFirstLoad();
|
||||||
waitFor(() -> activity.renderCount == 2 && activity.loadCallCount == 2);
|
waitFor(() -> activity.renderCount == 2 && activity.messageLoadCallCount == 1 && activity.loadCallCount == 1);
|
||||||
|
|
||||||
assertEquals(2, activity.loadCallCount);
|
assertEquals(1, activity.loadCallCount);
|
||||||
|
assertEquals(1, activity.messageLoadCallCount);
|
||||||
assertEquals(2, activity.renderCount);
|
assertEquals(2, activity.renderCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,7 +319,9 @@ public class ProjectDetailActivityRealtimeTest {
|
|||||||
|
|
||||||
public static class TestRealtimeProjectDetailActivity extends ProjectDetailActivity {
|
public static class TestRealtimeProjectDetailActivity extends ProjectDetailActivity {
|
||||||
int reloadCount;
|
int reloadCount;
|
||||||
|
int messageReloadCount;
|
||||||
volatile int loadCallCount;
|
volatile int loadCallCount;
|
||||||
|
volatile int messageLoadCallCount;
|
||||||
volatile int renderCount;
|
volatile int renderCount;
|
||||||
private CountDownLatch firstLoadStarted;
|
private CountDownLatch firstLoadStarted;
|
||||||
private CountDownLatch releaseFirstLoad;
|
private CountDownLatch releaseFirstLoad;
|
||||||
@@ -344,6 +352,26 @@ public class ProjectDetailActivityRealtimeTest {
|
|||||||
super.reload();
|
super.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ProjectSnapshot loadProjectMessagesSnapshotForRefresh() throws Exception {
|
||||||
|
messageReloadCount += 1;
|
||||||
|
messageLoadCallCount += 1;
|
||||||
|
if (messageLoadCallCount == 1 && firstLoadStarted != null && releaseFirstLoad != null) {
|
||||||
|
firstLoadStarted.countDown();
|
||||||
|
releaseFirstLoad.await(2, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
return new ProjectSnapshot(
|
||||||
|
new JSONObject().put(
|
||||||
|
"project",
|
||||||
|
new JSONObject()
|
||||||
|
.put("name", "北区试产线")
|
||||||
|
.put("messages", new JSONArray())
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
ProjectSnapshot loadProjectSnapshotForRefresh() throws Exception {
|
ProjectSnapshot loadProjectSnapshotForRefresh() throws Exception {
|
||||||
loadCallCount += 1;
|
loadCallCount += 1;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import android.widget.LinearLayout;
|
|||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@@ -131,6 +132,22 @@ public class ProjectDetailActivityUiTest {
|
|||||||
assertEquals(R.id.project_chat_scroll, ((View) contentLayout.getParent()).getId());
|
assertEquals(R.id.project_chat_scroll, ((View) contentLayout.getParent()).getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void chatRefreshUsesScrollViewAsChildScrollReference() {
|
||||||
|
Intent intent = new Intent()
|
||||||
|
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "project-1")
|
||||||
|
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "北区试产线回归");
|
||||||
|
TestProjectDetailActivity activity = Robolectric
|
||||||
|
.buildActivity(TestProjectDetailActivity.class, intent)
|
||||||
|
.setup()
|
||||||
|
.get();
|
||||||
|
|
||||||
|
SwipeRefreshLayout refreshLayout = activity.findViewById(R.id.screen_refresh_layout);
|
||||||
|
|
||||||
|
Object childScrollCallback = ReflectionHelpers.getField(refreshLayout, "mChildScrollUpCallback");
|
||||||
|
assertNotNull(childScrollCallback);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void composerRowLayoutChangeWithFocusedInput_scrollsChatToBottomAgain() {
|
public void composerRowLayoutChangeWithFocusedInput_scrollsChatToBottomAgain() {
|
||||||
Intent intent = new Intent()
|
Intent intent = new Intent()
|
||||||
@@ -176,6 +193,48 @@ public class ProjectDetailActivityUiTest {
|
|||||||
assertEquals(baselineScrollCount, activity.scrollChatToBottomCount);
|
assertEquals(baselineScrollCount, activity.scrollChatToBottomCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void composerBusyWithoutRefreshSpinnerStillDisablesComposerInputs() {
|
||||||
|
Intent intent = new Intent()
|
||||||
|
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "project-1")
|
||||||
|
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "北区试产线回归");
|
||||||
|
TestProjectDetailActivity activity = Robolectric
|
||||||
|
.buildActivity(TestProjectDetailActivity.class, intent)
|
||||||
|
.setup()
|
||||||
|
.get();
|
||||||
|
|
||||||
|
ReflectionHelpers.setField(activity, "composerSending", true);
|
||||||
|
ReflectionHelpers.callInstanceMethod(activity, "updateComposerSendButtonState");
|
||||||
|
|
||||||
|
Button sendButton = activity.findViewById(R.id.project_chat_send);
|
||||||
|
View composerInput = activity.findViewById(R.id.project_chat_input);
|
||||||
|
View attachView = activity.findViewById(R.id.project_chat_attach);
|
||||||
|
SwipeRefreshLayout refreshLayout = activity.findViewById(R.id.screen_refresh_layout);
|
||||||
|
|
||||||
|
assertFalse(refreshLayout.isRefreshing());
|
||||||
|
assertFalse(sendButton.isEnabled());
|
||||||
|
assertFalse(composerInput.isEnabled());
|
||||||
|
assertFalse(attachView.isEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void composerAttachmentUsesIconButtonFrameInsteadOfTextButton() {
|
||||||
|
Intent intent = new Intent()
|
||||||
|
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "project-1")
|
||||||
|
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "北区试产线回归");
|
||||||
|
TestProjectDetailActivity activity = Robolectric
|
||||||
|
.buildActivity(TestProjectDetailActivity.class, intent)
|
||||||
|
.setup()
|
||||||
|
.get();
|
||||||
|
|
||||||
|
View attachView = activity.findViewById(R.id.project_chat_attach);
|
||||||
|
|
||||||
|
assertTrue(attachView instanceof ImageButton);
|
||||||
|
ViewGroup.LayoutParams params = attachView.getLayoutParams();
|
||||||
|
assertTrue(params.width >= BossUi.dp(activity, 46));
|
||||||
|
assertTrue(params.height >= BossUi.dp(activity, 46));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void manualAnalysisAttachmentShowsActionChip() throws Exception {
|
public void manualAnalysisAttachmentShowsActionChip() throws Exception {
|
||||||
Intent intent = new Intent()
|
Intent intent = new Intent()
|
||||||
|
|||||||
Reference in New Issue
Block a user