diff --git a/android/app/src/main/java/com/hyzq/boss/BossScreenActivity.java b/android/app/src/main/java/com/hyzq/boss/BossScreenActivity.java index 985668f..9776544 100644 --- a/android/app/src/main/java/com/hyzq/boss/BossScreenActivity.java +++ b/android/app/src/main/java/com/hyzq/boss/BossScreenActivity.java @@ -2,7 +2,7 @@ package com.hyzq.boss; import android.os.Bundle; import android.view.View; -import android.widget.Button; +import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; @@ -18,9 +18,9 @@ public abstract class BossScreenActivity extends AppCompatActivity { protected final ExecutorService executor = Executors.newSingleThreadExecutor(); protected BossApiClient apiClient; - protected Button backButton; - protected Button refreshButton; - protected Button headerActionButton; + protected ImageButton backButton; + protected ImageButton refreshButton; + protected ImageButton headerActionButton; protected View topBarView; protected TextView titleView; protected TextView subtitleView; @@ -45,8 +45,9 @@ public abstract class BossScreenActivity extends AppCompatActivity { BossWindowInsets.applyStatusBarInset(topBarView); backButton.setOnClickListener(v -> finish()); - BossUi.applyTopActionButtonStyle(this, refreshButton, BossUi.TopActionButtonStyle.SECONDARY_TEXT); - BossUi.applyTopActionButtonStyle(this, headerActionButton, BossUi.TopActionButtonStyle.SECONDARY_TEXT); + BossUi.bindTopIconButton(this, backButton, BossUi.TopActionIcon.BACK, "返回"); + BossUi.bindTopIconButton(this, refreshButton, BossUi.TopActionIcon.REFRESH, "刷新"); + BossUi.bindTopIconButton(this, headerActionButton, BossUi.TopActionIcon.MORE, "更多"); refreshButton.setOnClickListener(v -> reload()); refreshLayout.setOnRefreshListener(this::reload); } @@ -68,8 +69,7 @@ public abstract class BossScreenActivity extends AppCompatActivity { protected void setHeaderAction(String label, android.view.View.OnClickListener listener) { headerActionButton.setVisibility(android.view.View.VISIBLE); - headerActionButton.setText(label); - BossUi.applyTopActionButtonStyle(this, headerActionButton, BossUi.TopActionButtonStyle.SECONDARY_TEXT); + BossUi.bindTopIconButton(this, headerActionButton, BossUi.topActionIconForLabel(label), label); headerActionButton.setOnClickListener(listener); } @@ -81,7 +81,8 @@ public abstract class BossScreenActivity extends AppCompatActivity { protected void setRefreshing(boolean refreshing) { refreshLayout.setRefreshing(refreshing); refreshButton.setEnabled(!refreshing); - refreshButton.setText(refreshing ? "同步中" : "刷新"); + refreshButton.setAlpha(refreshing ? 0.45f : 1f); + refreshButton.setContentDescription(refreshing ? "同步中" : "刷新"); } protected void replaceContent(android.view.View... views) { diff --git a/android/app/src/main/java/com/hyzq/boss/BossUi.java b/android/app/src/main/java/com/hyzq/boss/BossUi.java index 6cd791a..decbeb6 100644 --- a/android/app/src/main/java/com/hyzq/boss/BossUi.java +++ b/android/app/src/main/java/com/hyzq/boss/BossUi.java @@ -9,6 +9,7 @@ import android.graphics.Paint; import android.graphics.RectF; import android.graphics.Typeface; import android.graphics.drawable.GradientDrawable; +import android.widget.ImageButton; import android.text.Layout; import android.text.TextUtils; import android.util.TypedValue; @@ -22,6 +23,8 @@ import android.widget.TextView; import android.widget.Toast; import androidx.annotation.Nullable; +import androidx.core.widget.ImageViewCompat; +import android.content.res.ColorStateList; public final class BossUi { private static final int[] AVATAR_BG_COLORS = { @@ -51,6 +54,18 @@ public final class BossUi { COMPACT_ICON } + public enum TopActionIcon { + BACK, + CLOSE, + MORE, + ADD, + SEARCH, + REFRESH, + EDIT, + SAVE, + INFO + } + public static String formatRoleLabel(@Nullable String rawRole) { if (TextUtils.isEmpty(rawRole)) { return ""; @@ -101,6 +116,99 @@ public final class BossUi { button.setPadding(dp(context, 12), dp(context, 8), dp(context, 12), dp(context, 8)); } + public static void applyTopIconButtonStyle(Context context, ImageButton button) { + button.setBackgroundResource(R.drawable.bg_top_icon_button); + button.setMinimumWidth(dp(context, 40)); + button.setMinimumHeight(dp(context, 40)); + button.setPadding(dp(context, 8), dp(context, 8), dp(context, 8), dp(context, 8)); + ImageViewCompat.setImageTintList(button, ColorStateList.valueOf(context.getColor(R.color.boss_text_primary))); + } + + public static void bindTopIconButton(Context context, ImageButton button, TopActionIcon icon, String contentDescription) { + applyTopIconButtonStyle(context, button); + button.setImageResource(resolveTopActionIconRes(icon)); + button.setContentDescription(normalizeTopActionContentDescription(contentDescription, icon)); + } + + public static String normalizeTopActionContentDescription(@Nullable String raw, TopActionIcon icon) { + if (!TextUtils.isEmpty(raw)) { + if ("...".contentEquals(raw)) { + return "更多"; + } + return raw; + } + switch (icon) { + case BACK: + return "返回"; + case CLOSE: + return "关闭"; + case ADD: + return "新增"; + case SEARCH: + return "搜索"; + case REFRESH: + return "刷新"; + case EDIT: + return "编辑"; + case SAVE: + return "保存"; + case INFO: + return "信息"; + case MORE: + default: + return "更多"; + } + } + + public static TopActionIcon topActionIconForLabel(@Nullable String label) { + if (TextUtils.isEmpty(label)) { + return TopActionIcon.MORE; + } + if ("...".contentEquals(label)) { + return TopActionIcon.MORE; + } + if ("保存".contentEquals(label)) { + return TopActionIcon.SAVE; + } + if ("刷新".contentEquals(label)) { + return TopActionIcon.REFRESH; + } + if ("新增".contentEquals(label)) { + return TopActionIcon.ADD; + } + if ("编辑".contentEquals(label) || "编辑目标".contentEquals(label)) { + return TopActionIcon.EDIT; + } + if ("只读".contentEquals(label)) { + return TopActionIcon.INFO; + } + return TopActionIcon.MORE; + } + + private static int resolveTopActionIconRes(TopActionIcon icon) { + switch (icon) { + case BACK: + return R.drawable.ic_boss_back; + case CLOSE: + return R.drawable.ic_boss_close; + case ADD: + return R.drawable.ic_boss_add; + case SEARCH: + return R.drawable.ic_boss_search; + case REFRESH: + return R.drawable.ic_boss_refresh; + case EDIT: + return R.drawable.ic_boss_edit; + case SAVE: + return R.drawable.ic_boss_check; + case INFO: + return R.drawable.ic_boss_info; + case MORE: + default: + return R.drawable.ic_boss_more; + } + } + public static LinearLayout buildListRow( Context context, String title, 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 c5aa83d..f2d684a 100644 --- a/android/app/src/main/java/com/hyzq/boss/MainActivity.java +++ b/android/app/src/main/java/com/hyzq/boss/MainActivity.java @@ -14,6 +14,7 @@ import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.Button; import android.widget.EditText; import android.widget.FrameLayout; +import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.ScrollView; @@ -59,13 +60,13 @@ public class MainActivity extends AppCompatActivity { private Button loginButton; private ProgressBar loginProgress; - private Button backButton; + private ImageButton backButton; private TextView topTitle; private TextView topSubtitle; private LinearLayout topTitleGroup; private EditText topSearchInput; - private Button searchButton; - private Button refreshButton; + private ImageButton searchButton; + private ImageButton refreshButton; private View conversationQuickActionsOverlay; private View conversationQuickActionsScrim; private View conversationQuickActionsMenu; @@ -574,15 +575,11 @@ public class MainActivity extends AppCompatActivity { } private void configureTopAction(WechatSurfaceMapper.RootTopAction action) { - refreshButton.setText(action.label); - BossUi.applyTopActionButtonStyle( + BossUi.bindTopIconButton( this, refreshButton, - action.compactStyle - ? BossUi.TopActionButtonStyle.COMPACT_ICON - : action.primaryStyle - ? BossUi.TopActionButtonStyle.PRIMARY_TEXT - : BossUi.TopActionButtonStyle.SECONDARY_TEXT + resolveRootTopActionIcon(action.iconKey), + action.label ); } @@ -605,7 +602,7 @@ public class MainActivity extends AppCompatActivity { topTitleGroup.setVisibility(View.GONE); topSearchInput.setVisibility(View.VISIBLE); backButton.setVisibility(View.VISIBLE); - backButton.setText("取消"); + BossUi.bindTopIconButton(this, backButton, BossUi.TopActionIcon.CLOSE, "取消"); searchButton.setVisibility(View.GONE); refreshButton.setVisibility(View.GONE); if (!conversationSearchQuery.equals(topSearchInput.getText().toString())) { @@ -619,7 +616,7 @@ public class MainActivity extends AppCompatActivity { backButton.setVisibility(View.GONE); searchButton.setVisibility(conversationSelectionMode ? View.GONE : View.VISIBLE); refreshButton.setVisibility(View.VISIBLE); - BossUi.applyTopActionButtonStyle(this, searchButton, BossUi.TopActionButtonStyle.COMPACT_ICON); + BossUi.bindTopIconButton(this, searchButton, BossUi.TopActionIcon.SEARCH, "搜索"); WechatSurfaceMapper.RootTopAction action = WechatSurfaceMapper.rootTopAction(activeTab, false, conversationSelectionMode); configureTopAction(action); } @@ -633,6 +630,20 @@ public class MainActivity extends AppCompatActivity { WechatSurfaceMapper.RootTopAction action = WechatSurfaceMapper.rootTopAction(activeTab, refreshing, conversationSelectionMode); configureTopAction(action); refreshButton.setEnabled(!"refresh".equals(action.actionKey) || !refreshing); + refreshButton.setAlpha(refreshing && "refresh".equals(action.actionKey) ? 0.45f : 1f); + } + + private BossUi.TopActionIcon resolveRootTopActionIcon(String iconKey) { + if ("search".equals(iconKey)) { + return BossUi.TopActionIcon.SEARCH; + } + if ("refresh".equals(iconKey)) { + return BossUi.TopActionIcon.REFRESH; + } + if ("close".equals(iconKey)) { + return BossUi.TopActionIcon.CLOSE; + } + return BossUi.TopActionIcon.ADD; } private void handleTopAction() { diff --git a/android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java b/android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java index ae9c4d8..23d513e 100644 --- a/android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java +++ b/android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java @@ -1463,7 +1463,12 @@ public class ProjectDetailActivity extends BossScreenActivity { if (refreshLayout != null) { refreshLayout.setEnabled(bindings.enablePullRefresh); } - backButton.setText(bindings.backLabel); + BossUi.bindTopIconButton( + this, + backButton, + bindings.multiSelecting ? BossUi.TopActionIcon.CLOSE : BossUi.TopActionIcon.BACK, + bindings.backLabel + ); backButton.setOnClickListener(v -> { if (bindings.multiSelecting) { exitMultiSelect(); diff --git a/android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java b/android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java index fa45483..80aa89e 100644 --- a/android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java +++ b/android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java @@ -229,15 +229,15 @@ public final class WechatSurfaceMapper { public static RootTopAction rootTopAction(String activeTab, boolean refreshing, boolean selectionMode) { if ("devices".equals(activeTab)) { - return new RootTopAction("+添加", true, false, "add_device"); + return new RootTopAction("添加设备", false, true, "add", "add_device"); } if ("conversations".equals(activeTab)) { if (selectionMode) { - return new RootTopAction("取消", false, false, "cancel_select_conversations"); + return new RootTopAction("取消选择", false, true, "close", "cancel_select_conversations"); } - return new RootTopAction("+", false, true, "open_conversation_quick_actions"); + return new RootTopAction("快捷操作", false, true, "add", "open_conversation_quick_actions"); } - return new RootTopAction(refreshing ? "同步中" : "刷新", false, false, "refresh"); + return new RootTopAction("刷新", false, true, "refresh", "refresh"); } public static T resolveRefreshValue(T cachedValue, T freshValue, boolean requestSucceeded) { @@ -251,12 +251,14 @@ public final class WechatSurfaceMapper { public final String label; public final boolean primaryStyle; public final boolean compactStyle; + public final String iconKey; public final String actionKey; - RootTopAction(String label, boolean primaryStyle, boolean compactStyle, String actionKey) { + RootTopAction(String label, boolean primaryStyle, boolean compactStyle, String iconKey, String actionKey) { this.label = label; this.primaryStyle = primaryStyle; this.compactStyle = compactStyle; + this.iconKey = iconKey; this.actionKey = actionKey; } } diff --git a/android/app/src/main/res/drawable/bg_top_icon_button.xml b/android/app/src/main/res/drawable/bg_top_icon_button.xml new file mode 100644 index 0000000..330e9a7 --- /dev/null +++ b/android/app/src/main/res/drawable/bg_top_icon_button.xml @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/ic_boss_add.xml b/android/app/src/main/res/drawable/ic_boss_add.xml new file mode 100644 index 0000000..67f57cd --- /dev/null +++ b/android/app/src/main/res/drawable/ic_boss_add.xml @@ -0,0 +1,10 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_boss_back.xml b/android/app/src/main/res/drawable/ic_boss_back.xml new file mode 100644 index 0000000..0894cf2 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_boss_back.xml @@ -0,0 +1,10 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_boss_check.xml b/android/app/src/main/res/drawable/ic_boss_check.xml new file mode 100644 index 0000000..280d6ba --- /dev/null +++ b/android/app/src/main/res/drawable/ic_boss_check.xml @@ -0,0 +1,10 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_boss_close.xml b/android/app/src/main/res/drawable/ic_boss_close.xml new file mode 100644 index 0000000..5c2926f --- /dev/null +++ b/android/app/src/main/res/drawable/ic_boss_close.xml @@ -0,0 +1,10 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_boss_edit.xml b/android/app/src/main/res/drawable/ic_boss_edit.xml new file mode 100644 index 0000000..24bfdb7 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_boss_edit.xml @@ -0,0 +1,10 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_boss_info.xml b/android/app/src/main/res/drawable/ic_boss_info.xml new file mode 100644 index 0000000..c37bc10 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_boss_info.xml @@ -0,0 +1,10 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_boss_more.xml b/android/app/src/main/res/drawable/ic_boss_more.xml new file mode 100644 index 0000000..b694184 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_boss_more.xml @@ -0,0 +1,10 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_boss_refresh.xml b/android/app/src/main/res/drawable/ic_boss_refresh.xml new file mode 100644 index 0000000..139eeb5 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_boss_refresh.xml @@ -0,0 +1,10 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_boss_search.xml b/android/app/src/main/res/drawable/ic_boss_search.xml new file mode 100644 index 0000000..e05138d --- /dev/null +++ b/android/app/src/main/res/drawable/ic_boss_search.xml @@ -0,0 +1,10 @@ + + + + diff --git a/android/app/src/main/res/layout/activity_conversation_info.xml b/android/app/src/main/res/layout/activity_conversation_info.xml index c4da8c8..5f84aef 100644 --- a/android/app/src/main/res/layout/activity_conversation_info.xml +++ b/android/app/src/main/res/layout/activity_conversation_info.xml @@ -12,23 +12,20 @@ android:background="@color/boss_surface" android:gravity="center_vertical" android:orientation="horizontal" - android:paddingLeft="16dp" - android:paddingTop="16dp" - android:paddingRight="28dp" - android:paddingBottom="14dp"> + android:paddingLeft="20dp" + android:paddingTop="14dp" + android:paddingRight="20dp" + android:paddingBottom="12dp"> -