feat: group imported threads into project archives
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
</activity>
|
||||
|
||||
<activity android:name=".ProjectDetailActivity" android:exported="false" />
|
||||
<activity android:name=".ConversationFolderActivity" android:exported="false" />
|
||||
<activity android:name=".ProjectGoalsActivity" android:exported="false" />
|
||||
<activity android:name=".ProjectVersionsActivity" android:exported="false" />
|
||||
<activity android:name=".ProjectForwardActivity" android:exported="false" />
|
||||
@@ -38,6 +39,7 @@
|
||||
<activity android:name=".GroupCreateActivity" android:exported="false" />
|
||||
<activity android:name=".DeviceDetailActivity" android:exported="false" />
|
||||
<activity android:name=".DeviceEnrollmentActivity" android:exported="false" />
|
||||
<activity android:name=".DeviceImportDraftActivity" android:exported="false" />
|
||||
<activity android:name=".SkillInventoryActivity" android:exported="false" />
|
||||
<activity android:name=".SecurityActivity" android:exported="false" />
|
||||
<activity android:name=".SettingsActivity" android:exported="false" />
|
||||
|
||||
@@ -8,6 +8,7 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONArray;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
@@ -77,6 +78,14 @@ public class BossApiClient {
|
||||
return requestWithRestore("GET", "/api/v1/conversations", null);
|
||||
}
|
||||
|
||||
public ApiResponse getConversationHome() throws IOException, JSONException {
|
||||
return requestWithRestore("GET", "/api/v1/conversations/home", null);
|
||||
}
|
||||
|
||||
public ApiResponse getConversationFolder(String folderKey) throws IOException, JSONException {
|
||||
return requestWithRestore("GET", "/api/v1/conversation-folders/" + encode(folderKey), null);
|
||||
}
|
||||
|
||||
public ApiResponse getProjectDetail(String projectId) throws IOException, JSONException {
|
||||
return requestWithRestore("GET", "/api/v1/projects/" + encode(projectId), null);
|
||||
}
|
||||
@@ -232,6 +241,24 @@ public class BossApiClient {
|
||||
return requestWithRestore("POST", "/api/v1/devices/enrollments", payload);
|
||||
}
|
||||
|
||||
public ApiResponse getDeviceImportDraft(String deviceId) throws IOException, JSONException {
|
||||
return requestWithRestore("GET", "/api/v1/devices/" + encode(deviceId) + "/import-draft", null);
|
||||
}
|
||||
|
||||
public ApiResponse selectDeviceImportCandidates(String deviceId, JSONArray selectedCandidateIds) throws IOException, JSONException {
|
||||
JSONObject payload = new JSONObject();
|
||||
payload.put("selectedCandidateIds", (Object) (selectedCandidateIds == null ? new JSONArray() : selectedCandidateIds));
|
||||
return requestWithRestore("POST", "/api/v1/devices/" + encode(deviceId) + "/import-draft/select", payload);
|
||||
}
|
||||
|
||||
public ApiResponse reviewDeviceImportDraft(String deviceId) throws IOException, JSONException {
|
||||
return requestWithRestore("POST", "/api/v1/devices/" + encode(deviceId) + "/import-draft/review", new JSONObject());
|
||||
}
|
||||
|
||||
public ApiResponse applyDeviceImportDraft(String deviceId) throws IOException, JSONException {
|
||||
return requestWithRestore("POST", "/api/v1/devices/" + encode(deviceId) + "/import-draft/apply", new JSONObject());
|
||||
}
|
||||
|
||||
public ApiResponse getAccounts() throws IOException, JSONException {
|
||||
return requestWithRestore("GET", "/api/v1/accounts", null);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class ConversationFolderActivity extends BossScreenActivity {
|
||||
public static final String EXTRA_FOLDER_KEY = "folder_key";
|
||||
public static final String EXTRA_FOLDER_NAME = "folder_name";
|
||||
|
||||
private String folderKey;
|
||||
private String folderName;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
folderKey = getIntent().getStringExtra(EXTRA_FOLDER_KEY);
|
||||
folderName = getIntent().getStringExtra(EXTRA_FOLDER_NAME);
|
||||
configureScreen(folderName == null || folderName.isEmpty() ? "项目线程" : folderName, "同一项目下的线程列表");
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reload() {
|
||||
if (folderKey == null || folderKey.isEmpty()) {
|
||||
replaceContent(BossUi.buildEmptyCard(this, "缺少项目标识。"));
|
||||
setRefreshing(false);
|
||||
return;
|
||||
}
|
||||
setRefreshing(true);
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
BossApiClient.ApiResponse response = apiClient.getConversationFolder(folderKey);
|
||||
if (!response.ok()) {
|
||||
throw new IllegalStateException(response.message());
|
||||
}
|
||||
runOnUiThread(() -> renderFolder(response.json.optJSONObject("folder")));
|
||||
} catch (Exception error) {
|
||||
runOnUiThread(() -> {
|
||||
setRefreshing(false);
|
||||
replaceContent(BossUi.buildEmptyCard(this, "项目线程加载失败:" + error.getMessage()));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void renderFolder(@Nullable JSONObject folder) {
|
||||
replaceContent();
|
||||
if (folder == null) {
|
||||
appendContent(BossUi.buildEmptyCard(this, "未找到项目线程。"));
|
||||
setRefreshing(false);
|
||||
return;
|
||||
}
|
||||
|
||||
String resolvedFolderName = folder.optString("folderLabel", folderName == null ? "项目线程" : folderName);
|
||||
String deviceName = folder.optString("deviceName", "");
|
||||
int threadCount = folder.optInt("threadCount", 0);
|
||||
configureScreen(resolvedFolderName, deviceName.isEmpty() ? "项目线程" : deviceName);
|
||||
appendContent(BossUi.buildSoftPanel(
|
||||
this,
|
||||
resolvedFolderName,
|
||||
(deviceName.isEmpty() ? "当前设备" : deviceName) + "\n共 " + threadCount + " 个线程",
|
||||
"点击线程后进入具体聊天窗口。"
|
||||
));
|
||||
|
||||
JSONArray threads = folder.optJSONArray("threads");
|
||||
if (threads == null || threads.length() == 0) {
|
||||
appendContent(BossUi.buildEmptyCard(this, "当前项目下没有线程。"));
|
||||
setRefreshing(false);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < threads.length(); i++) {
|
||||
JSONObject item = threads.optJSONObject(i);
|
||||
if (item == null) continue;
|
||||
String projectId = item.optString("projectId", "");
|
||||
WechatSurfaceMapper.ConversationRow row = WechatSurfaceMapper.toConversationRow(item);
|
||||
appendContent(BossUi.buildConversationRow(
|
||||
this,
|
||||
row,
|
||||
v -> {
|
||||
if (projectId.isEmpty()) {
|
||||
showMessage("缺少 projectId");
|
||||
return;
|
||||
}
|
||||
openProject(projectId, row.threadTitle);
|
||||
}
|
||||
));
|
||||
}
|
||||
setRefreshing(false);
|
||||
}
|
||||
|
||||
private void openProject(String projectId, String projectName) {
|
||||
Intent intent = new Intent(this, ProjectDetailActivity.class);
|
||||
intent.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, projectId);
|
||||
intent.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, projectName);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
@@ -74,10 +74,18 @@ public class DeviceDetailActivity extends BossScreenActivity {
|
||||
null
|
||||
));
|
||||
}
|
||||
appendContent(BossUi.buildMenuRow(this, "导入项目", "勾选这台设备上要暴露到会话首页的项目和线程", null, v -> openImportDraft()));
|
||||
appendContent(BossUi.buildMenuRow(this, "查看技能", "查看当前设备同步的 Skill 清单", null, v -> openSkills()));
|
||||
setRefreshing(false);
|
||||
}
|
||||
|
||||
private void openImportDraft() {
|
||||
Intent intent = new Intent(this, DeviceImportDraftActivity.class);
|
||||
intent.putExtra(DeviceImportDraftActivity.EXTRA_DEVICE_ID, deviceId);
|
||||
intent.putExtra(DeviceImportDraftActivity.EXTRA_DEVICE_NAME, deviceName);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void openSkills() {
|
||||
Intent intent = new Intent(this, SkillInventoryActivity.class);
|
||||
intent.putExtra(SkillInventoryActivity.EXTRA_DEVICE_ID, deviceId);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.EditText;
|
||||
|
||||
@@ -80,6 +81,8 @@ public class DeviceEnrollmentActivity extends BossScreenActivity {
|
||||
runOnUiThread(() -> {
|
||||
JSONObject enrollment = response.json.optJSONObject("enrollment");
|
||||
JSONObject device = response.json.optJSONObject("device");
|
||||
android.widget.Button importButton = BossUi.buildSecondaryButton(this, "继续导入项目");
|
||||
importButton.setOnClickListener(v -> openImportDraft(device));
|
||||
replaceContent(
|
||||
BossUi.buildSoftPanel(
|
||||
this,
|
||||
@@ -89,7 +92,8 @@ public class DeviceEnrollmentActivity extends BossScreenActivity {
|
||||
+ "\ntoken " + (enrollment == null ? "-" : enrollment.optString("token", "-")),
|
||||
enrollment == null ? "ready" : enrollment.optString("status", "ready")
|
||||
+ " · 到期 " + enrollment.optString("expiresAt", "-")
|
||||
)
|
||||
),
|
||||
importButton
|
||||
);
|
||||
setRefreshing(false);
|
||||
});
|
||||
@@ -101,4 +105,20 @@ public class DeviceEnrollmentActivity extends BossScreenActivity {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void openImportDraft(@Nullable JSONObject device) {
|
||||
if (device == null) {
|
||||
showMessage("设备草稿未生成完成");
|
||||
return;
|
||||
}
|
||||
String deviceId = device.optString("id", "");
|
||||
if (deviceId.isEmpty()) {
|
||||
showMessage("缺少 deviceId");
|
||||
return;
|
||||
}
|
||||
Intent intent = new Intent(this, DeviceImportDraftActivity.class);
|
||||
intent.putExtra(DeviceImportDraftActivity.EXTRA_DEVICE_ID, deviceId);
|
||||
intent.putExtra(DeviceImportDraftActivity.EXTRA_DEVICE_NAME, device.optString("name", nameInput.getText().toString().trim()));
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
|
||||
public class DeviceImportDraftActivity extends BossScreenActivity {
|
||||
public static final String EXTRA_DEVICE_ID = "device_id";
|
||||
public static final String EXTRA_DEVICE_NAME = "device_name";
|
||||
|
||||
private String deviceId;
|
||||
private String deviceName;
|
||||
private @Nullable JSONObject currentDraft;
|
||||
private @Nullable JSONObject currentResolution;
|
||||
private final LinkedHashSet<String> selectedCandidateIds = new LinkedHashSet<>();
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
deviceId = getIntent().getStringExtra(EXTRA_DEVICE_ID);
|
||||
deviceName = getIntent().getStringExtra(EXTRA_DEVICE_NAME);
|
||||
configureScreen("导入项目", deviceName == null ? "选择要导入的 Codex 项目与线程" : deviceName);
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reload() {
|
||||
if (deviceId == null || deviceId.isEmpty()) {
|
||||
replaceContent(BossUi.buildEmptyCard(this, "缺少 deviceId。"));
|
||||
setRefreshing(false);
|
||||
return;
|
||||
}
|
||||
setRefreshing(true);
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
BossApiClient.ApiResponse response = apiClient.getDeviceImportDraft(deviceId);
|
||||
if (!response.ok()) {
|
||||
throw new IllegalStateException(response.message());
|
||||
}
|
||||
runOnUiThread(() -> applyPayload(response.json.optJSONObject("draft"), response.json.optJSONObject("resolution")));
|
||||
} catch (Exception error) {
|
||||
runOnUiThread(() -> {
|
||||
setRefreshing(false);
|
||||
replaceContent(BossUi.buildEmptyCard(this, "导入草稿加载失败:" + error.getMessage()));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void applyPayload(@Nullable JSONObject draft, @Nullable JSONObject resolution) {
|
||||
currentDraft = draft;
|
||||
currentResolution = resolution;
|
||||
selectedCandidateIds.clear();
|
||||
JSONArray selected = draft == null ? null : draft.optJSONArray("selectedCandidateIds");
|
||||
if (selected != null) {
|
||||
for (int i = 0; i < selected.length(); i++) {
|
||||
String candidateId = selected.optString(i, "");
|
||||
if (!candidateId.isEmpty()) {
|
||||
selectedCandidateIds.add(candidateId);
|
||||
}
|
||||
}
|
||||
}
|
||||
renderCurrentState();
|
||||
}
|
||||
|
||||
private void renderCurrentState() {
|
||||
JSONObject draft = currentDraft;
|
||||
JSONObject resolution = currentResolution;
|
||||
replaceContent();
|
||||
appendContent(BossUi.buildSoftPanel(
|
||||
this,
|
||||
"导入 Codex 项目",
|
||||
(deviceName == null ? "当前设备" : deviceName) + "\n勾选要暴露到会话首页的项目和线程。",
|
||||
draft == null
|
||||
? "等待设备完成首次 heartbeat"
|
||||
: "候选 " + (draft.optJSONArray("candidates") == null ? 0 : draft.optJSONArray("candidates").length()) + " · 状态 " + draft.optString("status", "-")
|
||||
));
|
||||
|
||||
if (draft == null) {
|
||||
appendContent(BossUi.buildEmptyCard(this, "设备完成配对并上报项目候选后,这里会出现可导入项目。"));
|
||||
setRefreshing(false);
|
||||
return;
|
||||
}
|
||||
|
||||
JSONArray candidates = draft.optJSONArray("candidates");
|
||||
if (candidates == null || candidates.length() == 0) {
|
||||
appendContent(BossUi.buildEmptyCard(this, "当前还没有可导入的线程。"));
|
||||
setRefreshing(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, JSONArray> grouped = new LinkedHashMap<>();
|
||||
for (int i = 0; i < candidates.length(); i++) {
|
||||
JSONObject candidate = candidates.optJSONObject(i);
|
||||
if (candidate == null) continue;
|
||||
String groupKey = candidate.optString("codexFolderRef", candidate.optString("folderRef", candidate.optString("folderName", "未命名项目")));
|
||||
JSONArray bucket = grouped.get(groupKey);
|
||||
if (bucket == null) {
|
||||
bucket = new JSONArray();
|
||||
grouped.put(groupKey, bucket);
|
||||
}
|
||||
bucket.put(candidate);
|
||||
}
|
||||
|
||||
for (JSONArray items : grouped.values()) {
|
||||
JSONObject first = items.optJSONObject(0);
|
||||
if (first == null) continue;
|
||||
String folderName = first.optString("folderName", "未命名项目");
|
||||
appendContent(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
folderName,
|
||||
items.length() + " 个线程",
|
||||
"勾选后会进入主 Agent 导入建议",
|
||||
null,
|
||||
null
|
||||
));
|
||||
for (int i = 0; i < items.length(); i++) {
|
||||
JSONObject candidate = items.optJSONObject(i);
|
||||
if (candidate == null) continue;
|
||||
String candidateId = candidate.optString("candidateId", "");
|
||||
boolean selectedState = selectedCandidateIds.contains(candidateId);
|
||||
appendContent(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
candidate.optString("threadDisplayName", "未命名线程"),
|
||||
"最近活跃:" + candidate.optString("lastActiveAt", "-"),
|
||||
null,
|
||||
selectedState ? "已选" : (candidate.optBoolean("suggestedImport", false) ? "推荐" : null),
|
||||
v -> toggleSelection(candidateId)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if (resolution != null) {
|
||||
appendContent(BossUi.buildCard(
|
||||
this,
|
||||
"导入建议",
|
||||
resolution.optString("summary", "已生成导入建议。"),
|
||||
"应用后会把选中的线程映射成正式聊天窗口。"
|
||||
));
|
||||
JSONArray items = resolution.optJSONArray("items");
|
||||
if (items != null) {
|
||||
for (int i = 0; i < items.length(); i++) {
|
||||
JSONObject item = items.optJSONObject(i);
|
||||
if (item == null) continue;
|
||||
appendContent(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
item.optString("threadDisplayName", "未命名线程"),
|
||||
item.optString("folderName", ""),
|
||||
item.optString("action", "") + " · " + item.optString("reason", ""),
|
||||
null,
|
||||
null
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button reviewButton = BossUi.buildMiniActionButton(this, "生成导入建议", true);
|
||||
reviewButton.setEnabled(!selectedCandidateIds.isEmpty());
|
||||
reviewButton.setOnClickListener(v -> reviewSelection());
|
||||
Button applyButton = BossUi.buildMiniActionButton(
|
||||
this,
|
||||
"applied".equals(draft.optString("status", "")) ? "已导入" : "应用导入",
|
||||
false
|
||||
);
|
||||
applyButton.setEnabled(resolution != null && !"applied".equals(draft.optString("status", "")));
|
||||
applyButton.setOnClickListener(v -> applyResolution());
|
||||
appendContent(BossUi.buildInlineActionRow(this, reviewButton, applyButton));
|
||||
setRefreshing(false);
|
||||
}
|
||||
|
||||
private void toggleSelection(String candidateId) {
|
||||
if (candidateId == null || candidateId.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (selectedCandidateIds.contains(candidateId)) {
|
||||
selectedCandidateIds.remove(candidateId);
|
||||
} else {
|
||||
selectedCandidateIds.add(candidateId);
|
||||
}
|
||||
renderCurrentState();
|
||||
}
|
||||
|
||||
private void reviewSelection() {
|
||||
if (deviceId == null || deviceId.isEmpty()) {
|
||||
showMessage("缺少 deviceId");
|
||||
return;
|
||||
}
|
||||
setRefreshing(true);
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
JSONArray selected = new JSONArray();
|
||||
for (String candidateId : selectedCandidateIds) {
|
||||
selected.put(candidateId);
|
||||
}
|
||||
BossApiClient.ApiResponse selectResponse = apiClient.selectDeviceImportCandidates(deviceId, selected);
|
||||
if (!selectResponse.ok()) {
|
||||
throw new IllegalStateException(selectResponse.message());
|
||||
}
|
||||
BossApiClient.ApiResponse reviewResponse = apiClient.reviewDeviceImportDraft(deviceId);
|
||||
if (!reviewResponse.ok()) {
|
||||
throw new IllegalStateException(reviewResponse.message());
|
||||
}
|
||||
runOnUiThread(() -> {
|
||||
showMessage("已生成导入建议");
|
||||
applyPayload(reviewResponse.json.optJSONObject("draft"), reviewResponse.json.optJSONObject("resolution"));
|
||||
});
|
||||
} catch (Exception error) {
|
||||
runOnUiThread(() -> {
|
||||
setRefreshing(false);
|
||||
showMessage("导入建议生成失败:" + error.getMessage());
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void applyResolution() {
|
||||
if (deviceId == null || deviceId.isEmpty()) {
|
||||
showMessage("缺少 deviceId");
|
||||
return;
|
||||
}
|
||||
setRefreshing(true);
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
BossApiClient.ApiResponse response = apiClient.applyDeviceImportDraft(deviceId);
|
||||
if (!response.ok()) {
|
||||
throw new IllegalStateException(response.message());
|
||||
}
|
||||
runOnUiThread(() -> {
|
||||
showMessage("已应用导入");
|
||||
applyPayload(response.json.optJSONObject("draft"), response.json.optJSONObject("resolution"));
|
||||
});
|
||||
} catch (Exception error) {
|
||||
runOnUiThread(() -> {
|
||||
setRefreshing(false);
|
||||
showMessage("导入应用失败:" + error.getMessage());
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -251,7 +251,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
boolean settingsOk = false;
|
||||
|
||||
try {
|
||||
conversations = apiClient.getConversations();
|
||||
conversations = apiClient.getConversationHome();
|
||||
conversationsOk = conversations.ok();
|
||||
} catch (Exception ignored) {
|
||||
conversationsOk = false;
|
||||
@@ -312,7 +312,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
maybeApplyPreferredEntry();
|
||||
renderCurrentTab();
|
||||
startRefreshing(false);
|
||||
if (!finalConversationsOk || !finalDevicesOk || !finalOtaOk || !finalSettingsOk) {
|
||||
if (RootRefreshPolicy.shouldShowFailure(activeTab, finalConversationsOk, finalDevicesOk, finalOtaOk, finalSettingsOk)) {
|
||||
showMessage("刷新失败,请稍后重试");
|
||||
}
|
||||
});
|
||||
@@ -452,11 +452,21 @@ public class MainActivity extends AppCompatActivity {
|
||||
JSONObject item = conversationsData.optJSONObject(i);
|
||||
if (item == null) continue;
|
||||
String projectId = item.optString("projectId", "");
|
||||
String conversationType = item.optString("conversationType", "");
|
||||
String folderKey = item.optString("folderKey", "");
|
||||
WechatSurfaceMapper.ConversationRow row = WechatSurfaceMapper.toConversationRow(item);
|
||||
screenContent.addView(BossUi.buildConversationRow(
|
||||
this,
|
||||
row,
|
||||
v -> {
|
||||
if ("folder_archive".equals(conversationType)) {
|
||||
if (folderKey.isEmpty()) {
|
||||
showMessage("缺少 folderKey");
|
||||
return;
|
||||
}
|
||||
openConversationFolder(folderKey, row.threadTitle);
|
||||
return;
|
||||
}
|
||||
if (projectId.isEmpty()) {
|
||||
showMessage("缺少 projectId");
|
||||
return;
|
||||
@@ -550,6 +560,13 @@ public class MainActivity extends AppCompatActivity {
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void openConversationFolder(String folderKey, String folderName) {
|
||||
Intent intent = new Intent(this, ConversationFolderActivity.class);
|
||||
intent.putExtra(ConversationFolderActivity.EXTRA_FOLDER_KEY, folderKey);
|
||||
intent.putExtra(ConversationFolderActivity.EXTRA_FOLDER_NAME, folderName);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void openDevice(String deviceId, String deviceName) {
|
||||
Intent intent = new Intent(this, DeviceDetailActivity.class);
|
||||
intent.putExtra(DeviceDetailActivity.EXTRA_DEVICE_ID, deviceId);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
public final class RootRefreshPolicy {
|
||||
private RootRefreshPolicy() {}
|
||||
|
||||
public static boolean shouldShowFailure(
|
||||
String activeTab,
|
||||
boolean conversationsOk,
|
||||
boolean devicesOk,
|
||||
boolean otaOk,
|
||||
boolean settingsOk
|
||||
) {
|
||||
if ("devices".equals(activeTab)) {
|
||||
return !devicesOk;
|
||||
}
|
||||
if ("me".equals(activeTab)) {
|
||||
return !settingsOk && !otaOk;
|
||||
}
|
||||
return !conversationsOk;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class MainActivityRefreshPolicyTest {
|
||||
@Test
|
||||
public void conversationsTabOnlyFailsWhenConversationRequestFails() {
|
||||
assertFalse(RootRefreshPolicy.shouldShowFailure("conversations", true, false, false, false));
|
||||
assertTrue(RootRefreshPolicy.shouldShowFailure("conversations", false, true, true, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void devicesTabDependsOnDeviceRequestOnly() {
|
||||
assertFalse(RootRefreshPolicy.shouldShowFailure("devices", false, true, false, false));
|
||||
assertTrue(RootRefreshPolicy.shouldShowFailure("devices", true, false, true, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void meTabAllowsOneSupportingRequestToSucceed() {
|
||||
assertFalse(RootRefreshPolicy.shouldShowFailure("me", false, false, false, true));
|
||||
assertFalse(RootRefreshPolicy.shouldShowFailure("me", false, false, true, false));
|
||||
assertTrue(RootRefreshPolicy.shouldShowFailure("me", false, false, false, false));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user