Commit 748a873d authored by Dark Zane's avatar Dark Zane Committed by GitHub

Merge branch 'fallenstardust:master' into YGOMOBILE-1

parents c61b629c 7f38818c
......@@ -63,6 +63,7 @@ import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
......@@ -95,9 +96,7 @@ import cn.garymb.ygomobile.ui.cards.deck_square.DeckSquareApiUtil;
import cn.garymb.ygomobile.ui.cards.deck_square.DeckSquareFileUtil;
import cn.garymb.ygomobile.ui.cards.deck_square.api_response.BasicResponse;
import cn.garymb.ygomobile.ui.cards.deck_square.api_response.DownloadDeckResponse;
import cn.garymb.ygomobile.ui.cards.deck_square.api_response.LoginToken;
import cn.garymb.ygomobile.ui.cards.deck_square.api_response.MyOnlineDeckDetail;
import cn.garymb.ygomobile.ui.cards.deck_square.api_response.PushSingleDeckResponse;
import cn.garymb.ygomobile.ui.home.HomeActivity;
import cn.garymb.ygomobile.ui.mycard.mcchat.util.ImageUtil;
import cn.garymb.ygomobile.ui.plus.AOnGestureListener;
......@@ -127,7 +126,7 @@ import ocgcore.enums.LimitType;
* RecyclerViewItemListener.OnItemListener中
*/
public class DeckManagerFragment extends BaseFragemnt implements RecyclerViewItemListener.OnItemListener, OnItemDragListener, YGODeckDialogUtil.OnDeckMenuListener, CardLoader.CallBack, CardSearcher.CallBack {
private static final String TAG = "seesee";
private static final String TAG = "DeckManagerFragment";
protected DrawerLayout mDrawerLayout;
protected RecyclerView mListView;
protected CardLoader mCardLoader;
......@@ -140,7 +139,7 @@ public class DeckManagerFragment extends BaseFragemnt implements RecyclerViewIte
private String mDeckId;
private LinearLayout ll_click_like;
private TextView tv_add_1;
public static List<MyOnlineDeckDetail> originalData; // 保存原始数据
protected int screenWidth;
//region ui onCreate/onDestroy
......@@ -165,7 +164,10 @@ public class DeckManagerFragment extends BaseFragemnt implements RecyclerViewIte
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
activity = (HomeActivity) getActivity();
originalData = new ArrayList<>();
layoutView = inflater.inflate(R.layout.fragment_deck_cards, container, false);
AnimationShake2(layoutView);
initView(layoutView);
......@@ -252,11 +254,15 @@ public class DeckManagerFragment extends BaseFragemnt implements RecyclerViewIte
/** 自动同步 */
if (SharedPreferenceUtil.getServerToken() != null) {
VUiKit.defer().when(() -> {
return DeckSquareApiUtil.synchronizeDecks();
try {
DeckSquareApiUtil.synchronizeDecks();
} catch (IOException e) {
return e;
}
return 0;
}).fail((e) -> {
LogUtil.i(TAG, "sync deck fail" + e.getMessage());
YGOUtil.showTextToast("Sync decks failed: " + e);
}).done((result) -> {
LogUtil.i(TAG, "sync deck success");
});
}
}
......@@ -1303,6 +1309,10 @@ public class DeckManagerFragment extends BaseFragemnt implements RecyclerViewIte
YGOUtil.showTextToast(R.string.done);
}
public static List<MyOnlineDeckDetail> getOriginalData() {
return originalData;
}
//在卡组选择的dialog中点击某个卡组(来自本地或服务器)后,dialog通过本回调函数通知本页面。
//在本页面中根据卡组来源(本地或服务器)显示卡组内容
@Override
......
package cn.garymb.ygomobile.ui.cards.deck_square;
import android.util.Log;
import android.widget.Toast;
import com.google.gson.Gson;
......@@ -15,6 +16,7 @@ import cn.garymb.ygomobile.AppsSettings;
import cn.garymb.ygomobile.Constants;
import cn.garymb.ygomobile.bean.DeckType;
import cn.garymb.ygomobile.bean.events.DeckFile;
import cn.garymb.ygomobile.ui.cards.DeckManagerFragment;
import cn.garymb.ygomobile.ui.cards.deck_square.api_response.BasicResponse;
import cn.garymb.ygomobile.ui.cards.deck_square.api_response.DeckIdResponse;
import cn.garymb.ygomobile.ui.cards.deck_square.api_response.DeckMultiIdResponse;
......@@ -681,90 +683,85 @@ public class DeckSquareApiUtil {
}
public static SyncMutliDeckResult synchronizeDecks() throws IOException {
SyncMutliDeckResult autoSyncResult = new SyncMutliDeckResult();
public static void synchronizeDecks() throws IOException {
// 检查用户是否登录
LoginToken loginToken = DeckSquareApiUtil.getLoginData();
if (loginToken == null) {
autoSyncResult.setFlag(false);
autoSyncResult.setInfo("need login");
return autoSyncResult;
return;
}
// 获取本地卡组列表
List<MyDeckItem> localDecks = DeckSquareFileUtil.getMyDeckItem();
// 获取在线卡组列表
MyDeckResponse onlineDecksResponse = DeckSquareApiUtil.getUserDecks(loginToken);
if (onlineDecksResponse == null || onlineDecksResponse.getData() == null) {
autoSyncResult.setFlag(false);
autoSyncResult.setInfo("no online decks");
return autoSyncResult;
}
List<MyOnlineDeckDetail> onlineDecks = onlineDecksResponse.getData();
// 用于标记在线卡组是否在本地有对应
Map<String, Boolean> onlineDeckProcessed = new HashMap<>();
for (MyOnlineDeckDetail onlineDeck : onlineDecks) {
onlineDeckProcessed.put(onlineDeck.getDeckName(), false);
}
// 获取在线卡组列表
VUiKit.defer().when(() -> {
MyDeckResponse result = DeckSquareApiUtil.getUserDecks(loginToken);//调用获取云备份卡组的接口方法
if (result == null) return null;
else return result.getData();
}).fail((e) -> {
LogUtil.e(TAG, "load mycard from server failed: " + e);
}).done((serverDecks) -> {
if (serverDecks != null) {//将服务端的卡组也放到OrignalData里,
DeckManagerFragment.getOriginalData().clear();
DeckManagerFragment.getOriginalData().addAll(serverDecks);
}
});
List<MyOnlineDeckDetail> onlineDecks = DeckManagerFragment.getOriginalData();
//遍历本地卡组与云备份卡组,过滤出差异项
List<MyDeckItem> syncUploadDecks = new ArrayList<>();
List<MyDeckItem> newPushDecks = new ArrayList<>();
List<MyOnlineDeckDetail> backupDownloadDecks = new ArrayList<>();
// 遍历本地卡组,处理同名卡组的情况
for (MyDeckItem localDeck : localDecks) {
boolean foundOnlineDeck = false;
//预处理每个本地卡组
String localDeckName = localDeck.getDeckName().replace(Constants.YDK_FILE_EX, "");
localDeck.setDeckName(localDeckName);
localDeck.setDeckCoverCard1(DeckUtil.getFirstCardCode(localDeck.getDeckPath()));
for (MyOnlineDeckDetail onlineDeck : onlineDecks) {
String onLineDeckName = onlineDeck.getDeckName().replace(Constants.YDK_FILE_EX, "");
if (localDeckName.equals(onLineDeckName)) {
// 标记该在线卡组已处理
onlineDeckProcessed.put(onLineDeckName, true);
// 标记该本地卡组已处理
foundOnlineDeck = true;
localDeck.setDeckId(onlineDeck.getDeckId());//为本地卡组添加同名云备份卡组的deckid
// 将每个本地卡组作为数组元素添加入syncUploadDecks
localDeck.setDeckName(localDeck.getDeckName().replace(Constants.YDK_FILE_EX, ""));//TODO 上版本很多人已经传了带.ydk的云备份,姑且只在这次再次上传时去掉.ydk
localDeck.setDeckCoverCard1(DeckUtil.getFirstCardCode(localDeck.getDeckPath()));
localDeck.setDeckId(onlineDeck.getDeckId());
syncUploadDecks.add(localDeck);// 将匹配到的本地卡组放入待上传的list中
syncUploadDecks.add(localDeck);
autoSyncResult.syncUpload.add(localDeck);
localDecks.remove(localDeck);// 移除云备份已存在的本地卡组,最后剩下的就是本地独有的卡组
onlineDecks.remove(onlineDeck);// 移除匹配到的云备份卡组,最后剩下的就是云备份独有的卡组
break;
}
}
// 本地卡组在在线列表中不存在,则需要获取新的deckid来直接上传
if (!foundOnlineDeck) {
localDeck.setDeckName(localDeck.getDeckName().replace(Constants.YDK_FILE_EX, ""));
localDeck.setDeckCoverCard1(DeckUtil.getFirstCardCode(localDeck.getDeckPath()));
newPushDecks.add(localDeck);
autoSyncResult.newUpload.add(localDeck);
}
}
if (!newPushDecks.isEmpty()) {
LogUtil.w(TAG, "seesee +要上传的 本地卡组: " + newPushDecks);
requestIdAndPushNewDecks(newPushDecks, loginToken);
}
newPushDecks.addAll(localDecks);// 将剩下的本地卡组传入待新上传的list,与syncUploadDecks并不执行相同的上传接口
backupDownloadDecks.addAll(onlineDecks);// 将剩下的在线卡组传入待新下载的list
// 处理只存在于在线的卡组(即本地没有同名卡组)
for (MyOnlineDeckDetail onlineDeck : onlineDecks) {
String onLineDeckName = onlineDeck.getDeckName().replace(Constants.YDK_FILE_EX, "");
if (!onlineDeckProcessed.get(onLineDeckName)) {
autoSyncResult.newDownload.add(onlineDeck);
LogUtil.w(TAG, "seesee sync-download new deck: " + onlineDeck.getDeckName());
SyncMutliDeckResult.DownloadResult downloadResult = downloadMissingDeckToLocal(onlineDeck, DeckSquareFileUtil.convertToUnixTimestamp(onlineDeck.getDeckUpdateDate()));
autoSyncResult.downloadResponse.add(downloadResult);
LogUtil.w(TAG, "seesee +要下载的 云备份卡组: " + backupDownloadDecks);
for (MyOnlineDeckDetail onlineDeck : backupDownloadDecks) {
// 确保文件名包含.ydk扩展名
String fileName = onlineDeck.getDeckName();
if (!fileName.toLowerCase().endsWith(Constants.YDK_FILE_EX)) {
fileName += Constants.YDK_FILE_EX;
}
String fileFullPath = AppsSettings.get().getDeckDir() + "/" + fileName;
// 保存在线卡组到本地
boolean saved = DeckSquareFileUtil.saveFileToPath(fileFullPath, onlineDeck.getDeckYdk(), DeckSquareFileUtil.convertToUnixTimestamp(onlineDeck.getDeckUpdateDate()));
if (!saved) LogUtil.e(TAG, "seesee Failed to save deck file: " + fileFullPath);
LogUtil.i(TAG, "seesee Deck saved to: " + fileFullPath);
}
// 上传本地卡组覆盖在线卡组
PushMultiResponse response = syncMyDecks(syncUploadDecks, loginToken);//TODO 一定要最后执行这行,否则会直接终止后续执行
autoSyncResult.pushResponse = response;
return autoSyncResult;
syncMyDecks(syncUploadDecks, loginToken);
if (!newPushDecks.isEmpty()) {
requestIdAndPushNewDecks(newPushDecks, loginToken);
LogUtil.w(TAG, "seesee +要上传的 本地卡组: " + newPushDecks);
}
}
......
......@@ -23,6 +23,7 @@ import java.util.TimeZone;
import cn.garymb.ygomobile.AppsSettings;
import cn.garymb.ygomobile.Constants;
import cn.garymb.ygomobile.ui.cards.deck_square.api_response.MyOnlineDeckDetail;
import cn.garymb.ygomobile.ui.cards.deck_square.bo.MyDeckItem;
import cn.garymb.ygomobile.utils.IOUtils;
import cn.garymb.ygomobile.utils.LogUtil;
......@@ -33,22 +34,22 @@ import ocgcore.data.Card;
public class DeckSquareFileUtil {
//
private static final String TAG = "decksquareApiUtil";
//private static final String TAG = DeckSquareListAdapter.class.getSimpleName();
// public static List<String> readLastLinesWithNIO(File file, int numLines) {
// try {
// List<String> lines = Files.readAllLines(file);
// int fromIndex = Math.max(0, lines.size() - numLines);
// return lines.subList(fromIndex, lines.size());
// } catch (IOException e) {
// e.printStackTrace();
// return Collections.emptyList();
// }
// }
//
// 使用示例
// Path logPath = Paths.get(context.getFilesDir().getAbsolutePath(), "log.txt");
// List<String> lastTwo = readLastLinesWithNIO(logPath, 2);
//将MyOnlineDeckDetail转MyDeckItem类型list,有时候会需要用到
public static List<MyDeckItem> toDeckItemList(List<MyOnlineDeckDetail> serverDecks) {
List<MyDeckItem> myOnlineDecks = new ArrayList<>();
for (MyOnlineDeckDetail detail : serverDecks) {
MyDeckItem item = new MyDeckItem();
item.setDeckName(detail.getDeckName());
item.setDeckId(detail.getDeckId());
item.setUserId(detail.getUserId());
item.setDeckCoverCard1(detail.getDeckCoverCard1());
item.setUpdateDate(detail.getDeckUpdateDate());
item.setPublic(detail.isPublic());
myOnlineDecks.add(item);
}
return myOnlineDecks;
}
//读取file指定的ydk文件,返回其内包含的deckId。如果不包含deckId,返回null
public static String getId(File file) {
......
......@@ -14,6 +14,8 @@ import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import java.io.IOException;
import cn.garymb.ygomobile.lite.R;
import cn.garymb.ygomobile.lite.databinding.FragmentDeckSquareMyDeckBinding;
import cn.garymb.ygomobile.ui.activities.WebActivity;
......@@ -196,16 +198,14 @@ public class DeckSquareMyDeckFragment extends Fragment {
});
/** 自动同步 */
VUiKit.defer().when(() -> {
return DeckSquareApiUtil.synchronizeDecks();
try {
DeckSquareApiUtil.synchronizeDecks();
} catch (IOException e) {
return e;
}
return 0;
}).fail((e) -> {
YGOUtil.showTextToast("Sync decks fail", Toast.LENGTH_LONG);
LogUtil.i(TAG, "Sync decks fail" + e.getMessage());
}).done((result) -> {
String info = "sync decks: upload " + result.syncUpload.size() + ", download " + result.newDownload.size();
YGOUtil.showTextToast(info, Toast.LENGTH_LONG);
});
YGOUtil.showTextToast("Sync decks failed: " + e);
}).done((result) -> {});
}
}
package cn.garymb.ygomobile.ui.cards.deck_square;
import static cn.garymb.ygomobile.ui.cards.DeckManagerFragment.originalData;
import android.util.Log;
import android.widget.ImageView;
......@@ -26,90 +28,78 @@ import cn.garymb.ygomobile.utils.YGODeckDialogUtil;
import cn.garymb.ygomobile.utils.YGOUtil;
//提供“我的”卡组数据,打开后先从sharePreference查询,没有则从服务器查询,然后缓存到sharePreference
public class MyDeckListAdapter extends BaseQuickAdapter<MyDeckItem, BaseViewHolder> {
public class MyDeckListAdapter extends BaseQuickAdapter<MyOnlineDeckDetail, BaseViewHolder> {
private static final String TAG = DeckSquareListAdapter.class.getSimpleName();
private YGODeckDialogUtil.OnDeckMenuListener onDeckMenuListener;//通知外部调用方,(如调用本fragment的activity)
private YGODeckDialogUtil.OnDeckDialogListener mDialogListener;
private ImageLoader imageLoader;
private List<MyDeckItem> originalData; // 保存原始数据
public MyDeckListAdapter(int layoutResId, YGODeckDialogUtil.OnDeckMenuListener onDeckMenuListener, YGODeckDialogUtil.OnDeckDialogListener mDialogListener) {
super(layoutResId);
originalData = new ArrayList<>();
imageLoader = new ImageLoader();
this.onDeckMenuListener = onDeckMenuListener;
this.mDialogListener = mDialogListener;
}
public void loadData() {
List<MyDeckItem> myOnlineDecks = new ArrayList<>();
LoginToken loginToken = DeckSquareApiUtil.getLoginData();
if (loginToken == null) {
return;
}
if (originalData.isEmpty()){
final DialogPlus dialog_read_ex = DialogPlus.show(getContext(), null, getContext().getString(R.string.fetch_online_deck));
VUiKit.defer().when(() -> {
MyDeckResponse result = DeckSquareApiUtil.getUserDecks(loginToken);
if (result == null) return null;
else return result.getData();
}).fail((e) -> {
Log.e(TAG, e + "");
if (dialog_read_ex.isShowing()) {//关闭异常
try {
dialog_read_ex.dismiss();
} catch (Exception ex) {
}
}
LogUtil.i(TAG, "load mycard from server failed:" + e);
}).done((serverDecks) -> {
if (serverDecks != null) {//将服务端的卡组也放到LocalDecks中
originalData.clear();//虽然判断originalData是空的才会执行到这里,但还是写上保险
originalData.addAll(serverDecks); // 保存原始数据
}
final DialogPlus dialog_read_ex = DialogPlus.show(getContext(), null, getContext().getString(R.string.fetch_online_deck));
VUiKit.defer().when(() -> {
MyDeckResponse result = DeckSquareApiUtil.getUserDecks(loginToken);
if (result == null) {
return null;
} else {
return result.getData();
}
LogUtil.i(TAG, "load mycard from server done");
}).fail((e) -> {
Log.e(TAG, e + "");
if (dialog_read_ex.isShowing()) {//关闭异常
try {
dialog_read_ex.dismiss();
} catch (Exception ex) {
getData().clear();
addData(serverDecks);
notifyDataSetChanged();
if (dialog_read_ex.isShowing()) {
try {
dialog_read_ex.dismiss();
} catch (Exception ex) {
}
}
}
LogUtil.i(TAG, "load mycard from server fail");
}).done((serverDecks) -> {
if (serverDecks != null) {//将服务端的卡组也放到LocalDecks中
for (MyOnlineDeckDetail detail : serverDecks) {
MyDeckItem item = new MyDeckItem();
item.setDeckName(detail.getDeckName());
item.setDeckId(detail.getDeckId());
item.setUserId(detail.getUserId());
item.setDeckCoverCard1(detail.getDeckCoverCard1());
item.setUpdateDate(detail.getDeckUpdateDate());
item.setPublic(detail.isPublic());
myOnlineDecks.add(item);
}
}
LogUtil.i(TAG, "load mycard from server done");
originalData.clear();
originalData.addAll(myOnlineDecks); // 保存原始数据
});
} else {
LogUtil.i(TAG, "load originalData done");
getData().clear();
addData(myOnlineDecks);
addData(originalData);
notifyDataSetChanged();
}
if (dialog_read_ex.isShowing()) {
try {
dialog_read_ex.dismiss();
} catch (Exception ex) {
}
}
});
}
// 筛选函数
public void filter(String keyword) {
List<MyDeckItem> filteredList = new ArrayList<>();
List<MyOnlineDeckDetail> filteredList = new ArrayList<>();
if (keyword.isEmpty()) {
// 如果关键词为空,则显示所有数据
filteredList.addAll(originalData);
} else {
// 遍历原始数据,筛选出包含关键词的item
for (MyDeckItem item : originalData) {
for (MyOnlineDeckDetail item : originalData) {
if (item.getDeckName().contains(keyword)) {
filteredList.add(item);
}
......@@ -121,10 +111,7 @@ public class MyDeckListAdapter extends BaseQuickAdapter<MyDeckItem, BaseViewHold
notifyDataSetChanged();
}
public List<MyDeckItem> getOriginalData(){
return this.originalData;
}
private void deleteMyDeckOnLine(MyDeckItem item) {
private void deleteMyDeckOnLine(MyOnlineDeckDetail item) {
if (item != null) {
LoginToken loginToken = DeckSquareApiUtil.getLoginData();
if (loginToken == null) {
......@@ -150,14 +137,14 @@ public class MyDeckListAdapter extends BaseQuickAdapter<MyDeckItem, BaseViewHold
* 注意,更新卡组状态要过很久才生效(实测延迟偶尔达5s)
* @param item
*/
private void changeDeckPublicState(MyDeckItem item) {
private void changeDeckPublicState(MyOnlineDeckDetail item) {
if (item != null) {
LoginToken loginToken = DeckSquareApiUtil.getLoginData();
if (loginToken == null) {
return;
}
VUiKit.defer().when(() -> {
BasicResponse result = DeckSquareApiUtil.setDeckPublic(item.getDeckId(), loginToken, item.getPublic());
BasicResponse result = DeckSquareApiUtil.setDeckPublic(item.getDeckId(), loginToken, item.isPublic());
return result;
}).fail(e -> {
LogUtil.i(TAG, "切换显示失败" + e.getMessage());
......@@ -168,12 +155,12 @@ public class MyDeckListAdapter extends BaseQuickAdapter<MyDeckItem, BaseViewHold
}
@Override
protected void convert(BaseViewHolder helper, MyDeckItem item) {
protected void convert(BaseViewHolder helper, MyOnlineDeckDetail item) {
helper.setText(R.id.my_deck_name, item.getDeckName());
helper.setText(R.id.deck_update_date, item.getUpdateDate());
helper.setText(R.id.deck_update_date, item.getDeckUpdateDate());
ImageView cardImage = helper.getView(R.id.deck_info_image);
long code = item.getDeckCoverCard1();
if (item.getPublic()) {
if (item.isPublic()) {
helper.setText(R.id.change_show_or_hide, R.string.in_public);
helper.getView(R.id.show_on_deck_square).setBackgroundResource(R.drawable.baseline_remove_red_eye_24);
helper.getView(R.id.ll_switch_show).setBackgroundResource(R.drawable.button_radius_red);
......@@ -191,7 +178,7 @@ public class MyDeckListAdapter extends BaseQuickAdapter<MyDeckItem, BaseViewHold
deleteMyDeckOnLine(item);
});
helper.getView(R.id.ll_switch_show).setOnClickListener(view -> {
if (item.getPublic()) {
if (item.isPublic()) {
helper.setText(R.id.change_show_or_hide, R.string.in_personal_use);
helper.getView(R.id.show_on_deck_square).setBackgroundResource(R.drawable.closed_eyes_24);
helper.getView(R.id.ll_switch_show).setBackgroundResource(R.drawable.button_radius_n);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment