feat: add conversation quick actions fan menu
This commit is contained in:
@@ -10,6 +10,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
@@ -65,6 +66,11 @@ public class MainActivity extends AppCompatActivity {
|
||||
private EditText topSearchInput;
|
||||
private Button searchButton;
|
||||
private Button refreshButton;
|
||||
private View conversationQuickActionsOverlay;
|
||||
private View conversationQuickActionsScrim;
|
||||
private View quickActionAddDevice;
|
||||
private View quickActionScan;
|
||||
private View quickActionGroupChat;
|
||||
private Button tabConversations;
|
||||
private Button tabDevices;
|
||||
private Button tabMe;
|
||||
@@ -89,6 +95,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
private boolean pinnedConversationsCollapsed = false;
|
||||
private boolean conversationSelectionMode = false;
|
||||
private boolean conversationSearchMode = false;
|
||||
private boolean conversationQuickActionsVisible = false;
|
||||
private boolean conversationAutoRefreshArmed = false;
|
||||
private boolean conversationAutoRefreshEnabled = false;
|
||||
private final Set<String> selectedConversationProjectIds = new LinkedHashSet<>();
|
||||
@@ -136,6 +143,10 @@ public class MainActivity extends AppCompatActivity {
|
||||
exitConversationSearchMode(true);
|
||||
return;
|
||||
}
|
||||
if (contentPanel.getVisibility() == View.VISIBLE && conversationQuickActionsVisible) {
|
||||
hideConversationQuickActions(true);
|
||||
return;
|
||||
}
|
||||
if (contentPanel.getVisibility() == View.VISIBLE && !"conversations".equals(activeTab)) {
|
||||
setActiveTab("conversations", false);
|
||||
persistLastRootTab("conversations");
|
||||
@@ -191,6 +202,11 @@ public class MainActivity extends AppCompatActivity {
|
||||
topSearchInput = findViewById(R.id.top_search_input);
|
||||
searchButton = findViewById(R.id.search_button);
|
||||
refreshButton = findViewById(R.id.refresh_button);
|
||||
conversationQuickActionsOverlay = findViewById(R.id.conversation_quick_actions_overlay);
|
||||
conversationQuickActionsScrim = findViewById(R.id.conversation_quick_actions_scrim);
|
||||
quickActionAddDevice = findViewById(R.id.quick_action_add_device);
|
||||
quickActionScan = findViewById(R.id.quick_action_scan);
|
||||
quickActionGroupChat = findViewById(R.id.quick_action_group_chat);
|
||||
tabConversations = findViewById(R.id.tab_conversations);
|
||||
tabDevices = findViewById(R.id.tab_devices);
|
||||
tabMe = findViewById(R.id.tab_me);
|
||||
@@ -221,6 +237,19 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
});
|
||||
refreshButton.setOnClickListener(v -> handleTopAction());
|
||||
conversationQuickActionsScrim.setOnClickListener(v -> hideConversationQuickActions(true));
|
||||
quickActionAddDevice.setOnClickListener(v -> {
|
||||
hideConversationQuickActions(false);
|
||||
startActivity(new Intent(this, DeviceEnrollmentActivity.class));
|
||||
});
|
||||
quickActionScan.setOnClickListener(v -> {
|
||||
hideConversationQuickActions(false);
|
||||
showMessage("扫一扫即将接入");
|
||||
});
|
||||
quickActionGroupChat.setOnClickListener(v -> {
|
||||
hideConversationQuickActions(false);
|
||||
enterConversationSelectionMode();
|
||||
});
|
||||
tabConversations.setOnClickListener(v -> setActiveTab("conversations", true));
|
||||
tabDevices.setOnClickListener(v -> setActiveTab("devices", true));
|
||||
tabMe.setOnClickListener(v -> setActiveTab("me", true));
|
||||
@@ -478,6 +507,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
if (!"conversations".equals(tab)) {
|
||||
exitConversationSelectionMode();
|
||||
exitConversationSearchMode(false);
|
||||
hideConversationQuickActions(false);
|
||||
}
|
||||
activeTab = tab;
|
||||
if (fromUser) {
|
||||
@@ -612,7 +642,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
exitConversationSelectionMode();
|
||||
return;
|
||||
}
|
||||
enterConversationSelectionMode();
|
||||
toggleConversationQuickActions();
|
||||
return;
|
||||
}
|
||||
String actionKey = WechatSurfaceMapper.rootTopAction(activeTab, false, conversationSelectionMode).actionKey;
|
||||
@@ -640,8 +670,12 @@ public class MainActivity extends AppCompatActivity {
|
||||
appendConversationSelectionControls(items);
|
||||
}
|
||||
JSONArray filteredConversations = filterConversationItems(conversationsData, conversationSearchQuery);
|
||||
if (conversationSelectionMode) {
|
||||
filteredConversations = filterManualGroupSelectableConversationItems(filteredConversations);
|
||||
}
|
||||
if (filteredConversations == null || filteredConversations.length() == 0) {
|
||||
items.add(() -> BossUi.buildEmptyCard(this, "当前没有会话数据。"));
|
||||
String emptyText = conversationSelectionMode ? "当前没有可发起群聊的线程。" : "当前没有会话数据。";
|
||||
items.add(() -> BossUi.buildEmptyCard(this, emptyText));
|
||||
showListPage(items);
|
||||
return;
|
||||
}
|
||||
@@ -722,15 +756,15 @@ public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private void appendConversationSelectionControls(List<RootListItem> items) {
|
||||
items.add(() -> buildSelectionSummaryView());
|
||||
items.add(() -> {
|
||||
Button cancelButton = BossUi.buildMiniActionButton(this, "取消", false);
|
||||
cancelButton.setOnClickListener(v -> exitConversationSelectionMode());
|
||||
|
||||
Button cancelButton = BossUi.buildMiniActionButton(this, "取消", false);
|
||||
cancelButton.setOnClickListener(v -> exitConversationSelectionMode());
|
||||
|
||||
Button createButton = BossUi.buildMiniActionButton(this, "发起群聊", true);
|
||||
createButton.setEnabled(selectedConversationProjectIds.size() >= 2);
|
||||
createButton.setOnClickListener(v -> createStandaloneGroupChatFromSelection());
|
||||
|
||||
items.add(() -> BossUi.buildInlineActionRow(this, cancelButton, createButton));
|
||||
Button createButton = BossUi.buildMiniActionButton(this, "发起群聊", true);
|
||||
createButton.setEnabled(selectedConversationProjectIds.size() >= 2);
|
||||
createButton.setOnClickListener(v -> createStandaloneGroupChatFromSelection());
|
||||
return BossUi.buildInlineActionRow(this, cancelButton, createButton);
|
||||
});
|
||||
}
|
||||
|
||||
private LinearLayout buildSelectionSummaryView() {
|
||||
@@ -768,6 +802,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private void enterConversationSelectionMode() {
|
||||
exitConversationSearchMode(false);
|
||||
hideConversationQuickActions(false);
|
||||
conversationSelectionMode = true;
|
||||
selectedConversationProjectIds.clear();
|
||||
syncTopActionVisualState(screenRefresh.isRefreshing());
|
||||
@@ -790,6 +825,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
if (!"conversations".equals(activeTab)) {
|
||||
return;
|
||||
}
|
||||
hideConversationQuickActions(false);
|
||||
conversationSearchMode = true;
|
||||
syncTopActionVisualState(screenRefresh.isRefreshing());
|
||||
topSearchInput.post(() -> {
|
||||
@@ -900,6 +936,128 @@ public class MainActivity extends AppCompatActivity {
|
||||
return filtered;
|
||||
}
|
||||
|
||||
static JSONArray filterManualGroupSelectableConversationItems(@Nullable JSONArray source) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
JSONArray filtered = new JSONArray();
|
||||
for (int i = 0; i < source.length(); i++) {
|
||||
JSONObject item = source.optJSONObject(i);
|
||||
if (GroupCreateActivity.isEligibleForManualGroupSelection(item, null)) {
|
||||
filtered.put(item);
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
private void toggleConversationQuickActions() {
|
||||
if (conversationQuickActionsVisible) {
|
||||
hideConversationQuickActions(true);
|
||||
return;
|
||||
}
|
||||
showConversationQuickActions();
|
||||
}
|
||||
|
||||
private void showConversationQuickActions() {
|
||||
if (conversationQuickActionsOverlay == null || conversationQuickActionsVisible
|
||||
|| !"conversations".equals(activeTab) || conversationSelectionMode || conversationSearchMode) {
|
||||
return;
|
||||
}
|
||||
conversationQuickActionsVisible = true;
|
||||
conversationQuickActionsOverlay.setVisibility(View.VISIBLE);
|
||||
conversationQuickActionsOverlay.bringToFront();
|
||||
conversationQuickActionsScrim.setAlpha(0f);
|
||||
prepareConversationQuickAction(quickActionAddDevice);
|
||||
prepareConversationQuickAction(quickActionScan);
|
||||
prepareConversationQuickAction(quickActionGroupChat);
|
||||
conversationQuickActionsScrim.animate()
|
||||
.alpha(1f)
|
||||
.setDuration(160L)
|
||||
.setInterpolator(new AccelerateDecelerateInterpolator())
|
||||
.start();
|
||||
animateConversationQuickAction(quickActionAddDevice, -BossUi.dp(this, 12), BossUi.dp(this, 84), 0L);
|
||||
animateConversationQuickAction(quickActionScan, -BossUi.dp(this, 90), BossUi.dp(this, 50), 24L);
|
||||
animateConversationQuickAction(quickActionGroupChat, -BossUi.dp(this, 172), BossUi.dp(this, 14), 48L);
|
||||
}
|
||||
|
||||
private void hideConversationQuickActions(boolean animated) {
|
||||
if (conversationQuickActionsOverlay == null) {
|
||||
return;
|
||||
}
|
||||
if (!conversationQuickActionsVisible && conversationQuickActionsOverlay.getVisibility() != View.VISIBLE) {
|
||||
return;
|
||||
}
|
||||
conversationQuickActionsVisible = false;
|
||||
if (!animated) {
|
||||
conversationQuickActionsOverlay.setVisibility(View.GONE);
|
||||
conversationQuickActionsScrim.setAlpha(0f);
|
||||
resetConversationQuickAction(quickActionAddDevice);
|
||||
resetConversationQuickAction(quickActionScan);
|
||||
resetConversationQuickAction(quickActionGroupChat);
|
||||
return;
|
||||
}
|
||||
conversationQuickActionsScrim.animate()
|
||||
.alpha(0f)
|
||||
.setDuration(140L)
|
||||
.setInterpolator(new AccelerateDecelerateInterpolator())
|
||||
.start();
|
||||
collapseConversationQuickAction(quickActionAddDevice, 0L);
|
||||
collapseConversationQuickAction(quickActionScan, 20L);
|
||||
collapseConversationQuickAction(quickActionGroupChat, 40L);
|
||||
conversationQuickActionsOverlay.postDelayed(() -> {
|
||||
if (!conversationQuickActionsVisible) {
|
||||
conversationQuickActionsOverlay.setVisibility(View.GONE);
|
||||
resetConversationQuickAction(quickActionAddDevice);
|
||||
resetConversationQuickAction(quickActionScan);
|
||||
resetConversationQuickAction(quickActionGroupChat);
|
||||
}
|
||||
}, 190L);
|
||||
}
|
||||
|
||||
private void prepareConversationQuickAction(View actionView) {
|
||||
actionView.setVisibility(View.VISIBLE);
|
||||
actionView.setAlpha(0f);
|
||||
actionView.setScaleX(0.86f);
|
||||
actionView.setScaleY(0.86f);
|
||||
actionView.setTranslationX(0f);
|
||||
actionView.setTranslationY(0f);
|
||||
}
|
||||
|
||||
private void animateConversationQuickAction(View actionView, float translationX, float translationY, long delayMs) {
|
||||
actionView.animate()
|
||||
.alpha(1f)
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.translationX(translationX)
|
||||
.translationY(translationY)
|
||||
.setStartDelay(delayMs)
|
||||
.setDuration(180L)
|
||||
.setInterpolator(new AccelerateDecelerateInterpolator())
|
||||
.start();
|
||||
}
|
||||
|
||||
private void collapseConversationQuickAction(View actionView, long delayMs) {
|
||||
actionView.animate()
|
||||
.alpha(0f)
|
||||
.scaleX(0.88f)
|
||||
.scaleY(0.88f)
|
||||
.translationX(0f)
|
||||
.translationY(0f)
|
||||
.setStartDelay(delayMs)
|
||||
.setDuration(140L)
|
||||
.setInterpolator(new AccelerateDecelerateInterpolator())
|
||||
.start();
|
||||
}
|
||||
|
||||
private void resetConversationQuickAction(View actionView) {
|
||||
actionView.animate().cancel();
|
||||
actionView.setAlpha(0f);
|
||||
actionView.setScaleX(0.86f);
|
||||
actionView.setScaleY(0.86f);
|
||||
actionView.setTranslationX(0f);
|
||||
actionView.setTranslationY(0f);
|
||||
}
|
||||
|
||||
static boolean matchesConversationQuery(JSONObject item, String rawQuery) {
|
||||
if (item == null) {
|
||||
return false;
|
||||
|
||||
@@ -235,7 +235,7 @@ public final class WechatSurfaceMapper {
|
||||
if (selectionMode) {
|
||||
return new RootTopAction("取消", false, false, "cancel_select_conversations");
|
||||
}
|
||||
return new RootTopAction("+", false, true, "select_conversations");
|
||||
return new RootTopAction("+", false, true, "open_conversation_quick_actions");
|
||||
}
|
||||
return new RootTopAction(refreshing ? "同步中" : "刷新", false, false, "refresh");
|
||||
}
|
||||
|
||||
@@ -251,4 +251,82 @@
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/conversation_quick_actions_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:visibility="gone">
|
||||
|
||||
<View
|
||||
android:id="@+id/conversation_quick_actions_scrim"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:alpha="0"
|
||||
android:background="#33000000" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/conversation_quick_actions_anchor"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="22dp"
|
||||
android:paddingRight="20dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/quick_action_add_device"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top|end"
|
||||
android:background="@drawable/bg_secondary_button"
|
||||
android:minWidth="0dp"
|
||||
android:paddingLeft="14dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingRight="14dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:text="添加设备"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/boss_text_primary"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/quick_action_scan"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top|end"
|
||||
android:background="@drawable/bg_secondary_button"
|
||||
android:minWidth="0dp"
|
||||
android:paddingLeft="14dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingRight="14dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:text="扫一扫"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/boss_text_primary"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/quick_action_group_chat"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top|end"
|
||||
android:background="@drawable/bg_secondary_button"
|
||||
android:minWidth="0dp"
|
||||
android:paddingLeft="14dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingRight="14dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:text="发起群聊"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/boss_text_primary"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="invisible" />
|
||||
</FrameLayout>
|
||||
</FrameLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
Reference in New Issue
Block a user