3482 lines
157 KiB
Java
3482 lines
157 KiB
Java
package com.example.livestreaming;
|
||
|
||
import android.Manifest;
|
||
import android.util.Log;
|
||
import android.content.res.AssetManager;
|
||
import android.content.ClipData;
|
||
import android.content.ClipboardManager;
|
||
import android.content.Intent;
|
||
import android.content.pm.PackageManager;
|
||
import android.net.Uri;
|
||
import android.os.Bundle;
|
||
import android.os.Handler;
|
||
import android.os.Looper;
|
||
import android.speech.RecognitionListener;
|
||
import android.speech.RecognizerIntent;
|
||
import android.speech.SpeechRecognizer;
|
||
import android.text.Editable;
|
||
import android.text.TextUtils;
|
||
import android.text.TextWatcher;
|
||
import android.view.View;
|
||
import android.widget.ArrayAdapter;
|
||
import android.widget.TextView;
|
||
import android.widget.Toast;
|
||
|
||
import androidx.annotation.NonNull;
|
||
import androidx.appcompat.app.AlertDialog;
|
||
import androidx.appcompat.app.AppCompatActivity;
|
||
import androidx.core.app.ActivityCompat;
|
||
import androidx.core.content.FileProvider;
|
||
import androidx.core.content.ContextCompat;
|
||
import androidx.core.view.GravityCompat;
|
||
import androidx.drawerlayout.widget.DrawerLayout;
|
||
import androidx.recyclerview.widget.GridLayoutManager;
|
||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||
import androidx.recyclerview.widget.RecyclerView;
|
||
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||
|
||
import com.bumptech.glide.Glide;
|
||
import com.example.livestreaming.databinding.ActivityMainBinding;
|
||
import com.example.livestreaming.databinding.DialogCreateRoomBinding;
|
||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||
import com.google.android.material.tabs.TabLayout;
|
||
import com.google.android.material.textfield.MaterialAutoCompleteTextView;
|
||
import com.google.android.material.textfield.TextInputLayout;
|
||
import com.example.livestreaming.net.ApiClient;
|
||
import com.example.livestreaming.net.ApiResponse;
|
||
import com.example.livestreaming.net.CommunityResponse;
|
||
import com.example.livestreaming.net.ConversationResponse;
|
||
import com.example.livestreaming.net.CreateRoomRequest;
|
||
import com.example.livestreaming.net.PageResponse;
|
||
import com.example.livestreaming.net.Room;
|
||
import com.example.livestreaming.net.StreamConfig;
|
||
|
||
import java.io.IOException;
|
||
import java.util.ArrayList;
|
||
import java.util.Collections;
|
||
import java.util.List;
|
||
import java.util.Map;
|
||
|
||
import retrofit2.Call;
|
||
import retrofit2.Callback;
|
||
import retrofit2.Response;
|
||
|
||
public class MainActivity extends AppCompatActivity {
|
||
|
||
private static final String TAG = "MainActivity";
|
||
|
||
private ActivityMainBinding binding;
|
||
private RoomsAdapter adapter;
|
||
|
||
private final List<Room> allRooms = new ArrayList<>();
|
||
private final List<Room> followRooms = new ArrayList<>(); // 关注页面的房间列表
|
||
private final List<Room> discoverRooms = new ArrayList<>(); // 发现页面的房间列表
|
||
private final List<NearbyUser> nearbyUsers = new ArrayList<>(); // 附近页面的用户列表
|
||
private NearbyUsersAdapter nearbyUsersAdapter; // 附近页面的适配器
|
||
|
||
// 新Tab布局相关
|
||
private RecommendUserAdapter recommendUserAdapter; // 关注页面推荐用户适配器
|
||
private ChannelTagAdapter myChannelAdapter; // 发现页面我的频道适配器
|
||
private ChannelTagAdapter recommendChannelAdapter; // 发现页面推荐频道适配器
|
||
private final List<RecommendUserAdapter.RecommendUser> recommendUsers = new ArrayList<>(); // 推荐用户列表
|
||
private final List<ChannelTagAdapter.ChannelTag> myChannels = new ArrayList<>(); // 我的频道列表
|
||
private final List<ChannelTagAdapter.ChannelTag> recommendChannels = new ArrayList<>(); // 推荐频道列表
|
||
|
||
private StaggeredGridLayoutManager roomsLayoutManager; // 房间列表的布局管理器
|
||
private LinearLayoutManager nearbyLayoutManager; // 附近用户列表的布局管理器
|
||
|
||
private String currentCategory = "推荐";
|
||
private String currentTopTab = "发现"; // 当前选中的顶部标签:关注、发现、附近
|
||
private CategoryFilterManager filterManager;
|
||
private int filterRequestId = 0; // 用于防止旧的筛选结果覆盖新的结果
|
||
|
||
// 缓存后端返回的所有直播间分类
|
||
private final List<com.example.livestreaming.net.CategoryResponse> allBackendCategories = new ArrayList<>();
|
||
|
||
private final Handler handler = new Handler(Looper.getMainLooper());
|
||
private Runnable pollRunnable;
|
||
|
||
private boolean isFetching;
|
||
private long lastFetchMs;
|
||
|
||
private static final int REQUEST_RECORD_AUDIO_PERMISSION = 200;
|
||
private static final int REQUEST_LOCATION_PERMISSION = 201;
|
||
private SpeechRecognizer speechRecognizer;
|
||
private Intent speechRecognizerIntent;
|
||
private boolean isListening = false;
|
||
|
||
@Override
|
||
protected void onCreate(Bundle savedInstanceState) {
|
||
super.onCreate(savedInstanceState);
|
||
|
||
// 用户打开APP时不需要强制登录,可以直接使用APP
|
||
// 只有在使用需要登录的功能时(如加好友、发送弹幕等),才检查登录状态
|
||
// 登录和注册功能已在LoginActivity中实现
|
||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||
setContentView(binding.getRoot());
|
||
|
||
// 清除之前缓存的自定义API地址,使用BuildConfig中的配置
|
||
ApiClient.clearCustomBaseUrl(getApplicationContext());
|
||
ApiClient.getService(getApplicationContext());
|
||
|
||
// 调试:打印当前使用的 API 地址
|
||
String currentApiUrl = ApiClient.getCurrentBaseUrl(getApplicationContext());
|
||
Log.d(TAG, "========== API 配置 ==========");
|
||
Log.d(TAG, "当前 API 地址: " + currentApiUrl);
|
||
Log.d(TAG, "BuildConfig EMULATOR: " + com.example.livestreaming.BuildConfig.API_BASE_URL_EMULATOR);
|
||
Log.d(TAG, "BuildConfig DEVICE: " + com.example.livestreaming.BuildConfig.API_BASE_URL_DEVICE);
|
||
Log.d(TAG, "==============================");
|
||
|
||
// 立即显示缓存数据,提升启动速度
|
||
setupRecyclerView();
|
||
setupUI();
|
||
loadAvatarFromPrefs();
|
||
setupSpeechRecognizer();
|
||
|
||
// 初始化未读消息数量
|
||
if (UnreadMessageManager.getUnreadCount(this) == 0) {
|
||
// 从会话列表获取总未读数量
|
||
fetchUnreadMessageCount();
|
||
}
|
||
|
||
// 初始化顶部标签页数据
|
||
initializeTopTabData();
|
||
|
||
// 默认显示"发现"界面
|
||
if (binding != null && binding.topTabs != null) {
|
||
// 在布局完成后,默认选中"发现"标签(索引为1:关注=0, 发现=1, 附近=2)
|
||
binding.topTabs.post(() -> {
|
||
TabLayout.Tab discoverTab = binding.topTabs.getTabAt(1); // "发现"标签的索引
|
||
if (discoverTab != null) {
|
||
// 选中"发现"标签,这会触发 onTabSelected 监听器,自动调用 showDiscoverTab()
|
||
discoverTab.select();
|
||
} else {
|
||
// 如果找不到标签,直接显示发现页面
|
||
showDiscoverTab();
|
||
}
|
||
});
|
||
} else {
|
||
// 如果 topTabs 为空,直接显示发现页面
|
||
showDiscoverTab();
|
||
}
|
||
|
||
// 异步加载资源文件,避免阻塞主线程
|
||
loadCoverAssetsAsync();
|
||
}
|
||
|
||
private void setupDrawerCards() {
|
||
if (binding == null || binding.drawerCards == null) return;
|
||
|
||
DrawerCardsAdapter adapter = new DrawerCardsAdapter(item -> {
|
||
if (item == null) return;
|
||
handleDrawerAction(item);
|
||
|
||
DrawerLayout drawerLayout = binding.drawerLayout;
|
||
if (drawerLayout != null) {
|
||
drawerLayout.closeDrawer(GravityCompat.START);
|
||
}
|
||
});
|
||
|
||
binding.drawerCards.drawerRecyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||
binding.drawerCards.drawerRecyclerView.setAdapter(adapter);
|
||
|
||
List<DrawerCardItem> items = new ArrayList<>();
|
||
items.add(new DrawerCardItem(DrawerCardItem.ACTION_PROFILE, "个人主页", "资料、作品、粉丝", R.drawable.ic_person_24));
|
||
items.add(new DrawerCardItem(DrawerCardItem.ACTION_MESSAGES, "消息", "私信、互动通知", R.drawable.ic_chat_24));
|
||
items.add(new DrawerCardItem(DrawerCardItem.ACTION_MY_FRIENDS, "我的好友", "通讯录与挚友", R.drawable.ic_people_24));
|
||
items.add(new DrawerCardItem(DrawerCardItem.ACTION_GROUPS, "我的群组", "群聊与群组管理", R.drawable.ic_group_24));
|
||
items.add(new DrawerCardItem(DrawerCardItem.ACTION_FISH_POND, "缘池", "附近与社交圈", R.drawable.ic_people_24));
|
||
items.add(new DrawerCardItem(DrawerCardItem.ACTION_FOLLOWING, "我的关注", "你关注的主播", R.drawable.ic_people_24));
|
||
// 隐藏粉丝和获赞菜单项
|
||
// items.add(new DrawerCardItem(DrawerCardItem.ACTION_FANS, "粉丝", "关注你的人", R.drawable.ic_people_24));
|
||
// items.add(new DrawerCardItem(DrawerCardItem.ACTION_LIKES, "获赞", "收到的点赞", R.drawable.ic_heart_24));
|
||
items.add(new DrawerCardItem(DrawerCardItem.ACTION_HISTORY, "观看历史", "最近看过的直播", R.drawable.ic_grid_24));
|
||
items.add(new DrawerCardItem(DrawerCardItem.ACTION_SEARCH, "搜索", "找主播/房间/标签", R.drawable.ic_search_24));
|
||
items.add(new DrawerCardItem(DrawerCardItem.ACTION_SETTINGS, "设置", "账号、隐私、通知", R.drawable.ic_menu_24));
|
||
items.add(new DrawerCardItem(DrawerCardItem.ACTION_HELP, "帮助与反馈", "常见问题与建议", R.drawable.ic_chat_24));
|
||
items.add(new DrawerCardItem(DrawerCardItem.ACTION_ABOUT, "关于", "版本信息与协议", R.drawable.ic_menu_24));
|
||
|
||
adapter.submitList(items);
|
||
}
|
||
|
||
private void handleDrawerAction(DrawerCardItem item) {
|
||
int action = item.getAction();
|
||
if (action == DrawerCardItem.ACTION_PROFILE) {
|
||
// 检查登录状态,个人主页需要登录
|
||
if (!AuthHelper.requireLogin(this, "查看个人主页需要登录")) {
|
||
return;
|
||
}
|
||
ProfileActivity.start(this);
|
||
finish();
|
||
return;
|
||
}
|
||
if (action == DrawerCardItem.ACTION_MESSAGES) {
|
||
// MessagesActivity内部已检查登录,这里直接跳转
|
||
MessagesActivity.start(this);
|
||
finish();
|
||
return;
|
||
}
|
||
if (action == DrawerCardItem.ACTION_MY_FRIENDS) {
|
||
// 检查登录状态,我的好友需要登录
|
||
if (!AuthHelper.requireLogin(this, "查看好友列表需要登录")) {
|
||
return;
|
||
}
|
||
startActivity(new Intent(this, MyFriendsActivity.class));
|
||
return;
|
||
}
|
||
if (action == DrawerCardItem.ACTION_GROUPS) {
|
||
// 检查登录状态,我的群组需要登录
|
||
if (!AuthHelper.requireLogin(this, "查看群组列表需要登录")) {
|
||
return;
|
||
}
|
||
GroupListActivity.start(this);
|
||
return;
|
||
}
|
||
if (action == DrawerCardItem.ACTION_FISH_POND) {
|
||
// 检查登录状态,缘池功能需要登录
|
||
if (!AuthHelper.requireLogin(this, "缘池功能需要登录")) {
|
||
return;
|
||
}
|
||
startActivity(new Intent(this, FishPondActivity.class));
|
||
finish();
|
||
return;
|
||
}
|
||
if (action == DrawerCardItem.ACTION_FOLLOWING) {
|
||
// 检查登录状态,我的关注需要登录
|
||
if (!AuthHelper.requireLogin(this, "查看关注列表需要登录")) {
|
||
return;
|
||
}
|
||
FollowingListActivity.start(this);
|
||
return;
|
||
}
|
||
if (action == DrawerCardItem.ACTION_FANS) {
|
||
// 检查登录状态,我的粉丝需要登录
|
||
if (!AuthHelper.requireLogin(this, "查看粉丝列表需要登录")) {
|
||
return;
|
||
}
|
||
FansListActivity.start(this);
|
||
return;
|
||
}
|
||
if (action == DrawerCardItem.ACTION_LIKES) {
|
||
// 检查登录状态,获赞列表需要登录
|
||
if (!AuthHelper.requireLogin(this, "查看获赞列表需要登录")) {
|
||
return;
|
||
}
|
||
LikesListActivity.start(this);
|
||
return;
|
||
}
|
||
if (action == DrawerCardItem.ACTION_HISTORY) {
|
||
// 检查登录状态,观看历史需要登录
|
||
if (!AuthHelper.requireLogin(this, "查看观看历史需要登录")) {
|
||
return;
|
||
}
|
||
MyRecordsActivity.start(this);
|
||
return;
|
||
}
|
||
if (action == DrawerCardItem.ACTION_SEARCH) {
|
||
// 搜索功能不需要登录,游客也可以搜索
|
||
SearchActivity.start(this);
|
||
return;
|
||
}
|
||
if (action == DrawerCardItem.ACTION_SETTINGS) {
|
||
// SettingsPageActivity内部已检查登录,这里直接跳转
|
||
SettingsPageActivity.start(this, "");
|
||
return;
|
||
}
|
||
if (action == DrawerCardItem.ACTION_HELP) {
|
||
// 帮助页面不需要登录
|
||
SettingsPageActivity.start(this, SettingsPageActivity.PAGE_HELP);
|
||
return;
|
||
}
|
||
if (action == DrawerCardItem.ACTION_ABOUT) {
|
||
SettingsPageActivity.start(this, SettingsPageActivity.PAGE_ABOUT);
|
||
return;
|
||
}
|
||
String t = item.getTitle() != null ? item.getTitle() : "";
|
||
Toast.makeText(this, "暂未接入:" + t, Toast.LENGTH_SHORT).show();
|
||
}
|
||
|
||
private void setupRecyclerView() {
|
||
adapter = new RoomsAdapter(room -> {
|
||
if (room == null) return;
|
||
Intent intent = new Intent(MainActivity.this, RoomDetailActivity.class);
|
||
intent.putExtra(RoomDetailActivity.EXTRA_ROOM_ID, room.getId());
|
||
startActivity(intent);
|
||
});
|
||
|
||
// 保存房间列表的布局管理器
|
||
roomsLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
|
||
roomsLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);
|
||
|
||
// 创建附近用户列表的布局管理器
|
||
nearbyLayoutManager = new LinearLayoutManager(this);
|
||
|
||
binding.roomsRecyclerView.setLayoutManager(roomsLayoutManager);
|
||
binding.roomsRecyclerView.setAdapter(adapter);
|
||
|
||
// 配置RecyclerView动画,优化筛选时的过渡效果
|
||
androidx.recyclerview.widget.DefaultItemAnimator animator = new androidx.recyclerview.widget.DefaultItemAnimator();
|
||
animator.setAddDuration(200); // 添加动画时长
|
||
animator.setRemoveDuration(200); // 删除动画时长
|
||
animator.setMoveDuration(200); // 移动动画时长
|
||
animator.setChangeDuration(200); // 变更动画时长
|
||
binding.roomsRecyclerView.setItemAnimator(animator);
|
||
|
||
// 注意:房间数据会在 showDiscoverTab() 中从 discoverRooms 加载
|
||
// 这里不再直接显示数据,而是等待顶部标签初始化完成后再显示
|
||
}
|
||
|
||
private void setupUI() {
|
||
// 启用下拉刷新
|
||
if (binding.swipeRefresh != null) {
|
||
binding.swipeRefresh.setOnRefreshListener(() -> {
|
||
fetchDiscoverRooms();
|
||
});
|
||
}
|
||
|
||
setupDrawerCards();
|
||
|
||
binding.menuButton.setOnClickListener(new DebounceClickListener() {
|
||
@Override
|
||
public void onDebouncedClick(View v) {
|
||
DrawerLayout drawerLayout = binding.drawerLayout;
|
||
if (drawerLayout == null) return;
|
||
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
|
||
drawerLayout.closeDrawer(GravityCompat.START);
|
||
} else {
|
||
drawerLayout.openDrawer(GravityCompat.START);
|
||
}
|
||
}
|
||
});
|
||
|
||
binding.avatarButton.setOnClickListener(new DebounceClickListener() {
|
||
@Override
|
||
public void onDebouncedClick(View v) {
|
||
// 检查登录状态,个人主页需要登录
|
||
if (!AuthHelper.requireLogin(MainActivity.this, "查看个人主页需要登录")) {
|
||
return;
|
||
}
|
||
ProfileActivity.start(MainActivity.this);
|
||
finish();
|
||
}
|
||
});
|
||
|
||
// 设置搜索按钮点击事件
|
||
View searchButton = findViewById(R.id.searchButton);
|
||
if (searchButton != null) {
|
||
searchButton.setOnClickListener(new DebounceClickListener() {
|
||
@Override
|
||
public void onDebouncedClick(View v) {
|
||
SearchActivity.start(MainActivity.this);
|
||
}
|
||
});
|
||
}
|
||
|
||
// 设置通知图标点击事件(如果存在)
|
||
try {
|
||
View notificationIcon = findViewById(R.id.notificationIcon);
|
||
if (notificationIcon != null) {
|
||
notificationIcon.setOnClickListener(new DebounceClickListener() {
|
||
@Override
|
||
public void onDebouncedClick(View v) {
|
||
NotificationsActivity.start(MainActivity.this);
|
||
}
|
||
});
|
||
}
|
||
} catch (Exception e) {
|
||
// 如果通知图标不存在,忽略错误
|
||
}
|
||
|
||
binding.topTabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||
@Override
|
||
public void onTabSelected(TabLayout.Tab tab) {
|
||
if (tab == null) return;
|
||
// 用户点击后,显示选中样式(显示指示器,改变文字颜色)
|
||
binding.topTabs.setSelectedTabIndicatorHeight(2); // 显示指示器
|
||
// 更新布局属性以显示选中样式
|
||
binding.topTabs.setTabTextColors(
|
||
getResources().getColor(android.R.color.darker_gray, null), // 未选中颜色
|
||
getResources().getColor(R.color.purple_500, null) // 选中颜色
|
||
);
|
||
CharSequence title = tab.getText();
|
||
currentTopTab = title != null ? title.toString() : "发现";
|
||
// 切换显示的内容
|
||
switchTopTabContent(currentTopTab);
|
||
}
|
||
|
||
@Override
|
||
public void onTabUnselected(TabLayout.Tab tab) {
|
||
// 取消选中时,恢复默认样式
|
||
}
|
||
|
||
@Override
|
||
public void onTabReselected(TabLayout.Tab tab) {
|
||
if (tab == null) return;
|
||
CharSequence title = tab.getText();
|
||
currentTopTab = title != null ? title.toString() : "发现";
|
||
// 切换显示的内容
|
||
switchTopTabContent(currentTopTab);
|
||
}
|
||
});
|
||
|
||
// 从后端加载分类数据
|
||
loadCategoriesFromBackend();
|
||
|
||
binding.categoryTabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||
@Override
|
||
public void onTabSelected(TabLayout.Tab tab) {
|
||
if (tab == null) return;
|
||
CharSequence title = tab.getText();
|
||
currentCategory = title != null ? title.toString() : "推荐";
|
||
// 保存选中的分类
|
||
CategoryFilterManager.saveLastCategory(MainActivity.this, currentCategory);
|
||
// 应用筛选(带动画)
|
||
applyCategoryFilterWithAnimation(currentCategory);
|
||
}
|
||
|
||
@Override
|
||
public void onTabUnselected(TabLayout.Tab tab) {
|
||
}
|
||
|
||
@Override
|
||
public void onTabReselected(TabLayout.Tab tab) {
|
||
if (tab == null) return;
|
||
CharSequence title = tab.getText();
|
||
currentCategory = title != null ? title.toString() : "推荐";
|
||
// 保存选中的分类
|
||
CategoryFilterManager.saveLastCategory(MainActivity.this, currentCategory);
|
||
// 应用筛选(带动画)
|
||
applyCategoryFilterWithAnimation(currentCategory);
|
||
}
|
||
});
|
||
|
||
binding.roomsRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||
@Override
|
||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||
if (dy <= 0) return;
|
||
RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();
|
||
if (!(lm instanceof StaggeredGridLayoutManager)) return;
|
||
StaggeredGridLayoutManager sglm = (StaggeredGridLayoutManager) lm;
|
||
int total = sglm.getItemCount();
|
||
int[] last = sglm.findLastVisibleItemPositions(null);
|
||
int lastVisible = -1;
|
||
if (last != null) {
|
||
for (int v : last) {
|
||
if (v > lastVisible) lastVisible = v;
|
||
}
|
||
}
|
||
if (total <= 0) return;
|
||
|
||
if (lastVisible >= total - 4) {
|
||
long now = System.currentTimeMillis();
|
||
if (!isFetching && now - lastFetchMs > 1500) {
|
||
// 注释掉加载更多,使用静态数据
|
||
// fetchRooms();
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
// 设置添加直播按钮点击事件(添加防抖)
|
||
binding.fabAddLive.setOnClickListener(new DebounceClickListener() {
|
||
@Override
|
||
public void onDebouncedClick(View v) {
|
||
showCreateRoomDialog();
|
||
}
|
||
});
|
||
|
||
// 设置搜索框文本监听和图标切换
|
||
setupSearchBox();
|
||
|
||
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
|
||
bottomNavigation.setSelectedItemId(R.id.nav_home);
|
||
|
||
// 更新未读消息徽章
|
||
UnreadMessageManager.updateBadge(bottomNavigation);
|
||
|
||
bottomNavigation.setOnItemSelectedListener(item -> {
|
||
int id = item.getItemId();
|
||
if (id == R.id.nav_home) {
|
||
return true;
|
||
}
|
||
if (id == R.id.nav_friends) {
|
||
startActivity(new Intent(this, FishPondActivity.class));
|
||
finish();
|
||
return true;
|
||
}
|
||
if (id == R.id.nav_wish_tree) {
|
||
WishTreeActivity.start(this);
|
||
finish();
|
||
return true;
|
||
}
|
||
if (id == R.id.nav_messages) {
|
||
MessagesActivity.start(this);
|
||
finish();
|
||
return true;
|
||
}
|
||
if (id == R.id.nav_profile) {
|
||
// 检查登录状态,个人主页需要登录
|
||
if (!AuthHelper.requireLogin(this, "查看个人主页需要登录")) {
|
||
return false;
|
||
}
|
||
ProfileActivity.start(this);
|
||
finish();
|
||
return true;
|
||
}
|
||
return true;
|
||
});
|
||
}
|
||
|
||
private void setupSearchBox() {
|
||
// 监听搜索框文本变化
|
||
binding.searchEdit.addTextChangedListener(new TextWatcher() {
|
||
@Override
|
||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||
}
|
||
|
||
@Override
|
||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||
// 根据文本是否为空切换图标
|
||
String text = s != null ? s.toString().trim() : "";
|
||
if (text.isEmpty()) {
|
||
// 文本为空,显示麦克风图标
|
||
binding.micIcon.setImageResource(R.drawable.ic_mic_24);
|
||
binding.micIcon.setContentDescription("mic");
|
||
// 清除搜索筛选,显示所有房间
|
||
applySearchFilter("");
|
||
} else {
|
||
// 文本不为空,显示搜索图标
|
||
binding.micIcon.setImageResource(R.drawable.ic_search_24);
|
||
binding.micIcon.setContentDescription("search");
|
||
// 实时筛选房间
|
||
applySearchFilter(text);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void afterTextChanged(Editable s) {
|
||
}
|
||
});
|
||
|
||
// 设置麦克风/搜索图标点击事件
|
||
binding.micIcon.setOnClickListener(v -> {
|
||
String searchText = binding.searchEdit.getText() != null ?
|
||
binding.searchEdit.getText().toString().trim() : "";
|
||
|
||
if (searchText.isEmpty()) {
|
||
// 如果文本为空,启动语音识别
|
||
startVoiceRecognition();
|
||
} else {
|
||
// 如果文本不为空,跳转到搜索页面
|
||
SearchActivity.start(MainActivity.this, searchText);
|
||
}
|
||
});
|
||
}
|
||
|
||
private void setupSpeechRecognizer() {
|
||
// 检查设备是否支持语音识别
|
||
if (!SpeechRecognizer.isRecognitionAvailable(this)) {
|
||
return;
|
||
}
|
||
|
||
speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this);
|
||
speechRecognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
|
||
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
|
||
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "zh-CN"); // 设置为中文
|
||
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
|
||
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1);
|
||
|
||
speechRecognizer.setRecognitionListener(new RecognitionListener() {
|
||
@Override
|
||
public void onReadyForSpeech(Bundle params) {
|
||
isListening = true;
|
||
// 可以在这里显示"正在聆听..."的提示
|
||
binding.micIcon.setAlpha(0.5f);
|
||
}
|
||
|
||
@Override
|
||
public void onBeginningOfSpeech() {
|
||
// 开始说话
|
||
}
|
||
|
||
@Override
|
||
public void onRmsChanged(float rmsdB) {
|
||
// 音量变化,可以用来显示音量动画
|
||
}
|
||
|
||
@Override
|
||
public void onBufferReceived(byte[] buffer) {
|
||
// 接收音频数据
|
||
}
|
||
|
||
@Override
|
||
public void onEndOfSpeech() {
|
||
isListening = false;
|
||
binding.micIcon.setAlpha(1.0f);
|
||
}
|
||
|
||
@Override
|
||
public void onError(int error) {
|
||
isListening = false;
|
||
binding.micIcon.setAlpha(1.0f);
|
||
|
||
String errorMessage;
|
||
switch (error) {
|
||
case SpeechRecognizer.ERROR_AUDIO:
|
||
errorMessage = "音频错误";
|
||
break;
|
||
case SpeechRecognizer.ERROR_CLIENT:
|
||
errorMessage = "客户端错误";
|
||
break;
|
||
case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS:
|
||
errorMessage = "权限不足";
|
||
requestAudioPermission();
|
||
return;
|
||
case SpeechRecognizer.ERROR_NETWORK:
|
||
errorMessage = "网络错误";
|
||
break;
|
||
case SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
|
||
errorMessage = "网络超时";
|
||
break;
|
||
case SpeechRecognizer.ERROR_NO_MATCH:
|
||
errorMessage = "未识别到语音";
|
||
break;
|
||
case SpeechRecognizer.ERROR_RECOGNIZER_BUSY:
|
||
errorMessage = "识别器忙碌";
|
||
break;
|
||
case SpeechRecognizer.ERROR_SERVER:
|
||
errorMessage = "服务器错误";
|
||
break;
|
||
case SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
|
||
errorMessage = "未检测到语音";
|
||
break;
|
||
default:
|
||
errorMessage = "未知错误";
|
||
break;
|
||
}
|
||
|
||
// 只在非用户取消的情况下显示错误提示
|
||
if (error != SpeechRecognizer.ERROR_CLIENT) {
|
||
Toast.makeText(MainActivity.this, errorMessage, Toast.LENGTH_SHORT).show();
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onResults(Bundle results) {
|
||
isListening = false;
|
||
binding.micIcon.setAlpha(1.0f);
|
||
|
||
ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
|
||
if (matches != null && !matches.isEmpty()) {
|
||
String spokenText = matches.get(0);
|
||
// 将识别结果填充到搜索框
|
||
binding.searchEdit.setText(spokenText);
|
||
binding.searchEdit.setSelection(spokenText.length());
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onPartialResults(Bundle partialResults) {
|
||
// 部分结果,可以实时显示
|
||
ArrayList<String> matches = partialResults.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
|
||
if (matches != null && !matches.isEmpty()) {
|
||
String partialText = matches.get(0);
|
||
// 可以在这里实时更新搜索框(可选)
|
||
// binding.searchEdit.setText(partialText);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onEvent(int eventType, Bundle params) {
|
||
// 事件回调
|
||
}
|
||
});
|
||
}
|
||
|
||
private void startVoiceRecognition() {
|
||
// 检查权限
|
||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
|
||
!= PackageManager.PERMISSION_GRANTED) {
|
||
requestAudioPermission();
|
||
return;
|
||
}
|
||
|
||
// 检查是否正在监听
|
||
if (isListening) {
|
||
stopVoiceRecognition();
|
||
return;
|
||
}
|
||
|
||
// 检查SpeechRecognizer是否可用
|
||
if (speechRecognizer == null || speechRecognizerIntent == null) {
|
||
Toast.makeText(this, "语音识别功能不可用", Toast.LENGTH_SHORT).show();
|
||
return;
|
||
}
|
||
|
||
try {
|
||
speechRecognizer.startListening(speechRecognizerIntent);
|
||
} catch (Exception e) {
|
||
Toast.makeText(this, "启动语音识别失败", Toast.LENGTH_SHORT).show();
|
||
}
|
||
}
|
||
|
||
private void stopVoiceRecognition() {
|
||
if (speechRecognizer != null && isListening) {
|
||
speechRecognizer.stopListening();
|
||
isListening = false;
|
||
binding.micIcon.setAlpha(1.0f);
|
||
}
|
||
}
|
||
|
||
private void requestAudioPermission() {
|
||
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) {
|
||
new AlertDialog.Builder(this)
|
||
.setTitle("需要麦克风权限")
|
||
.setMessage("语音搜索需要访问麦克风权限,请在设置中允许")
|
||
.setPositiveButton("确定", (dialog, which) -> {
|
||
ActivityCompat.requestPermissions(this,
|
||
new String[]{Manifest.permission.RECORD_AUDIO},
|
||
REQUEST_RECORD_AUDIO_PERMISSION);
|
||
})
|
||
.setNegativeButton("取消", null)
|
||
.show();
|
||
} else {
|
||
ActivityCompat.requestPermissions(this,
|
||
new String[]{Manifest.permission.RECORD_AUDIO},
|
||
REQUEST_RECORD_AUDIO_PERMISSION);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||
if (requestCode == REQUEST_RECORD_AUDIO_PERMISSION) {
|
||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||
// 权限已授予,可以开始语音识别
|
||
startVoiceRecognition();
|
||
} else {
|
||
Toast.makeText(this, "需要麦克风权限才能使用语音搜索", Toast.LENGTH_SHORT).show();
|
||
}
|
||
} else if (requestCode == REQUEST_LOCATION_PERMISSION) {
|
||
// 处理位置权限请求结果
|
||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||
// 权限已授予,刷新附近页面显示数据
|
||
showNearbyTab();
|
||
} else {
|
||
// 权限被拒绝
|
||
// 检查是否是因为用户选择了"不再询问"
|
||
// 如果 shouldShowRequestPermissionRationale 返回 false,说明用户可能选择了"不再询问"
|
||
boolean shouldShowRationale = ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION);
|
||
if (!shouldShowRationale) {
|
||
// 用户可能选择了"不再询问",提示去设置中开启
|
||
new AlertDialog.Builder(this)
|
||
.setTitle("需要位置权限")
|
||
.setMessage("您已拒绝位置权限,如需使用附近功能,请在设置中手动开启位置权限。")
|
||
.setPositiveButton("去设置", (dialog, which) -> {
|
||
Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||
intent.setData(android.net.Uri.parse("package:" + getPackageName()));
|
||
startActivity(intent);
|
||
})
|
||
.setNegativeButton("取消", null)
|
||
.show();
|
||
}
|
||
// 不切换页面,让用户停留在附近页面
|
||
// 界面已经显示了需要权限的提示,用户可以再次点击"授权"按钮
|
||
}
|
||
}
|
||
}
|
||
|
||
@Override
|
||
protected void onDestroy() {
|
||
super.onDestroy();
|
||
if (speechRecognizer != null) {
|
||
speechRecognizer.destroy();
|
||
speechRecognizer = null;
|
||
}
|
||
}
|
||
|
||
private void loadAvatarFromPrefs() {
|
||
try {
|
||
String avatarUri = getSharedPreferences("profile_prefs", MODE_PRIVATE)
|
||
.getString("profile_avatar_uri", null);
|
||
if (!TextUtils.isEmpty(avatarUri)) {
|
||
Uri uri = Uri.parse(avatarUri);
|
||
// 如果是 file:// 协议,尝试转换为 FileProvider URI
|
||
if ("file".equals(uri.getScheme())) {
|
||
try {
|
||
java.io.File file = new java.io.File(uri.getPath());
|
||
if (file.exists()) {
|
||
uri = FileProvider.getUriForFile(
|
||
this,
|
||
getPackageName() + ".fileprovider",
|
||
file
|
||
);
|
||
}
|
||
} catch (Exception e) {
|
||
// 如果转换失败,使用原始 URI
|
||
}
|
||
}
|
||
Glide.with(this)
|
||
.load(uri)
|
||
.circleCrop()
|
||
.error(R.drawable.ic_account_circle_24)
|
||
.placeholder(R.drawable.ic_account_circle_24)
|
||
.into(binding.avatarButton);
|
||
return;
|
||
}
|
||
|
||
int avatarRes = getSharedPreferences("profile_prefs", MODE_PRIVATE)
|
||
.getInt("profile_avatar_res", 0);
|
||
if (avatarRes != 0) {
|
||
Glide.with(this)
|
||
.load(avatarRes)
|
||
.circleCrop()
|
||
.error(R.drawable.ic_account_circle_24)
|
||
.into(binding.avatarButton);
|
||
} else {
|
||
binding.avatarButton.setImageResource(R.drawable.ic_account_circle_24);
|
||
}
|
||
} catch (Exception ignored) {
|
||
if (binding != null) {
|
||
binding.avatarButton.setImageResource(R.drawable.ic_account_circle_24);
|
||
}
|
||
}
|
||
}
|
||
|
||
@Override
|
||
protected void onResume() {
|
||
super.onResume();
|
||
if (binding != null) {
|
||
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
|
||
bottomNavigation.setSelectedItemId(R.id.nav_home);
|
||
loadAvatarFromPrefs();
|
||
// 更新未读消息徽章
|
||
UnreadMessageManager.updateBadge(bottomNavigation);
|
||
}
|
||
|
||
// 确保通话信令 WebSocket 保持连接(用于接收来电通知)
|
||
LiveStreamingApplication app = (LiveStreamingApplication) getApplication();
|
||
app.connectCallSignalingIfLoggedIn();
|
||
|
||
// 检查主播状态并显示/隐藏开播按钮
|
||
checkAndUpdateStreamerButton();
|
||
}
|
||
|
||
/**
|
||
* 检查主播状态并更新开播按钮的显示
|
||
*/
|
||
private void checkAndUpdateStreamerButton() {
|
||
// 如果用户未登录,隐藏开播按钮
|
||
if (!AuthHelper.isLoggedIn(this)) {
|
||
if (binding.fabAddLive != null) {
|
||
binding.fabAddLive.setVisibility(View.GONE);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 检查主播资格
|
||
ApiClient.getService(getApplicationContext()).checkStreamerStatus()
|
||
.enqueue(new Callback<ApiResponse<Map<String, Object>>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<Map<String, Object>>> call,
|
||
Response<ApiResponse<Map<String, Object>>> response) {
|
||
if (!response.isSuccessful() || response.body() == null) {
|
||
// 接口调用失败,隐藏按钮
|
||
if (binding.fabAddLive != null) {
|
||
binding.fabAddLive.setVisibility(View.GONE);
|
||
}
|
||
return;
|
||
}
|
||
|
||
ApiResponse<Map<String, Object>> body = response.body();
|
||
if (body.getCode() != 200 || body.getData() == null) {
|
||
// 接口返回错误,隐藏按钮
|
||
if (binding.fabAddLive != null) {
|
||
binding.fabAddLive.setVisibility(View.GONE);
|
||
}
|
||
return;
|
||
}
|
||
|
||
Map<String, Object> data = body.getData();
|
||
Boolean isStreamer = data.get("isStreamer") != null && (Boolean) data.get("isStreamer");
|
||
Boolean isBanned = data.get("isBanned") != null && (Boolean) data.get("isBanned");
|
||
|
||
// 只有认证主播且未被封禁才显示开播按钮
|
||
if (isStreamer && !isBanned && binding.fabAddLive != null) {
|
||
binding.fabAddLive.setVisibility(View.VISIBLE);
|
||
} else if (binding.fabAddLive != null) {
|
||
binding.fabAddLive.setVisibility(View.GONE);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<Map<String, Object>>> call, Throwable t) {
|
||
// 网络错误,隐藏按钮
|
||
if (binding.fabAddLive != null) {
|
||
binding.fabAddLive.setVisibility(View.GONE);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 从后端获取未读消息总数
|
||
*/
|
||
private void fetchUnreadMessageCount() {
|
||
// 检查登录状态
|
||
if (!AuthHelper.isLoggedIn(this)) {
|
||
return;
|
||
}
|
||
|
||
// 从会话列表接口获取未读消息总数
|
||
ApiClient.getService(getApplicationContext()).getConversations()
|
||
.enqueue(new Callback<ApiResponse<List<ConversationResponse>>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<List<ConversationResponse>>> call,
|
||
Response<ApiResponse<List<ConversationResponse>>> response) {
|
||
ApiResponse<List<ConversationResponse>> body = response.body();
|
||
if (response.isSuccessful() && body != null && body.isOk() && body.getData() != null) {
|
||
int totalUnread = 0;
|
||
for (ConversationResponse conv : body.getData()) {
|
||
if (conv != null && conv.getUnreadCount() != null) {
|
||
totalUnread += conv.getUnreadCount();
|
||
}
|
||
}
|
||
UnreadMessageManager.setUnreadCount(MainActivity.this, totalUnread);
|
||
// 更新底部导航栏徽章
|
||
if (binding != null && binding.bottomNavInclude != null) {
|
||
UnreadMessageManager.updateBadge(binding.bottomNavInclude.bottomNavigation);
|
||
}
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<List<ConversationResponse>>> call, Throwable t) {
|
||
// 网络错误,忽略
|
||
}
|
||
});
|
||
}
|
||
|
||
@Override
|
||
protected void onPause() {
|
||
super.onPause();
|
||
// 暂停时停止语音识别
|
||
stopVoiceRecognition();
|
||
}
|
||
|
||
@Override
|
||
protected void onStart() {
|
||
super.onStart();
|
||
// 注释掉网络轮询,使用静态数据
|
||
// startPolling();
|
||
}
|
||
|
||
@Override
|
||
protected void onStop() {
|
||
super.onStop();
|
||
stopPolling();
|
||
}
|
||
|
||
@Override
|
||
public void onBackPressed() {
|
||
DrawerLayout drawerLayout = binding != null ? binding.drawerLayout : null;
|
||
if (drawerLayout != null && drawerLayout.isDrawerOpen(GravityCompat.START)) {
|
||
drawerLayout.closeDrawer(GravityCompat.START);
|
||
return;
|
||
}
|
||
super.onBackPressed();
|
||
}
|
||
|
||
private void startPolling() {
|
||
stopPolling();
|
||
pollRunnable = () -> {
|
||
// 检查Activity是否还在前台
|
||
if (!isFinishing() && !isDestroyed()) {
|
||
fetchRooms();
|
||
// 增加轮询间隔,减少网络请求频率
|
||
handler.postDelayed(pollRunnable, 15000);
|
||
}
|
||
};
|
||
// 延迟首次请求,让界面先显示
|
||
handler.postDelayed(pollRunnable, 2000);
|
||
}
|
||
|
||
private void stopPolling() {
|
||
if (pollRunnable != null) {
|
||
handler.removeCallbacks(pollRunnable);
|
||
pollRunnable = null;
|
||
}
|
||
}
|
||
|
||
private void showCreateRoomDialog() {
|
||
// 先检查登录状态
|
||
if (!AuthHelper.requireLogin(this, "创建直播间需要登录")) {
|
||
return;
|
||
}
|
||
|
||
// 检查主播资格
|
||
checkStreamerStatusAndShowDialog();
|
||
}
|
||
|
||
private void checkStreamerStatusAndShowDialog() {
|
||
// 显示加载提示
|
||
Toast.makeText(this, "正在检查主播资格...", Toast.LENGTH_SHORT).show();
|
||
|
||
ApiClient.getService(getApplicationContext()).checkStreamerStatus()
|
||
.enqueue(new Callback<ApiResponse<Map<String, Object>>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<Map<String, Object>>> call,
|
||
Response<ApiResponse<Map<String, Object>>> response) {
|
||
if (!response.isSuccessful() || response.body() == null) {
|
||
// 接口调用失败,可能是旧版本后端,允许继续创建
|
||
showCreateRoomDialogInternal();
|
||
return;
|
||
}
|
||
|
||
ApiResponse<Map<String, Object>> body = response.body();
|
||
if (body.getCode() != 200 || body.getData() == null) {
|
||
// 接口返回错误,可能是旧版本后端,允许继续创建
|
||
showCreateRoomDialogInternal();
|
||
return;
|
||
}
|
||
|
||
Map<String, Object> data = body.getData();
|
||
Boolean isStreamer = data.get("isStreamer") != null && (Boolean) data.get("isStreamer");
|
||
Boolean isBanned = data.get("isBanned") != null && (Boolean) data.get("isBanned");
|
||
Boolean hasApplication = data.get("hasApplication") != null && (Boolean) data.get("hasApplication");
|
||
Object appStatusObj = data.get("applicationStatus");
|
||
Integer applicationStatus = appStatusObj != null ? ((Number) appStatusObj).intValue() : null;
|
||
|
||
if (isBanned) {
|
||
// 被封禁
|
||
String banReason = (String) data.get("banReason");
|
||
new AlertDialog.Builder(MainActivity.this)
|
||
.setTitle("无法开播")
|
||
.setMessage("您的主播资格已被封禁" + (banReason != null ? ":" + banReason : ""))
|
||
.setPositiveButton("确定", null)
|
||
.show();
|
||
return;
|
||
}
|
||
|
||
if (!isStreamer) {
|
||
// 不是主播
|
||
if (hasApplication && applicationStatus != null && applicationStatus == 0) {
|
||
// 有待审核的申请
|
||
new AlertDialog.Builder(MainActivity.this)
|
||
.setTitle("申请审核中")
|
||
.setMessage("您的主播认证申请正在审核中,请耐心等待")
|
||
.setPositiveButton("确定", null)
|
||
.show();
|
||
} else {
|
||
// 没有申请或申请被拒绝,提示申请认证
|
||
new AlertDialog.Builder(MainActivity.this)
|
||
.setTitle("需要主播认证")
|
||
.setMessage("只有认证主播才能开播,是否现在申请主播认证?")
|
||
.setPositiveButton("去申请", (d, w) -> {
|
||
// 跳转到主播认证申请页面
|
||
StreamerApplyActivity.start(MainActivity.this);
|
||
})
|
||
.setNegativeButton("取消", null)
|
||
.show();
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 是认证主播,显示创建直播间对话框
|
||
showCreateRoomDialogInternal();
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<Map<String, Object>>> call, Throwable t) {
|
||
// 网络错误,可能是旧版本后端,允许继续创建
|
||
Log.w(TAG, "检查主播资格失败,允许继续创建", t);
|
||
showCreateRoomDialogInternal();
|
||
}
|
||
});
|
||
}
|
||
|
||
private void showCreateRoomDialogInternal() {
|
||
// 显示选择对话框:手机开播 或 OBS推流
|
||
new AlertDialog.Builder(this)
|
||
.setTitle("选择开播方式")
|
||
.setItems(new String[]{"📱 手机开播", "💻 OBS推流"}, (dialog, which) -> {
|
||
if (which == 0) {
|
||
// 手机开播 - 跳转到 BroadcastActivity
|
||
Intent intent = new Intent(MainActivity.this, BroadcastActivity.class);
|
||
startActivity(intent);
|
||
} else {
|
||
// OBS推流 - 显示创建直播间对话框
|
||
showOBSCreateRoomDialog();
|
||
}
|
||
})
|
||
.setNegativeButton("取消", null)
|
||
.show();
|
||
}
|
||
|
||
private void showOBSCreateRoomDialog() {
|
||
View dialogView = getLayoutInflater().inflate(R.layout.dialog_create_room, null);
|
||
DialogCreateRoomBinding dialogBinding = DialogCreateRoomBinding.bind(dialogView);
|
||
|
||
// 使用正确的资源ID获取typeSpinner
|
||
int typeSpinnerId = getResources().getIdentifier("typeSpinner", "id", getPackageName());
|
||
MaterialAutoCompleteTextView typeSpinner = dialogView.findViewById(typeSpinnerId);
|
||
|
||
// 从后端加载直播类型分类
|
||
loadLiveTypesForDialog(typeSpinner);
|
||
|
||
AlertDialog dialog = new AlertDialog.Builder(this)
|
||
.setTitle("创建直播间")
|
||
.setView(dialogView)
|
||
.setNegativeButton("取消", null)
|
||
.setPositiveButton("创建", null)
|
||
.create();
|
||
|
||
dialog.setOnShowListener(d -> {
|
||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||
String title = dialogBinding.titleEdit.getText() != null ? dialogBinding.titleEdit.getText().toString().trim() : "";
|
||
String typeDisplay = (typeSpinner != null && typeSpinner.getText() != null) ? typeSpinner.getText().toString().trim() : "";
|
||
|
||
if (TextUtils.isEmpty(title)) {
|
||
dialogBinding.titleLayout.setError("标题不能为空");
|
||
return;
|
||
} else {
|
||
dialogBinding.titleLayout.setError(null);
|
||
}
|
||
|
||
if (TextUtils.isEmpty(typeDisplay)) {
|
||
int typeLayoutId = getResources().getIdentifier("typeLayout", "id", getPackageName());
|
||
TextInputLayout typeLayout = dialogView.findViewById(typeLayoutId);
|
||
if (typeLayout != null) {
|
||
typeLayout.setError("请先选择直播类型");
|
||
}
|
||
return;
|
||
} else {
|
||
int typeLayoutId = getResources().getIdentifier("typeLayout", "id", getPackageName());
|
||
TextInputLayout typeLayout = dialogView.findViewById(typeLayoutId);
|
||
if (typeLayout != null) {
|
||
typeLayout.setError(null);
|
||
}
|
||
}
|
||
|
||
// 从后端返回的类型列表中查找对应的类型编码
|
||
String typeCode = "game"; // 默认值:游戏
|
||
Object tag = typeSpinner.getTag();
|
||
|
||
Log.d(TAG, "创建直播间 - 用户选择的类型显示名称: " + typeDisplay);
|
||
Log.d(TAG, "创建直播间 - typeSpinner.getTag() 类型: " + (tag != null ? tag.getClass().getName() : "null"));
|
||
|
||
if (tag instanceof List) {
|
||
@SuppressWarnings("unchecked")
|
||
List<com.example.livestreaming.net.LiveTypeResponse> types =
|
||
(List<com.example.livestreaming.net.LiveTypeResponse>) tag;
|
||
|
||
Log.d(TAG, "创建直播间 - 从Tag中获取到 " + types.size() + " 个类型");
|
||
|
||
boolean found = false;
|
||
for (com.example.livestreaming.net.LiveTypeResponse type : types) {
|
||
Log.d(TAG, "创建直播间 - 比对类型: " + type.getName() + " vs " + typeDisplay);
|
||
if (type.getName().equals(typeDisplay)) {
|
||
typeCode = type.getCode();
|
||
found = true;
|
||
Log.d(TAG, "创建直播间 - 匹配成功!使用类型编码: " + typeCode);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!found) {
|
||
Log.w(TAG, "创建直播间 - 未找到匹配的类型,使用默认值: " + typeCode);
|
||
}
|
||
} else {
|
||
Log.w(TAG, "创建直播间 - Tag不是List类型,使用默认映射");
|
||
// 如果没有从后端获取到类型列表,使用默认映射
|
||
switch (typeDisplay) {
|
||
case "游戏":
|
||
typeCode = "game";
|
||
break;
|
||
case "才艺":
|
||
typeCode = "talent";
|
||
break;
|
||
case "户外":
|
||
typeCode = "outdoor";
|
||
break;
|
||
case "音乐":
|
||
typeCode = "music";
|
||
break;
|
||
case "美食":
|
||
typeCode = "food";
|
||
break;
|
||
case "聊天":
|
||
typeCode = "chat";
|
||
break;
|
||
default:
|
||
Log.w(TAG, "创建直播间 - 未知的类型名称: " + typeDisplay + ",使用默认值: game");
|
||
typeCode = "game";
|
||
break;
|
||
}
|
||
Log.d(TAG, "创建直播间 - 默认映射结果: " + typeDisplay + " -> " + typeCode);
|
||
}
|
||
|
||
// 获取用户昵称
|
||
String streamerName = getSharedPreferences("profile_prefs", MODE_PRIVATE)
|
||
.getString("profile_name", "未知用户");
|
||
|
||
Log.d(TAG, "创建直播间 - 最终参数: title=" + title + ", streamerName=" + streamerName + ", typeCode=" + typeCode);
|
||
|
||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
|
||
|
||
// 调用后端接口创建直播间,使用从后端获取的类型编码
|
||
ApiClient.getService(getApplicationContext()).createRoom(new CreateRoomRequest(title, streamerName, typeCode))
|
||
.enqueue(new Callback<ApiResponse<Room>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<Room>> call, Response<ApiResponse<Room>> response) {
|
||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
|
||
|
||
ApiResponse<Room> body = response.body();
|
||
Room room = body != null ? body.getData() : null;
|
||
if (!response.isSuccessful() || body == null || !body.isOk() || room == null) {
|
||
String msg;
|
||
if (!response.isSuccessful()) {
|
||
String err = null;
|
||
try {
|
||
okhttp3.ResponseBody eb = response.errorBody();
|
||
err = eb != null ? eb.string() : null;
|
||
} catch (Exception ignored) {
|
||
}
|
||
if (!TextUtils.isEmpty(err)) {
|
||
msg = "HTTP " + response.code() + ":" + err;
|
||
} else {
|
||
msg = "HTTP " + response.code() + ":创建失败";
|
||
}
|
||
} else if (body == null) {
|
||
msg = "服务返回空数据:创建失败";
|
||
} else if (!body.isOk()) {
|
||
String m = body.getMessage();
|
||
if (!TextUtils.isEmpty(m)) {
|
||
msg = m;
|
||
} else {
|
||
msg = "接口返回异常(code=" + body.getCode() + ")";
|
||
}
|
||
} else {
|
||
msg = "创建失败:返回无房间数据";
|
||
}
|
||
Toast.makeText(MainActivity.this, "创建失败: " + msg, Toast.LENGTH_SHORT).show();
|
||
return;
|
||
}
|
||
|
||
dialog.dismiss();
|
||
fetchRooms();
|
||
|
||
// 显示优化的推流信息弹窗
|
||
showOptimizedStreamInfo(room);
|
||
|
||
// 可选:跳转到房间详情
|
||
// Intent intent = new Intent(MainActivity.this, RoomDetailActivity.class);
|
||
// intent.putExtra(RoomDetailActivity.EXTRA_ROOM_ID, room.getId());
|
||
// startActivity(intent);
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<Room>> call, Throwable t) {
|
||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
|
||
String errorMsg = "网络错误";
|
||
if (t != null) {
|
||
if (t.getMessage() != null && t.getMessage().contains("Unable to resolve host")) {
|
||
errorMsg = "无法连接服务器,请检查网络";
|
||
} else if (t.getMessage() != null && t.getMessage().contains("timeout")) {
|
||
errorMsg = "连接超时,请检查服务器是否运行";
|
||
} else if (t.getMessage() != null && t.getMessage().contains("Connection refused")) {
|
||
errorMsg = "连接被拒绝,请确保后端服务已启动";
|
||
} else {
|
||
errorMsg = "网络错误:" + t.getMessage();
|
||
}
|
||
}
|
||
Toast.makeText(MainActivity.this, errorMsg, Toast.LENGTH_LONG).show();
|
||
}
|
||
});
|
||
});
|
||
});
|
||
|
||
dialog.show();
|
||
}
|
||
|
||
private void showOptimizedStreamInfo(Room room) {
|
||
String streamKey = room != null ? room.getStreamKey() : null;
|
||
String rtmp = room != null && room.getStreamUrls() != null ? room.getStreamUrls().getRtmp() : null;
|
||
|
||
// 直接使用服务器返回的RTMP地址
|
||
String rtmpForObs = rtmp;
|
||
|
||
// 创建自定义弹窗布局
|
||
View dialogView = getLayoutInflater().inflate(R.layout.dialog_stream_info, null);
|
||
|
||
// 找到视图组件
|
||
TextView addressText = dialogView.findViewById(R.id.addressText);
|
||
TextView keyText = dialogView.findViewById(R.id.keyText);
|
||
|
||
// 设置文本内容
|
||
String displayAddress = !TextUtils.isEmpty(rtmpForObs) ? rtmpForObs : rtmp;
|
||
addressText.setText(displayAddress != null ? displayAddress : "");
|
||
keyText.setText(streamKey != null ? streamKey : "");
|
||
|
||
AlertDialog dialog = new AlertDialog.Builder(this)
|
||
.setTitle("🎉 直播间创建成功")
|
||
.setView(dialogView)
|
||
.setPositiveButton("开始直播", (d, w) -> {
|
||
d.dismiss();
|
||
// 跳转到直播播放页面
|
||
Intent intent = new Intent(MainActivity.this, RoomDetailActivity.class);
|
||
intent.putExtra(RoomDetailActivity.EXTRA_ROOM_ID, room.getId());
|
||
startActivity(intent);
|
||
})
|
||
.setNegativeButton("稍后开始", (d, w) -> d.dismiss())
|
||
.create();
|
||
|
||
dialog.show();
|
||
|
||
// 设置复制按钮点击事件
|
||
dialogView.findViewById(R.id.copyAddressBtn).setOnClickListener(v -> {
|
||
copyToClipboard("推流地址", displayAddress);
|
||
Toast.makeText(this, "推流地址已复制", Toast.LENGTH_SHORT).show();
|
||
});
|
||
|
||
dialogView.findViewById(R.id.copyKeyBtn).setOnClickListener(v -> {
|
||
copyToClipboard("推流密钥", streamKey);
|
||
Toast.makeText(this, "推流密钥已复制", Toast.LENGTH_SHORT).show();
|
||
});
|
||
}
|
||
|
||
private void copyToClipboard(String label, String text) {
|
||
if (TextUtils.isEmpty(text)) {
|
||
Toast.makeText(this, "内容为空", Toast.LENGTH_SHORT).show();
|
||
return;
|
||
}
|
||
ClipboardManager cm = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
||
if (cm == null) return;
|
||
cm.setPrimaryClip(ClipData.newPlainText(label, text));
|
||
Toast.makeText(this, "已复制", Toast.LENGTH_SHORT).show();
|
||
}
|
||
|
||
private void fetchRooms() {
|
||
Log.d(TAG, "fetchRooms() 开始");
|
||
// 避免重复请求
|
||
if (isFetching) {
|
||
Log.d(TAG, "fetchRooms() 已在请求中,跳过");
|
||
return;
|
||
}
|
||
|
||
isFetching = true;
|
||
lastFetchMs = System.currentTimeMillis();
|
||
|
||
// 隐藏空状态和错误状态
|
||
hideEmptyState();
|
||
hideErrorState();
|
||
|
||
// 只在没有数据时显示骨架屏
|
||
if (adapter.getItemCount() == 0) {
|
||
LoadingStateManager.showSkeleton(binding.roomsRecyclerView, 6);
|
||
binding.loading.setVisibility(View.GONE);
|
||
} else {
|
||
binding.loading.setVisibility(View.GONE);
|
||
}
|
||
|
||
String baseUrl = ApiClient.getCurrentBaseUrl(getApplicationContext());
|
||
Log.d(TAG, "fetchRooms() 请求 API: " + baseUrl + "api/front/live/public/rooms");
|
||
|
||
ApiClient.getService(getApplicationContext()).getRooms().enqueue(new Callback<ApiResponse<List<Room>>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<List<Room>>> call, Response<ApiResponse<List<Room>>> response) {
|
||
Log.d(TAG, "fetchRooms() onResponse: code=" + response.code());
|
||
|
||
// 先恢复真实适配器
|
||
binding.roomsRecyclerView.setAdapter(adapter);
|
||
binding.loading.setVisibility(View.GONE);
|
||
LoadingStateManager.stopRefreshing(binding.swipeRefresh);
|
||
isFetching = false;
|
||
|
||
ApiResponse<List<Room>> body = response.body();
|
||
Log.d(TAG, "fetchRooms() body=" + (body != null ? "not null, isOk=" + body.isOk() : "null"));
|
||
|
||
List<Room> rooms = response.isSuccessful() && body != null && body.isOk() && body.getData() != null
|
||
? body.getData()
|
||
: Collections.emptyList();
|
||
|
||
Log.d(TAG, "fetchRooms() rooms count: " + (rooms != null ? rooms.size() : 0));
|
||
|
||
if (rooms == null || rooms.isEmpty()) {
|
||
showNoRoomsState();
|
||
} else {
|
||
hideEmptyState();
|
||
}
|
||
|
||
allRooms.clear();
|
||
if (rooms != null) allRooms.addAll(rooms);
|
||
adapter.submitList(new ArrayList<>(allRooms));
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<List<Room>>> call, Throwable t) {
|
||
Log.e(TAG, "fetchRooms() onFailure: " + t.getMessage(), t);
|
||
|
||
// 先恢复真实适配器
|
||
binding.roomsRecyclerView.setAdapter(adapter);
|
||
binding.loading.setVisibility(View.GONE);
|
||
LoadingStateManager.stopRefreshing(binding.swipeRefresh);
|
||
isFetching = false;
|
||
|
||
// 显示网络错误
|
||
ErrorHandler.handleApiError(binding.getRoot(), t, () -> fetchRooms());
|
||
showNetworkErrorState();
|
||
|
||
allRooms.clear();
|
||
adapter.submitList(new ArrayList<>());
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 显示无直播间空状态
|
||
*/
|
||
private void showNoRoomsState() {
|
||
// 发现页面使用专用的空状态容器,避免重复显示
|
||
if ("发现".equals(currentTopTab)) {
|
||
updateDiscoverEmptyState();
|
||
return;
|
||
}
|
||
|
||
if (binding.emptyStateView != null) {
|
||
binding.emptyStateView.setNoRoomsState();
|
||
binding.emptyStateView.setOnActionClickListener(v -> fetchRooms());
|
||
binding.emptyStateView.setVisibility(View.VISIBLE);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 显示网络错误状态
|
||
*/
|
||
private void showNetworkErrorState() {
|
||
if (binding.emptyStateView != null) {
|
||
binding.emptyStateView.setNetworkErrorState();
|
||
binding.emptyStateView.setOnActionClickListener(v -> fetchRooms());
|
||
binding.emptyStateView.setVisibility(View.VISIBLE);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 隐藏空状态视图
|
||
*/
|
||
private void hideEmptyState() {
|
||
if (binding.emptyStateView != null) {
|
||
binding.emptyStateView.setVisibility(View.GONE);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 隐藏错误状态(实际上和hideEmptyState一样)
|
||
*/
|
||
private void hideErrorState() {
|
||
hideEmptyState();
|
||
}
|
||
|
||
/**
|
||
* 应用分类筛选(带动画效果)
|
||
* 使用异步筛选提升性能,并添加平滑的过渡动画
|
||
* 使用filterRequestId防止旧的筛选结果覆盖新的结果
|
||
*/
|
||
private void applyCategoryFilterWithAnimation(String category) {
|
||
String c = category != null ? category : "推荐";
|
||
|
||
// 增加请求ID,确保只有最新的筛选结果被应用
|
||
final int requestId = ++filterRequestId;
|
||
|
||
// 显示加载状态(如果数据量较大)
|
||
if (allRooms.size() > 50) {
|
||
binding.loading.setVisibility(View.VISIBLE);
|
||
}
|
||
|
||
// 使用筛选管理器异步筛选
|
||
if (filterManager != null) {
|
||
filterManager.filterRoomsAsync(allRooms, c, filteredRooms -> {
|
||
// 检查这个结果是否仍然是最新的请求
|
||
if (requestId != filterRequestId) {
|
||
// 这是一个旧的请求结果,忽略它
|
||
return;
|
||
}
|
||
|
||
// 隐藏加载状态
|
||
binding.loading.setVisibility(View.GONE);
|
||
|
||
// 添加淡入动画
|
||
binding.roomsRecyclerView.animate()
|
||
.alpha(0.7f)
|
||
.setDuration(100)
|
||
.withEndAction(() -> {
|
||
// 再次检查请求ID(防止在动画期间又有新的筛选请求)
|
||
if (requestId != filterRequestId) {
|
||
return;
|
||
}
|
||
|
||
// 更新列表数据(ListAdapter会自动处理DiffUtil动画)
|
||
adapter.submitList(filteredRooms, () -> {
|
||
// 最后一次检查请求ID
|
||
if (requestId != filterRequestId) {
|
||
return;
|
||
}
|
||
|
||
// 数据更新完成后,恢复透明度并添加淡入效果
|
||
binding.roomsRecyclerView.animate()
|
||
.alpha(1.0f)
|
||
.setDuration(200)
|
||
.start();
|
||
});
|
||
|
||
// 更新空状态
|
||
updateEmptyStateForList(filteredRooms);
|
||
})
|
||
.start();
|
||
});
|
||
} else {
|
||
// 降级到同步筛选(如果筛选管理器未初始化)
|
||
applyCategoryFilterSync(c);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 同步筛选(降级方案)
|
||
*/
|
||
private void applyCategoryFilterSync(String category) {
|
||
String c = category != null ? category : "推荐";
|
||
if ("推荐".equals(c) || "全部".equals(c)) {
|
||
// 添加淡入动画,保持与其他筛选场景的一致性
|
||
binding.roomsRecyclerView.animate()
|
||
.alpha(0.7f)
|
||
.setDuration(100)
|
||
.withEndAction(() -> {
|
||
adapter.submitList(new ArrayList<>(allRooms), () -> {
|
||
binding.roomsRecyclerView.animate()
|
||
.alpha(1.0f)
|
||
.setDuration(200)
|
||
.start();
|
||
});
|
||
updateEmptyStateForList(allRooms);
|
||
})
|
||
.start();
|
||
return;
|
||
}
|
||
|
||
List<Room> filtered = new ArrayList<>();
|
||
for (Room r : allRooms) {
|
||
if (r == null) continue;
|
||
// 优先使用 categoryName 筛选,如果没有则使用 type
|
||
String roomCategory = r.getCategoryName();
|
||
if (roomCategory == null || roomCategory.isEmpty()) {
|
||
roomCategory = r.getType();
|
||
}
|
||
if (c.equals(roomCategory)) {
|
||
filtered.add(r);
|
||
}
|
||
}
|
||
|
||
// 添加淡入动画
|
||
binding.roomsRecyclerView.animate()
|
||
.alpha(0.7f)
|
||
.setDuration(100)
|
||
.withEndAction(() -> {
|
||
adapter.submitList(filtered, () -> {
|
||
binding.roomsRecyclerView.animate()
|
||
.alpha(1.0f)
|
||
.setDuration(200)
|
||
.start();
|
||
});
|
||
updateEmptyStateForList(filtered);
|
||
})
|
||
.start();
|
||
}
|
||
|
||
/**
|
||
* 应用搜索筛选
|
||
* 根据搜索文本筛选房间列表(仅按房间标题和主播名称,不按分类筛选)
|
||
*/
|
||
private void applySearchFilter(String searchQuery) {
|
||
String query = searchQuery != null ? searchQuery.trim().toLowerCase() : "";
|
||
|
||
// 如果搜索文本为空,恢复分类筛选
|
||
if (query.isEmpty()) {
|
||
applyCategoryFilterWithAnimation(currentCategory);
|
||
return;
|
||
}
|
||
|
||
// 根据当前顶部标签选择要筛选的数据源
|
||
List<Room> sourceRooms;
|
||
if ("关注".equals(currentTopTab)) {
|
||
sourceRooms = followRooms;
|
||
} else if ("附近".equals(currentTopTab)) {
|
||
// 附近页面不显示房间,不需要搜索筛选
|
||
return;
|
||
} else {
|
||
// 发现页面
|
||
sourceRooms = discoverRooms.isEmpty() ? allRooms : discoverRooms;
|
||
}
|
||
|
||
// 只根据搜索文本筛选(按房间标题和主播名称),不考虑分类
|
||
List<Room> searchFiltered = new ArrayList<>();
|
||
for (Room r : sourceRooms) {
|
||
if (r == null) continue;
|
||
String title = r.getTitle() != null ? r.getTitle().toLowerCase() : "";
|
||
String streamer = r.getStreamerName() != null ? r.getStreamerName().toLowerCase() : "";
|
||
if (title.contains(query) || streamer.contains(query)) {
|
||
searchFiltered.add(r);
|
||
}
|
||
}
|
||
|
||
// 更新列表
|
||
adapter.submitList(searchFiltered);
|
||
updateEmptyStateForList(searchFiltered);
|
||
}
|
||
|
||
/**
|
||
* 恢复分类标签的选中状态
|
||
*/
|
||
private void restoreCategoryTabSelection() {
|
||
if (binding == null || binding.categoryTabs == null) return;
|
||
|
||
// 延迟执行,确保TabLayout已完全初始化
|
||
binding.categoryTabs.post(() -> {
|
||
for (int i = 0; i < binding.categoryTabs.getTabCount(); i++) {
|
||
TabLayout.Tab tab = binding.categoryTabs.getTabAt(i);
|
||
if (tab != null) {
|
||
CharSequence tabText = tab.getText();
|
||
if (tabText != null && currentCategory.equals(tabText.toString())) {
|
||
tab.select();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 根据列表数据更新空状态显示
|
||
*/
|
||
private void updateEmptyStateForList(List<Room> rooms) {
|
||
// 更新发现页面的空状态
|
||
View discoverEmptyContainer = findViewById(R.id.discoverEmptyContainer);
|
||
|
||
if (rooms == null || rooms.isEmpty()) {
|
||
// 发现页面只使用 discoverEmptyContainer,避免重复显示
|
||
if ("发现".equals(currentTopTab)) {
|
||
// 隐藏通用空状态
|
||
hideEmptyState();
|
||
// 显示发现页面专用空状态
|
||
if (discoverEmptyContainer != null) {
|
||
discoverEmptyContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
} else {
|
||
// 其他页面使用通用空状态
|
||
if (binding.emptyStateView != null) {
|
||
binding.emptyStateView.setIcon(R.drawable.ic_search_24);
|
||
binding.emptyStateView.setTitle("该分类下暂无直播间");
|
||
binding.emptyStateView.setMessage("换个分类看看吧");
|
||
binding.emptyStateView.hideActionButton();
|
||
binding.emptyStateView.setVisibility(View.VISIBLE);
|
||
}
|
||
}
|
||
if (binding.roomsRecyclerView != null) {
|
||
binding.roomsRecyclerView.setVisibility(View.GONE);
|
||
}
|
||
} else {
|
||
hideEmptyState();
|
||
// 隐藏发现页面的空状态
|
||
if (discoverEmptyContainer != null) {
|
||
discoverEmptyContainer.setVisibility(View.GONE);
|
||
}
|
||
if (binding.roomsRecyclerView != null) {
|
||
binding.roomsRecyclerView.setVisibility(View.VISIBLE);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* 从后端加载分类数据
|
||
*/
|
||
private void loadCategoriesFromBackend() {
|
||
Log.d(TAG, "loadCategoriesFromBackend() 开始加载直播间分类");
|
||
|
||
// 调用后端接口获取直播间分类列表
|
||
ApiClient.getService(getApplicationContext()).getLiveRoomCategories()
|
||
.enqueue(new Callback<ApiResponse<List<com.example.livestreaming.net.CategoryResponse>>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<List<com.example.livestreaming.net.CategoryResponse>>> call,
|
||
Response<ApiResponse<List<com.example.livestreaming.net.CategoryResponse>>> response) {
|
||
Log.d(TAG, "loadCategoriesFromBackend() onResponse: code=" + response.code());
|
||
|
||
ApiResponse<List<com.example.livestreaming.net.CategoryResponse>> body = response.body();
|
||
List<com.example.livestreaming.net.CategoryResponse> categories =
|
||
response.isSuccessful() && body != null && body.isOk() && body.getData() != null
|
||
? body.getData()
|
||
: null;
|
||
|
||
if (categories != null && !categories.isEmpty()) {
|
||
Log.d(TAG, "loadCategoriesFromBackend() 成功获取 " + categories.size() + " 个分类");
|
||
// 更新分类标签
|
||
updateCategoryTabs(categories);
|
||
} else {
|
||
Log.w(TAG, "loadCategoriesFromBackend() 未获取到分类数据,使用默认分类");
|
||
// 使用默认分类
|
||
useDefaultCategories();
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<List<com.example.livestreaming.net.CategoryResponse>>> call, Throwable t) {
|
||
Log.e(TAG, "loadCategoriesFromBackend() onFailure: " + t.getMessage(), t);
|
||
// 网络错误,使用默认分类
|
||
useDefaultCategories();
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 更新分类标签
|
||
*/
|
||
private void updateCategoryTabs(List<com.example.livestreaming.net.CategoryResponse> categories) {
|
||
if (binding == null || binding.categoryTabs == null) return;
|
||
|
||
// 缓存后端分类数据,供频道管理对话框使用
|
||
allBackendCategories.clear();
|
||
allBackendCategories.addAll(categories);
|
||
|
||
runOnUiThread(() -> {
|
||
// 清空现有标签
|
||
binding.categoryTabs.removeAllTabs();
|
||
|
||
// 添加"推荐"作为第一个标签
|
||
binding.categoryTabs.addTab(binding.categoryTabs.newTab().setText("推荐"));
|
||
|
||
// 直接使用后端返回的分类
|
||
for (com.example.livestreaming.net.CategoryResponse cat : categories) {
|
||
if (cat != null && cat.getName() != null) {
|
||
binding.categoryTabs.addTab(binding.categoryTabs.newTab().setText(cat.getName()));
|
||
}
|
||
}
|
||
|
||
// 恢复上次选中的分类
|
||
String lastCategory = CategoryFilterManager.getLastCategory(this);
|
||
restoreCategoryTabSelection();
|
||
|
||
Log.d(TAG, "updateCategoryTabs() 分类标签更新完成,共 " + binding.categoryTabs.getTabCount() + " 个标签(直接使用后端分类)");
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 使用默认分类(降级方案)
|
||
*/
|
||
private void useDefaultCategories() {
|
||
if (binding == null || binding.categoryTabs == null) return;
|
||
|
||
runOnUiThread(() -> {
|
||
// 清空现有标签
|
||
binding.categoryTabs.removeAllTabs();
|
||
|
||
// 使用后端分类(如果已加载)
|
||
if (!allBackendCategories.isEmpty()) {
|
||
// 添加"推荐"作为第一个标签
|
||
binding.categoryTabs.addTab(binding.categoryTabs.newTab().setText("推荐"));
|
||
|
||
// 添加后端分类
|
||
for (com.example.livestreaming.net.CategoryResponse cat : allBackendCategories) {
|
||
if (cat != null && cat.getName() != null) {
|
||
binding.categoryTabs.addTab(binding.categoryTabs.newTab().setText(cat.getName()));
|
||
}
|
||
}
|
||
} else {
|
||
// 后端分类未加载,使用硬编码默认值
|
||
binding.categoryTabs.addTab(binding.categoryTabs.newTab().setText("推荐"));
|
||
binding.categoryTabs.addTab(binding.categoryTabs.newTab().setText("娱乐"));
|
||
binding.categoryTabs.addTab(binding.categoryTabs.newTab().setText("游戏"));
|
||
binding.categoryTabs.addTab(binding.categoryTabs.newTab().setText("音乐"));
|
||
binding.categoryTabs.addTab(binding.categoryTabs.newTab().setText("户外"));
|
||
}
|
||
|
||
// 恢复上次选中的分类
|
||
restoreCategoryTabSelection();
|
||
|
||
Log.d(TAG, "useDefaultCategories() 使用默认分类,共 " + binding.categoryTabs.getTabCount() + " 个标签");
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 为创建直播间对话框加载直播类型
|
||
* 从后端数据库获取直播类型列表(游戏、才艺、户外、音乐、美食、聊天等)
|
||
*/
|
||
private void loadLiveTypesForDialog(MaterialAutoCompleteTextView typeSpinner) {
|
||
if (typeSpinner == null) {
|
||
Log.w(TAG, "loadLiveTypesForDialog() typeSpinner is null");
|
||
return;
|
||
}
|
||
|
||
Log.d(TAG, "loadLiveTypesForDialog() 开始从后端加载直播类型");
|
||
|
||
// 调用后端接口获取直播类型列表
|
||
ApiClient.getService(getApplicationContext()).getLiveTypes()
|
||
.enqueue(new Callback<ApiResponse<List<com.example.livestreaming.net.LiveTypeResponse>>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<List<com.example.livestreaming.net.LiveTypeResponse>>> call,
|
||
Response<ApiResponse<List<com.example.livestreaming.net.LiveTypeResponse>>> response) {
|
||
Log.d(TAG, "loadLiveTypesForDialog() onResponse: code=" + response.code());
|
||
|
||
ApiResponse<List<com.example.livestreaming.net.LiveTypeResponse>> body = response.body();
|
||
List<com.example.livestreaming.net.LiveTypeResponse> types =
|
||
response.isSuccessful() && body != null && body.isOk() && body.getData() != null
|
||
? body.getData()
|
||
: null;
|
||
|
||
runOnUiThread(() -> {
|
||
if (types != null && !types.isEmpty()) {
|
||
Log.d(TAG, "loadLiveTypesForDialog() 成功获取 " + types.size() + " 个直播类型");
|
||
|
||
// 打印所有类型信息,便于调试
|
||
for (com.example.livestreaming.net.LiveTypeResponse type : types) {
|
||
Log.d(TAG, " 类型: " + type.getName() + " (code=" + type.getCode() + ", sort=" + type.getSort() + ")");
|
||
}
|
||
|
||
// 提取类型名称
|
||
String[] typeNames = new String[types.size()];
|
||
for (int i = 0; i < types.size(); i++) {
|
||
typeNames[i] = types.get(i).getName();
|
||
}
|
||
|
||
// 使用 simple_list_item_1 而不是 simple_dropdown_item_1line
|
||
ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this,
|
||
android.R.layout.simple_list_item_1, typeNames);
|
||
typeSpinner.setAdapter(adapter);
|
||
|
||
// 设置默认选中第一项,使用 false 参数避免触发过滤
|
||
if (typeNames.length > 0) {
|
||
typeSpinner.setText(typeNames[0], false);
|
||
Log.d(TAG, "loadLiveTypesForDialog() 默认选中: " + typeNames[0]);
|
||
}
|
||
|
||
// 保存类型列表供后续使用
|
||
typeSpinner.setTag(types);
|
||
Log.d(TAG, "loadLiveTypesForDialog() 类型列表已保存到Tag中");
|
||
} else {
|
||
Log.w(TAG, "loadLiveTypesForDialog() 未获取到类型数据,使用默认类型");
|
||
useDefaultLiveTypes(typeSpinner);
|
||
}
|
||
});
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<List<com.example.livestreaming.net.LiveTypeResponse>>> call, Throwable t) {
|
||
Log.e(TAG, "loadLiveTypesForDialog() onFailure: " + t.getMessage(), t);
|
||
// 网络错误,使用默认类型
|
||
runOnUiThread(() -> {
|
||
useDefaultLiveTypes(typeSpinner);
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 使用默认的直播类型(当后端接口失败时的备用方案)
|
||
*/
|
||
private void useDefaultLiveTypes(MaterialAutoCompleteTextView typeSpinner) {
|
||
String[] defaultTypes = {"游戏", "才艺", "户外", "音乐", "美食", "聊天"};
|
||
ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this,
|
||
android.R.layout.simple_list_item_1, defaultTypes);
|
||
typeSpinner.setAdapter(adapter);
|
||
|
||
// 设置默认选中第一项
|
||
if (defaultTypes.length > 0) {
|
||
typeSpinner.setText(defaultTypes[0], false);
|
||
}
|
||
|
||
Log.d(TAG, "useDefaultLiveTypes() 使用默认类型,共 " + defaultTypes.length + " 个");
|
||
}
|
||
|
||
private void loadCoverAssetsAsync() {
|
||
// 在后台线程加载资源文件,避免阻塞UI
|
||
new Thread(() -> {
|
||
AssetManager am = getAssets();
|
||
List<String> files = new ArrayList<>();
|
||
try {
|
||
String[] list = am.list("img");
|
||
if (list != null) {
|
||
for (String f : list) {
|
||
if (f == null) continue;
|
||
String lower = f.toLowerCase();
|
||
if (lower.endsWith(".png") || lower.endsWith(".jpg") || lower.endsWith(".jpeg") || lower.endsWith(".webp") || lower.endsWith(".gif")) {
|
||
files.add(f);
|
||
}
|
||
}
|
||
}
|
||
} catch (IOException ignored) {
|
||
}
|
||
|
||
// 回到主线程更新UI
|
||
runOnUiThread(() -> {
|
||
if (adapter != null) {
|
||
adapter.setCoverAssetFiles(files);
|
||
}
|
||
});
|
||
}).start();
|
||
}
|
||
|
||
/**
|
||
* 初始化顶部标签页数据
|
||
* 顶部标签页(关注/发现/附近)为固定配置,不需要从后端动态获取
|
||
*/
|
||
private void initializeTopTabData() {
|
||
// 初始化关注页面数据
|
||
followRooms.clear();
|
||
|
||
// 初始化发现页面数据 - 从后端获取真实直播间
|
||
fetchDiscoverRooms();
|
||
|
||
// 初始化附近页面数据
|
||
nearbyUsers.clear();
|
||
}
|
||
|
||
/**
|
||
* 从后端获取发现页面的直播间列表
|
||
*/
|
||
private void fetchDiscoverRooms() {
|
||
Log.d(TAG, "fetchDiscoverRooms() 开始,API: " + ApiClient.getCurrentBaseUrl(getApplicationContext()));
|
||
|
||
// 不显示骨架屏,直接用空列表初始化
|
||
binding.roomsRecyclerView.setAdapter(adapter);
|
||
adapter.submitList(new ArrayList<>());
|
||
|
||
ApiClient.getService(getApplicationContext()).getRooms().enqueue(new Callback<ApiResponse<List<Room>>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<List<Room>>> call, Response<ApiResponse<List<Room>>> response) {
|
||
Log.d(TAG, "fetchDiscoverRooms() onResponse: code=" + response.code());
|
||
|
||
// 停止刷新动画
|
||
LoadingStateManager.stopRefreshing(binding.swipeRefresh);
|
||
|
||
// 恢复真实适配器
|
||
binding.roomsRecyclerView.setAdapter(adapter);
|
||
|
||
ApiResponse<List<Room>> body = response.body();
|
||
List<Room> rooms = response.isSuccessful() && body != null && body.isOk() && body.getData() != null
|
||
? body.getData()
|
||
: Collections.emptyList();
|
||
|
||
discoverRooms.clear();
|
||
allRooms.clear();
|
||
Log.d(TAG, "fetchDiscoverRooms() 获取到 " + (rooms != null ? rooms.size() : 0) + " 个直播间");
|
||
|
||
if (rooms != null && !rooms.isEmpty()) {
|
||
discoverRooms.addAll(rooms);
|
||
allRooms.addAll(rooms);
|
||
hideEmptyState();
|
||
// 隐藏加载视图
|
||
if (binding.loading != null) binding.loading.setVisibility(View.GONE);
|
||
} else {
|
||
// 没有直播间时立即显示空状态
|
||
Log.d(TAG, "fetchDiscoverRooms() 无直播间,显示空状态");
|
||
showNoRoomsState();
|
||
// 隐藏加载视图
|
||
if (binding.loading != null) binding.loading.setVisibility(View.GONE);
|
||
}
|
||
|
||
// 提交空列表或数据列表
|
||
adapter.submitList(new ArrayList<>(allRooms));
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<List<Room>>> call, Throwable t) {
|
||
Log.e(TAG, "fetchDiscoverRooms() onFailure: " + t.getMessage(), t);
|
||
// 停止刷新动画
|
||
LoadingStateManager.stopRefreshing(binding.swipeRefresh);
|
||
// 无论结果如何,先恢复真实适配器
|
||
binding.roomsRecyclerView.setAdapter(adapter);
|
||
|
||
// 网络错误,显示错误状态
|
||
discoverRooms.clear();
|
||
|
||
if ("发现".equals(currentTopTab)) {
|
||
allRooms.clear();
|
||
adapter.submitList(new ArrayList<>());
|
||
showNetworkErrorState();
|
||
}
|
||
|
||
ErrorHandler.handleApiError(binding.getRoot(), t, () -> fetchDiscoverRooms());
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 切换顶部标签页内容
|
||
*/
|
||
private void switchTopTabContent(String tabName) {
|
||
if (tabName == null) tabName = "发现";
|
||
|
||
if ("关注".equals(tabName)) {
|
||
showFollowTab();
|
||
} else if ("发现".equals(tabName)) {
|
||
showDiscoverTab();
|
||
} else if ("附近".equals(tabName)) {
|
||
showNearbyTab();
|
||
} else {
|
||
// 默认显示发现页面
|
||
showDiscoverTab();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 显示关注页面 - 显示已关注主播的直播间
|
||
*/
|
||
private void showFollowTab() {
|
||
// 隐藏分类标签容器(关注页面不需要分类筛选)
|
||
View categoryTabsContainer = findViewById(R.id.categoryTabsContainer);
|
||
if (categoryTabsContainer != null) {
|
||
categoryTabsContainer.setVisibility(View.GONE);
|
||
}
|
||
|
||
// 隐藏发布按钮
|
||
if (binding.btnPublish != null) {
|
||
binding.btnPublish.setVisibility(View.GONE);
|
||
}
|
||
|
||
// 隐藏其他Tab内容
|
||
View discoverTab = findViewById(R.id.discoverTabContent);
|
||
View nearbyTab = findViewById(R.id.nearbyTabContent);
|
||
View followTab = findViewById(R.id.followTabContent);
|
||
|
||
if (discoverTab != null) {
|
||
discoverTab.setVisibility(View.GONE);
|
||
}
|
||
if (nearbyTab != null) {
|
||
nearbyTab.setVisibility(View.GONE);
|
||
}
|
||
if (followTab != null) {
|
||
followTab.setVisibility(View.GONE);
|
||
}
|
||
|
||
// 显示房间列表容器(使用发现页面的容器)
|
||
if (binding.swipeRefresh != null) {
|
||
binding.swipeRefresh.setVisibility(View.VISIBLE);
|
||
}
|
||
hideEmptyState();
|
||
|
||
// 恢复房间列表的布局管理器和适配器
|
||
if (binding.roomsRecyclerView != null) {
|
||
binding.roomsRecyclerView.setLayoutManager(roomsLayoutManager);
|
||
binding.roomsRecyclerView.setAdapter(adapter);
|
||
binding.roomsRecyclerView.setVisibility(View.VISIBLE);
|
||
}
|
||
|
||
// 设置下拉刷新
|
||
if (binding.swipeRefresh != null) {
|
||
binding.swipeRefresh.setOnRefreshListener(() -> {
|
||
fetchFollowedStreamersRooms();
|
||
});
|
||
}
|
||
|
||
// 加载已关注主播的直播间
|
||
fetchFollowedStreamersRooms();
|
||
}
|
||
|
||
/**
|
||
* 获取已关注主播的直播间列表
|
||
*/
|
||
private void fetchFollowedStreamersRooms() {
|
||
Log.d(TAG, "fetchFollowedStreamersRooms() 开始");
|
||
|
||
// 检查登录状态
|
||
if (!AuthHelper.isLoggedIn(this)) {
|
||
// 未登录,显示空状态
|
||
followRooms.clear();
|
||
allRooms.clear();
|
||
adapter.submitList(new ArrayList<>());
|
||
LoadingStateManager.stopRefreshing(binding.swipeRefresh);
|
||
|
||
if (binding.emptyStateView != null) {
|
||
binding.emptyStateView.setCustomState(
|
||
"请先登录",
|
||
"登录后可以查看已关注主播的直播间",
|
||
"去登录",
|
||
v -> {
|
||
LoginActivity.start(this);
|
||
}
|
||
);
|
||
binding.emptyStateView.setVisibility(View.VISIBLE);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 显示加载状态
|
||
if (adapter.getItemCount() == 0) {
|
||
LoadingStateManager.showSkeleton(binding.roomsRecyclerView, 6);
|
||
}
|
||
|
||
// 获取所有直播间
|
||
ApiClient.getService(getApplicationContext()).getRooms()
|
||
.enqueue(new Callback<ApiResponse<List<Room>>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<List<Room>>> call,
|
||
Response<ApiResponse<List<Room>>> response) {
|
||
Log.d(TAG, "fetchFollowedStreamersRooms() onResponse");
|
||
|
||
// 恢复真实适配器
|
||
binding.roomsRecyclerView.setAdapter(adapter);
|
||
LoadingStateManager.stopRefreshing(binding.swipeRefresh);
|
||
|
||
ApiResponse<List<Room>> body = response.body();
|
||
if (!response.isSuccessful() || body == null || !body.isOk() || body.getData() == null) {
|
||
// 请求失败
|
||
followRooms.clear();
|
||
allRooms.clear();
|
||
adapter.submitList(new ArrayList<>());
|
||
|
||
if (binding.emptyStateView != null) {
|
||
binding.emptyStateView.setNetworkErrorState();
|
||
binding.emptyStateView.setOnActionClickListener(v -> fetchFollowedStreamersRooms());
|
||
binding.emptyStateView.setVisibility(View.VISIBLE);
|
||
}
|
||
return;
|
||
}
|
||
|
||
List<Room> allRoomsList = body.getData();
|
||
|
||
// 获取已关注的用户列表
|
||
ApiClient.getService(getApplicationContext())
|
||
.getFollowingList(1, 1000)
|
||
.enqueue(new Callback<ApiResponse<PageResponse<Map<String, Object>>>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<PageResponse<Map<String, Object>>>> call,
|
||
Response<ApiResponse<PageResponse<Map<String, Object>>>> response) {
|
||
ApiResponse<PageResponse<Map<String, Object>>> body = response.body();
|
||
if (!response.isSuccessful() || body == null || !body.isOk() ||
|
||
body.getData() == null || body.getData().getList() == null) {
|
||
// 获取关注列表失败,显示空状态
|
||
showNoFollowedStreamersState();
|
||
return;
|
||
}
|
||
|
||
List<Map<String, Object>> followingList = body.getData().getList();
|
||
Log.d(TAG, "获取到关注列表,数量: " + followingList.size());
|
||
|
||
if (followingList.isEmpty()) {
|
||
// 没有关注任何人
|
||
showNoFollowedStreamersState();
|
||
return;
|
||
}
|
||
|
||
// 提取已关注用户的ID列表
|
||
List<Integer> followedUserIds = new ArrayList<>();
|
||
for (Map<String, Object> user : followingList) {
|
||
Object uidObj = user.get("uid");
|
||
if (uidObj instanceof Number) {
|
||
int uid = ((Number) uidObj).intValue();
|
||
followedUserIds.add(uid);
|
||
Log.d(TAG, "关注的用户ID: " + uid + ", 昵称: " + user.get("nickname"));
|
||
}
|
||
}
|
||
|
||
Log.d(TAG, "所有直播间数量: " + allRoomsList.size());
|
||
|
||
// 筛选出已关注主播的直播间
|
||
List<Room> filteredRooms = new ArrayList<>();
|
||
for (Room room : allRoomsList) {
|
||
if (room != null) {
|
||
Integer roomUid = room.getUid();
|
||
Log.d(TAG, "直播间: " + room.getTitle() + ", uid=" + roomUid +
|
||
", isLive=" + room.isLive() +
|
||
", 是否匹配=" + (roomUid != null && followedUserIds.contains(roomUid)));
|
||
|
||
if (roomUid != null && followedUserIds.contains(roomUid)) {
|
||
filteredRooms.add(room);
|
||
Log.d(TAG, "匹配成功!添加直播间: " + room.getTitle());
|
||
}
|
||
}
|
||
}
|
||
|
||
Log.d(TAG, "筛选后的直播间数量: " + filteredRooms.size());
|
||
|
||
// 更新数据
|
||
followRooms.clear();
|
||
followRooms.addAll(filteredRooms);
|
||
allRooms.clear();
|
||
allRooms.addAll(filteredRooms);
|
||
adapter.submitList(new ArrayList<>(filteredRooms));
|
||
|
||
// 更新空状态
|
||
if (filteredRooms.isEmpty()) {
|
||
showNoLiveFollowedStreamersState();
|
||
} else {
|
||
hideEmptyState();
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<PageResponse<Map<String, Object>>>> call,
|
||
Throwable t) {
|
||
Log.e(TAG, "获取关注列表失败", t);
|
||
showNoFollowedStreamersState();
|
||
}
|
||
});
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<List<Room>>> call, Throwable t) {
|
||
Log.e(TAG, "fetchFollowedStreamersRooms() onFailure", t);
|
||
|
||
// 恢复真实适配器
|
||
binding.roomsRecyclerView.setAdapter(adapter);
|
||
LoadingStateManager.stopRefreshing(binding.swipeRefresh);
|
||
|
||
followRooms.clear();
|
||
allRooms.clear();
|
||
adapter.submitList(new ArrayList<>());
|
||
|
||
if (binding.emptyStateView != null) {
|
||
binding.emptyStateView.setNetworkErrorState();
|
||
binding.emptyStateView.setOnActionClickListener(v -> fetchFollowedStreamersRooms());
|
||
binding.emptyStateView.setVisibility(View.VISIBLE);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 显示没有关注任何主播的空状态
|
||
*/
|
||
private void showNoFollowedStreamersState() {
|
||
followRooms.clear();
|
||
allRooms.clear();
|
||
adapter.submitList(new ArrayList<>());
|
||
|
||
if (binding.emptyStateView != null) {
|
||
binding.emptyStateView.setCustomState(
|
||
"还没有关注主播",
|
||
"快去发现页面关注你喜欢的主播吧",
|
||
"去发现",
|
||
v -> {
|
||
// 切换到发现页面
|
||
TabLayout.Tab discoverTab = binding.topTabs.getTabAt(1);
|
||
if (discoverTab != null) {
|
||
discoverTab.select();
|
||
}
|
||
}
|
||
);
|
||
binding.emptyStateView.setVisibility(View.VISIBLE);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 显示已关注的主播都没有在直播的空状态
|
||
*/
|
||
private void showNoLiveFollowedStreamersState() {
|
||
if (binding.emptyStateView != null) {
|
||
binding.emptyStateView.setCustomState(
|
||
"关注的主播都不在线",
|
||
"你关注的主播暂时都没有开播",
|
||
"刷新",
|
||
v -> fetchFollowedStreamersRooms()
|
||
);
|
||
binding.emptyStateView.setVisibility(View.VISIBLE);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 显示发现页面
|
||
*/
|
||
private void showDiscoverTab() {
|
||
// 显示分类标签容器(包含标签和展开箭头)
|
||
View categoryTabsContainer = findViewById(R.id.categoryTabsContainer);
|
||
if (categoryTabsContainer != null) {
|
||
categoryTabsContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
|
||
// 显示发布按钮
|
||
if (binding.btnPublish != null) {
|
||
binding.btnPublish.setVisibility(View.VISIBLE);
|
||
}
|
||
|
||
// 设置展开箭头按钮点击事件
|
||
View btnExpandCategories = findViewById(R.id.btnExpandCategories);
|
||
if (btnExpandCategories != null) {
|
||
btnExpandCategories.setOnClickListener(v -> showChannelManagerDialog());
|
||
}
|
||
|
||
// 设置发布按钮点击事件
|
||
if (binding.btnPublish != null) {
|
||
binding.btnPublish.setOnClickListener(v -> {
|
||
// 检查登录状态
|
||
if (!AuthHelper.requireLogin(MainActivity.this, "发布内容需要登录")) {
|
||
return;
|
||
}
|
||
// 跳转到发布页面
|
||
showCreateRoomDialog();
|
||
});
|
||
}
|
||
|
||
// 隐藏其他Tab内容
|
||
View followTab = findViewById(R.id.followTabContent);
|
||
View nearbyTab = findViewById(R.id.nearbyTabContent);
|
||
View discoverTab = findViewById(R.id.discoverTabContent);
|
||
|
||
if (followTab != null) {
|
||
followTab.setVisibility(View.GONE);
|
||
}
|
||
if (nearbyTab != null) {
|
||
nearbyTab.setVisibility(View.GONE);
|
||
}
|
||
// 发现页面的空状态提示容器
|
||
if (discoverTab != null) {
|
||
discoverTab.setVisibility(View.VISIBLE);
|
||
}
|
||
if (binding.swipeRefresh != null) {
|
||
binding.swipeRefresh.setVisibility(View.VISIBLE);
|
||
}
|
||
hideEmptyState();
|
||
|
||
// 设置发现页面空状态刷新按钮点击事件
|
||
View btnRefreshDiscover = findViewById(R.id.btnRefreshDiscover);
|
||
if (btnRefreshDiscover != null) {
|
||
btnRefreshDiscover.setOnClickListener(v -> fetchDiscoverRooms());
|
||
}
|
||
|
||
// 恢复房间列表的布局管理器和适配器
|
||
if (binding.roomsRecyclerView != null) {
|
||
binding.roomsRecyclerView.setLayoutManager(roomsLayoutManager);
|
||
binding.roomsRecyclerView.setAdapter(adapter);
|
||
binding.roomsRecyclerView.setVisibility(View.VISIBLE);
|
||
}
|
||
|
||
// 使用房间适配器显示推荐内容
|
||
if (adapter != null) {
|
||
// 先显示所有推荐房间,然后应用分类筛选
|
||
allRooms.clear();
|
||
allRooms.addAll(discoverRooms);
|
||
applyCategoryFilterWithAnimation(currentCategory);
|
||
}
|
||
|
||
// 更新空状态
|
||
updateDiscoverEmptyState();
|
||
}
|
||
|
||
/**
|
||
* 更新发现页面的空状态
|
||
*/
|
||
private void updateDiscoverEmptyState() {
|
||
View discoverEmptyContainer = findViewById(R.id.discoverEmptyContainer);
|
||
TextView discoverEmptyText = findViewById(R.id.discoverEmptyText);
|
||
|
||
if (allRooms.isEmpty()) {
|
||
// 显示空状态
|
||
if (discoverEmptyContainer != null) {
|
||
discoverEmptyContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
if (discoverEmptyText != null) {
|
||
discoverEmptyText.setText("该分类下暂无直播间");
|
||
}
|
||
if (binding.roomsRecyclerView != null) {
|
||
binding.roomsRecyclerView.setVisibility(View.GONE);
|
||
}
|
||
} else {
|
||
// 隐藏空状态
|
||
if (discoverEmptyContainer != null) {
|
||
discoverEmptyContainer.setVisibility(View.GONE);
|
||
}
|
||
if (binding.roomsRecyclerView != null) {
|
||
binding.roomsRecyclerView.setVisibility(View.VISIBLE);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 显示附近页面
|
||
*/
|
||
private void showNearbyTab() {
|
||
// 隐藏分类标签容器(附近页面不需要分类筛选)
|
||
View categoryTabsContainer = findViewById(R.id.categoryTabsContainer);
|
||
if (categoryTabsContainer != null) {
|
||
categoryTabsContainer.setVisibility(View.GONE);
|
||
}
|
||
|
||
// 隐藏发布按钮
|
||
if (binding.btnPublish != null) {
|
||
binding.btnPublish.setVisibility(View.GONE);
|
||
}
|
||
|
||
// 隐藏其他Tab内容,显示附近Tab
|
||
View followTab = findViewById(R.id.followTabContent);
|
||
View discoverTab = findViewById(R.id.discoverTabContent);
|
||
View nearbyTab = findViewById(R.id.nearbyTabContent);
|
||
|
||
if (followTab != null) {
|
||
followTab.setVisibility(View.GONE);
|
||
}
|
||
if (discoverTab != null) {
|
||
discoverTab.setVisibility(View.GONE);
|
||
}
|
||
if (binding.swipeRefresh != null) {
|
||
binding.swipeRefresh.setVisibility(View.GONE);
|
||
}
|
||
if (nearbyTab != null) {
|
||
nearbyTab.setVisibility(View.VISIBLE);
|
||
}
|
||
hideEmptyState();
|
||
|
||
// 初始化附近用户适配器(如果还没有)
|
||
if (nearbyUsersAdapter == null) {
|
||
nearbyUsersAdapter = new NearbyUsersAdapter(user -> {
|
||
if (user == null) return;
|
||
// 点击附近用户,跳转到用户主页
|
||
UserProfileReadOnlyActivity.start(MainActivity.this,
|
||
user.getId(),
|
||
user.getName(),
|
||
user.getDistanceText(), // 位置信息
|
||
"", // bio
|
||
""); // avatarUrl
|
||
});
|
||
// 设置添加好友按钮点击事件
|
||
nearbyUsersAdapter.setOnAddFriendClickListener(user -> {
|
||
if (user == null) return;
|
||
sendFriendRequestToNearbyUser(user);
|
||
});
|
||
}
|
||
|
||
// 设置附近内容RecyclerView
|
||
RecyclerView nearbyContentList = findViewById(R.id.nearbyContentList);
|
||
if (nearbyContentList != null && nearbyContentList.getAdapter() == null) {
|
||
nearbyContentList.setLayoutManager(new LinearLayoutManager(this));
|
||
nearbyContentList.setAdapter(nearbyUsersAdapter);
|
||
}
|
||
|
||
// 设置附近页面的SwipeRefreshLayout
|
||
SwipeRefreshLayout nearbySwipeRefresh = findViewById(R.id.nearbySwipeRefresh);
|
||
if (nearbySwipeRefresh != null) {
|
||
nearbySwipeRefresh.setOnRefreshListener(this::loadNearbyUsers);
|
||
}
|
||
|
||
// 设置刷新按钮点击事件
|
||
View btnOpenLocal = findViewById(R.id.btnOpenLocal);
|
||
if (btnOpenLocal != null) {
|
||
btnOpenLocal.setOnClickListener(v -> loadNearbyUsers());
|
||
}
|
||
|
||
// 设置开启定位按钮点击事件
|
||
View btnEnableLocation = findViewById(R.id.btnEnableLocation);
|
||
if (btnEnableLocation != null) {
|
||
btnEnableLocation.setOnClickListener(v -> requestLocationPermission());
|
||
}
|
||
|
||
// 检查位置权限
|
||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
|
||
!= PackageManager.PERMISSION_GRANTED
|
||
&& ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
|
||
!= PackageManager.PERMISSION_GRANTED) {
|
||
// 显示空状态提示,隐藏其他状态
|
||
View emptyNearbyContainer = findViewById(R.id.emptyNearbyContainer);
|
||
View nearbyLoadingContainer = findViewById(R.id.nearbyLoadingContainer);
|
||
RecyclerView nearbyList = findViewById(R.id.nearbyContentList);
|
||
if (nearbyLoadingContainer != null) {
|
||
nearbyLoadingContainer.setVisibility(View.GONE);
|
||
}
|
||
if (emptyNearbyContainer != null) {
|
||
emptyNearbyContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
if (nearbyList != null) {
|
||
nearbyList.setVisibility(View.GONE);
|
||
}
|
||
// 请求位置权限
|
||
requestLocationPermission();
|
||
return;
|
||
}
|
||
|
||
// 有权限,加载附近用户
|
||
loadNearbyUsers();
|
||
}
|
||
|
||
/**
|
||
* 显示关注页面时从后端获取关注主播的直播间列表
|
||
* 注意:关注功能需要用户登录,在showFollowTab()中会检查登录状态
|
||
*/
|
||
private void fetchFollowRooms() {
|
||
// 检查登录状态
|
||
if (!AuthHelper.isLoggedIn(this)) {
|
||
followRooms.clear();
|
||
if (adapter != null) {
|
||
adapter.submitList(new ArrayList<>());
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 显示加载状态
|
||
if (binding.loading != null) {
|
||
binding.loading.setVisibility(View.VISIBLE);
|
||
}
|
||
|
||
// 从关注列表获取关注的用户ID,然后筛选出正在直播的房间
|
||
ApiClient.getService(getApplicationContext()).getFollowingList(1, 100)
|
||
.enqueue(new Callback<ApiResponse<PageResponse<Map<String, Object>>>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<PageResponse<Map<String, Object>>>> call,
|
||
Response<ApiResponse<PageResponse<Map<String, Object>>>> response) {
|
||
if (binding.loading != null) {
|
||
binding.loading.setVisibility(View.GONE);
|
||
}
|
||
|
||
ApiResponse<PageResponse<Map<String, Object>>> body = response.body();
|
||
if (response.isSuccessful() && body != null && body.isOk() && body.getData() != null) {
|
||
List<Map<String, Object>> followingList = body.getData().getList();
|
||
if (followingList != null && !followingList.isEmpty()) {
|
||
// 获取所有直播间,然后筛选出关注用户的直播间
|
||
fetchAndFilterFollowRooms(followingList);
|
||
} else {
|
||
followRooms.clear();
|
||
if ("关注".equals(currentTopTab) && adapter != null) {
|
||
adapter.submitList(new ArrayList<>());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<PageResponse<Map<String, Object>>>> call, Throwable t) {
|
||
if (binding.loading != null) {
|
||
binding.loading.setVisibility(View.GONE);
|
||
}
|
||
followRooms.clear();
|
||
if ("关注".equals(currentTopTab) && adapter != null) {
|
||
adapter.submitList(new ArrayList<>());
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 获取所有直播间并筛选出关注用户的直播间
|
||
*/
|
||
private void fetchAndFilterFollowRooms(List<Map<String, Object>> followList) {
|
||
// 从关注列表中提取用户ID
|
||
if (followList == null || followList.isEmpty()) {
|
||
if (adapter != null) {
|
||
adapter.submitList(new ArrayList<>());
|
||
}
|
||
return;
|
||
}
|
||
// TODO: 实现根据关注列表筛选直播间
|
||
if (adapter != null) {
|
||
adapter.submitList(new ArrayList<>());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 构建发现页面的房间列表(推荐算法前端实现)
|
||
*/
|
||
private List<Room> buildDiscoverRooms() {
|
||
// TODO: 接入后端接口 - 获取推荐直播间列表
|
||
// 接口路径: GET /api/rooms/recommend 或 GET /api/rooms?type=recommend
|
||
// 请求参数:
|
||
// - userId: 当前用户ID(从token中获取,用于个性化推荐)
|
||
// - page (可选): 页码
|
||
// - pageSize (可选): 每页数量
|
||
// 返回数据格式: ApiResponse<List<Room>>
|
||
// Room对象应包含: id, title, streamerName, type, isLive, coverUrl, viewerCount, recommendScore等字段
|
||
// 后端应根据用户观看历史、点赞记录、关注关系等进行个性化推荐
|
||
List<Room> list = new ArrayList<>();
|
||
|
||
// 不再使用模拟数据,只从后端接口获取真实推荐直播间数据
|
||
|
||
return list;
|
||
}
|
||
|
||
/**
|
||
* 构建附近页面的用户列表(使用模拟位置数据)
|
||
*/
|
||
private List<NearbyUser> buildNearbyUsers() {
|
||
// TODO: 接入后端接口 - 获取附近用户列表
|
||
// 接口路径: GET /api/users/nearby
|
||
// 请求参数:
|
||
// - latitude: 当前用户纬度(必填)
|
||
// - longitude: 当前用户经度(必填)
|
||
// - radius (可选): 搜索半径(单位:米,默认5000)
|
||
// - page (可选): 页码
|
||
// - pageSize (可选): 每页数量
|
||
// 返回数据格式: ApiResponse<List<NearbyUser>>
|
||
// NearbyUser对象应包含: id, name, avatarUrl, distance (距离,单位:米), isLive, location等字段
|
||
// 需要先获取用户位置权限,然后调用此接口
|
||
List<NearbyUser> list = new ArrayList<>();
|
||
|
||
// 不再使用模拟数据,只从后端接口获取真实附近用户数据
|
||
|
||
return list;
|
||
}
|
||
|
||
/**
|
||
* 请求位置权限
|
||
*/
|
||
private void requestLocationPermission() {
|
||
// 检查是否应该显示权限说明
|
||
// shouldShowRequestPermissionRationale 返回 true 表示用户之前拒绝过,但还可以再次请求
|
||
// 返回 false 可能是第一次请求,也可能是用户选择了"不再询问"
|
||
boolean shouldShowRationale = ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION);
|
||
|
||
if (shouldShowRationale) {
|
||
// 用户之前拒绝过,但还可以再次请求,显示说明后直接请求权限
|
||
new AlertDialog.Builder(this)
|
||
.setTitle("需要位置权限")
|
||
.setMessage("附近功能需要访问位置信息,以便为您推荐附近的用户和直播。")
|
||
.setPositiveButton("授权", (dialog, which) -> {
|
||
// 直接请求权限
|
||
ActivityCompat.requestPermissions(this,
|
||
new String[]{
|
||
Manifest.permission.ACCESS_FINE_LOCATION,
|
||
Manifest.permission.ACCESS_COARSE_LOCATION
|
||
},
|
||
REQUEST_LOCATION_PERMISSION);
|
||
})
|
||
.setNegativeButton("取消", null)
|
||
.setCancelable(true)
|
||
.show();
|
||
} else {
|
||
// 第一次请求权限,或者用户选择了"不再询问"
|
||
// 直接请求权限,如果用户选择了"不再询问",系统会静默失败
|
||
// 我们会在权限回调中处理这种情况
|
||
ActivityCompat.requestPermissions(this,
|
||
new String[]{
|
||
Manifest.permission.ACCESS_FINE_LOCATION,
|
||
Manifest.permission.ACCESS_COARSE_LOCATION
|
||
},
|
||
REQUEST_LOCATION_PERMISSION);
|
||
}
|
||
}
|
||
|
||
// 用于取消超时回调的Runnable
|
||
private Runnable followLoadingTimeoutRunnable;
|
||
private Runnable nearbyLoadingTimeoutRunnable;
|
||
|
||
/**
|
||
* 停止关注页面的下拉刷新
|
||
*/
|
||
private void stopFollowSwipeRefresh() {
|
||
SwipeRefreshLayout followSwipeRefresh = findViewById(R.id.followSwipeRefresh);
|
||
if (followSwipeRefresh != null && followSwipeRefresh.isRefreshing()) {
|
||
followSwipeRefresh.setRefreshing(false);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 停止附近页面的下拉刷新
|
||
*/
|
||
private void stopNearbySwipeRefresh() {
|
||
SwipeRefreshLayout nearbySwipeRefresh = findViewById(R.id.nearbySwipeRefresh);
|
||
if (nearbySwipeRefresh != null && nearbySwipeRefresh.isRefreshing()) {
|
||
nearbySwipeRefresh.setRefreshing(false);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 加载推荐用户列表(关注页面)
|
||
* 有关注显示关注用户,没有关注则随机展示10名用户
|
||
*/
|
||
private void loadRecommendUsers() {
|
||
// 显示加载状态,隐藏其他状态
|
||
View followLoadingContainer = findViewById(R.id.followLoadingContainer);
|
||
View emptyRecommendContainer = findViewById(R.id.emptyRecommendContainer);
|
||
View emptyFollowContainer = findViewById(R.id.emptyFollowContainer);
|
||
View followedUsersContainer = findViewById(R.id.followedUsersContainer);
|
||
RecyclerView recommendUsersList = findViewById(R.id.recommendUsersList);
|
||
|
||
// 如果不是下拉刷新触发的,才显示加载容器
|
||
// 获取SwipeRefreshLayout引用,判断是否是下拉刷新触发
|
||
SwipeRefreshLayout followSwipeRefresh = findViewById(R.id.followSwipeRefresh);
|
||
boolean isSwipeRefresh = followSwipeRefresh != null && followSwipeRefresh.isRefreshing();
|
||
|
||
// 如果不是下拉刷新触发的,才显示加载容器
|
||
if (!isSwipeRefresh) {
|
||
if (followLoadingContainer != null) {
|
||
followLoadingContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
}
|
||
if (emptyRecommendContainer != null) {
|
||
emptyRecommendContainer.setVisibility(View.GONE);
|
||
}
|
||
if (recommendUsersList != null) {
|
||
recommendUsersList.setVisibility(View.GONE);
|
||
}
|
||
|
||
// 取消之前的超时回调
|
||
if (followLoadingTimeoutRunnable != null) {
|
||
handler.removeCallbacks(followLoadingTimeoutRunnable);
|
||
}
|
||
|
||
// 设置3秒超时,确保加载圈不会一直显示
|
||
followLoadingTimeoutRunnable = () -> {
|
||
// 停止下拉刷新
|
||
stopFollowSwipeRefresh();
|
||
if (followLoadingContainer != null && followLoadingContainer.getVisibility() == View.VISIBLE) {
|
||
followLoadingContainer.setVisibility(View.GONE);
|
||
// 如果还在加载,显示空状态
|
||
if (recommendUsers.isEmpty()) {
|
||
if (recommendUsersList != null) {
|
||
recommendUsersList.setVisibility(View.GONE);
|
||
}
|
||
if (emptyRecommendContainer != null) {
|
||
emptyRecommendContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
handler.postDelayed(followLoadingTimeoutRunnable, 3000);
|
||
|
||
// 先检查是否有关注的用户
|
||
if (AuthHelper.isLoggedIn(this)) {
|
||
checkFollowingAndLoadUsers(followLoadingContainer, emptyRecommendContainer, emptyFollowContainer,
|
||
followedUsersContainer, recommendUsersList);
|
||
} else {
|
||
// 未登录,直接加载随机推荐用户
|
||
if (emptyFollowContainer != null) {
|
||
emptyFollowContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
if (followedUsersContainer != null) {
|
||
followedUsersContainer.setVisibility(View.GONE);
|
||
}
|
||
loadRandomRecommendUsers(followLoadingContainer, emptyRecommendContainer, recommendUsersList);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查关注列表并加载用户
|
||
*/
|
||
private void checkFollowingAndLoadUsers(View followLoadingContainer, View emptyRecommendContainer,
|
||
View emptyFollowContainer, View followedUsersContainer, RecyclerView recommendUsersList) {
|
||
ApiClient.getService(getApplicationContext()).getFollowingList(1, 10)
|
||
.enqueue(new Callback<ApiResponse<PageResponse<Map<String, Object>>>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<PageResponse<Map<String, Object>>>> call,
|
||
Response<ApiResponse<PageResponse<Map<String, Object>>>> response) {
|
||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||
PageResponse<Map<String, Object>> data = response.body().getData();
|
||
if (data != null && data.getList() != null && !data.getList().isEmpty()) {
|
||
// 有关注的用户,显示关注区域
|
||
if (emptyFollowContainer != null) {
|
||
emptyFollowContainer.setVisibility(View.GONE);
|
||
}
|
||
if (followedUsersContainer != null) {
|
||
followedUsersContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
// TODO: 加载关注用户的动态
|
||
} else {
|
||
// 没有关注的用户
|
||
if (emptyFollowContainer != null) {
|
||
emptyFollowContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
if (followedUsersContainer != null) {
|
||
followedUsersContainer.setVisibility(View.GONE);
|
||
}
|
||
}
|
||
} else {
|
||
if (emptyFollowContainer != null) {
|
||
emptyFollowContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
}
|
||
// 加载随机推荐用户
|
||
loadRandomRecommendUsers(followLoadingContainer, emptyRecommendContainer, recommendUsersList);
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<PageResponse<Map<String, Object>>>> call, Throwable t) {
|
||
if (emptyFollowContainer != null) {
|
||
emptyFollowContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
loadRandomRecommendUsers(followLoadingContainer, emptyRecommendContainer, recommendUsersList);
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 加载随机推荐用户(最多10名)
|
||
* 优先使用推荐接口,失败则使用附近用户接口
|
||
*/
|
||
private void loadRandomRecommendUsers(View followLoadingContainer, View emptyRecommendContainer,
|
||
RecyclerView recommendUsersList) {
|
||
// 从后端获取推荐用户
|
||
ApiClient.getService(getApplicationContext()).getRecommendUsers(30)
|
||
.enqueue(new Callback<ApiResponse<List<CommunityResponse.MatchUser>>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<List<CommunityResponse.MatchUser>>> call,
|
||
Response<ApiResponse<List<CommunityResponse.MatchUser>>> response) {
|
||
// 取消超时回调
|
||
if (followLoadingTimeoutRunnable != null) {
|
||
handler.removeCallbacks(followLoadingTimeoutRunnable);
|
||
}
|
||
|
||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||
List<CommunityResponse.MatchUser> users = response.body().getData();
|
||
if (users != null && !users.isEmpty()) {
|
||
displayRecommendUsers(users, followLoadingContainer, emptyRecommendContainer, recommendUsersList);
|
||
} else {
|
||
// 推荐用户为空,尝试使用附近用户接口
|
||
loadUsersFromNearby(followLoadingContainer, emptyRecommendContainer, recommendUsersList);
|
||
}
|
||
} else {
|
||
// 请求失败,尝试使用附近用户接口
|
||
loadUsersFromNearby(followLoadingContainer, emptyRecommendContainer, recommendUsersList);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<List<CommunityResponse.MatchUser>>> call, Throwable t) {
|
||
Log.e(TAG, "加载推荐用户失败: " + t.getMessage());
|
||
// 取消超时回调
|
||
if (followLoadingTimeoutRunnable != null) {
|
||
handler.removeCallbacks(followLoadingTimeoutRunnable);
|
||
}
|
||
// 尝试使用附近用户接口
|
||
loadUsersFromNearby(followLoadingContainer, emptyRecommendContainer, recommendUsersList);
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 从附近用户API加载用户(备选方案)
|
||
*/
|
||
private void loadUsersFromNearby(View followLoadingContainer, View emptyRecommendContainer,
|
||
RecyclerView recommendUsersList) {
|
||
// 使用附近用户接口获取用户列表(传入0,0坐标,后端会返回所有用户)
|
||
ApiClient.getService(getApplicationContext()).getNearbyUsers(0, 0, 50000, 30)
|
||
.enqueue(new Callback<ApiResponse<CommunityResponse.NearbyUserList>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<CommunityResponse.NearbyUserList>> call,
|
||
Response<ApiResponse<CommunityResponse.NearbyUserList>> response) {
|
||
// 停止下拉刷新
|
||
stopFollowSwipeRefresh();
|
||
// 隐藏加载状态
|
||
if (followLoadingContainer != null) {
|
||
followLoadingContainer.setVisibility(View.GONE);
|
||
}
|
||
|
||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||
CommunityResponse.NearbyUserList userList = response.body().getData();
|
||
if (userList != null && userList.getUsers() != null && !userList.getUsers().isEmpty()) {
|
||
List<CommunityResponse.NearbyUser> users = userList.getUsers();
|
||
recommendUsers.clear();
|
||
String baseUrl = ApiClient.getCurrentBaseUrl(getApplicationContext());
|
||
|
||
// 随机打乱用户列表
|
||
List<CommunityResponse.NearbyUser> shuffledUsers = new ArrayList<>(users);
|
||
Collections.shuffle(shuffledUsers);
|
||
|
||
// 最多取10名用户
|
||
int count = Math.min(10, shuffledUsers.size());
|
||
for (int i = 0; i < count; i++) {
|
||
CommunityResponse.NearbyUser user = shuffledUsers.get(i);
|
||
String avatarUrl = "";
|
||
if (user.avatar != null && !user.avatar.isEmpty()) {
|
||
if (user.avatar.startsWith("http://") || user.avatar.startsWith("https://")) {
|
||
avatarUrl = user.avatar;
|
||
} else if (user.avatar.startsWith("crmebimage/")) {
|
||
avatarUrl = baseUrl.replace("/api/", "/") + user.avatar;
|
||
} else {
|
||
avatarUrl = user.avatar;
|
||
}
|
||
}
|
||
|
||
// 只显示个性签名,没有签名则显示默认文本
|
||
String desc = (user.bio != null && !user.bio.isEmpty()) ? user.bio : "这个人很懒,什么都没写";
|
||
recommendUsers.add(new RecommendUserAdapter.RecommendUser(
|
||
user.id, user.nickname, avatarUrl, desc, false));
|
||
}
|
||
if (recommendUserAdapter != null) {
|
||
recommendUserAdapter.submitList(new ArrayList<>(recommendUsers));
|
||
}
|
||
// 显示列表
|
||
if (recommendUsersList != null) {
|
||
recommendUsersList.setVisibility(View.VISIBLE);
|
||
}
|
||
if (emptyRecommendContainer != null) {
|
||
emptyRecommendContainer.setVisibility(View.GONE);
|
||
}
|
||
} else {
|
||
showEmptyRecommendState(followLoadingContainer, emptyRecommendContainer, recommendUsersList);
|
||
}
|
||
} else {
|
||
showEmptyRecommendState(followLoadingContainer, emptyRecommendContainer, recommendUsersList);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<CommunityResponse.NearbyUserList>> call, Throwable t) {
|
||
Log.e(TAG, "从附近用户加载失败: " + t.getMessage());
|
||
showEmptyRecommendState(followLoadingContainer, emptyRecommendContainer, recommendUsersList);
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 显示推荐用户列表
|
||
*/
|
||
private void displayRecommendUsers(List<CommunityResponse.MatchUser> users,
|
||
View followLoadingContainer, View emptyRecommendContainer, RecyclerView recommendUsersList) {
|
||
// 停止下拉刷新
|
||
stopFollowSwipeRefresh();
|
||
// 隐藏加载状态
|
||
if (followLoadingContainer != null) {
|
||
followLoadingContainer.setVisibility(View.GONE);
|
||
}
|
||
|
||
recommendUsers.clear();
|
||
String baseUrl = ApiClient.getCurrentBaseUrl(getApplicationContext());
|
||
|
||
// 随机打乱用户列表
|
||
List<CommunityResponse.MatchUser> shuffledUsers = new ArrayList<>(users);
|
||
Collections.shuffle(shuffledUsers);
|
||
|
||
// 最多取10名用户
|
||
int count = Math.min(10, shuffledUsers.size());
|
||
for (int i = 0; i < count; i++) {
|
||
CommunityResponse.MatchUser user = shuffledUsers.get(i);
|
||
String avatarUrl = "";
|
||
if (user.avatar != null && !user.avatar.isEmpty()) {
|
||
if (user.avatar.startsWith("http://") || user.avatar.startsWith("https://")) {
|
||
avatarUrl = user.avatar;
|
||
} else if (user.avatar.startsWith("crmebimage/")) {
|
||
avatarUrl = baseUrl.replace("/api/", "/") + user.avatar;
|
||
} else {
|
||
avatarUrl = user.avatar;
|
||
}
|
||
}
|
||
|
||
// 只显示个性签名,没有签名则显示默认文本
|
||
String desc = (user.bio != null && !user.bio.isEmpty()) ? user.bio : "这个人很懒,什么都没写";
|
||
recommendUsers.add(new RecommendUserAdapter.RecommendUser(user.id, user.nickname, avatarUrl, desc, false));
|
||
}
|
||
if (recommendUserAdapter != null) {
|
||
recommendUserAdapter.submitList(new ArrayList<>(recommendUsers));
|
||
}
|
||
// 显示列表
|
||
if (recommendUsersList != null) {
|
||
recommendUsersList.setVisibility(View.VISIBLE);
|
||
}
|
||
if (emptyRecommendContainer != null) {
|
||
emptyRecommendContainer.setVisibility(View.GONE);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 显示推荐用户空状态
|
||
*/
|
||
private void showEmptyRecommendState(View followLoadingContainer, View emptyRecommendContainer,
|
||
RecyclerView recommendUsersList) {
|
||
// 停止下拉刷新
|
||
stopFollowSwipeRefresh();
|
||
if (followLoadingContainer != null) {
|
||
followLoadingContainer.setVisibility(View.GONE);
|
||
}
|
||
if (recommendUsersList != null) {
|
||
recommendUsersList.setVisibility(View.GONE);
|
||
}
|
||
if (emptyRecommendContainer != null) {
|
||
emptyRecommendContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 关注用户
|
||
*/
|
||
private void followUser(RecommendUserAdapter.RecommendUser user, int position) {
|
||
if (!AuthHelper.isLoggedIn(this)) {
|
||
AuthHelper.requireLogin(this, "关注用户需要登录");
|
||
return;
|
||
}
|
||
|
||
Map<String, Object> body = new java.util.HashMap<>();
|
||
body.put("userId", user.getId());
|
||
|
||
ApiClient.getService(getApplicationContext()).followUser(body)
|
||
.enqueue(new Callback<ApiResponse<Map<String, Object>>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<Map<String, Object>>> call, Response<ApiResponse<Map<String, Object>>> response) {
|
||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||
user.setFollowed(true);
|
||
if (recommendUserAdapter != null) {
|
||
recommendUserAdapter.notifyItemChanged(position);
|
||
}
|
||
Toast.makeText(MainActivity.this, "关注成功", Toast.LENGTH_SHORT).show();
|
||
} else {
|
||
Toast.makeText(MainActivity.this, "关注失败", Toast.LENGTH_SHORT).show();
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<Map<String, Object>>> call, Throwable t) {
|
||
Toast.makeText(MainActivity.this, "网络错误", Toast.LENGTH_SHORT).show();
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 向附近用户发送好友请求
|
||
*/
|
||
private void sendFriendRequestToNearbyUser(NearbyUser user) {
|
||
if (!AuthHelper.isLoggedIn(this)) {
|
||
AuthHelper.requireLogin(this, "添加好友需要登录");
|
||
return;
|
||
}
|
||
|
||
try {
|
||
int userId = Integer.parseInt(user.getId());
|
||
Map<String, Object> body = new java.util.HashMap<>();
|
||
body.put("targetUserId", userId);
|
||
body.put("message", "我想加你为好友");
|
||
|
||
ApiClient.getService(getApplicationContext()).sendFriendRequest(body)
|
||
.enqueue(new Callback<ApiResponse<Boolean>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<Boolean>> call, Response<ApiResponse<Boolean>> response) {
|
||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||
Toast.makeText(MainActivity.this, "好友请求已发送", Toast.LENGTH_SHORT).show();
|
||
} else {
|
||
String msg = response.body() != null && response.body().getMessage() != null
|
||
? response.body().getMessage() : "发送失败";
|
||
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<Boolean>> call, Throwable t) {
|
||
Toast.makeText(MainActivity.this, "网络错误", Toast.LENGTH_SHORT).show();
|
||
}
|
||
});
|
||
} catch (NumberFormatException e) {
|
||
Toast.makeText(this, "用户ID格式错误", Toast.LENGTH_SHORT).show();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 加载频道数据(发现页面)
|
||
* 只保留5个固定标签:推荐、游戏、才艺、直播、音乐
|
||
*/
|
||
private void loadChannelData() {
|
||
// 初始化默认频道(5个固定标签)
|
||
if (myChannels.isEmpty()) {
|
||
myChannels.add(new ChannelTagAdapter.ChannelTag(1, "推荐", true));
|
||
myChannels.add(new ChannelTagAdapter.ChannelTag(2, "游戏", true));
|
||
myChannels.add(new ChannelTagAdapter.ChannelTag(3, "才艺", true));
|
||
myChannels.add(new ChannelTagAdapter.ChannelTag(4, "直播", true));
|
||
myChannels.add(new ChannelTagAdapter.ChannelTag(5, "音乐", true));
|
||
}
|
||
|
||
if (myChannelAdapter != null) {
|
||
myChannelAdapter.submitList(new ArrayList<>(myChannels));
|
||
myChannelAdapter.setSelectedPosition(0); // 默认选中推荐
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 加载附近用户(附近页面)
|
||
* 展示同城用户,最多10名,刷新时随机展示同省用户
|
||
*/
|
||
private void loadNearbyUsers() {
|
||
View emptyNearbyContainer = findViewById(R.id.emptyNearbyContainer);
|
||
RecyclerView nearbyList = findViewById(R.id.nearbyContentList);
|
||
View nearbyLoadingContainer = findViewById(R.id.nearbyLoadingContainer);
|
||
TextView nearbyCountText = findViewById(R.id.nearbyCountText);
|
||
TextView locationText = findViewById(R.id.locationText);
|
||
|
||
// 获取SwipeRefreshLayout引用,判断是否是下拉刷新触发
|
||
SwipeRefreshLayout nearbySwipeRefresh = findViewById(R.id.nearbySwipeRefresh);
|
||
boolean isSwipeRefresh = nearbySwipeRefresh != null && nearbySwipeRefresh.isRefreshing();
|
||
|
||
// 如果不是下拉刷新触发的,才显示加载容器
|
||
if (!isSwipeRefresh) {
|
||
if (nearbyLoadingContainer != null) {
|
||
nearbyLoadingContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
}
|
||
if (emptyNearbyContainer != null) {
|
||
emptyNearbyContainer.setVisibility(View.GONE);
|
||
}
|
||
if (nearbyList != null) {
|
||
nearbyList.setVisibility(View.GONE);
|
||
}
|
||
if (locationText != null) {
|
||
locationText.setText("正在搜索附近的人...");
|
||
}
|
||
|
||
// 取消之前的超时回调
|
||
if (nearbyLoadingTimeoutRunnable != null) {
|
||
handler.removeCallbacks(nearbyLoadingTimeoutRunnable);
|
||
}
|
||
|
||
// 设置3秒超时,确保加载圈不会一直显示
|
||
nearbyLoadingTimeoutRunnable = () -> {
|
||
// 停止下拉刷新
|
||
stopNearbySwipeRefresh();
|
||
if (nearbyLoadingContainer != null && nearbyLoadingContainer.getVisibility() == View.VISIBLE) {
|
||
nearbyLoadingContainer.setVisibility(View.GONE);
|
||
// 如果还在加载,显示空状态
|
||
if (nearbyUsers.isEmpty()) {
|
||
if (nearbyList != null) {
|
||
nearbyList.setVisibility(View.GONE);
|
||
}
|
||
if (emptyNearbyContainer != null) {
|
||
emptyNearbyContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
if (locationText != null) {
|
||
locationText.setText("加载超时,请重试");
|
||
}
|
||
}
|
||
}
|
||
};
|
||
handler.postDelayed(nearbyLoadingTimeoutRunnable, 3000);
|
||
|
||
// 从后端获取附近用户(获取更多用户以便随机筛选)
|
||
ApiClient.getService(getApplicationContext()).getNearbyUsers(0, 0, 50000, 50)
|
||
.enqueue(new Callback<ApiResponse<CommunityResponse.NearbyUserList>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<CommunityResponse.NearbyUserList>> call,
|
||
Response<ApiResponse<CommunityResponse.NearbyUserList>> response) {
|
||
// 取消超时回调
|
||
if (nearbyLoadingTimeoutRunnable != null) {
|
||
handler.removeCallbacks(nearbyLoadingTimeoutRunnable);
|
||
}
|
||
// 停止下拉刷新
|
||
stopNearbySwipeRefresh();
|
||
// 隐藏加载状态
|
||
if (nearbyLoadingContainer != null) {
|
||
nearbyLoadingContainer.setVisibility(View.GONE);
|
||
}
|
||
|
||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||
CommunityResponse.NearbyUserList userList = response.body().getData();
|
||
if (userList != null && userList.getUsers() != null && !userList.getUsers().isEmpty()) {
|
||
nearbyUsers.clear();
|
||
|
||
// 随机打乱用户列表
|
||
List<CommunityResponse.NearbyUser> shuffledUsers = new ArrayList<>(userList.getUsers());
|
||
Collections.shuffle(shuffledUsers);
|
||
|
||
// 最多取10名用户
|
||
int count = Math.min(10, shuffledUsers.size());
|
||
for (int i = 0; i < count; i++) {
|
||
CommunityResponse.NearbyUser user = shuffledUsers.get(i);
|
||
String location = user.location != null ? user.location : "";
|
||
nearbyUsers.add(new NearbyUser(String.valueOf(user.id), user.nickname, location, user.isOnline));
|
||
}
|
||
|
||
if (nearbyUsersAdapter != null) {
|
||
nearbyUsersAdapter.submitList(new ArrayList<>(nearbyUsers));
|
||
}
|
||
|
||
// 更新附近人数
|
||
if (nearbyCountText != null) {
|
||
nearbyCountText.setText("共" + nearbyUsers.size() + "人");
|
||
}
|
||
if (locationText != null) {
|
||
locationText.setText("已找到附近的人");
|
||
}
|
||
|
||
// 显示列表,隐藏空状态
|
||
if (emptyNearbyContainer != null) {
|
||
emptyNearbyContainer.setVisibility(View.GONE);
|
||
}
|
||
if (nearbyList != null) {
|
||
nearbyList.setVisibility(View.VISIBLE);
|
||
}
|
||
} else {
|
||
// 没有数据,显示空状态
|
||
if (nearbyCountText != null) {
|
||
nearbyCountText.setText("");
|
||
}
|
||
if (locationText != null) {
|
||
locationText.setText("附近暂无用户");
|
||
}
|
||
if (emptyNearbyContainer != null) {
|
||
emptyNearbyContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
if (nearbyList != null) {
|
||
nearbyList.setVisibility(View.GONE);
|
||
}
|
||
}
|
||
} else {
|
||
// 请求失败
|
||
if (locationText != null) {
|
||
locationText.setText("获取附近用户失败");
|
||
}
|
||
if (emptyNearbyContainer != null) {
|
||
emptyNearbyContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
if (nearbyList != null) {
|
||
nearbyList.setVisibility(View.GONE);
|
||
}
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<CommunityResponse.NearbyUserList>> call, Throwable t) {
|
||
Log.e(TAG, "加载附近用户失败: " + t.getMessage());
|
||
// 取消超时回调
|
||
if (nearbyLoadingTimeoutRunnable != null) {
|
||
handler.removeCallbacks(nearbyLoadingTimeoutRunnable);
|
||
}
|
||
// 停止下拉刷新
|
||
stopNearbySwipeRefresh();
|
||
// 隐藏加载状态,显示空状态
|
||
if (nearbyLoadingContainer != null) {
|
||
nearbyLoadingContainer.setVisibility(View.GONE);
|
||
}
|
||
if (locationText != null) {
|
||
locationText.setText("网络错误,请重试");
|
||
}
|
||
if (emptyNearbyContainer != null) {
|
||
emptyNearbyContainer.setVisibility(View.VISIBLE);
|
||
}
|
||
if (nearbyList != null) {
|
||
nearbyList.setVisibility(View.GONE);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 显示频道管理底部弹窗
|
||
*/
|
||
private void showChannelManagerDialog() {
|
||
// 创建底部弹窗
|
||
com.google.android.material.bottomsheet.BottomSheetDialog dialog =
|
||
new com.google.android.material.bottomsheet.BottomSheetDialog(this);
|
||
View dialogView = getLayoutInflater().inflate(R.layout.bottom_sheet_channel_manager, null);
|
||
dialog.setContentView(dialogView);
|
||
|
||
// 获取视图引用
|
||
RecyclerView myChannelRecycler = dialogView.findViewById(R.id.myChannelsRecycler);
|
||
RecyclerView recommendChannelRecycler = dialogView.findViewById(R.id.recommendChannelsRecycler);
|
||
TextView btnEditChannel = dialogView.findViewById(R.id.btnEditChannels);
|
||
View btnCloseChannelManager = dialogView.findViewById(R.id.btnCloseChannelManager);
|
||
|
||
// 初始化我的频道适配器
|
||
ChannelManagerAdapter myAdapter = new ChannelManagerAdapter();
|
||
myAdapter.setFixedCount(4); // 前4个固定
|
||
myAdapter.setRecommendMode(false);
|
||
|
||
// 初始化推荐频道适配器
|
||
ChannelManagerAdapter recommendAdapter = new ChannelManagerAdapter();
|
||
recommendAdapter.setRecommendMode(true);
|
||
|
||
// 设置布局管理器 - 使用FlexboxLayoutManager或GridLayoutManager
|
||
myChannelRecycler.setLayoutManager(new androidx.recyclerview.widget.GridLayoutManager(this, 4));
|
||
recommendChannelRecycler.setLayoutManager(new androidx.recyclerview.widget.GridLayoutManager(this, 4));
|
||
|
||
myChannelRecycler.setAdapter(myAdapter);
|
||
recommendChannelRecycler.setAdapter(recommendAdapter);
|
||
|
||
// 加载我的频道数据(从SharedPreferences或默认值)
|
||
List<ChannelManagerAdapter.ChannelItem> myChannelList = loadMyChannels();
|
||
myAdapter.submitList(myChannelList);
|
||
|
||
// 加载推荐频道数据
|
||
List<ChannelManagerAdapter.ChannelItem> recommendList = loadRecommendChannels(myChannelList);
|
||
recommendAdapter.submitList(recommendList);
|
||
|
||
// 设置我的频道点击事件
|
||
myAdapter.setOnChannelClickListener(new ChannelManagerAdapter.OnChannelClickListener() {
|
||
@Override
|
||
public void onChannelClick(ChannelManagerAdapter.ChannelItem item, int position) {
|
||
// 点击频道,切换到该分类
|
||
dialog.dismiss();
|
||
selectCategoryTab(item.getName());
|
||
}
|
||
|
||
@Override
|
||
public void onChannelDelete(ChannelManagerAdapter.ChannelItem item, int position) {
|
||
// 删除频道
|
||
List<ChannelManagerAdapter.ChannelItem> currentList = myAdapter.getItems();
|
||
currentList.remove(position);
|
||
myAdapter.submitList(new ArrayList<>(currentList));
|
||
saveMyChannels(currentList);
|
||
// 更新推荐列表
|
||
recommendAdapter.submitList(loadRecommendChannels(currentList));
|
||
}
|
||
|
||
@Override
|
||
public void onChannelAdd(ChannelManagerAdapter.ChannelItem item, int position) {
|
||
// 不处理
|
||
}
|
||
});
|
||
|
||
// 设置推荐频道点击事件
|
||
recommendAdapter.setOnChannelClickListener(new ChannelManagerAdapter.OnChannelClickListener() {
|
||
@Override
|
||
public void onChannelClick(ChannelManagerAdapter.ChannelItem item, int position) {
|
||
// 不处理
|
||
}
|
||
|
||
@Override
|
||
public void onChannelDelete(ChannelManagerAdapter.ChannelItem item, int position) {
|
||
// 不处理
|
||
}
|
||
|
||
@Override
|
||
public void onChannelAdd(ChannelManagerAdapter.ChannelItem item, int position) {
|
||
// 添加到我的频道
|
||
List<ChannelManagerAdapter.ChannelItem> currentList = myAdapter.getItems();
|
||
currentList.add(new ChannelManagerAdapter.ChannelItem(item.getId(), item.getName()));
|
||
myAdapter.submitList(new ArrayList<>(currentList));
|
||
saveMyChannels(currentList);
|
||
// 更新推荐列表
|
||
recommendAdapter.submitList(loadRecommendChannels(currentList));
|
||
}
|
||
});
|
||
|
||
// 编辑按钮点击事件
|
||
final boolean[] isEditMode = {false};
|
||
if (btnEditChannel != null) {
|
||
btnEditChannel.setOnClickListener(v -> {
|
||
isEditMode[0] = !isEditMode[0];
|
||
myAdapter.setEditMode(isEditMode[0]);
|
||
btnEditChannel.setText(isEditMode[0] ? "完成" : "编辑");
|
||
});
|
||
}
|
||
|
||
// 关闭按钮点击事件
|
||
if (btnCloseChannelManager != null) {
|
||
btnCloseChannelManager.setOnClickListener(v -> dialog.dismiss());
|
||
}
|
||
|
||
dialog.show();
|
||
}
|
||
|
||
/**
|
||
* 加载我的频道列表
|
||
*/
|
||
private List<ChannelManagerAdapter.ChannelItem> loadMyChannels() {
|
||
List<ChannelManagerAdapter.ChannelItem> channels = new ArrayList<>();
|
||
|
||
// 从SharedPreferences加载
|
||
android.content.SharedPreferences prefs = getSharedPreferences("channel_prefs", MODE_PRIVATE);
|
||
|
||
// 检查是否需要迁移到新版本(使用后端分类)
|
||
int savedVersion = prefs.getInt("channel_version", 0);
|
||
if (savedVersion < 2) {
|
||
// 旧版本数据,清除并使用后端分类
|
||
prefs.edit()
|
||
.remove("my_channels")
|
||
.putInt("channel_version", 2)
|
||
.apply();
|
||
Log.d(TAG, "loadMyChannels() 清除旧版本频道数据,使用后端分类");
|
||
}
|
||
|
||
String savedChannels = prefs.getString("my_channels", null);
|
||
|
||
if (savedChannels != null && !savedChannels.isEmpty()) {
|
||
String[] channelNames = savedChannels.split(",");
|
||
for (int i = 0; i < channelNames.length; i++) {
|
||
channels.add(new ChannelManagerAdapter.ChannelItem(
|
||
String.valueOf(i), channelNames[i], i < 4));
|
||
}
|
||
} else {
|
||
// 使用后端分类作为默认频道(如果有的话)
|
||
if (!allBackendCategories.isEmpty()) {
|
||
// 添加"推荐"作为第一个固定频道
|
||
channels.add(new ChannelManagerAdapter.ChannelItem("0", "推荐", true));
|
||
// 添加后端分类(最多取前4个作为默认)
|
||
int count = Math.min(allBackendCategories.size(), 4);
|
||
for (int i = 0; i < count; i++) {
|
||
com.example.livestreaming.net.CategoryResponse cat = allBackendCategories.get(i);
|
||
if (cat != null && cat.getName() != null) {
|
||
channels.add(new ChannelManagerAdapter.ChannelItem(
|
||
String.valueOf(cat.getId()), cat.getName(), true));
|
||
}
|
||
}
|
||
} else {
|
||
// 后端分类未加载,使用硬编码默认值
|
||
channels.add(new ChannelManagerAdapter.ChannelItem("0", "推荐", true));
|
||
channels.add(new ChannelManagerAdapter.ChannelItem("1", "娱乐", true));
|
||
channels.add(new ChannelManagerAdapter.ChannelItem("2", "游戏", true));
|
||
channels.add(new ChannelManagerAdapter.ChannelItem("3", "音乐", true));
|
||
channels.add(new ChannelManagerAdapter.ChannelItem("4", "户外", true));
|
||
}
|
||
}
|
||
|
||
return channels;
|
||
}
|
||
|
||
/**
|
||
* 保存我的频道列表
|
||
*/
|
||
private void saveMyChannels(List<ChannelManagerAdapter.ChannelItem> channels) {
|
||
StringBuilder sb = new StringBuilder();
|
||
for (int i = 0; i < channels.size(); i++) {
|
||
if (i > 0) sb.append(",");
|
||
sb.append(channels.get(i).getName());
|
||
}
|
||
|
||
android.content.SharedPreferences prefs = getSharedPreferences("channel_prefs", MODE_PRIVATE);
|
||
prefs.edit().putString("my_channels", sb.toString()).apply();
|
||
|
||
// 更新分类标签
|
||
updateCategoryTabsFromChannels(channels);
|
||
}
|
||
|
||
/**
|
||
* 加载推荐频道列表(排除已添加的)
|
||
*/
|
||
private List<ChannelManagerAdapter.ChannelItem> loadRecommendChannels(List<ChannelManagerAdapter.ChannelItem> myChannels) {
|
||
// 获取已添加的频道名称
|
||
java.util.Set<String> addedNames = new java.util.HashSet<>();
|
||
for (ChannelManagerAdapter.ChannelItem item : myChannels) {
|
||
addedNames.add(item.getName());
|
||
}
|
||
|
||
List<ChannelManagerAdapter.ChannelItem> recommendList = new ArrayList<>();
|
||
|
||
// 优先使用后端分类
|
||
if (!allBackendCategories.isEmpty()) {
|
||
for (com.example.livestreaming.net.CategoryResponse cat : allBackendCategories) {
|
||
if (cat != null && cat.getName() != null && !addedNames.contains(cat.getName())) {
|
||
recommendList.add(new ChannelManagerAdapter.ChannelItem(
|
||
String.valueOf(cat.getId()), cat.getName()));
|
||
}
|
||
}
|
||
} else {
|
||
// 后端分类未加载,使用硬编码默认值
|
||
String[] defaultChannels = {"娱乐", "游戏", "音乐", "户外", "聊天"};
|
||
for (int i = 0; i < defaultChannels.length; i++) {
|
||
if (!addedNames.contains(defaultChannels[i])) {
|
||
recommendList.add(new ChannelManagerAdapter.ChannelItem(String.valueOf(i), defaultChannels[i]));
|
||
}
|
||
}
|
||
}
|
||
|
||
return recommendList;
|
||
}
|
||
|
||
/**
|
||
* 根据频道列表更新分类标签
|
||
*/
|
||
private void updateCategoryTabsFromChannels(List<ChannelManagerAdapter.ChannelItem> channels) {
|
||
if (binding.categoryTabs == null) return;
|
||
|
||
binding.categoryTabs.removeAllTabs();
|
||
for (ChannelManagerAdapter.ChannelItem channel : channels) {
|
||
binding.categoryTabs.addTab(binding.categoryTabs.newTab().setText(channel.getName()));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 选中指定分类标签
|
||
*/
|
||
private void selectCategoryTab(String categoryName) {
|
||
if (binding.categoryTabs == null) return;
|
||
|
||
for (int i = 0; i < binding.categoryTabs.getTabCount(); i++) {
|
||
TabLayout.Tab tab = binding.categoryTabs.getTabAt(i);
|
||
if (tab != null && categoryName.equals(tab.getText())) {
|
||
tab.select();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|