Compare commits

...

15 Commits

Author SHA1 Message Date
c726e2f6e8 Update 2025-08-04 21:59:40 +08:00
cd0fa107d2 图片 2025-08-03 23:16:51 +08:00
bda5630685 混淆 2025-08-03 23:13:22 +08:00
e95b6a1066 Union兼容
订阅初步完成
2025-08-03 17:26:29 +08:00
7fdff8d14b 加密3 2025-07-30 23:47:26 +08:00
ce685521de a2 2025-07-30 22:36:07 +08:00
fb91d2a0cb 存档2 2025-07-29 14:55:03 +08:00
c22fccc96e 存档 2025-07-29 14:54:53 +08:00
ea70700f44 增加机厅更改 2025-05-29 23:08:27 +08:00
3c3af19b6d Bet 2025-05-28 18:22:03 +08:00
e72ace70ca 1.6.5 beta更新 2025-05-25 22:45:13 +08:00
e6254b5ea4 主题功能,nfc修复 2025-05-24 02:43:47 +08:00
ac60d8181d zhuti 2025-05-24 00:53:47 +08:00
98be6c946e 部分Bug修复 2025-05-14 21:55:24 +08:00
29c6c2567c 功能增加正式版 2025-05-14 21:07:00 +08:00
76 changed files with 6418 additions and 575 deletions

1
.idea/.name generated Normal file
View File

@@ -0,0 +1 @@
FindMaimaiUltra

9
.idea/FindMaimaiDX_Ultra.iml generated Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

2
.idea/compiler.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
<bytecodeTargetLevel target="21" />
</component>
</project>

View File

@@ -4,6 +4,14 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-08-03T05:22:05.625472Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=LNTSE66XRSFEKNW8" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>

1
.idea/misc.xml generated
View File

@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="FrameworkDetectionExcludesConfiguration">

View File

@@ -1,3 +1,6 @@
# 好啦,这个为爱发电的项目也接近尾声了...
后续的更新以及官方权限将会由用户 findmaimai@163.com 控制,这个项目的git就存档啦
# FindMaimaiUltra
FindMaimaiUltra 是一个 Android 应用程序,用于查询和展示 Maimai舞萌游戏的成绩数据。它提供了丰富的功能包括成绩查询、歌曲成绩展示、地图功能、搜索和排序功能等。

View File

@@ -11,7 +11,7 @@ android {
minSdk 29
targetSdk 34
versionCode 1
versionName "1.6.4"
versionName "2.0.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -27,7 +27,7 @@ android {
}
buildTypes {
release {
minifyEnabled false
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

View File

@@ -1,21 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# 保护除指定类外的所有类和成员不被混淆
-keep class !org.astral.findmaimaiultra.service.InMemoryJarLoader, !org.astral.findmaimaiultra.been.faker.SegaApi2025,!org.astral.findmaimaiultra.ui.widget.WidgetFragment, !org.astral.findmaimaiultra.ui.login.LinkQQBot { *; }
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# 保留必要的属性这些是Android运行所必需的
-keepattributes Signature
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes SourceFile,LineNumberTable
-keepattributes EnclosingMethod
-keepattributes InnerClasses
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
# 添加 missing_rules.txt 中的规则以解决警告
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
-dontwarn org.conscrypt.Conscrypt$Version
-dontwarn org.conscrypt.Conscrypt
-dontwarn org.conscrypt.ConscryptHostnameVerifier
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
-dontwarn org.openjsse.net.ssl.OpenJSSE

File diff suppressed because one or more lines are too long

View File

@@ -18,7 +18,7 @@ import org.astral.findmaimaiultra.been.Place;
import java.util.List;
public class PlaceAdapter extends RecyclerView.Adapter<PlaceAdapter.PlaceViewHolder> {
private String theme;
private List<Place> placeList;
private OnItemClickListener listener;
@@ -26,9 +26,10 @@ public class PlaceAdapter extends RecyclerView.Adapter<PlaceAdapter.PlaceViewHol
void onItemClick(Place place);
}
public PlaceAdapter(List<Place> placeList, OnItemClickListener listener) {
public PlaceAdapter(List<Place> placeList,String theme, OnItemClickListener listener) {
this.placeList = placeList;
this.listener = listener;
this.theme = theme;
}
@NonNull
@@ -69,6 +70,40 @@ public class PlaceAdapter extends RecyclerView.Adapter<PlaceAdapter.PlaceViewHol
} else {
holder.imageView.setImageDrawable(ContextCompat.getDrawable(holder.itemView.getContext(), R.drawable.rank_a));
}
//通过theme设置textview颜色
if (theme.contains("Pink")) {
holder.nameTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.textcolorPrimary));
holder.provinceTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.textcolorPrimary));
holder.cityTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.textcolorPrimary));
holder.areaTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.textcolorPrimary));
holder.addressTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.textcolorPrimary));
} else if (theme.contains("Blue")) {
holder.nameTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.textcolorPrimary2));
holder.provinceTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.textcolorPrimary2));
holder.cityTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.textcolorPrimary2));
holder.areaTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.textcolorPrimary2));
holder.addressTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.textcolorPrimary2));
} else if (theme.contains("Green")) {
holder.nameTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.lineBaseGreen));
holder.provinceTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.lineBaseGreen));
holder.cityTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.lineBaseGreen));
holder.areaTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.lineBaseGreen));
holder.addressTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.lineBaseGreen));
}else if (theme.contains("White")) {
holder.nameTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.white));
holder.provinceTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.white));
holder.cityTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.white));
holder.areaTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.white));
holder.addressTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.white));
}else if (theme.contains("Gray")) {
holder.nameTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.black));
holder.provinceTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.black));
holder.cityTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.black));
holder.areaTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.black));
holder.addressTextView.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.black));
}
Log.i("rating", rating + "|" + place.getName());
// 控制竖线的位置
controlVerticalLines(holder, place);

View File

@@ -1,10 +1,12 @@
package org.astral.findmaimaiultra.adapter;
import android.content.SharedPreferences;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
@@ -20,6 +22,7 @@ import java.util.List;
public class SuggestMusicRatingAdapter extends RecyclerView.Adapter<SuggestMusicRatingAdapter.ViewHolder> {
private List<MusicRating> musicRatings;
private final SharedPreferences projectE;
private OnItemClickListener listener;
public interface OnItemClickListener {
@@ -31,10 +34,12 @@ public class SuggestMusicRatingAdapter extends RecyclerView.Adapter<SuggestMusic
this.listener = listener;
}
public SuggestMusicRatingAdapter(List<MusicRating> musicRatings) {
public SuggestMusicRatingAdapter(List<MusicRating> musicRatings, SharedPreferences projectE) {
this.musicRatings = musicRatings;
this.projectE = projectE;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -91,6 +96,26 @@ public class SuggestMusicRatingAdapter extends RecyclerView.Adapter<SuggestMusic
listener.onItemClick(musicRating);
}
});
// Long-press listener to delete project
holder.itemView.setOnLongClickListener(v -> {
// Remove project from SharedPreferences
String projectKey = "project" + musicRating.getMusicId();
if (projectE.contains(projectKey)) {
SharedPreferences.Editor editor = projectE.edit();
editor.remove(projectKey);
editor.apply();
// Remove item from the list and refresh
musicRatings.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, musicRatings.size());
Toast.makeText(v.getContext(), "Project removed", Toast.LENGTH_SHORT).show();
}
return true;
});
}
private int getAchievementImageResId(int achievement) {

View File

@@ -9,6 +9,30 @@ public class Place implements Parcelable {
private String name;
private String province;
private String city;
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("Place{");
sb.append("id=").append(id);
sb.append(", name='").append(name).append('\'');
sb.append(", province='").append(province).append('\'');
sb.append(", city='").append(city).append('\'');
sb.append(", area='").append(area).append('\'');
sb.append(", address='").append(address).append('\'');
sb.append(", isUse=").append(isUse);
sb.append(", x=").append(x);
sb.append(", y=").append(y);
sb.append(", count=").append(count);
sb.append(", good=").append(good);
sb.append(", bad=").append(bad);
sb.append(", num=").append(num);
sb.append(", numJ=").append(numJ);
sb.append(", meituan_link='").append(meituan_link).append('\'');
sb.append(", douyin_link='").append(douyin_link).append('\'');
sb.append('}');
return sb.toString();
}
private String area;
private String address;
private int isUse;

View File

@@ -0,0 +1,31 @@
package org.astral.findmaimaiultra.been;
public class ProjectSong {
private int id;
private double targetLevel;
private double targetA;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getTargetLevel() {
return targetLevel;
}
public void setTargetLevel(double targetLevel) {
this.targetLevel = targetLevel;
}
public double getTargetA() {
return targetA;
}
public void setTargetA(double targetA) {
this.targetA = targetA;
}
}

View File

@@ -0,0 +1,70 @@
package org.astral.findmaimaiultra.been.faker;
import com.google.gson.annotations.SerializedName;
public class MaimaiConfig {
/**
* API接口地址
*/
@SerializedName("api")
private String api ;
/**
* AES加密密钥
*/
@SerializedName("AES_KEY")
private String AES_KEY;
/**
* AES加密初始向量
*/
@SerializedName("AES_IV")
private String AES_IV ;
/**
* 混淆参数
*/
@SerializedName("OBFUSCATE_PARAM")
private String OBFUSCATE_PARAM;
@SerializedName("clientId")
private String clientId;
public String getApi() {
return api;
}
public void setApi(String api) {
this.api = api;
}
public String getAES_KEY() {
return AES_KEY;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public void setAES_KEY(String AES_KEY) {
this.AES_KEY = AES_KEY;
}
public String getAES_IV() {
return AES_IV;
}
public void setAES_IV(String AES_IV) {
this.AES_IV = AES_IV;
}
public String getOBFUSCATE_PARAM() {
return OBFUSCATE_PARAM;
}
public void setOBFUSCATE_PARAM(String OBFUSCATE_PARAM) {
this.OBFUSCATE_PARAM = OBFUSCATE_PARAM;
}
}

View File

@@ -0,0 +1,176 @@
package org.astral.findmaimaiultra.been.faker;
import android.util.Log;
import com.google.gson.*;
import okhttp3.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.io.*;
import java.nio.charset.*;
import java.security.*;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.zip.*;
/**
* @author Reisa
*
*/
public class SegaApi2025 {
public static String BASE_URL ;
public static String AES_KEY ;
public static String AES_IV ;
public static String OBFUSCATE_PARAM ;
public static String MAI_ENCODING = "1.50";
public static String clientId;
private final OkHttpClient httpClient;
public static int placeId = 1345;
public static int regionId = 18;
private final Gson gson;
public SegaApi2025() {
this.httpClient = new OkHttpClient.Builder()
.connectTimeout(10, java.util.concurrent.TimeUnit.SECONDS)
.readTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
.build();
this.gson = new GsonBuilder().disableHtmlEscaping().create();
}
private static class AesPkcs7 {
private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding"; // Java使用PKCS5Padding实际等同于PKCS7
private final SecretKeySpec keySpec;
private final IvParameterSpec ivSpec;
public AesPkcs7(String key, String iv) {
this.keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
this.ivSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
}
public byte[] encrypt(byte[] data) throws Exception {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
return cipher.doFinal(data);
}
public byte[] decrypt(byte[] encryptedData) throws Exception {
try {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
return cipher.doFinal(encryptedData);
} catch (Exception e) {
System.err.println("[解密错误]" + e.getMessage());
return encryptedData; // 与Python版一致失败时返回原始数据
}
}
}
// 生成API哈希值
private String getHashApi(String apiName) throws NoSuchAlgorithmException {
String input = apiName + "MaimaiChn" + OBFUSCATE_PARAM;
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] hashBytes = md.digest(input.getBytes(StandardCharsets.UTF_8));
StringBuilder hexString = new StringBuilder();
for (byte b : hashBytes) {
String hex = String.format("%02x", b);
hexString.append(hex);
}
return hexString.toString();
}
// 主API调用方法
public JsonObject sdgbApi(String data, String apiName, String userId) throws Exception {
// Log.d("123456",BASE_URL);
// 1. 参数校验
Objects.requireNonNull(apiName, "API名称不能为空");
// 2. 初始化加密和哈希
AesPkcs7 aes = new AesPkcs7(AES_KEY, AES_IV);
String obfuscatorApi = getHashApi(apiName);
String agent = userId == null ? clientId : userId;
// 3. 数据处理JSON → 压缩 → 加密
byte[] compressedData = compress(data.getBytes(StandardCharsets.UTF_8));
byte[] encryptedData = aes.encrypt(compressedData);
// 4. 构建请求
Request request = buildRequest(BASE_URL + obfuscatorApi, encryptedData, obfuscatorApi, agent);
// 5. 发送请求并处理响应
try (Response response = httpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("HTTP请求失败: " + response.code());
}
byte[] decryptedData = aes.decrypt(response.body().bytes());
byte[] decompressedData;
// 尝试解压检查zlib头
if (decryptedData.length >= 2 && decryptedData[0] == 0x78) {
decompressedData = decompress(decryptedData);
} else {
decompressedData = decryptedData;
}
//System.out.println(new String(decompressedData, StandardCharsets.UTF_8));
return JsonParser.parseString(new String(decompressedData, StandardCharsets.UTF_8))
.getAsJsonObject();
}
}
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
private Request buildRequest(String url, byte[] body, String obfuscatorApi, String agent) {
return new Request.Builder()
.url(url)
.header("Content-Type", "application/json")
.header("User-Agent", obfuscatorApi + "#" + agent)
.header("charset", "UTF-8")
.header("Mai-Encoding", MAI_ENCODING)
.header("Content-Encoding", "deflate")
.header("Accept-Encoding", new String(Base64.getDecoder().decode("QEBAU0tJUF9IRUFERVJAQEA=")))
.header("Expect", "100-continue")
.post(RequestBody.create(body, MediaType.get("application/json")))
.build();
}
private byte[] compress(byte[] data) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (DeflaterOutputStream dos = new DeflaterOutputStream(bos)) {
dos.write(data);
}
return bos.toByteArray();
}
private byte[] decompress(byte[] data) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (InflaterInputStream iis = new InflaterInputStream(new ByteArrayInputStream(data))) {
byte[] buffer = new byte[1024];
int len;
while ((len = iis.read(buffer)) > 0) {
bos.write(buffer, 0, len);
}
}
return bos.toByteArray();
}
public static void main(String[] args) throws Exception {
SegaApi2025 api = new SegaApi2025();
// 测试sdgbApi
JsonObject testData = new JsonObject();
testData.addProperty("userId", Integer.parseInt("11931174"));
testData.addProperty("nextIndex",Long.parseLong("10000000000") * 5);
testData.addProperty("maxCount",9999);
// JsonObject result = api.sdgbApi(testData.toString(), "GetUserItemApi", "11931174");
// System.out.println("API响应: " + result.toString());
}
}

View File

@@ -0,0 +1,54 @@
package org.astral.findmaimaiultra.been.sega;
import java.util.List;
public class All_chart {
private int userId;
private int length;
private int nextIndex;
private List<UserMusicList> userMusicList;
// Getters and Setters
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int getNextIndex() {
return nextIndex;
}
public void setNextIndex(int nextIndex) {
this.nextIndex = nextIndex;
}
public List<UserMusicList> getUserMusicList() {
return userMusicList;
}
public void setUserMusicList(List<UserMusicList> userMusicList) {
this.userMusicList = userMusicList;
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", length=" + length +
", nextIndex=" + nextIndex +
", userMusicList=" + userMusicList +
'}';
}
}

View File

@@ -0,0 +1,107 @@
package org.astral.findmaimaiultra.been.sega;
import com.google.gson.JsonObject;
import java.util.List;
public class BackUp {
private int userId;
private List<UserItem> userItem;
private All_chart all_chart;
private JsonObject userExtend;
private JsonObject userOption;
private JsonObject userRating;
private JsonObject userActivity;
private JsonObject userCharacter;
private JsonObject userCharge;
private JsonObject userMission;
private JsonObject userData;
public JsonObject getUserCharge() {
return userCharge;
}
public void setUserCharge(JsonObject userCharge) {
this.userCharge = userCharge;
}
public int getUserId() {
return userId;
}
public JsonObject getUserExtend() {
return userExtend;
}
public void setUserExtend(JsonObject userExtend) {
this.userExtend = userExtend;
}
public JsonObject getUserOption() {
return userOption;
}
public void setUserOption(JsonObject userOption) {
this.userOption = userOption;
}
public JsonObject getUserRating() {
return userRating;
}
public void setUserRating(JsonObject userRating) {
this.userRating = userRating;
}
public JsonObject getUserActivity() {
return userActivity;
}
public void setUserActivity(JsonObject userActivity) {
this.userActivity = userActivity;
}
public JsonObject getUserCharacter() {
return userCharacter;
}
public void setUserCharacter(JsonObject userCharacter) {
this.userCharacter = userCharacter;
}
public JsonObject getUserMission() {
return userMission;
}
public void setUserMission(JsonObject userMission) {
this.userMission = userMission;
}
public JsonObject getUserData() {
return userData;
}
public void setUserData(JsonObject userData) {
this.userData = userData;
}
public void setUserId(int userId) {
this.userId = userId;
}
public List<UserItem> getUserItem() {
return userItem;
}
public void setUserItem(List<UserItem> userItem) {
this.userItem = userItem;
}
public All_chart getAll_chart() {
return all_chart;
}
public void setAll_chart(All_chart all_chart) {
this.all_chart = all_chart;
}
}

View File

@@ -0,0 +1,89 @@
package org.astral.findmaimaiultra.been.sega;
import com.google.gson.JsonObject;
import org.astral.findmaimaiultra.been.sega.info.Activity;
public class Command {
private int userId;
private BackUp backUp;
private String command;
private Activity activity;
private JsonObject loginData;
private JsonObject loginResult;
private JsonObject userData;
private JsonObject playLogs;
private JsonObject upsertAll;
public JsonObject getLoginData() {
return loginData;
}
public Activity getActivity() {
return activity;
}
public void setActivity(Activity activity) {
this.activity = activity;
}
public void setLoginData(JsonObject loginData) {
this.loginData = loginData;
}
public JsonObject getLoginResult() {
return loginResult;
}
public void setLoginResult(JsonObject loginResult) {
this.loginResult = loginResult;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public BackUp getBackUp() {
return backUp;
}
public void setBackUp(BackUp backUp) {
this.backUp = backUp;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
public JsonObject getUserData() {
return userData;
}
public void setUserData(JsonObject userData) {
this.userData = userData;
}
public JsonObject getPlayLogs() {
return playLogs;
}
public void setPlayLogs(JsonObject playLogs) {
this.playLogs = playLogs;
}
public JsonObject getUpsertAll() {
return upsertAll;
}
public void setUpsertAll(JsonObject upsertAll) {
this.upsertAll = upsertAll;
}
}

View File

@@ -0,0 +1,50 @@
package org.astral.findmaimaiultra.been.sega;
public class GetUserIdByQRCode {
private double errorID;
private String key;
private String timestamp;
private int userID;
@Override
public String toString() {
return "GetUserIdByQRCode{" +
"errorID=" + errorID +
", key='" + key + '\'' +
", timestamp='" + timestamp + '\'' +
", userID=" + userID +
'}';
}
public double getErrorID() {
return errorID;
}
public void setErrorID(double errorID) {
this.errorID = errorID;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public int getUserID() {
return userID;
}
public void setUserID(int userID) {
this.userID = userID;
}
}

View File

@@ -0,0 +1,50 @@
package org.astral.findmaimaiultra.been.sega;
import com.google.gson.Gson;
public class MessageCom {
private int code;
private long timestamp;
private String msg;
private String data;
public MessageCom(int i, String a, String byUid) {
this.code = i;
this.msg = a;
this.data = byUid;
}
public MessageCom() {
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}

View File

@@ -0,0 +1,43 @@
package org.astral.findmaimaiultra.been.sega;
import org.astral.findmaimaiultra.been.sega.info.UserData;
public class PhotoMessage {
private UserScore userScore;
private All_chart maiUser;
private UserData userData;
private UserDataAll userDataFaker;
public UserScore getUserScore() {
return userScore;
}
public void setUserScore(UserScore userScore) {
this.userScore = userScore;
}
public All_chart getMaiUser() {
return maiUser;
}
public void setMaiUser(All_chart maiUser) {
this.maiUser = maiUser;
}
public UserData getUserData() {
return userData;
}
public void setUserData(UserData userData) {
this.userData = userData;
}
public UserDataAll getUserDataFaker() {
return userDataFaker;
}
public void setUserDataFaker(UserDataAll userDataFaker) {
this.userDataFaker = userDataFaker;
}
}

View File

@@ -0,0 +1,33 @@
package org.astral.findmaimaiultra.been.sega;
import java.util.List;
public class RegionData {
private int userId;
private int length;
private List<UserRegion> userRegionList;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public List<UserRegion> getUserRegionList() {
return userRegionList;
}
public void setUserRegionList(List<UserRegion> userRegionList) {
this.userRegionList = userRegionList;
}
}

View File

@@ -0,0 +1,83 @@
package org.astral.findmaimaiultra.been.sega;
import org.astral.findmaimaiultra.been.sega.All_chart;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ReisaRating {
private String id;
private int userId;
private String userName;
private String unionUserId;
private All_chart all_chart;
private int rating;
private List<Long> updateTimes;
private Map<Integer, UserItem> colls;
public Map<Integer, UserItem> getColls() {
return colls;
}
public void setColls(Map<Integer, UserItem> colls) {
this.colls = colls;
}
public List<Long> getUpdateTimes() {
return updateTimes;
}
public void setUpdateTimes(List<Long> updateTimes) {
this.updateTimes = updateTimes;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUnionUserId() {
return unionUserId;
}
public void setUnionUserId(String unionUserId) {
this.unionUserId = unionUserId;
}
public All_chart getAll_chart() {
return all_chart;
}
public void setAll_chart(All_chart all_chart) {
this.all_chart = all_chart;
}
public int getRating() {
return rating;
}
public void setRating(int rating) {
this.rating = rating;
}
}

View File

@@ -0,0 +1,173 @@
package org.astral.findmaimaiultra.been.sega;
import java.util.List;
public class Sega_chart {
private int musicId;
private String musicName;
private int level;
private double level_info;
private int romVersion;
private int achievement;
private int rating;
private String type;
private int playCount;
private int comboStatus;
private int syncStatus;
private int deluxscoreMax;
private int scoreRank;
private int extNum1;
private int extNum2;
private List<String> alias;
public List<String> getAlias() {
return alias;
}
public void setAlias(List<String> alias) {
this.alias = alias;
}
public int getPlayCount() {
return playCount;
}
public void setPlayCount(int playCount) {
this.playCount = playCount;
}
public int getComboStatus() {
return comboStatus;
}
public void setComboStatus(int comboStatus) {
this.comboStatus = comboStatus;
}
public int getSyncStatus() {
return syncStatus;
}
public void setSyncStatus(int syncStatus) {
this.syncStatus = syncStatus;
}
public int getDeluxscoreMax() {
return deluxscoreMax;
}
public void setDeluxscoreMax(int deluxscoreMax) {
this.deluxscoreMax = deluxscoreMax;
}
public int getScoreRank() {
return scoreRank;
}
public void setScoreRank(int scoreRank) {
this.scoreRank = scoreRank;
}
public int getExtNum1() {
return extNum1;
}
public void setExtNum1(int extNum1) {
this.extNum1 = extNum1;
}
public int getExtNum2() {
return extNum2;
}
public void setExtNum2(int extNum2) {
this.extNum2 = extNum2;
}
public String getMusicName() {
return musicName;
}
public void setMusicName(String musicName) {
this.musicName = musicName;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public double getLevel_info() {
return level_info;
}
public void setLevel_info(double level_info) {
this.level_info = level_info;
}
public int getRating() {
return rating;
}
public void setRating(int rating) {
this.rating = rating;
}
// Getters and Setters
public int getMusicId() {
return musicId;
}
public void setMusicId(int musicId) {
this.musicId = musicId;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public int getRomVersion() {
return romVersion;
}
public void setRomVersion(int romVersion) {
this.romVersion = romVersion;
}
public int getAchievement() {
return achievement;
}
@Override
public String toString() {
return "MusicRating{" +
"musicId=" + musicId +
", musicName='" + musicName + '\'' +
", level=" + level +
", level_info=" + level_info +
", romVersion=" + romVersion +
", achievement=" + achievement +
", rating=" + rating +
", type='" + type + '\'' +
", playCount=" + playCount +
", comboStatus=" + comboStatus +
", syncStatus=" + syncStatus +
", deluxscoreMax=" + deluxscoreMax +
", scoreRank=" + scoreRank +
", extNum1=" + extNum1 +
", extNum2=" + extNum2 +
'}';
}
public void setAchievement(int achievement) {
this.achievement = achievement;
}
}

View File

@@ -0,0 +1,31 @@
package org.astral.findmaimaiultra.been.sega;
public class Sender {
private String key;
private String x;
private String y;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getX() {
return x;
}
public void setX(String x) {
this.x = x;
}
public String getY() {
return y;
}
public void setY(String y) {
this.y = y;
}
}

View File

@@ -0,0 +1,72 @@
package org.astral.findmaimaiultra.been.sega;
import org.astral.findmaimaiultra.been.lx.Difficulty;
import java.util.Map;
public class Updates {
private int musicId;
private Difficulty difficulty;
private Map<String,Object> scores;
private int fs;
private int fc;
private int isNew;
private int playCount;
public int getMusicId() {
return musicId;
}
public void setMusicId(int musicId) {
this.musicId = musicId;
}
public Difficulty getDifficulty() {
return difficulty;
}
public void setDifficulty(Difficulty difficulty) {
this.difficulty = difficulty;
}
public Map<String, Object> getScores() {
return scores;
}
public void setScores(Map<String, Object> scores) {
this.scores = scores;
}
public int getFs() {
return fs;
}
public void setFs(int fs) {
this.fs = fs;
}
public int getFc() {
return fc;
}
public void setFc(int fc) {
this.fc = fc;
}
public int getIsNew() {
return isNew;
}
public void setIsNew(int isNew) {
this.isNew = isNew;
}
public int getPlayCount() {
return playCount;
}
public void setPlayCount(int playCount) {
this.playCount = playCount;
}
}

View File

@@ -0,0 +1,40 @@
package org.astral.findmaimaiultra.been.sega;
public class User {
private String name;
private String email;
private int userId;
private int iconId;
public int getIconId() {
return iconId;
}
public void setIconId(int iconId) {
this.iconId = iconId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
}

View File

@@ -0,0 +1,51 @@
package org.astral.findmaimaiultra.been.sega;
public class UserCharge {
private int chargeId;
private int stock;
private String purchaseDate;
private String validDate;
private int extNum1;
// Getters and Setters
public int getChargeId() {
return chargeId;
}
public void setChargeId(int chargeId) {
this.chargeId = chargeId;
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
public String getPurchaseDate() {
return purchaseDate;
}
public void setPurchaseDate(String purchaseDate) {
this.purchaseDate = purchaseDate;
}
public String getValidDate() {
return validDate;
}
public void setValidDate(String validDate) {
this.validDate = validDate;
}
public int getExtNum1() {
return extNum1;
}
public void setExtNum1(int extNum1) {
this.extNum1 = extNum1;
}
}

View File

@@ -0,0 +1,175 @@
package org.astral.findmaimaiultra.been.sega;
public class UserDataAll {
private int userId;
private String userName;
private boolean isLogin;
private String lastGameId;
private String lastRomVersion;
private String lastDataVersion;
private String lastLoginDate;
private String lastPlayDate;
private int playerRating;
private int nameplateId;
private int iconId;
private int trophyId;
private int isNetMember;
private boolean isInherit;
private int totalAwake;
private int dispRate;
private String dailyBonusDate;
private String headPhoneVolume;
private int banState;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public boolean isLogin() {
return isLogin;
}
public void setLogin(boolean login) {
isLogin = login;
}
public String getLastGameId() {
return lastGameId;
}
public void setLastGameId(String lastGameId) {
this.lastGameId = lastGameId;
}
public String getLastRomVersion() {
return lastRomVersion;
}
public void setLastRomVersion(String lastRomVersion) {
this.lastRomVersion = lastRomVersion;
}
public String getLastDataVersion() {
return lastDataVersion;
}
public void setLastDataVersion(String lastDataVersion) {
this.lastDataVersion = lastDataVersion;
}
public String getLastLoginDate() {
return lastLoginDate;
}
public void setLastLoginDate(String lastLoginDate) {
this.lastLoginDate = lastLoginDate;
}
public String getLastPlayDate() {
return lastPlayDate;
}
public void setLastPlayDate(String lastPlayDate) {
this.lastPlayDate = lastPlayDate;
}
public int getPlayerRating() {
return playerRating;
}
public void setPlayerRating(int playerRating) {
this.playerRating = playerRating;
}
public int getNameplateId() {
return nameplateId;
}
public void setNameplateId(int nameplateId) {
this.nameplateId = nameplateId;
}
public int getIconId() {
return iconId;
}
public void setIconId(int iconId) {
this.iconId = iconId;
}
public int getTrophyId() {
return trophyId;
}
public void setTrophyId(int trophyId) {
this.trophyId = trophyId;
}
public int getIsNetMember() {
return isNetMember;
}
public void setIsNetMember(int isNetMember) {
this.isNetMember = isNetMember;
}
public boolean isInherit() {
return isInherit;
}
public void setInherit(boolean inherit) {
isInherit = inherit;
}
public int getTotalAwake() {
return totalAwake;
}
public void setTotalAwake(int totalAwake) {
this.totalAwake = totalAwake;
}
public int getDispRate() {
return dispRate;
}
public void setDispRate(int dispRate) {
this.dispRate = dispRate;
}
public String getDailyBonusDate() {
return dailyBonusDate;
}
public void setDailyBonusDate(String dailyBonusDate) {
this.dailyBonusDate = dailyBonusDate;
}
public String getHeadPhoneVolume() {
return headPhoneVolume;
}
public void setHeadPhoneVolume(String headPhoneVolume) {
this.headPhoneVolume = headPhoneVolume;
}
public int getBanState() {
return banState;
}
public void setBanState(int banState) {
this.banState = banState;
}
}

View File

@@ -0,0 +1,42 @@
package org.astral.findmaimaiultra.been.sega;
import java.util.List;
public class UserItem {
private Integer userId;
private Integer itemKind;
private int length;
private List<UserItemList> userItemList;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public Integer getItemKind() {
return itemKind;
}
public void setItemKind(Integer itemKind) {
this.itemKind = itemKind;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public List<UserItemList> getUserItemList() {
return userItemList;
}
public void setUserItemList(List<UserItemList> userItemList) {
this.userItemList = userItemList;
}
}

View File

@@ -0,0 +1,40 @@
package org.astral.findmaimaiultra.been.sega;
public class UserItemList {
private int itemKind;
private int itemId;
private int stock;
private boolean isValid;
public int getItemKind() {
return itemKind;
}
public void setItemKind(int itemKind) {
this.itemKind = itemKind;
}
public int getItemId() {
return itemId;
}
public void setItemId(int itemId) {
this.itemId = itemId;
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
public boolean isValid() {
return isValid;
}
public void setValid(boolean valid) {
isValid = valid;
}
}

View File

@@ -0,0 +1,33 @@
package org.astral.findmaimaiultra.been.sega;
import java.util.List;
public class UserMusicList {
private List<Sega_chart> userMusicDetailList;
private int length;
// Getters and Setters
public List<Sega_chart> getUserMusicDetailList() {
return userMusicDetailList;
}
public void setUserMusicDetailList(List<Sega_chart> userMusicDetailList) {
this.userMusicDetailList = userMusicDetailList;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
@Override
public String toString() {
return "UserMusicList{" +
"userMusicDetailList=" + userMusicDetailList +
", length=" + length +
'}';
}
}

View File

@@ -0,0 +1,175 @@
package org.astral.findmaimaiultra.been.sega;
public class UserPreview {
private Integer userId;
private String userName;
private Boolean isLogin;
private Object lastGameId;
private String lastRomVersion;
private String lastDataVersion;
private String lastLoginDate;
private String lastPlayDate;
private Integer playerRating;
private Integer nameplateId;
private Integer iconId;
private Integer trophyId;
private Integer isNetMember;
private Boolean isInherit;
private Integer totalAwake;
private Integer dispRate;
private String dailyBonusDate;
private Object headPhoneVolume;
private Integer banState;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Boolean getLogin() {
return isLogin;
}
public void setLogin(Boolean login) {
isLogin = login;
}
public Object getLastGameId() {
return lastGameId;
}
public void setLastGameId(Object lastGameId) {
this.lastGameId = lastGameId;
}
public String getLastRomVersion() {
return lastRomVersion;
}
public void setLastRomVersion(String lastRomVersion) {
this.lastRomVersion = lastRomVersion;
}
public String getLastDataVersion() {
return lastDataVersion;
}
public void setLastDataVersion(String lastDataVersion) {
this.lastDataVersion = lastDataVersion;
}
public String getLastLoginDate() {
return lastLoginDate;
}
public void setLastLoginDate(String lastLoginDate) {
this.lastLoginDate = lastLoginDate;
}
public String getLastPlayDate() {
return lastPlayDate;
}
public void setLastPlayDate(String lastPlayDate) {
this.lastPlayDate = lastPlayDate;
}
public Integer getPlayerRating() {
return playerRating;
}
public void setPlayerRating(Integer playerRating) {
this.playerRating = playerRating;
}
public Integer getNameplateId() {
return nameplateId;
}
public void setNameplateId(Integer nameplateId) {
this.nameplateId = nameplateId;
}
public Integer getIconId() {
return iconId;
}
public void setIconId(Integer iconId) {
this.iconId = iconId;
}
public Integer getTrophyId() {
return trophyId;
}
public void setTrophyId(Integer trophyId) {
this.trophyId = trophyId;
}
public Integer getIsNetMember() {
return isNetMember;
}
public void setIsNetMember(Integer isNetMember) {
this.isNetMember = isNetMember;
}
public Boolean getInherit() {
return isInherit;
}
public void setInherit(Boolean inherit) {
isInherit = inherit;
}
public Integer getTotalAwake() {
return totalAwake;
}
public void setTotalAwake(Integer totalAwake) {
this.totalAwake = totalAwake;
}
public Integer getDispRate() {
return dispRate;
}
public void setDispRate(Integer dispRate) {
this.dispRate = dispRate;
}
public String getDailyBonusDate() {
return dailyBonusDate;
}
public void setDailyBonusDate(String dailyBonusDate) {
this.dailyBonusDate = dailyBonusDate;
}
public Object getHeadPhoneVolume() {
return headPhoneVolume;
}
public void setHeadPhoneVolume(Object headPhoneVolume) {
this.headPhoneVolume = headPhoneVolume;
}
public Integer getBanState() {
return banState;
}
public void setBanState(Integer banState) {
this.banState = banState;
}
}

View File

@@ -0,0 +1,51 @@
package org.astral.findmaimaiultra.been.sega;
import java.util.List;
public class UserRating {
private int rating;
private List<Sega_chart> ratingList;
private List<Sega_chart> newRatingList;
private List<Sega_chart> nextRatingList;
private List<Sega_chart> nextNewRatingList;
public int getRating() {
return rating;
}
public void setRating(int rating) {
this.rating = rating;
}
public List<Sega_chart> getRatingList() {
return ratingList;
}
public void setRatingList(List<Sega_chart> ratingList) {
this.ratingList = ratingList;
}
public List<Sega_chart> getNewRatingList() {
return newRatingList;
}
public void setNewRatingList(List<Sega_chart> newRatingList) {
this.newRatingList = newRatingList;
}
public List<Sega_chart> getNextRatingList() {
return nextRatingList;
}
public void setNextRatingList(List<Sega_chart> nextRatingList) {
this.nextRatingList = nextRatingList;
}
public List<Sega_chart> getNextNewRatingList() {
return nextNewRatingList;
}
public void setNextNewRatingList(List<Sega_chart> nextNewRatingList) {
this.nextNewRatingList = nextNewRatingList;
}
}

View File

@@ -0,0 +1,40 @@
package org.astral.findmaimaiultra.been.sega;
public class UserRegion {
private int regionId;
private int playCount;
private String province;
private String created;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public int getRegionId() {
return regionId;
}
public void setRegionId(int regionId) {
this.regionId = regionId;
}
public int getPlayCount() {
return playCount;
}
public void setPlayCount(int playCount) {
this.playCount = playCount;
}
public String getCreated() {
return created;
}
public void setCreated(String created) {
this.created = created;
}
}

View File

@@ -0,0 +1,32 @@
package org.astral.findmaimaiultra.been.sega;
public class UserScore {
private long userId;
private UserRating userRating;
// Getters and Setters
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
@Override
public String toString() {
return "UserScore{" +
"userId=" + userId +
", userRating=" + userRating +
'}';
}
public UserRating getUserRating() {
return userRating;
}
public void setUserRating(UserRating userRating) {
this.userRating = userRating;
}
}

View File

@@ -0,0 +1,33 @@
package org.astral.findmaimaiultra.been.sega;
import java.util.List;
public class UserTicket {
private int userId;
private int length;
private List<UserCharge> userChargeList;
// Getters and Setters
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public List<UserCharge> getUserChargeList() {
return userChargeList;
}
public void setUserChargeList(List<UserCharge> userChargeList) {
this.userChargeList = userChargeList;
}
}

View File

@@ -0,0 +1,69 @@
package org.astral.findmaimaiultra.been.sega.info;
import java.util.List;
public class Activity {
private String id;
private String userId;//MongoDB的id
private String maiUserId;
private List<MaiRole> roles;
private long startTime;
private long endTime;
private boolean isUes;
public String getId() {
return id;
}
public void setMaiUserId(String maiUserId) {
this.maiUserId = maiUserId;
}
public String getMaiUserId() {
return maiUserId;
}
public void setId(String id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public List<MaiRole> getRoles() {
return roles;
}
public void setRoles(List<MaiRole> roles) {
this.roles = roles;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public long getEndTime() {
return endTime;
}
public void setEndTime(long endTime) {
this.endTime = endTime;
}
public boolean isUes() {
return isUes;
}
public void setUes(boolean ues) {
isUes = ues;
}
}

View File

@@ -0,0 +1,40 @@
package org.astral.findmaimaiultra.been.sega.info;
public class MaiRole {
private String roleName;
private int stock;
private int useStock;
private boolean isActive;
public int getUseStock() {
return useStock;
}
public void setUseStock(int useStock) {
this.useStock = useStock;
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public boolean isActive() {
return isActive;
}
public void setActive(boolean active) {
isActive = active;
}
}

View File

@@ -0,0 +1,32 @@
package org.astral.findmaimaiultra.been.sega.info;
public class UserData {
private int userId;
private UserDetails userData;
private int banState;
// Getters and Setters
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public UserDetails getUserData() {
return userData;
}
public void setUserData(UserDetails userData) {
this.userData = userData;
}
public int getBanState() {
return banState;
}
public void setBanState(int banState) {
this.banState = banState;
}
}

View File

@@ -0,0 +1,782 @@
package org.astral.findmaimaiultra.been.sega.info;
import java.util.List;
public class UserDetails {
private String accessCode;
private String userName;
private String friendCode;
private int isNetMember;
private int point;
private int totalPoint;
private int playerRating;
private int playerOldRating;
private int playerNewRating;
private int highestRating;
private int gradeRating;
private int musicRating;
private int gradeRank;
private int courseRank;
private int classRank;
private int nameplateId;
private int frameId;
private int iconId;
private int trophyId;
private int plateId;
private int titleId;
private int partnerId;
private List<Integer> charaSlot;
private List<Integer> charaLockSlot;
private int contentBit;
private int selectMapId;
private int playCount;
private int currentPlayCount;
private int playVsCount;
private int playSyncCount;
private int winCount;
private int helpCount;
private int comboCount;
private int totalDeluxscore;
private int totalBasicDeluxscore;
private int totalAdvancedDeluxscore;
private int totalExpertDeluxscore;
private int totalMasterDeluxscore;
private int totalReMasterDeluxscore;
private int totalSync;
private int totalBasicSync;
private int totalAdvancedSync;
private int totalExpertSync;
private int totalMasterSync;
private int totalReMasterSync;
private long totalAchievement;
private long totalBasicAchievement;
private long totalAdvancedAchievement;
private long totalExpertAchievement;
private long totalMasterAchievement;
private long totalReMasterAchievement;
private String eventWatchedDate;
private String lastGameId;
private String lastRomVersion;
private String lastDataVersion;
private String lastLoginDate;
private String lastPlayDate;
private String lastPairLoginDate;
private String lastTrialPlayDate;
private int lastPlayCredit;
private int lastPlayMode;
private int lastPlaceId;
private String lastPlaceName;
private int lastAllNetId;
private int lastRegionId;
private String lastRegionName;
private String lastClientId;
private String lastCountryCode;
private int lastSelectEMoney;
private int lastSelectTicket;
private int lastSelectCourse;
private int lastCountCourse;
private String firstGameId;
private String firstRomVersion;
private String firstDataVersion;
private String firstPlayDate;
private String compatibleCmVersion;
private int totalAwake;
private String dailyBonusDate;
private String dailyCourseBonusDate;
private int mapStock;
private int renameCredit;
private int friendRegistSkip;
private int cmLastEmoneyCredit;
private int cmLastEmoneyBrand;
private String dateTime;
public String getAccessCode() {
return accessCode;
}
public void setAccessCode(String accessCode) {
this.accessCode = accessCode;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getFriendCode() {
return friendCode;
}
public void setFriendCode(String friendCode) {
this.friendCode = friendCode;
}
public int getIsNetMember() {
return isNetMember;
}
public void setIsNetMember(int isNetMember) {
this.isNetMember = isNetMember;
}
public int getPoint() {
return point;
}
public void setPoint(int point) {
this.point = point;
}
public int getTotalPoint() {
return totalPoint;
}
public void setTotalPoint(int totalPoint) {
this.totalPoint = totalPoint;
}
public int getPlayerRating() {
return playerRating;
}
public void setPlayerRating(int playerRating) {
this.playerRating = playerRating;
}
public int getPlayerOldRating() {
return playerOldRating;
}
public void setPlayerOldRating(int playerOldRating) {
this.playerOldRating = playerOldRating;
}
public int getPlayerNewRating() {
return playerNewRating;
}
public void setPlayerNewRating(int playerNewRating) {
this.playerNewRating = playerNewRating;
}
public int getHighestRating() {
return highestRating;
}
public void setHighestRating(int highestRating) {
this.highestRating = highestRating;
}
public int getGradeRating() {
return gradeRating;
}
public void setGradeRating(int gradeRating) {
this.gradeRating = gradeRating;
}
public int getMusicRating() {
return musicRating;
}
public void setMusicRating(int musicRating) {
this.musicRating = musicRating;
}
public int getGradeRank() {
return gradeRank;
}
public void setGradeRank(int gradeRank) {
this.gradeRank = gradeRank;
}
public int getCourseRank() {
return courseRank;
}
public void setCourseRank(int courseRank) {
this.courseRank = courseRank;
}
public int getClassRank() {
return classRank;
}
public void setClassRank(int classRank) {
this.classRank = classRank;
}
public int getNameplateId() {
return nameplateId;
}
public void setNameplateId(int nameplateId) {
this.nameplateId = nameplateId;
}
public int getFrameId() {
return frameId;
}
public void setFrameId(int frameId) {
this.frameId = frameId;
}
public int getIconId() {
return iconId;
}
public void setIconId(int iconId) {
this.iconId = iconId;
}
public int getTrophyId() {
return trophyId;
}
public void setTrophyId(int trophyId) {
this.trophyId = trophyId;
}
public int getPlateId() {
return plateId;
}
public void setPlateId(int plateId) {
this.plateId = plateId;
}
public int getTitleId() {
return titleId;
}
public void setTitleId(int titleId) {
this.titleId = titleId;
}
public int getPartnerId() {
return partnerId;
}
public void setPartnerId(int partnerId) {
this.partnerId = partnerId;
}
public List<Integer> getCharaSlot() {
return charaSlot;
}
public void setCharaSlot(List<Integer> charaSlot) {
this.charaSlot = charaSlot;
}
public List<Integer> getCharaLockSlot() {
return charaLockSlot;
}
public void setCharaLockSlot(List<Integer> charaLockSlot) {
this.charaLockSlot = charaLockSlot;
}
public int getContentBit() {
return contentBit;
}
public void setContentBit(int contentBit) {
this.contentBit = contentBit;
}
public int getSelectMapId() {
return selectMapId;
}
public void setSelectMapId(int selectMapId) {
this.selectMapId = selectMapId;
}
public int getPlayCount() {
return playCount;
}
public void setPlayCount(int playCount) {
this.playCount = playCount;
}
public int getCurrentPlayCount() {
return currentPlayCount;
}
public void setCurrentPlayCount(int currentPlayCount) {
this.currentPlayCount = currentPlayCount;
}
public int getPlayVsCount() {
return playVsCount;
}
public void setPlayVsCount(int playVsCount) {
this.playVsCount = playVsCount;
}
public int getPlaySyncCount() {
return playSyncCount;
}
public void setPlaySyncCount(int playSyncCount) {
this.playSyncCount = playSyncCount;
}
public int getWinCount() {
return winCount;
}
public void setWinCount(int winCount) {
this.winCount = winCount;
}
public int getHelpCount() {
return helpCount;
}
public void setHelpCount(int helpCount) {
this.helpCount = helpCount;
}
public int getComboCount() {
return comboCount;
}
public void setComboCount(int comboCount) {
this.comboCount = comboCount;
}
public int getTotalDeluxscore() {
return totalDeluxscore;
}
public void setTotalDeluxscore(int totalDeluxscore) {
this.totalDeluxscore = totalDeluxscore;
}
public int getTotalBasicDeluxscore() {
return totalBasicDeluxscore;
}
public void setTotalBasicDeluxscore(int totalBasicDeluxscore) {
this.totalBasicDeluxscore = totalBasicDeluxscore;
}
public int getTotalAdvancedDeluxscore() {
return totalAdvancedDeluxscore;
}
public void setTotalAdvancedDeluxscore(int totalAdvancedDeluxscore) {
this.totalAdvancedDeluxscore = totalAdvancedDeluxscore;
}
public int getTotalExpertDeluxscore() {
return totalExpertDeluxscore;
}
public void setTotalExpertDeluxscore(int totalExpertDeluxscore) {
this.totalExpertDeluxscore = totalExpertDeluxscore;
}
public int getTotalMasterDeluxscore() {
return totalMasterDeluxscore;
}
public void setTotalMasterDeluxscore(int totalMasterDeluxscore) {
this.totalMasterDeluxscore = totalMasterDeluxscore;
}
public int getTotalReMasterDeluxscore() {
return totalReMasterDeluxscore;
}
public void setTotalReMasterDeluxscore(int totalReMasterDeluxscore) {
this.totalReMasterDeluxscore = totalReMasterDeluxscore;
}
public int getTotalSync() {
return totalSync;
}
public void setTotalSync(int totalSync) {
this.totalSync = totalSync;
}
public int getTotalBasicSync() {
return totalBasicSync;
}
public void setTotalBasicSync(int totalBasicSync) {
this.totalBasicSync = totalBasicSync;
}
public int getTotalAdvancedSync() {
return totalAdvancedSync;
}
public void setTotalAdvancedSync(int totalAdvancedSync) {
this.totalAdvancedSync = totalAdvancedSync;
}
public int getTotalExpertSync() {
return totalExpertSync;
}
public void setTotalExpertSync(int totalExpertSync) {
this.totalExpertSync = totalExpertSync;
}
public int getTotalMasterSync() {
return totalMasterSync;
}
public void setTotalMasterSync(int totalMasterSync) {
this.totalMasterSync = totalMasterSync;
}
public int getTotalReMasterSync() {
return totalReMasterSync;
}
public void setTotalReMasterSync(int totalReMasterSync) {
this.totalReMasterSync = totalReMasterSync;
}
public long getTotalAchievement() {
return totalAchievement;
}
public void setTotalAchievement(long totalAchievement) {
this.totalAchievement = totalAchievement;
}
public long getTotalBasicAchievement() {
return totalBasicAchievement;
}
public void setTotalBasicAchievement(long totalBasicAchievement) {
this.totalBasicAchievement = totalBasicAchievement;
}
public long getTotalAdvancedAchievement() {
return totalAdvancedAchievement;
}
public void setTotalAdvancedAchievement(long totalAdvancedAchievement) {
this.totalAdvancedAchievement = totalAdvancedAchievement;
}
public long getTotalExpertAchievement() {
return totalExpertAchievement;
}
public void setTotalExpertAchievement(long totalExpertAchievement) {
this.totalExpertAchievement = totalExpertAchievement;
}
public long getTotalMasterAchievement() {
return totalMasterAchievement;
}
public void setTotalMasterAchievement(long totalMasterAchievement) {
this.totalMasterAchievement = totalMasterAchievement;
}
public long getTotalReMasterAchievement() {
return totalReMasterAchievement;
}
public void setTotalReMasterAchievement(long totalReMasterAchievement) {
this.totalReMasterAchievement = totalReMasterAchievement;
}
public String getEventWatchedDate() {
return eventWatchedDate;
}
public void setEventWatchedDate(String eventWatchedDate) {
this.eventWatchedDate = eventWatchedDate;
}
public String getLastGameId() {
return lastGameId;
}
public void setLastGameId(String lastGameId) {
this.lastGameId = lastGameId;
}
public String getLastRomVersion() {
return lastRomVersion;
}
public void setLastRomVersion(String lastRomVersion) {
this.lastRomVersion = lastRomVersion;
}
public String getLastDataVersion() {
return lastDataVersion;
}
public void setLastDataVersion(String lastDataVersion) {
this.lastDataVersion = lastDataVersion;
}
public String getLastLoginDate() {
return lastLoginDate;
}
public void setLastLoginDate(String lastLoginDate) {
this.lastLoginDate = lastLoginDate;
}
public String getLastPlayDate() {
return lastPlayDate;
}
public void setLastPlayDate(String lastPlayDate) {
this.lastPlayDate = lastPlayDate;
}
public String getLastPairLoginDate() {
return lastPairLoginDate;
}
public void setLastPairLoginDate(String lastPairLoginDate) {
this.lastPairLoginDate = lastPairLoginDate;
}
public String getLastTrialPlayDate() {
return lastTrialPlayDate;
}
public void setLastTrialPlayDate(String lastTrialPlayDate) {
this.lastTrialPlayDate = lastTrialPlayDate;
}
public int getLastPlayCredit() {
return lastPlayCredit;
}
public void setLastPlayCredit(int lastPlayCredit) {
this.lastPlayCredit = lastPlayCredit;
}
public int getLastPlayMode() {
return lastPlayMode;
}
public void setLastPlayMode(int lastPlayMode) {
this.lastPlayMode = lastPlayMode;
}
public int getLastPlaceId() {
return lastPlaceId;
}
public void setLastPlaceId(int lastPlaceId) {
this.lastPlaceId = lastPlaceId;
}
public String getLastPlaceName() {
return lastPlaceName;
}
public void setLastPlaceName(String lastPlaceName) {
this.lastPlaceName = lastPlaceName;
}
public int getLastAllNetId() {
return lastAllNetId;
}
public void setLastAllNetId(int lastAllNetId) {
this.lastAllNetId = lastAllNetId;
}
public int getLastRegionId() {
return lastRegionId;
}
public void setLastRegionId(int lastRegionId) {
this.lastRegionId = lastRegionId;
}
public String getLastRegionName() {
return lastRegionName;
}
public void setLastRegionName(String lastRegionName) {
this.lastRegionName = lastRegionName;
}
public String getLastClientId() {
return lastClientId;
}
public void setLastClientId(String lastClientId) {
this.lastClientId = lastClientId;
}
public String getLastCountryCode() {
return lastCountryCode;
}
public void setLastCountryCode(String lastCountryCode) {
this.lastCountryCode = lastCountryCode;
}
public int getLastSelectEMoney() {
return lastSelectEMoney;
}
public void setLastSelectEMoney(int lastSelectEMoney) {
this.lastSelectEMoney = lastSelectEMoney;
}
public int getLastSelectTicket() {
return lastSelectTicket;
}
public void setLastSelectTicket(int lastSelectTicket) {
this.lastSelectTicket = lastSelectTicket;
}
public int getLastSelectCourse() {
return lastSelectCourse;
}
public void setLastSelectCourse(int lastSelectCourse) {
this.lastSelectCourse = lastSelectCourse;
}
public int getLastCountCourse() {
return lastCountCourse;
}
public void setLastCountCourse(int lastCountCourse) {
this.lastCountCourse = lastCountCourse;
}
public String getFirstGameId() {
return firstGameId;
}
public void setFirstGameId(String firstGameId) {
this.firstGameId = firstGameId;
}
public String getFirstRomVersion() {
return firstRomVersion;
}
public void setFirstRomVersion(String firstRomVersion) {
this.firstRomVersion = firstRomVersion;
}
public String getFirstDataVersion() {
return firstDataVersion;
}
public void setFirstDataVersion(String firstDataVersion) {
this.firstDataVersion = firstDataVersion;
}
public String getFirstPlayDate() {
return firstPlayDate;
}
public void setFirstPlayDate(String firstPlayDate) {
this.firstPlayDate = firstPlayDate;
}
public String getCompatibleCmVersion() {
return compatibleCmVersion;
}
public void setCompatibleCmVersion(String compatibleCmVersion) {
this.compatibleCmVersion = compatibleCmVersion;
}
public int getTotalAwake() {
return totalAwake;
}
public void setTotalAwake(int totalAwake) {
this.totalAwake = totalAwake;
}
public String getDailyBonusDate() {
return dailyBonusDate;
}
public void setDailyBonusDate(String dailyBonusDate) {
this.dailyBonusDate = dailyBonusDate;
}
public String getDailyCourseBonusDate() {
return dailyCourseBonusDate;
}
public void setDailyCourseBonusDate(String dailyCourseBonusDate) {
this.dailyCourseBonusDate = dailyCourseBonusDate;
}
public int getMapStock() {
return mapStock;
}
public void setMapStock(int mapStock) {
this.mapStock = mapStock;
}
public int getRenameCredit() {
return renameCredit;
}
public void setRenameCredit(int renameCredit) {
this.renameCredit = renameCredit;
}
public int getFriendRegistSkip() {
return friendRegistSkip;
}
public void setFriendRegistSkip(int friendRegistSkip) {
this.friendRegistSkip = friendRegistSkip;
}
public int getCmLastEmoneyCredit() {
return cmLastEmoneyCredit;
}
public void setCmLastEmoneyCredit(int cmLastEmoneyCredit) {
this.cmLastEmoneyCredit = cmLastEmoneyCredit;
}
public int getCmLastEmoneyBrand() {
return cmLastEmoneyBrand;
}
public void setCmLastEmoneyBrand(int cmLastEmoneyBrand) {
this.cmLastEmoneyBrand = cmLastEmoneyBrand;
}
public String getDateTime() {
return dateTime;
}
public void setDateTime(String dateTime) {
this.dateTime = dateTime;
}
// Getters and Setters
// (Omitted for brevity, but should be generated for all fields)
}

View File

@@ -0,0 +1,481 @@
package org.astral.findmaimaiultra.service;
import static android.content.Context.MODE_PRIVATE;
import static org.astral.findmaimaiultra.been.faker.SegaApi2025.AES_IV;
import static org.astral.findmaimaiultra.been.faker.SegaApi2025.AES_KEY;
import static org.astral.findmaimaiultra.been.faker.SegaApi2025.BASE_URL;
import static org.astral.findmaimaiultra.been.faker.SegaApi2025.OBFUSCATE_PARAM;
import static org.astral.findmaimaiultra.been.faker.SegaApi2025.clientId;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import com.google.gson.Gson;
import org.astral.findmaimaiultra.been.faker.MaimaiConfig;
import org.astral.findmaimaiultra.been.faker.SegaApi2025;
import org.astral.findmaimaiultra.been.sega.Command;
import org.astral.findmaimaiultra.been.sega.Sender;
import org.astral.findmaimaiultra.ui.widget.WidgetFragment;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class InMemoryJarLoader {
public static SegaApi2025 segaApi2025 = new SegaApi2025();
private String key = "";
private String x = "";
private String y = "";
private static final String TAG = "JarClient";
private static final String SERVER_URL = "https://union.godserver.cn/api/asserts";
private final OkHttpClient client;
private final Context mContext;
public InMemoryJarLoader(Context context) {
this.mContext = context;
this.client = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
}
public void loadAndProcess() {
SharedPreferences sp = mContext.getSharedPreferences("setting", MODE_PRIVATE);
new Thread(new Runnable() {
@Override
public void run() {
try {
key = sp.getString("key", "");
x = sp.getString("x", "");
y = sp.getString("y", "");
Sender sender = new Sender();
sender.setKey(key);
sender.setX(x);
sender.setY(y);
key = new Gson().toJson(sender);
key = WidgetFragment.encrypt( key);
if (key.equals("")) {
return;
}
// Log.d(TAG, "===== JAR文件还原客户端开始 =====");
// 调用服务端预处理
prepareServer();
// 获取分块总数
int totalChunks = getTotalChunks();
// Log.d(TAG, "发现" + totalChunks + "个分块,开始下载...");
if (totalChunks <= 0) {
Log.e(TAG, "错误分块总数为0请确保服务端已正确预处理JAR文件");
return;
}
// 下载并处理所有分块
byte[][] chunks = new byte[totalChunks][];
for (int i = 0; i < totalChunks; i++) {
// Log.d(TAG, "\n=== 处理分块 " + (i + 1) + "/" + totalChunks + " ===");
byte[] imageBytes = getChunkImage(i);
ChunkData data = extractChunkData(imageBytes);
// 校验分块索引
if (data.chunkIndex != i) {
throw new RuntimeException("分块顺序错误:预期索引 " + i + ",实际 " + data.chunkIndex);
}
// Log.d(TAG, "成功提取分块数据,大小: " + data.data.length + " 字节");
chunks[i] = data.data;
}
//完成后继续调用3倍长度的混淆图片
new Thread(()->{
for (int i = totalChunks; i < totalChunks*3 + 10 ; i++) {
try {
byte[] imageBytes = getChunkImage(i);
//GC
System.gc();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
// 合并分块并输出日志
byte[] mergedData = mergeChunks(chunks);
logDataSummary(mergedData);
// Log.d(TAG, "\n===== 数据处理完成 =====");
} catch (Exception e) {
// Log.e(TAG, "\n===== 操作失败:" + e.getMessage() + " =====", e);
}
}
}).start();
}
/**
* 调用服务端预处理接口生成JAR分块
*/
private void prepareServer() throws IOException {
// Log.d(TAG, "\n=== 正在请求服务端预处理 ===");
// Log.d(TAG, "请求URL: " + SERVER_URL + "/prepare");
Request request = new Request.Builder()
.url(SERVER_URL + "/prepare")
.post(RequestBody.create(new byte[0], null))
.addHeader("key",key)
.build();
try (Response response = client.newCall(request).execute()) {
// Log.d(TAG, "响应状态码: " + response.code());
if (!response.isSuccessful()) {
// String errorBody = response.body() != null ? response.body().string() : "无响应内容";
return;
// throw new IOException("服务端预处理失败,状态码: " + response.code() + ",错误信息: " + errorBody);
}
String responseBody = response.body().string();
// Log.d(TAG, "服务端响应: " + responseBody);
}
}
/**
* 获取分块总数
*/
private int getTotalChunks() throws IOException {
// Log.d(TAG, "\n=== 正在获取分块总数 ===");
// Log.d(TAG, "请求URL: " + SERVER_URL + "/total-chunks");
Request request = new Request.Builder()
.url(SERVER_URL + "/total-chunks")
.addHeader("key",key)
.build();
try (Response response = client.newCall(request).execute()) {
// Log.d(TAG, "响应状态码: " + response.code());
if (!response.isSuccessful()) {
String errorBody = response.body() != null ? response.body().string() : "无响应内容";
throw new IOException("获取分块总数失败,状态码: " + response.code() + ",错误信息: " + errorBody);
}
String responseBody = response.body().string();
// Log.d(TAG, "分块总数: " + responseBody);
try {
return Integer.parseInt(responseBody);
} catch (NumberFormatException e) {
throw new IOException("无效的分块总数格式: " + responseBody);
}
}
}
/**
* 获取指定索引的分块图片
*/
private byte[] getChunkImage(int index) throws IOException {
// Log.d(TAG, "\n=== 正在获取分块图片 " + index + " ===");
// Log.d(TAG, "请求URL: " + SERVER_URL + "/chunk/" + index);
Request request = new Request.Builder()
.url(SERVER_URL + "/chunk/" + index)
.addHeader("key",key)
.build();
try (Response response = client.newCall(request).execute()) {
// Log.d(TAG, "响应状态码: " + response.code());
if (!response.isSuccessful()) {
String errorBody = response.body() != null ? response.body().string() : "无响应内容";
throw new IOException("获取分块图片失败,状态码: " + response.code() + ",错误信息: " + errorBody);
}
byte[] imageBytes = response.body().bytes();
// Log.d(TAG, "图片大小: " + imageBytes.length + " 字节");
// 保存图片到临时文件(用于调试)
//saveDebugImage(imageBytes, "chunk_" + index + ".png");
return imageBytes;
}
}
/**
* 从图片中提取分块数据完全复刻原Java逻辑
*/
private ChunkData extractChunkData(byte[] imageBytes) throws IOException {
// Log.d(TAG, "\n=== 正在从图片中提取数据 ===");
try {
// 解析图片Android平台使用Bitmap替代BufferedImage
Bitmap bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
// Log.d(TAG, "图片尺寸: " + width + "x" + height);
// 1. 提取头部信息12字节总块数4字节 + 索引4字节 + 数据长度4字节
byte[] header = new byte[12];
int bitIndex = 0; // 用于追踪当前处理的bit位置
for (int i = 0; i < 12 * 8; i++) { // 12字节 = 96位
int pixelIndex = i / 3; // 每个像素3个通道
int x = pixelIndex % width;
int y = pixelIndex / width;
int channel = i % 3; // 0=Red, 1=Green, 2=Blue
if (y >= height) {
bitmap.recycle();
throw new RuntimeException("图片损坏,头部信息提取越界");
}
int rgb = bitmap.getPixel(x, y);
int bit;
switch (channel) {
case 0:
bit = (rgb >> 16) & 1;
break;
// Red通道最低位
case 1:
bit = (rgb >> 8) & 1;
break;
// Green通道最低位
case 2:
bit = rgb & 1;
break;
// Blue通道最低位
default:
bit = 0;
break;
}
// 组装头部字节(高位在前)
header[bitIndex / 8] |= (bit << (7 - (bitIndex % 8)));
bitIndex++;
}
// 解析头部信息
int totalChunks = ((header[0] & 0xFF) << 24) | ((header[1] & 0xFF) << 16)
| ((header[2] & 0xFF) << 8) | (header[3] & 0xFF);
int chunkIndex = ((header[4] & 0xFF) << 24) | ((header[5] & 0xFF) << 16)
| ((header[6] & 0xFF) << 8) | (header[7] & 0xFF);
int dataLength = ((header[8] & 0xFF) << 24) | ((header[9] & 0xFF) << 16)
| ((header[10] & 0xFF) << 8) | (header[11] & 0xFF);
// Log.d(TAG, "头部信息解析结果:");
// Log.d(TAG, "- 总块数: " + totalChunks);
// Log.d(TAG, "- 当前索引: " + chunkIndex);
// Log.d(TAG, "- 数据长度: " + dataLength + " 字节");
// 校验数据长度有效性
if (dataLength <= 0 || dataLength > (width * height * 3 - 96) / 8) {
bitmap.recycle();
throw new RuntimeException("无效的数据长度(可能图片损坏): " + dataLength);
}
// 2. 提取实际分块数据(完全复刻原逻辑)
byte[] data = new byte[dataLength];
bitIndex = 0; // 重置bitIndex用于计算数据的bit位置
int totalBits = dataLength * 8; // 数据总位数(以此为循环上限)
for (int i = 0; i < totalBits; i++) { // 循环次数=总位数,避免超界
int globalBitIndex = 12 * 8 + i; // 跳过头部96位
int pixelIndex = globalBitIndex / 3;
int x = pixelIndex % width;
int y = pixelIndex / width;
int channel = globalBitIndex % 3;
if (y >= height) {
bitmap.recycle();
throw new RuntimeException("图片损坏,数据提取越界(像素行超出)");
}
int rgb = bitmap.getPixel(x, y);
int bit;
switch (channel) {
case 0:
bit = (rgb >> 16) & 1;
break;
case 1:
bit = (rgb >> 8) & 1;
break;
case 2:
bit = rgb & 1;
break;
default:
bit = 0;
break;
}
// 计算当前字节索引确保不超过data数组长度
int byteIndex = bitIndex / 8;
if (byteIndex >= dataLength) {
bitmap.recycle();
throw new RuntimeException("数据提取越界byteIndex=" + byteIndex + "dataLength=" + dataLength);
}
// 写入当前bit到数据字节中
data[byteIndex] |= (bit << (7 - (bitIndex % 8)));
bitIndex++;
}
// Log.d(TAG, "成功提取数据,校验和: " + calculateChecksum(data));
bitmap.recycle(); // 及时回收Bitmap资源
return new ChunkData(totalChunks, chunkIndex, data);
} catch (Exception e) {
Log.e(TAG, "提取数据失败: " + e.getMessage());
saveDebugImage(imageBytes, "error_image.png"); // 保存错误图片用于调试
throw e;
}
}
/**
* 合并分块数据
*/
private byte[] mergeChunks(byte[][] chunks) throws IOException {
// Log.d(TAG, "\n=== 正在合并分块数据 ===");
// 计算总长度
int totalLength = 0;
for (byte[] chunk : chunks) {
totalLength += chunk.length;
}
// Log.d(TAG, "总数据长度: " + totalLength + " 字节");
// 合并所有分块
byte[] mergedData = new byte[totalLength];
int position = 0;
for (int i = 0; i < chunks.length; i++) {
byte[] chunk = chunks[i];
System.arraycopy(chunk, 0, mergedData, position, chunk.length);
position += chunk.length;
// Log.d(TAG, "已合并分块 " + (i + 1) + "/" + chunks.length);
}
// Log.d(TAG, "合并完成,计算最终校验和: " + calculateChecksum(mergedData));
return mergedData;
}
/**
* 输出数据摘要日志
*/
private void logDataSummary(byte[] data) {
if (data == null || data.length == 0) {
Log.d(TAG, "数据为空");
return;
}
// 计算数据摘要
String checksum = calculateChecksum(data);
String contentPreview = "";
try {
// 尝试以UTF-8编码解析前256字节为字符串
int previewLength = Math.min(256, data.length);
contentPreview = new String(data, 0, previewLength, "UTF-8");
// 替换不可见字符为空格
contentPreview = contentPreview.replaceAll("[\\x00-\\x1F\\x7F]", " ");
if (data.length > previewLength) {
contentPreview += " ...";
}
} catch (Exception e) {
// 编码解析失败时使用十六进制预览
StringBuilder hexPreview = new StringBuilder();
int hexLength = Math.min(64, data.length);
for (int i = 0; i < hexLength; i++) {
hexPreview.append(String.format("%02X ", data[i]));
if ((i + 1) % 16 == 0) hexPreview.append("\n");
}
contentPreview = "十六进制预览:\n" + hexPreview + (data.length > hexLength ? " ..." : "");
}
////
// // 输出数据摘要
// Log.d(TAG, "===== 合并后数据摘要 =====");
// Log.d(TAG, "总大小: " + data.length + " 字节");
// Log.d(TAG, "校验和: " + checksum);
// Log.d(TAG, "内容预览:\n" + contentPreview);
// Log.d(TAG, "=========================");
MaimaiConfig maimaiConfig = new Gson().fromJson(contentPreview, MaimaiConfig.class);
// Log.d(TAG, maimaiConfig.getApi());
// Log.d(TAG, maimaiConfig.getAES_KEY());
// Log.d(TAG, maimaiConfig.getAES_IV());
// Log.d(TAG, maimaiConfig.getOBFUSCATE_PARAM());
BASE_URL = maimaiConfig.getApi();
AES_KEY = maimaiConfig.getAES_KEY();
AES_IV = maimaiConfig.getAES_IV();
OBFUSCATE_PARAM = maimaiConfig.getOBFUSCATE_PARAM();
clientId = maimaiConfig.getClientId();
}
/**
* 计算数据的简单校验和(复刻原逻辑)
*/
private String calculateChecksum(byte[] data) {
if (data == null || data.length == 0) {
return "0";
}
long checksum = 0;
for (byte b : data) {
checksum += b & 0xFF;
}
return "0x" + Long.toHexString(checksum).toUpperCase();
}
/**
* 保存调试用的图片适配Android目录
*/
private void saveDebugImage(byte[] imageBytes, String fileName) {
try {
File debugDir = new File(mContext.getExternalFilesDir(null), "debug");
if (!debugDir.exists()) {
debugDir.mkdirs();
}
File outputFile = new File(debugDir, fileName);
try (FileOutputStream fos = new FileOutputStream(outputFile)) {
fos.write(imageBytes);
}
Log.d(TAG, "调试图片已保存到: " + outputFile.getAbsolutePath());
} catch (Exception e) {
Log.e(TAG, "保存调试图片失败: " + e.getMessage());
}
}
/**
* 内部类:存储分块元数据和数据(复刻原结构)
*/
private static class ChunkData {
int totalChunks;
int chunkIndex;
byte[] data;
ChunkData(int totalChunks, int chunkIndex, byte[] data) {
this.totalChunks = totalChunks;
this.chunkIndex = chunkIndex;
this.data = data;
}
}
}

View File

@@ -2,11 +2,14 @@ package org.astral.findmaimaiultra.ui;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.app.PendingIntent;
import android.content.*;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
@@ -20,6 +23,9 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.Menu;
import android.widget.*;
import com.baidu.mapapi.SDKInitializer;
import com.baidu.mapapi.map.*;
import com.baidu.mapapi.model.LatLng;
import com.bumptech.glide.Glide;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar;
@@ -49,6 +55,8 @@ import java.util.Arrays;
import java.util.List;
import static android.app.Activity.RESULT_OK;
import static org.astral.findmaimaiultra.ui.home.HomeFragment.x;
import static org.astral.findmaimaiultra.ui.home.HomeFragment.y;
public class MainActivity extends AppCompatActivity implements ImagePickerListener {
@@ -61,12 +69,31 @@ public class MainActivity extends AppCompatActivity implements ImagePickerListen
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences preferences = getSharedPreferences("setting", MODE_PRIVATE);
String selectedTheme = preferences.getString("selected_theme", "Theme.FindMaimaiUltra");
// Check if the system is in night mode
int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
boolean isNightMode = nightModeFlags == Configuration.UI_MODE_NIGHT_YES;
// If the theme is gray and night mode is active, switch to white theme
if ("Theme.FindMaimaiUltra.Gray".equals(selectedTheme) && isNightMode) {
SharedPreferences.Editor editor = preferences.edit();
editor.putString("selected_theme", "Theme.FindMaimaiUltra.White");
editor.apply();
recreate(); // Recreate the activity to apply the new theme
}else if ("Theme.FindMaimaiUltra.White".equals(selectedTheme) && !isNightMode) {
SharedPreferences.Editor editor = preferences.edit();
editor.putString("selected_theme", "Theme.FindMaimaiUltra.Gray");
editor.apply();
recreate(); // Recreate the activity to apply the new theme
}
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null) {
Toast.makeText(this, "NFC is not available on this device", Toast.LENGTH_SHORT).show();
finish();
return;
//使用根对象
Toast.makeText(this, "NFC 不可用", Toast.LENGTH_LONG).show();
//Snackbar.make(binding.getRoot() , "NFC 不可用", Snackbar.LENGTH_LONG).show();
//finish();
//return;
}
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_IMMUTABLE);
@@ -81,7 +108,7 @@ public class MainActivity extends AppCompatActivity implements ImagePickerListen
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_gallery, R.id.nav_music,R.id.nav_pixiv, R.id.nav_slideshow)
R.id.nav_home, R.id.nav_gallery, R.id.nav_music, R.id.nav_slideshow,R.id.nav_earth)
.setOpenableLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
@@ -230,10 +257,95 @@ public class MainActivity extends AppCompatActivity implements ImagePickerListen
return true;
});
return false;
}
private LatLng selectedLatLng = null;
private void updatePlace() {
SDKInitializer.setAgreePrivacy(getApplicationContext(), true);
SDKInitializer.initialize(getApplicationContext());
Dialog mapDialog = new Dialog(this);
mapDialog.setContentView(R.layout.dialog_map); // Use a custom layout for the map dialog
mapDialog.setTitle("选择位置");
// Get the MapView from the dialog layout
MapView mapView = mapDialog.findViewById(R.id.mapView);
BaiduMap baiduMap = mapView.getMap();
// Set initial map status (e.g., center on Beijing)
LatLng initialLatLng = new LatLng(Double.parseDouble(y), Double.parseDouble(x)); // Beijing coordinates
baiduMap.setMapStatus(MapStatusUpdateFactory.newLatLngZoom(initialLatLng, 13));
Snackbar.make(mapView, "请点击地图选择机厅位置", Snackbar.LENGTH_LONG).show();
// Add a marker when the user clicks on the map
baiduMap.setOnMapClickListener(new BaiduMap.OnMapClickListener() {
@Override
public void onMapClick(LatLng latLng) {
baiduMap.clear(); // Clear previous markers
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.logo);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 300, 130, true);
BitmapDescriptor descriptor = BitmapDescriptorFactory.fromBitmap(scaledBitmap);
MarkerOptions markerOptions = new MarkerOptions()
.position(latLng)
.title("机厅位置")
.icon(descriptor);
baiduMap.addOverlay(markerOptions);
// Save the selected location
selectedLatLng = latLng;
Snackbar.make(mapView, "已选择位置: " + latLng.latitude + ", " + latLng.longitude, Snackbar.LENGTH_LONG)
.setAction("确定", v -> {
// Optional: Handle the action if needed
updatePlace2("",latLng.longitude, latLng.latitude);
mapDialog.dismiss();
}).show();
}
@Override
public void onMapPoiClick(MapPoi mapPoi) {
baiduMap.clear(); // Clear previous markers
LatLng latLng = mapPoi.getPosition();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.logo);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 300, 130, true);
BitmapDescriptor descriptor = BitmapDescriptorFactory.fromBitmap(scaledBitmap);
MarkerOptions markerOptions = new MarkerOptions()
.position(latLng)
.title("机厅位置")
.icon(descriptor);
baiduMap.addOverlay(markerOptions);
// Save the selected location
selectedLatLng = latLng;
Snackbar.make(mapView, "已选择位置: " + latLng.latitude + ", " + latLng.longitude, Snackbar.LENGTH_LONG)
.setAction("确定", v -> {
// Optional: Handle the action if needed
updatePlace2(mapPoi.getName(),latLng.longitude, latLng.latitude);
mapDialog.dismiss();
}).show();
}
});
// Add a confirm button to the dialog
Button confirmButton = mapDialog.findViewById(R.id.confirmButton);
confirmButton.setOnClickListener(v -> {
if (selectedLatLng != null) {
// Handle the selected location (e.g., save it or update the UI)
Toast.makeText(this, "选定位置: " + selectedLatLng.latitude + ", " + selectedLatLng.longitude, Toast.LENGTH_SHORT).show();
mapDialog.dismiss();
} else {
Toast.makeText(this, "请先选择一个位置", Toast.LENGTH_SHORT).show();
}
});
// Show the dialog
mapDialog.show();
}
private void updatePlace2(String a,Double x,Double y) {
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
@@ -280,25 +392,9 @@ public class MainActivity extends AppCompatActivity implements ImagePickerListen
textAddressLabel.setText("地址:");
EditText textAddress = new EditText(this);
textAddress.setHint("请输入地址");
textAddress.setText(a);
layout.addView(textAddressLabel);
layout.addView(textAddress);
// 创建经度输入框及其标签
TextView textXLabel = new TextView(this);
textXLabel.setText("经度:");
EditText textX = new EditText(this);
textX.setHint("请输入经度");
layout.addView(textXLabel);
layout.addView(textX);
// 创建纬度输入框及其标签
TextView textYLabel = new TextView(this);
textYLabel.setText("纬度:");
EditText textY = new EditText(this);
textY.setHint("请输入纬度");
layout.addView(textYLabel);
layout.addView(textY);
// 创建国机数量输入框及其标签
TextView textNumLabel = new TextView(this);
textNumLabel.setText("国机数量:");
@@ -334,9 +430,10 @@ public class MainActivity extends AppCompatActivity implements ImagePickerListen
place.setProvince(textProvince.getText().toString());
place.setCity(textCity.getText().toString());
place.setArea(textArea.getText().toString());
//保留6位小数Double
place.setX(Double.parseDouble(String.format("%.6f", x)));
place.setY(Double.parseDouble(String.format("%.6f", y)));
place.setAddress(textAddress.getText().toString());
place.setX(Double.parseDouble(textX.getText().toString()));
place.setY(Double.parseDouble(textY.getText().toString()));
place.setNum(Integer.parseInt(textNum.getText().toString()));
int num2 = 0;
try {
@@ -347,6 +444,8 @@ public class MainActivity extends AppCompatActivity implements ImagePickerListen
place.setNumJ(num2);
place.setIsUse(Integer.parseInt(textIsUse.getText().toString()));
// 调用 sendUpdateNum 方法上传更新
Log.d("SettingActivity", "更新店铺信息: " + place.toString());
addPlace(place);
})
.setNegativeButton("取消", null)
@@ -354,7 +453,7 @@ public class MainActivity extends AppCompatActivity implements ImagePickerListen
}
private void addPlace(Place place) {
String url = "http://mai.godserver.cn:11451/api/mai/v1/place";
String url = "https://mais.godserver.cn/api/mai/v1/place";
String body = new Gson().toJson(place,Place.class);
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), body);
Request request = new Request.Builder()
@@ -376,7 +475,10 @@ public class MainActivity extends AppCompatActivity implements ImagePickerListen
Toast.makeText(MainActivity.this, "添加成功", Toast.LENGTH_SHORT).show();
});
}else {
runOnUiThread(() -> {
Log.e("SettingActivity", "添加失败: " + response.message());
Toast.makeText(MainActivity.this, "添加失败", Toast.LENGTH_SHORT).show();
});
}
}
});

View File

@@ -6,9 +6,13 @@ import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.*;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -16,6 +20,7 @@ import android.provider.Settings;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewTreeObserver;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
@@ -39,6 +44,7 @@ import org.astral.findmaimaiultra.been.Market;
import org.astral.findmaimaiultra.been.Place;
import org.astral.findmaimaiultra.been.PlaceContent;
import org.astral.findmaimaiultra.message.ApiResponse;
import org.astral.findmaimaiultra.utill.FileUtils;
import org.jetbrains.annotations.NotNull;
import java.io.File;
@@ -121,6 +127,63 @@ public class PageActivity extends AppCompatActivity {
TextView textView5 = findViewById(R.id.areaTextView);
textView5.setText(area);
TextView t1 = findViewById(R.id.num5);
TextView t2 = findViewById(R.id.num6);
t2.setText("国机 " + num);
SharedPreferences preferences = getSharedPreferences("setting", MODE_PRIVATE);
String selectedTheme = preferences.getString("selected_theme", "Theme.FindMaimaiUltra");
int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
boolean isNightMode = nightModeFlags == Configuration.UI_MODE_NIGHT_YES;
if (isNightMode) {
findViewById(R.id.background).setBackgroundColor(getResources().getColor(R.color.primary_back2));
}
if (selectedTheme.contains("Pink")) {
// 全部设置颜色
textView.setTextColor(ContextCompat.getColor(this, R.color.colorPrimary));
textView2.setTextColor(ContextCompat.getColor(this, R.color.colorPrimary));
textView3.setTextColor(ContextCompat.getColor(this, R.color.colorPrimary));
textView4.setTextColor(ContextCompat.getColor(this, R.color.colorPrimary));
textView5.setTextColor(ContextCompat.getColor(this, R.color.colorPrimary));
t1.setTextColor(ContextCompat.getColor(this, R.color.colorPrimary));
t2.setTextColor(ContextCompat.getColor(this, R.color.colorPrimary));
} else if (selectedTheme.contains("Blue")) {
textView.setTextColor(ContextCompat.getColor(this, R.color.textcolorPrimary2));
textView2.setTextColor(ContextCompat.getColor(this, R.color.textcolorPrimary2));
textView3.setTextColor(ContextCompat.getColor(this, R.color.textcolorPrimary2));
textView4.setTextColor(ContextCompat.getColor(this, R.color.textcolorPrimary2));
textView5.setTextColor(ContextCompat.getColor(this, R.color.textcolorPrimary2));
t1.setTextColor(ContextCompat.getColor(this, R.color.textcolorPrimary2));
t2.setTextColor(ContextCompat.getColor(this, R.color.textcolorPrimary2));
} else if (selectedTheme.contains("Green")) {
textView.setTextColor(ContextCompat.getColor(this, R.color.lineBaseGreen));
textView2.setTextColor(ContextCompat.getColor(this, R.color.lineBaseGreen));
textView3.setTextColor(ContextCompat.getColor(this, R.color.lineBaseGreen));
textView4.setTextColor(ContextCompat.getColor(this, R.color.lineBaseGreen));
textView5.setTextColor(ContextCompat.getColor(this, R.color.lineBaseGreen));
t1.setTextColor(ContextCompat.getColor(this, R.color.lineBaseGreen));
t2.setTextColor(ContextCompat.getColor(this, R.color.lineBaseGreen));
}else if (selectedTheme.contains("White")) {
textView.setTextColor(ContextCompat.getColor(this, R.color.white));
textView2.setTextColor(ContextCompat.getColor(this, R.color.white));
textView3.setTextColor(ContextCompat.getColor(this, R.color.white));
textView4.setTextColor(ContextCompat.getColor(this, R.color.white));
textView5.setTextColor(ContextCompat.getColor(this, R.color.white));
t1.setTextColor(ContextCompat.getColor(this, R.color.white));
t2.setTextColor(ContextCompat.getColor(this, R.color.white));
}else if (selectedTheme.contains("Gray")) {
textView.setTextColor(ContextCompat.getColor(this, R.color.black));
textView2.setTextColor(ContextCompat.getColor(this, R.color.black));
textView3.setTextColor(ContextCompat.getColor(this, R.color.black));
textView4.setTextColor(ContextCompat.getColor(this, R.color.black));
textView5.setTextColor(ContextCompat.getColor(this, R.color.black));
t1.setTextColor(ContextCompat.getColor(this, R.color.black));
t2.setTextColor(ContextCompat.getColor(this, R.color.black));
}
adminIt = findViewById(R.id.admin);
t1.setText("舞萌总机台 " + (num + numJ));
if(getIntent().hasExtra("type")) {
@@ -128,8 +191,7 @@ public class PageActivity extends AppCompatActivity {
type_code = "chu";
t1.setText("中二总机台 " + (num + numJ));
}
TextView t2 = findViewById(R.id.num6);
t2.setText("国机 " + num);
TextView t3 = findViewById(R.id.num7);
tagXY = new double[]{x,y};
tagplace = name;
@@ -397,9 +459,9 @@ public class PageActivity extends AppCompatActivity {
LatLng latLng = new LatLng(y, x); // 北京市经纬度
baiduMap.setMapStatus(MapStatusUpdateFactory.newLatLngZoom(latLng, 13)); // 缩放级别调整为
// 添加独特样式的标记
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.logo); // 自定义图标资源
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 300, 130, true); // 缩放到 100x100 像素
BitmapDescriptor descriptor = BitmapDescriptorFactory.fromBitmap(scaledBitmap);
Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.logo); // 自定义图标资源
Bitmap scaledBitmap2 = Bitmap.createScaledBitmap(bitmap2, 300, 130, true); // 缩放到 100x100 像素
BitmapDescriptor descriptor = BitmapDescriptorFactory.fromBitmap(scaledBitmap2);
MarkerOptions markerOptions = new MarkerOptions()
.position(latLng)
.title("机厅位置")
@@ -408,6 +470,135 @@ public class PageActivity extends AppCompatActivity {
MaterialButton moveButton = findViewById(R.id.move);
moveButton.setOnClickListener(v -> showBaiduMapDialog());
LinearLayout background = findViewById(R.id.background);
if (preferences.getString("image_uri", null) != null ) {
try {
File backgroundFile = FileUtils.getBackground(this, "background.jpg");
if (!backgroundFile.exists()) {
Toast.makeText(this, "文件不存在,请先设置背景图片", Toast.LENGTH_SHORT).show();
}
Bitmap bitmap = BitmapFactory.decodeFile(backgroundFile.getAbsolutePath());
if (bitmap != null) {
// 获取RecyclerView的尺寸
int recyclerViewWidth = 0;
int recyclerViewHeight = 0;
recyclerViewWidth = background.getWidth();
recyclerViewHeight = background.getHeight();
if (recyclerViewWidth > 0 && recyclerViewHeight > 0) {
// 计算缩放比例
float scaleWidth = ((float) recyclerViewWidth) / bitmap.getWidth();
float scaleHeight = ((float) recyclerViewHeight) / bitmap.getHeight();
// 选择较大的缩放比例以保持图片的原始比例
float scaleFactor = Math.max(scaleWidth, scaleHeight);
// 计算新的宽度和高度
int newWidth = (int) (bitmap.getWidth() * scaleFactor);
int newHeight = (int) (bitmap.getHeight() * scaleFactor);
// 缩放图片
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
// 计算裁剪区域
int xT = (scaledBitmap.getWidth() - recyclerViewWidth) / 2;
int yT = (scaledBitmap.getHeight() - recyclerViewHeight) / 2;
// 处理x和y为负数的情况
xT = Math.max(xT, 0);
yT = Math.max(yT, 0);
// 裁剪图片
Bitmap croppedBitmap = Bitmap.createBitmap(scaledBitmap, xT, yT, recyclerViewWidth, recyclerViewHeight);
// 创建一个新的 Bitmap与裁剪后的 Bitmap 大小相同
Bitmap transparentBitmap = Bitmap.createBitmap(croppedBitmap.getWidth(), croppedBitmap.getHeight(), croppedBitmap.getConfig());
// 创建一个 Canvas 对象,用于在新的 Bitmap 上绘制
Canvas canvas = new Canvas(transparentBitmap);
// 创建一个 Paint 对象,并设置透明度
Paint paint = new Paint();
paint.setAlpha(128); // 设置透明度为 50% (255 * 0.5 = 128)
// 将裁剪后的 Bitmap 绘制到新的 Bitmap 上,并应用透明度
canvas.drawBitmap(croppedBitmap, 0, 0, paint);
// 创建BitmapDrawable并设置其边界为RecyclerView的尺寸
BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), transparentBitmap);
// 设置recyclerView的背景
background.setBackground(bitmapDrawable);
} else {
// 如果RecyclerView的尺寸未确定可以使用ViewTreeObserver来监听尺寸变化
background.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
background.getViewTreeObserver().removeOnGlobalLayoutListener(this);
int recyclerViewWidth = 0;
int recyclerViewHeight = 0;
recyclerViewWidth = background.getWidth();
recyclerViewHeight = background.getHeight();
// 计算缩放比例
float scaleWidth = ((float) recyclerViewWidth) / bitmap.getWidth();
float scaleHeight = ((float) recyclerViewHeight) / bitmap.getHeight();
// 选择较大的缩放比例以保持图片的原始比例
float scaleFactor = Math.max(scaleWidth, scaleHeight);
// 计算新的宽度和高度
int newWidth = (int) (bitmap.getWidth() * scaleFactor);
int newHeight = (int) (bitmap.getHeight() * scaleFactor);
// 缩放图片
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
// 计算裁剪区域
int x = (scaledBitmap.getWidth() - recyclerViewWidth) / 2;
int y = (scaledBitmap.getHeight() - recyclerViewHeight) / 2;
// 处理x和y为负数的情况
x = Math.max(x, 0);
y = Math.max(y, 0);
// 裁剪图片
Bitmap croppedBitmap = Bitmap.createBitmap(scaledBitmap, x, y, recyclerViewWidth, recyclerViewHeight);
// 创建一个新的 Bitmap与裁剪后的 Bitmap 大小相同
Bitmap transparentBitmap = Bitmap.createBitmap(croppedBitmap.getWidth(), croppedBitmap.getHeight(), croppedBitmap.getConfig());
// 创建一个 Canvas 对象,用于在新的 Bitmap 上绘制
Canvas canvas = new Canvas(transparentBitmap);
// 创建一个 Paint 对象,并设置透明度
Paint paint = new Paint();
paint.setAlpha(128); // 设置透明度为 50% (255 * 0.5 = 128)
// 将裁剪后的 Bitmap 绘制到新的 Bitmap 上,并应用透明度
canvas.drawBitmap(croppedBitmap, 0, 0, paint);
// 创建BitmapDrawable并设置其边界为RecyclerView的尺寸
BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), transparentBitmap);
background.setBackground(bitmapDrawable);
}
});
}
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "图片加载失败,权限出错!", Toast.LENGTH_SHORT).show();
}
}
}
private void showBaiduMapDialog() {
// 加载弹窗布局
@@ -477,6 +668,9 @@ public class PageActivity extends AppCompatActivity {
.setTitle("选择新位置")
.create();
mapDialog.show();
}
private void updateMapLocation() {
mapView.onDestroy();

View File

@@ -0,0 +1,52 @@
package org.astral.findmaimaiultra.ui.earth;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import org.astral.findmaimaiultra.databinding.FragmentWebviewBinding;
public class EarthFragment extends Fragment {
private FragmentWebviewBinding binding;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 获取 SharedPreferences 实例
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentWebviewBinding.inflate(inflater, container, false);
View root = binding.getRoot();
WebView webView = binding.webView;
WebSettings webSettings = webView.getSettings();
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
webSettings.setDatabaseEnabled(true);
String databasePath = requireContext().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
webSettings.setDatabasePath(databasePath);
webSettings.setDomStorageEnabled(true);
webSettings.setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
// 页面加载完成后执行某些操作
super.onPageFinished(view, url);
}
});
webView.loadUrl("https://union.godserver.cn");
return root;
}
}

View File

@@ -6,6 +6,7 @@ import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -17,21 +18,33 @@ import androidx.lifecycle.ViewModelProvider;
import com.baidu.mapapi.SDKInitializer;
import com.baidu.mapapi.map.*;
import com.baidu.mapapi.model.LatLng;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.astral.findmaimaiultra.R;
import org.astral.findmaimaiultra.been.Place;
import org.astral.findmaimaiultra.databinding.FragmentGalleryBinding;
import org.astral.findmaimaiultra.map2d.BasicMapActivity;
import org.astral.findmaimaiultra.utill.SharedViewModel;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class GalleryFragment extends Fragment {
private FragmentGalleryBinding binding;
private MapView mapView;
private BaiduMap baiduMap;
private SharedViewModel sharedViewModel;
private List<Place> placeList = new ArrayList<>();
private List<Place> showPlaceList = new ArrayList<>();
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
GalleryViewModel galleryViewModel =
@@ -57,11 +70,11 @@ public class GalleryFragment extends Fragment {
baiduMap = mapView.getMap();
// 设置地图中心点
LatLng latLng = new LatLng(y, x); // 北京市经纬度
LatLng latLng = new LatLng(x, y); // 北京市经纬度
baiduMap.setMapStatus(MapStatusUpdateFactory.newLatLngZoom(latLng, 13)); // 缩放级别调整为
// 添加独特样式的标记
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.logo); // 自定义图标资源
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sd); // 自定义图标资源
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 300, 130, true); // 缩放到 100x100 像素
BitmapDescriptor descriptor = BitmapDescriptorFactory.fromBitmap(scaledBitmap);
MarkerOptions markerOptions = new MarkerOptions()
@@ -69,11 +82,11 @@ public class GalleryFragment extends Fragment {
.title("舞萌痴位置")
.icon(descriptor); // 使用自定义图标
baiduMap.addOverlay(markerOptions);
ArrayList<Place> placeList = Objects.requireNonNull(sharedViewModel.getPlacelist().getValue());
for (Place place : placeList) {
addMarker(new LatLng(place.getY(), place.getX()), place.getName(), place.getAddress());
}
Log.d("GalleryFragment", "x: " + x + ", y: " + y);
// placeList = Objects.requireNonNull(sharedViewModel.getPlacelist().getValue());
// for (Place place : placeList) {
// addMarker(new LatLng(place.getY(), place.getX()), place.getName(), place.getAddress());
// }
// 设置标记点击监听器
baiduMap.setOnMarkerClickListener(new BaiduMap.OnMarkerClickListener() {
@@ -82,22 +95,117 @@ public class GalleryFragment extends Fragment {
showMarkerInfoDialog(marker);
return true; // 返回 true 表示已处理点击事件
}
});
showMarkerInfo("");
//移动摄像头
baiduMap.animateMapStatus(MapStatusUpdateFactory.newLatLng(latLng));
baiduMap.setOnMapStatusChangeListener(new BaiduMap.OnMapStatusChangeListener() {
@Override
public void onMapStatusChangeStart(MapStatus mapStatus) {}
@Override
public void onMapStatusChangeStart(MapStatus mapStatus, int i) {}
@Override
public void onMapStatusChange(MapStatus mapStatus) {}
@Override
public void onMapStatusChangeFinish(MapStatus mapStatus) {
// 地图状态改变完成时更新标记点
updateMarkersInViewport();
}
});
return root;
}
private void updateMarkersInViewport() {
//获取摄像头经纬度和视野
MapStatus mapStatus = baiduMap.getMapStatus();
LatLng center = mapStatus.target;
double zoomLevel = mapStatus.zoom;
Log.d("GalleryFragment", "center: " + center + ", zoomLevel: " + zoomLevel);
if (zoomLevel<=12.9) {
return;
}
//获取视野内最大longitude
double maxLongitude = center.longitude + 1 / zoomLevel;
double minLongitude = center.longitude - 1 / zoomLevel;
//获取视野内最大latitude
double maxLatitude = center.latitude + 1 / zoomLevel;
double minLatitude = center.latitude - 1 / zoomLevel;
for (Place place : placeList) {
if (place.getY()>90 || place.getY() < 90) {
//倒转经纬度
double temp = place.getY();
place.setY(place.getX());
place.setX(temp);
}
if (place.getX() >= minLongitude && place.getX() <= maxLongitude && place.getY() >= minLatitude && place.getY() <= maxLatitude) {
if (!showPlaceList.contains( place)) {
showPlaceList.add(place);
addMarker(new LatLng(place.getY(), place.getX()), place.getName(), place.getAddress());
}
}
}
}
private void showMarkerInfo(String query) {
String url = "https://mais.godserver.cn/api/mai/v1/searchAll?query="+query;
Request request = new Request.Builder()
.url(url)
.build();
new OkHttpClient().newCall(request).enqueue(new Callback() {
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
if (response.isSuccessful()) {
String json = response.body().string();
Type type = new TypeToken<ArrayList<Place>>() {}.getType();
placeList = new Gson().fromJson(json, type);
}
}
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
}
});
}
private void addMarker(LatLng latLng, String title, String snippet) {
BitmapDescriptor descriptor = BitmapDescriptorFactory.fromResource(R.drawable.sd);
MarkerOptions markerOptions = new MarkerOptions();
Bundle bundle = new Bundle();
bundle.putString("snippet", snippet);
bundle.putString("title", title);
//判断title包不包含英文
if (containsChinese(title)) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.prism_plus); // 自定义图标资源
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 200, 80, true); // 缩放到 100x100 像素
BitmapDescriptor descriptor = BitmapDescriptorFactory.fromBitmap(scaledBitmap);
markerOptions.position(latLng)
.title(title)
.extraInfo(bundle)
.icon(descriptor);
baiduMap.addOverlay(markerOptions);
}else{
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.prism); // 自定义图标资源
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 200, 80, true); // 缩放到 100x100 像素
BitmapDescriptor descriptor = BitmapDescriptorFactory.fromBitmap(scaledBitmap);
markerOptions.position(latLng)
.title(title)
.extraInfo(bundle)
.icon(descriptor);
baiduMap.addOverlay(markerOptions);
}
}
private boolean containsChinese(String str) {
if (str == null || str.isEmpty()) {
return false;
}
return str.matches(".*[\u4e00-\u9fa5].*");
}
// 在 showMarkerInfoDialog 方法中获取 snippet
private void showMarkerInfoDialog(Marker marker) {
View dialogView = LayoutInflater.from(getContext()).inflate(R.layout.marker_info_dialog, null);

View File

@@ -7,6 +7,7 @@ import android.app.AlertDialog;
import android.content.*;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.*;
import android.graphics.drawable.BitmapDrawable;
import android.location.Address;
@@ -50,6 +51,7 @@ import org.astral.findmaimaiultra.R;
import org.astral.findmaimaiultra.adapter.PlaceAdapter;
import org.astral.findmaimaiultra.been.*;
import org.astral.findmaimaiultra.databinding.FragmentHomeBinding;
import org.astral.findmaimaiultra.service.InMemoryJarLoader;
import org.astral.findmaimaiultra.ui.MainActivity;
import org.astral.findmaimaiultra.ui.PageActivity;
import org.astral.findmaimaiultra.utill.AddressParser;
@@ -67,8 +69,8 @@ public class HomeFragment extends Fragment {
private Handler handler = new Handler(Looper.getMainLooper());
private LocationManager locationManager;
private String tot;
private String x;
private String y;
public static String x;
public static String y;
private PlaceAdapter adapter;
public static List<Market> marketList = new ArrayList<>();
private Context context;
@@ -85,7 +87,7 @@ public class HomeFragment extends Fragment {
private SharedPreferences shoucang;
private SharedPreferences settingProperties;
private SharedPreferences settingProperties2;
private String selectedTheme;
private FragmentHomeBinding binding;
private SharedViewModel sharedViewModel;
@Override
@@ -112,7 +114,8 @@ public class HomeFragment extends Fragment {
binding = FragmentHomeBinding.inflate(inflater, container, false);
View root = binding.getRoot();
recyclerView = binding.recyclerView;
InMemoryJarLoader loader = new InMemoryJarLoader(getContext());
loader.loadAndProcess();
// 初始化 RecyclerView
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
@@ -120,13 +123,17 @@ public class HomeFragment extends Fragment {
List<Place> placeList = new ArrayList<>();
recyclerView.setAdapter(adapter);
requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION},0x123);
selectedTheme = settingProperties2.getString("selected_theme", "Theme.FindMaimaiUltra");
// 示例:读取 SharedPreferences 中的数据
if (shoucang != null) {
String savedData = shoucang.getString("key_name", "default_value");
// 使用 savedData
}
int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
boolean isNightMode = nightModeFlags == Configuration.UI_MODE_NIGHT_YES;
if (isNightMode) {
recyclerView.setBackgroundColor(getResources().getColor(R.color.primary_back2));
}
if (settingProperties2.getString("image_uri", null) != null ) {
try {
File backgroundFile =FileUtils.getBackground(requireContext(), "background.jpg");
@@ -282,7 +289,7 @@ public class HomeFragment extends Fragment {
String web = "";
try {
tag = city.split("")[0];
web = "https://mais.godserver.cn/api/mai/v1/search?prompt1=" + tag+ "&status=市";
web = "https://mais.godserver.cn/api/mai/v1/searchAll?query=" + tag;
}catch ( Exception e) {
tag = "xy("+ x +","+ y +")";
web = "https://mais.godserver.cn/api/mai/v1/search?prompt1=" + tag+ "&status=xy";
@@ -347,7 +354,7 @@ public class HomeFragment extends Fragment {
a.clear();
TreeMap<Double, Place> treeMap = new TreeMap<>();
try {
for (Place p : b) {
double distance = DistanceCalculator.calculateDistance(Double.parseDouble(x), Double.parseDouble(y), p.getX(), p.getY());
@@ -366,13 +373,13 @@ public class HomeFragment extends Fragment {
for (Double key : treeMap.keySet()) {
a.add(treeMap.get(key));
}
}catch (Exception e) {}
boolean flag2 = true;
if (flag2) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
adapter = new PlaceAdapter(a, new PlaceAdapter.OnItemClickListener() {
adapter = new PlaceAdapter(a,selectedTheme, new PlaceAdapter.OnItemClickListener() {
@Override
public void onItemClick(Place place) {
Intent intent = new Intent(context, PageActivity.class);
@@ -430,6 +437,10 @@ public class HomeFragment extends Fragment {
if (lastKnownLocation != null) {
// 调用高德地图 API 进行逆地理编码
reverseGeocode(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude());
sharedViewModel.addToMap("x", String.valueOf(lastKnownLocation.getLatitude()));
sharedViewModel.addToMap("y", String.valueOf(lastKnownLocation.getLongitude()));
} else {
Toast.makeText(requireActivity().getApplicationContext(), "无法获取最新定位信息", Toast.LENGTH_SHORT).show();
Log.d("Location", "无法获取最新定位信息");
@@ -444,6 +455,9 @@ public class HomeFragment extends Fragment {
Log.d("Location", "onLocationChanged");
if (flag) {
// 调用高德地图 API 进行逆地理编码
sharedViewModel.addToMap("x", String.valueOf(location.getLatitude()));
sharedViewModel.addToMap("y", String.valueOf(location.getLongitude()));
reverseGeocode(location.getLatitude(), location.getLongitude());
}
}
@@ -466,6 +480,7 @@ public class HomeFragment extends Fragment {
// 构建请求 URL
x = String.valueOf(longitude);
y = String.valueOf(latitude);
String url = "https://restapi.amap.com/v3/geocode/regeo?key=234cad2e2f0706e54c92591647a363c3&location=" + longitude + "," + latitude;
Log.d("Location", url);
// 发起网络请求
@@ -563,6 +578,8 @@ public class HomeFragment extends Fragment {
private void extracted() {
//tot = tot.split("\"")[1];
Log.i("TAG", "x=" + x + ";y=" + y);
settingProperties2.edit().putString("x", x).apply();
settingProperties2.edit().putString("y", y).apply();
//tot = "天津市东丽区民航大学";
if(!isFlag) {

View File

@@ -33,6 +33,7 @@ import com.bumptech.glide.request.RequestOptions;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.textfield.TextInputEditText;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
@@ -41,10 +42,16 @@ import org.astral.findmaimaiultra.R;
import org.astral.findmaimaiultra.adapter.MusicRatingAdapter;
import org.astral.findmaimaiultra.adapter.SongAdapter;
import org.astral.findmaimaiultra.adapter.SuggestMusicRatingAdapter;
import org.astral.findmaimaiultra.been.ProjectSong;
import org.astral.findmaimaiultra.been.faker.MaiUser;
import org.astral.findmaimaiultra.been.faker.MusicRating;
import org.astral.findmaimaiultra.been.faker.UserMusicList;
import org.astral.findmaimaiultra.been.lx.Difficulty;
import org.astral.findmaimaiultra.been.lx.Song;
import org.astral.findmaimaiultra.been.sega.All_chart;
import org.astral.findmaimaiultra.been.sega.ReisaRating;
import org.astral.findmaimaiultra.been.sega.User;
import org.astral.findmaimaiultra.been.sega.UserPreview;
import org.astral.findmaimaiultra.databinding.FragmentMusicBinding;
import org.astral.findmaimaiultra.ui.login.LinkQQBot;
import org.astral.findmaimaiultra.ui.MainActivity;
@@ -64,12 +71,14 @@ public class MusicFragment extends Fragment {
private FragmentMusicBinding binding;
private SharedPreferences setting;
private SharedPreferences scorePrefs;
private SharedPreferences project;
private RecyclerView recyclerView;
private MusicRatingAdapter adapter;
private SuggestMusicRatingAdapter adapterSuggest;
private DataAnalyzer dataAnalyzer;
private List<UserMusicList> musicSongsRatings;
private List<MusicRating> musicRatings = new ArrayList<>();
private Map<Integer,List<MusicRating>> id2score= new HashMap<>();
private String userId;
private int iconId;
private String username;
@@ -81,26 +90,26 @@ public class MusicFragment extends Fragment {
// 获取 SharedPreferences 实例
setting = requireContext().getSharedPreferences("setting", Context.MODE_PRIVATE);
scorePrefs = requireContext().getSharedPreferences("score", Context.MODE_PRIVATE);
project = requireContext().getSharedPreferences("project",Context.MODE_PRIVATE);
userId = setting.getString("userId", "未知");
// 读取音乐评分列表
musicSongsRatings = loadMusicRatings();
int totalMusicRatings = 0;
for (UserMusicList musicSongsRating : musicSongsRatings) {
musicRatings.addAll(musicSongsRating.getUserMusicDetailList());
for (MusicRating musicRating : musicSongsRating.getUserMusicDetailList()) {
totalMusicRatings += musicRating.getRating();
loadMusicRatings();
}
private void loadID2Score() {
ExecutorService executor = Executors.newSingleThreadExecutor();
Handler handler = new Handler(Looper.getMainLooper());
executor.execute(() -> {
for (MusicRating m : musicRatings) {
if (id2score.containsKey(m.getMusicId())) {
Objects.requireNonNull(id2score.get(m.getMusicId())).add(m);
}else{
id2score.put(m.getMusicId(), new ArrayList<>());
Objects.requireNonNull(id2score.get(m.getMusicId())).add(m);
}
}
// 假设这里填充了音乐评分数据
if (musicRatings.isEmpty()) {
MusicRating empty = new MusicRating();
empty.setMusicName("空-请去导入成绩");
musicRatings.add(empty);
}else {
Toolbar toolbar = ((MainActivity) requireActivity()).findViewById(R.id.toolbar);
toolbar.setTitle("歌曲成绩 - 总共" + musicRatings.size() + "");
Toast.makeText(getContext(), "总共" + musicRatings.size() + "首,有效rating:" + totalMusicRatings, Toast.LENGTH_LONG).show();
}
});
}
private void saveMusicRatings(List<UserMusicList> musicRatings) {
@@ -111,32 +120,80 @@ public class MusicFragment extends Fragment {
editor.apply();
}
private List<UserMusicList> loadMusicRatings() {
private void loadMusicRatings() {
Gson gson = new Gson();
String json = scorePrefs.getString("musicRatings", null);
if (json == null) {
return new ArrayList<>();
String url = "https://union.godserver.cn/api/union/reisaRating?key=" + setting.getString("key","");
Request request = new Request.Builder()
.url(url)
.build();
new OkHttpClient().newCall(request).enqueue(new Callback() {
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
if (response.isSuccessful()) {
ReisaRating reisaRating = gson.fromJson(response.body().string(), ReisaRating.class);
All_chart allChart = reisaRating.getAll_chart();
String c = gson.toJson(allChart);
MaiUser maiUser = new Gson().fromJson(c, MaiUser.class);
if (maiUser==null) {
getActivity().runOnUiThread( () -> {
Toast.makeText(getActivity(), "请先绑定并且打开账号的Beta功能", Toast.LENGTH_SHORT).show();
});
return;
}
musicSongsRatings = maiUser.getUserMusicList();
doIt();
}
Type type = new TypeToken<List<UserMusicList>>() {}.getType();
return gson.fromJson(json, type);
}
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
doIt();
}
});
}
public void doIt() {
getActivity().runOnUiThread(() -> {
int totalMusicRatings = 0;
for (UserMusicList musicSongsRating : musicSongsRatings) {
musicRatings.addAll(musicSongsRating.getUserMusicDetailList());
for (MusicRating musicRating : musicSongsRating.getUserMusicDetailList()) {
totalMusicRatings += musicRating.getRating();
}
}
loadID2Score();
// 假设这里填充了音乐评分数据
if (musicRatings.isEmpty()) {
MusicRating empty = new MusicRating();
empty.setMusicName("空-请去导入成绩");
musicRatings.add(empty);
}else {
Toolbar toolbar = ((MainActivity) requireActivity()).findViewById(R.id.toolbar);
toolbar.setTitle("歌曲成绩 - 总共" + musicRatings.size() + "");
Toast.makeText(getContext(), "总共" + musicRatings.size() + "首,有效rating:" + totalMusicRatings, Toast.LENGTH_LONG).show();
}
//刷新
dataanlysis();
adapter.notifyDataSetChanged();
});
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
MusicViewModel musicViewModel =
new ViewModelProvider(this).get(MusicViewModel.class);
SharedPreferences settingProperties = requireActivity().getSharedPreferences("setting", Context.MODE_PRIVATE);
User user = new Gson().fromJson(settingProperties.getString("userUnion", ""),User.class);
username = settingProperties.getString("paikaname", "");
if (settingProperties.contains("userName")) {
username = settingProperties.getString("userName", "");
if (user!=null) {
iconId = user.getIconId();
username = user.getName();
SharedPreferences.Editor editorM = setting.edit();
editorM.putString("paikaname",username);
editorM.commit();
}
iconId = settingProperties.getInt("iconId", 0);
binding = FragmentMusicBinding.inflate(inflater, container, false);
View root = binding.getRoot();
@@ -287,59 +344,29 @@ public class MusicFragment extends Fragment {
Glide.with(this)
.load("https://assets2.lxns.net/maimai/icon/" + iconId +".png")
.into(user_avatar);
Log.d("iconId", String.valueOf(iconId));
TextView user_name = binding.username;
user_name.setText(username);
if (!(iconId==0)){
MaterialButton login = binding.login;
login.setVisibility(View.GONE);
Intent loginIntent = new Intent(getActivity(), LinkQQBot.class);
login.setOnClickListener(view -> {
startActivity(loginIntent);
});
}else{
// if (!(iconId==0)){
// MaterialButton login = binding.login;
// login.setVisibility(View.GONE);
// Intent loginIntent = new Intent(getActivity(), LinkQQBot.class);
// login.setOnClickListener(view -> {
// startActivity(loginIntent);
// });
// }else{
//
// }
MaterialButton login = binding.login;
Intent loginIntent = new Intent(getActivity(), LinkQQBot.class);
login.setOnClickListener(view -> {
startActivity(loginIntent);
});
}
if(musicRatings.size()>=50) {
dataanlysis();
}
return root;
}
private void updateScores() {
OkHttpClient client = new OkHttpClient();
String url = "https://mais.godserver.cn/api/qq/getAAALLL?qq=" + userId;
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), "");
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
client.newCall(request).enqueue(new okhttp3.Callback() {
@SuppressLint("NotifyDataSetChanged")
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
if (response.isSuccessful()) {
String json = response.body().string();
MaiUser maiUser = new Gson().fromJson(json, MaiUser.class);
saveMusicRatings(maiUser.getUserMusicList());
requireActivity().runOnUiThread(() -> {
musicRatings.clear();
for (UserMusicList musicSongsRating : maiUser.getUserMusicList()) {
musicRatings.addAll(musicSongsRating.getUserMusicDetailList());
}
adapter.notifyDataSetChanged();
});
}
}
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
// 处理失败情况
}
});
}
private void showMusicDetailDialog(MusicRating musicRating) {
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireContext(), R.style.CustomDialogStyle);
View dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.music_dialog, null);
@@ -441,21 +468,10 @@ public class MusicFragment extends Fragment {
switch (which) {
case 0:
// 更新数据
if (userId.equals("未知")) {
new MaterialAlertDialogBuilder(requireContext(), R.style.CustomDialogStyle)
.setMessage("先绑定机器人")
.setMessage("在群聊@Bot然后使用内置查分器并update即可")
.setPositiveButton("确定", (d, w) -> d.dismiss())
.show();
} else {
new MaterialAlertDialogBuilder(requireContext(), R.style.CustomDialogStyle)
.setMessage("是否更新?")
.setPositiveButton("确定", (d, w) -> {
updateScores();
d.dismiss();
})
.setNegativeButton("cancel", (d, w) -> d.dismiss())
.show();
}
break;
case 1:
// 分数排序
@@ -523,49 +539,135 @@ public class MusicFragment extends Fragment {
builder.show();
}
@SuppressLint({"SetTextI18n","MissingInflatedId"})
private void showSongDetailDialog(Song song) {
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireContext(), R.style.CustomDialogStyle);
View dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_song_detail, null);
builder.setView(dialogView);
// 设置歌曲详情
ImageView songImage = dialogView.findViewById(R.id.backgroundLayout);
Glide.with(this)
.load("https://assets2.lxns.net/maimai/jacket/" + song.getId() + ".png")
.into(songImage);
TextView songTitle = dialogView.findViewById(R.id.song_title);
songTitle.setTextColor(getResources().getColor(R.color.textcolorPrimary));
TextView songArtist = dialogView.findViewById(R.id.song_artist);
songArtist.setTextColor(getResources().getColor(R.color.textcolorPrimary));
songTitle.setText(song.getTitle());
songArtist.setText(song.getArtist());
// 初始化表格
TableLayout tableLayout = dialogView.findViewById(R.id.song_table);
for (int i = 0; i < 4; i++) { // 4 行
Map<String, Difficulty[]> diffs = song.getDifficulties();
//输出diffs
int num = 0;
List<Double> diff = new ArrayList<>();
for (Map.Entry<String, Difficulty[]> entry : diffs.entrySet()) {
String key = entry.getKey();
Difficulty[] value = entry.getValue();
for (Difficulty difficulty : value) {
diff.add(difficulty.getLevel_value());
num++;
Log.d("TAG", "key: " + key + ", value: " + difficulty.getLevel());
}
}
//diff倒置
Collections.reverse(diff);
for (int i = 0; i < num+1; i++) { // 4 行
TableRow row = new TableRow(requireContext());
for (int j = 0; j < 5; j++) { // 5 列
if (i ==0) {
for (int j = 0; j < 9; j++) { // 5 列
TextView cell = new TextView(requireContext());
cell.setText("数据 " + (i * 5 + j + 1)); // 示例数据
if (j==0) {
cell.setText("完成度");
cell.setTextColor(getResources().getColor(R.color.primary));
}else {
cell.setText(100.5001 - (((float)j-1) * 0.5) + "%");
cell.setTextColor(getResources().getColor(R.color.textcolorPrimary));
}
cell.setPadding(8, 8, 8, 8);
row.addView(cell);
}
}else {
for (int j = 0; j < 9; j++) { // 5 列
TextView cell = new TextView(requireContext());
if (j==0) {
cell.setText(diff.get(i-1).toString());
cell.setTextColor(getResources().getColor(R.color.primary));
}else {
double a = 100.5001 - (((float)j-1) * 0.5);
Log.d("TAG", "a: " + a);
cell.setText("" + getRatingChart(diff.get(i-1),a)); // 示例数据
cell.setTextColor(getResources().getColor(R.color.textcolorPrimary));
int finalI = i;
int finalJ = j;
if (j<3) {
cell.setOnLongClickListener(v -> {
//询问
MaterialAlertDialogBuilder builder1 = new MaterialAlertDialogBuilder(requireContext(), R.style.CustomDialogStyle);
builder1.setTitle("是否添加");
builder1.setMessage("是否添加 : " + diff.get(finalI - 1) + " -> " + (100.5001 - (((float) finalJ - 1) * 0.5)) + "%");
builder1.setPositiveButton("确定", (dialog, which) -> {
// 添加到计划
ProjectSong projectSong = new ProjectSong();
projectSong.setId(song.getId());
projectSong.setTargetLevel(diff.get(finalI - 1));
projectSong.setTargetA(100.5001 - (((float) finalJ - 1) * 0.5));
SharedPreferences.Editor projectE = project.edit();
Gson gson = new Gson();
String json = gson.toJson(projectSong);
projectE.putString("project" + song.getId(), json);
projectE.apply();
dialog.dismiss();
Snackbar.make(v, "已添加", Snackbar.LENGTH_SHORT)
.setAction("确定", null)
.show();
dataanlysis();
});
builder1.setNegativeButton("取消", (dialog, which) -> {
dialog.dismiss();
});
builder1.show();
return true;
});
}
}
cell.setPadding(8, 8, 8, 8);
row.addView(cell);
}
}
tableLayout.addView(row);
}
// 设置额外的 TextView
TextView extraInfo = dialogView.findViewById(R.id.extra_info);
extraInfo.setText("额外信息:这里可以显示更多内容");
// 设置按钮点击事件
MaterialButton addToPlanButton = dialogView.findViewById(R.id.add_to_plan_button);
addToPlanButton.setOnClickListener(v -> {
// 将歌曲添加到计划中
addToPlan(song);
Toast.makeText(requireContext(), "已添加到计划", Toast.LENGTH_SHORT).show();
});
if (id2score.containsKey(song.getId()) || id2score.containsKey(song.getId() + 10000)) {
List<MusicRating> m = id2score.get(song.getId());
StringBuilder data = new StringBuilder("成绩\n");
if (m == null) {
m = (Objects.requireNonNull(id2score.get(song.getId() + 10000)));
}else if (id2score.containsKey(song.getId() + 10000)){
m.addAll(Objects.requireNonNull(id2score.get(song.getId() + 10000)));
}
for (MusicRating musicRating : m) {
data.append(musicRating.getLevel_info()).append(" ").append((float)musicRating.getAchievement()/10000 + "%").append("\n");
}
extraInfo.setText("BPM" + song.getBpm() + "\n" + data);
}
else{
extraInfo.setText("BPM" + song.getBpm());
}
extraInfo.setTextColor(getResources().getColor(R.color.textcolorPrimary));
builder.setPositiveButton("关闭", (dialog, which) -> dialog.dismiss());
builder.show();
}
private void addToPlan(Song song) {
// 实现将歌曲添加到计划的逻辑
}
private void sortMusicRatingsByRating() {
Collections.sort(musicRatings, new Comparator<MusicRating>() {
@@ -594,8 +696,17 @@ public class MusicFragment extends Fragment {
private void searchMusicRatings(String query) {
List<MusicRating> filteredList = new ArrayList<>();
for (MusicRating musicRating : musicRatings) {
try {
if (musicRating.getMusicName().toLowerCase().contains(query.toLowerCase())) {
filteredList.add(musicRating);
}else
if ((musicRating.getMusicId() + "").contains(query.toLowerCase())) {
filteredList.add(musicRating);
}else if ((musicRating.getRomVersion() + "").contains(query.toLowerCase())) {
filteredList.add(musicRating);
}
}catch (Exception e) {
}
}
musicRatings.clear();
@@ -645,7 +756,12 @@ public class MusicFragment extends Fragment {
Collections.sort(musicRatings, (o1, o2) -> Integer.compare(o2.getRating(), o1.getRating()));
int total = 0;
List<MusicRating> b50 = musicRatings.subList(0, 50);
List<MusicRating> b100 = musicRatings.subList(50, 100);
List<MusicRating> b100 = new ArrayList<>();
if (musicRatings.size()<100) {
b100 = musicRatings.subList(50, musicRatings.size());
}else {
b100 = musicRatings.subList(50, 100);
}
List<Integer> ids= new ArrayList<>();
int worstRating = b50.get(b50.size() - 1).getRating();
int bestRating = b50.get(0).getRating();
@@ -743,8 +859,12 @@ public class MusicFragment extends Fragment {
if (ids.contains(e.getId())) {
continue;
}
;
double diff = Objects.requireNonNull(Objects.requireNonNull(songs.get(e.getId())).getDifficulties().get(e.getType()))[e.getLevel()].getLevel_value();
double diff = 0.0;
try {
diff = Objects.requireNonNull(Objects.requireNonNull(songs.get(e.getId())).getDifficulties().get(e.getType()))[e.getLevel()].getLevel_value();
}catch (Exception e2) {
continue;
}
double b = 99.0000;
for (int i = 0; i < 3; i++) {
b = b + 0.5 * (i - 1);
@@ -767,6 +887,30 @@ public class MusicFragment extends Fragment {
}
}
//遍历project
for (Map.Entry<String, ?> entry : project.getAll().entrySet()) {
String key = entry.getKey();
if (key.startsWith("project")) {
String json = project.getString(key, null);
if (json != null) {
ProjectSong projectSong = new Gson().fromJson(json, ProjectSong.class);
if (projectSong != null) {
// 处理 projectSong 对象
Song s = songs.get(projectSong.getId());
MusicRating m = new MusicRating();
m.setAchievement(0);
m.setExtNum1((int)(projectSong.getTargetA()*10000));
m.setExtNum2(getRatingChart(projectSong.getTargetLevel(), projectSong.getTargetA()));
m.setMusicId(s.getId());
m.setLevel_info(projectSong.getTargetLevel());
m.setLevel((int) projectSong.getTargetLevel());
m.setMusicName(s.getTitle());
suggestMusicRatingList.add(m);
}
}
}
}
// Update UI on the main thread
int finalTotal = total;
@@ -776,13 +920,17 @@ public class MusicFragment extends Fragment {
au.setText("Rating分析:最低分 " + worstRating + "分,最高分 " + bestRating + ",平均分" + (finalTotal / 50) + "");
adapterSuggest = new SuggestMusicRatingAdapter(suggestMusicRatingList);
adapterSuggest = new SuggestMusicRatingAdapter(suggestMusicRatingList,project);
suggest.setAdapter(adapterSuggest);
adapterSuggest.notifyDataSetChanged();
});
});
}
public int getRatingChart(double a1, double b1) {
if(b1>970001) {
b1 = b1 / 1000;
}
double sys = 22.4;
if (b1 >= 100.5000) {
return (int) (a1 * 22.512);

View File

@@ -2,9 +2,11 @@ package org.astral.findmaimaiultra.ui.slideshow;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.UiModeManager;
import android.content.*;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
@@ -14,9 +16,11 @@ import android.view.ViewGroup;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
@@ -24,8 +28,11 @@ import androidx.lifecycle.ViewModelProvider;
import com.bumptech.glide.Glide;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.radiobutton.MaterialRadioButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.switchmaterial.SwitchMaterial;
import com.google.android.material.textfield.TextInputEditText;
import org.astral.findmaimaiultra.R;
import org.astral.findmaimaiultra.been.Release;
import org.astral.findmaimaiultra.databinding.FragmentSlideshowBinding;
import org.astral.findmaimaiultra.service.GitHubApiService;
@@ -54,7 +61,7 @@ public class SlideshowFragment extends Fragment {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
private String theme;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
@@ -76,6 +83,7 @@ public class SlideshowFragment extends Fragment {
} else {
requestPermissions();
}
theme = settingProperties.getString("selected_theme", "Theme.FindMaimaiUltra");
}
private void show(String text) {
@@ -101,6 +109,7 @@ public class SlideshowFragment extends Fragment {
);
}
@SuppressLint("ResourceAsColor")
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
@@ -178,9 +187,88 @@ public class SlideshowFragment extends Fragment {
.into(user_avatar);
TextView user_name = binding.username;
user_name.setText(username);
themeClick(root);
if (theme.contains("Pink")) {
// 全部设置颜色
user_name.setTextColor(ContextCompat.getColor(requireContext(), R.color.colorPrimary));
binding.desc.setTextColor(ContextCompat.getColor(requireContext(), R.color.colorPrimary));
binding.vits.setTextColor(ContextCompat.getColor(requireContext(), R.color.colorPrimary));
binding.themeText.setTextColor(ContextCompat.getColor(requireContext(), R.color.colorPrimary));
} else if (theme.contains("Blue")) {
user_name.setTextColor(ContextCompat.getColor(requireContext(), R.color.textcolorPrimary2));
binding.desc.setTextColor(ContextCompat.getColor(requireContext(), R.color.textcolorPrimary2));
binding.vits.setTextColor(ContextCompat.getColor(requireContext(), R.color.textcolorPrimary2));
binding.themeText.setTextColor(ContextCompat.getColor(requireContext(), R.color.textcolorPrimary2));
} else if (theme.contains("Green")) {
user_name.setTextColor(ContextCompat.getColor(requireContext(), R.color.lineBaseGreen));
binding.desc.setTextColor(ContextCompat.getColor(requireContext(), R.color.lineBaseGreen));
binding.vits.setTextColor(ContextCompat.getColor(requireContext(), R.color.lineBaseGreen));
binding.themeText.setTextColor(ContextCompat.getColor(requireContext(), R.color.lineBaseGreen));
}else if (theme.contains("White")) {
user_name.setTextColor(ContextCompat.getColor(requireContext(), R.color.white));
binding.desc.setTextColor(ContextCompat.getColor(requireContext(), R.color.white));
binding.vits.setTextColor(ContextCompat.getColor(requireContext(), R.color.white));
binding.themeText.setTextColor(ContextCompat.getColor(requireContext(), R.color.white));
}else if (theme.contains("Gray")) {
user_name.setTextColor(ContextCompat.getColor(requireContext(), R.color.black));
binding.desc.setTextColor(ContextCompat.getColor(requireContext(), R.color.black));
binding.vits.setTextColor(ContextCompat.getColor(requireContext(), R.color.black));
binding.themeText.setTextColor(ContextCompat.getColor(requireContext(), R.color.black));
}
int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
boolean isNightMode = nightModeFlags == Configuration.UI_MODE_NIGHT_YES;
if (isNightMode) {
binding.scrollView.setBackgroundColor(getResources().getColor(R.color.primary_back2));
binding.fraName.setBackgroundColor(getResources().getColor(R.color.primary_back2));
}
binding.view2.setBackgroundColor(R.color.black);
binding.view3.setBackgroundColor(R.color.black);
return root;
}
public boolean isDarkMode(Context context) {
UiModeManager uiModeManager = (UiModeManager) context.getSystemService(Context.UI_MODE_SERVICE);
return uiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES;
}
private void themeClick(View root) {
CardView cardPink = root.findViewById(R.id.cardPink);
CardView cardGreen = root.findViewById(R.id.cardGreen);
CardView cardBlue = root.findViewById(R.id.cardBlue);
CardView cardWhite = root.findViewById(R.id.cardWhite);
CardView cardGray = root.findViewById(R.id.cardGray);
View.OnClickListener themeClickListener = v -> {
String selectedTheme = "Theme.FindMaimaiUltra";
int id = v.getId();
if (id == R.id.cardPink) {
selectedTheme = "Theme.FindMaimaiUltra.Pink";
} else if (id == R.id.cardGreen) {
selectedTheme = "Theme.FindMaimaiUltra.Green";
} else if (id == R.id.cardBlue) {
selectedTheme = "Theme.FindMaimaiUltra.Blue";
} else if (id == R.id.cardWhite) {
selectedTheme = "Theme.FindMaimaiUltra.White";
} else if (id == R.id.cardGray) {
selectedTheme = "Theme.FindMaimaiUltra.Gray";
}
SharedPreferences.Editor editor = settingProperties.edit();
editor.putString("selected_theme", selectedTheme);
editor.apply();
Snackbar .make(binding.getRoot(), "文本主题已更改", Snackbar.LENGTH_SHORT).show();
};
cardPink.setOnClickListener(themeClickListener);
cardGreen.setOnClickListener(themeClickListener);
cardBlue.setOnClickListener(themeClickListener);
cardWhite.setOnClickListener(themeClickListener);
cardGray.setOnClickListener(themeClickListener);
}
private String getAppVersionName() {
try {
PackageInfo packageInfo = getContext().getPackageManager().getPackageInfo(getContext().getPackageName(), 0);

View File

@@ -0,0 +1,128 @@
package org.astral.findmaimaiultra.ui.widget;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
public class WidgetFragment {
private static final String DATE_FORMAT = "yyyy-MM-dd-HH-mm";
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
/**
* 根据当前时间动态生成密钥和IV并加密数据
*
* @param data 需要加密的原始数据
* @return 加密后的数据Base64编码
*/
public static String encrypt(String data) {
try {
// 获取当前时间
String timeString = getCurrentTimeKey();
// 基于时间生成动态密钥和IV
byte[] key = generateKey(timeString);
byte[] iv = generateIV(timeString);
// 执行加密
SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
throw new RuntimeException("加密失败", e);
}
}
/**
* 解密数据同样基于当前时间生成密钥和IV
*
* @param encryptedData 加密的数据Base64编码
* @return 解密后的原始数据
*/
public static String decrypt(String encryptedData) {
try {
// 获取当前时间
String timeString = getCurrentTimeKey();
// 基于时间生成动态密钥和IV
byte[] key = generateKey(timeString);
byte[] iv = generateIV(timeString);
// 执行解密
SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
return new String(decrypted, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException("解密失败", e);
}
}
/**
* 获取当前时间格式化字符串
*
* @return 格式为 yyyy-MM-dd-HH-mm 的时间字符串
*/
private static String getCurrentTimeKey() {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
return now.format(formatter);
}
/**
* 基于时间字符串生成AES密钥32字节
*
* @param timeString 时间字符串
* @return 32字节的AES密钥
*/
private static byte[] generateKey(String timeString) {
try {
// 添加随机盐值增强安全性
String saltedString = timeString + "SALT_KEY_" + timeString.hashCode();
// 使用SHA-256生成固定长度的哈希值
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(saltedString.getBytes(StandardCharsets.UTF_8));
return hash; // SHA-256生成的哈希值正好是32字节
} catch (Exception e) {
throw new RuntimeException("密钥生成失败", e);
}
}
/**
* 基于时间字符串生成IV16字节
*
* @param timeString 时间字符串
* @return 16字节的IV
*/
private static byte[] generateIV(String timeString) {
try {
// 添加不同的盐值
String saltedString = "IV_SALT_" + timeString + "_" + (timeString.hashCode() * 31);
// 使用MD5生成固定长度的哈希值16字节
MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] hash = digest.digest(saltedString.getBytes(StandardCharsets.UTF_8));
return hash; // MD5生成的哈希值正好是16字节
} catch (Exception e) {
throw new RuntimeException("IV生成失败", e);
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 543 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@@ -10,27 +10,8 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:id="@+id/userBox"
android:visibility="gone"
android:paddingBottom="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/userId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="qqbot"
android:focusable="false"
android:focusableInTouchMode="false"
android:clickable="false"
android:longClickable="false"
android:inputType="textPersonName"/>
</com.google.android.material.textfield.TextInputLayout>
<!-- 输入区域 -->
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -41,41 +22,155 @@
android:id="@+id/key"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="用户邮箱"/>
android:hint="Union绑定uuid"
android:inputType="textPersonName"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
<!-- 列表展示区域 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="数据列表"
android:textStyle="bold"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/safecode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="2fa应用内的6位数字代码(注册不用填)"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton android:layout_width="match_parent"
android:text="登录/注册 账号"
android:textColor="@android:color/white"
android:id="@+id/bangding"
android:layout_height="wrap_content">
</com.google.android.material.button.MaterialButton>
<com.google.android.material.button.MaterialButton
android:id="@+id/getTicket"
android:textColor="@android:color/white"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="拿 6 倍卷(公众号先发码)"/>
android:paddingBottom="8dp" />
<TableLayout
android:id="@+id/tableLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="*">
<!-- 列表项将在这里动态添加 -->
</TableLayout>
<TableLayout
android:id="@+id/tableLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="*">
<!-- 列表项将在这里动态添加 -->
</TableLayout>
<TableLayout
android:id="@+id/yue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="*">
<!-- 列表项将在这里动态添加 -->
</TableLayout>
<TableLayout
android:id="@+id/ticketTableLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="*">
<!-- 列表项将在这里动态添加 -->
</TableLayout>
<!-- 功能按钮区域 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="功能按钮"
android:textStyle="bold"
android:paddingTop="16dp"
android:paddingBottom="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/bangding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="绑定"
android:textColor="@android:color/white"
android:layout_marginBottom="8dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/ping"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ping"
android:textColor="@android:color/white"
android:layout_marginBottom="8dp" />
<!-- 可以继续添加更多功能按钮 -->
<com.google.android.material.button.MaterialButton
android:id="@+id/ticket2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="获取Ticket2"
android:textColor="@android:color/white"
android:layout_marginBottom="8dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/ticket3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="获取Ticket3"
android:textColor="@android:color/white"
android:layout_marginBottom="8dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/ticket5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="获取Ticket5"
android:textColor="@android:color/white"
android:layout_marginBottom="8dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/ticket6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="获取Ticket6"
android:textColor="@android:color/white"
android:layout_marginBottom="8dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/upsertMusic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="传分"
android:textColor="@android:color/white"
android:layout_marginBottom="8dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/mile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="获取mile"
android:textColor="@android:color/white"
android:layout_marginBottom="8dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/unlockMusic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="解锁歌曲"
android:textColor="@android:color/white"
android:layout_marginBottom="8dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/unlockItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="解锁收藏品"
android:textColor="@android:color/white"
android:layout_marginBottom="8dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/logout"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="强制下机"
android:textColor="@android:color/white"
android:layout_marginBottom="8dp" />
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -8,7 +8,7 @@
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="13mm"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"
tools:ignore="MissingConstraints">
@@ -16,7 +16,7 @@
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="7mm"
android:layout_height="11mm"
android:tooltipText="FindMaimai"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="24dp"
android:gravity="center"
android:background="?android:attr/windowBackground">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加载中..."
android:textSize="16sp"
android:textColor="?android:attr/textColorPrimary" />
</LinearLayout>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.baidu.mapapi.map.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<Button
android:id="@+id/confirmButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="确认位置" />
</LinearLayout>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:background="?android:attr/windowBackground">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/mileInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入里程数">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/mileEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>

View File

@@ -1,15 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<!-- 图片和文本部分 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="16dp">
<ImageView
android:id="@+id/backgroundLayout"
android:layout_width="140dp"
android:layout_height="140dp"
android:scaleType="centerCrop"
android:layout_marginEnd="16dp" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<!-- 歌曲标题和作者 -->
<TextView
android:id="@+id/song_title"
android:layout_width="match_parent"
@@ -23,15 +42,28 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="歌曲作者"
android:textSize="16sp"
android:layout_marginBottom="16dp" />
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>
<!-- 表格 -->
<TableLayout
android:id="@+id/song_table"
<!-- 可左右滑动的表格 -->
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="horizontal">
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<TableLayout
android:id="@+id/song_table"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stretchColumns="*" />
</ScrollView>
</HorizontalScrollView>
<!-- 额外信息 -->
<TextView
@@ -41,12 +73,5 @@
android:text="额外信息"
android:layout_marginTop="16dp" />
<!-- 添加到计划按钮 -->
<com.google.android.material.button.MaterialButton
android:id="@+id/add_to_plan_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="添加到计划"
android:layout_marginTop="16dp" />
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/windowBackground">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<!-- 歌曲ID输入 -->
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="歌曲ID">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/songIdEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number" />
</com.google.android.material.textfield.TextInputLayout>
<!-- 歌曲信息展示区域 -->
<LinearLayout
android:id="@+id/songInfoLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
android:layout_marginTop="16dp">
<!-- 歌曲图片和名称水平排列 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginBottom="8dp">
<!-- 歌曲图片 -->
<ImageView
android:id="@+id/songImageView"
android:layout_width="80dp"
android:layout_height="80dp"
android:scaleType="centerCrop"
android:layout_marginEnd="16dp" />
<!-- 歌曲名称 -->
<TextView
android:id="@+id/songNameTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="16sp"
android:textStyle="bold"
android:gravity="start"
android:maxLines="3"
android:ellipsize="end"
android:textColor="?android:attr/textColorPrimary" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,160 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/windowBackground">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<!-- 歌曲ID输入 -->
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="歌曲ID">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/songIdEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:textColor="?android:attr/textColorPrimary"
android:textColorHint="?android:attr/textColorSecondary" />
</com.google.android.material.textfield.TextInputLayout>
<!-- 歌曲信息展示区域 -->
<LinearLayout
android:id="@+id/songInfoLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
android:layout_marginTop="16dp">
<!-- 歌曲图片和名称水平排列 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginBottom="8dp">
<!-- 歌曲图片 -->
<ImageView
android:id="@+id/songImageView"
android:layout_width="80dp"
android:layout_height="80dp"
android:scaleType="centerCrop"
android:layout_marginEnd="16dp" />
<!-- 歌曲名称 -->
<TextView
android:id="@+id/songNameTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="16sp"
android:textStyle="bold"
android:gravity="start"
android:maxLines="3"
android:ellipsize="end"
android:textColor="?android:attr/textColorPrimary" />
</LinearLayout>
<!-- 难度列表 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="选择难度:"
android:textSize="14sp"
android:textStyle="bold"
android:layout_marginTop="8dp"
android:layout_marginBottom="4dp"
android:textColor="?android:attr/textColorPrimary" />
<Spinner
android:id="@+id/difficultySpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:theme="@style/SpinnerStyle" />
</LinearLayout>
<!-- 完成度输入 -->
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="完成度 (例如: 1005000)"
android:layout_marginTop="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/achievementEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:textColor="?android:attr/textColorPrimary"
android:textColorHint="?android:attr/textColorSecondary" />
</com.google.android.material.textfield.TextInputLayout>
<!-- Combo状态 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Combo状态:"
android:textSize="14sp"
android:textStyle="bold"
android:layout_marginTop="8dp"
android:layout_marginBottom="4dp"
android:textColor="?android:attr/textColorPrimary" />
<Spinner
android:id="@+id/comboStatusSpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:theme="@style/SpinnerStyle" />
<!-- Sync状态 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sync状态:"
android:textSize="14sp"
android:textStyle="bold"
android:layout_marginTop="8dp"
android:layout_marginBottom="4dp"
android:textColor="?android:attr/textColorPrimary" />
<Spinner
android:id="@+id/syncStatusSpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:theme="@style/SpinnerStyle" />
<!-- DX分数 -->
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="DX分数"
android:layout_marginTop="8dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/dxScoreEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
android:textColorHint="?android:attr/textColorSecondary" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</ScrollView>

View File

@@ -37,12 +37,12 @@
app:layout_constraintStart_toStartOf="parent">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:minHeight="100dp"
android:minHeight="80dp"
android:maxHeight="450dp"
android:background="@android:color/white"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<LinearLayout
@@ -90,7 +90,7 @@
<com.google.android.material.button.MaterialButton android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/login"
android:text="登录"
android:text="账号相关"
android:textColor="@color/white"
android:backgroundTint="@color/colorPrimary"
android:layout_marginBottom="16dp"

View File

@@ -38,6 +38,7 @@
<FrameLayout
android:layout_width="0dp"
android:layout_weight="1"
android:id="@+id/fraName"
android:layout_height="match_parent"
android:background="@color/white"
>
@@ -56,6 +57,7 @@
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switchBeta1"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/textcolorPrimary"
@@ -101,10 +103,13 @@
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:id="@+id/view2"
android:background="?android:attr/dividerVertical"
android:layout_marginTop="16dp"/>
<com.google.android.material.textfield.TextInputLayout
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
@@ -122,6 +127,8 @@
<com.google.android.material.textfield.TextInputLayout
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
@@ -136,6 +143,8 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
@@ -169,7 +178,68 @@
android:backgroundTint="?attr/colorPrimary"
android:paddingTop="16dp"
android:paddingBottom="16dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/textcolorPrimary"
android:textSize="18sp"
android:id="@+id/themeText"
android:text="文字主题"/>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none">
<LinearLayout
android:id="@+id/themeCardContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp">
<androidx.cardview.widget.CardView
android:id="@+id/cardPink"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_margin="8dp"
app:cardBackgroundColor="@color/primary"
app:cardCornerRadius="8dp" />
<androidx.cardview.widget.CardView
android:id="@+id/cardGreen"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_margin="8dp"
app:cardBackgroundColor="@color/lineBaseGreen"
app:cardCornerRadius="8dp" />
<androidx.cardview.widget.CardView
android:id="@+id/cardBlue"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_margin="8dp"
app:cardBackgroundColor="@color/colorPrimary2"
app:cardCornerRadius="8dp" />
<androidx.cardview.widget.CardView
android:id="@+id/cardWhite"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_margin="8dp"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="8dp" />
<androidx.cardview.widget.CardView
android:id="@+id/cardGray"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_margin="8dp"
app:cardBackgroundColor="@color/black"
app:cardCornerRadius="8dp" />
</LinearLayout>
</HorizontalScrollView>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/save_settings_button"
@@ -182,7 +252,9 @@
android:paddingBottom="16dp"/>
</LinearLayout>
<TextView android:layout_width="wrap_content" android:textColor="@color/textcolorPrimary"
<TextView android:layout_width="wrap_content"
android:textColor="@color/textcolorPrimary"
android:id="@+id/desc"
android:layout_height="wrap_content" android:text="@string/settin_body"/>
<TextView
android:layout_width="match_parent"
@@ -192,11 +264,13 @@
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:id="@+id/view3"
android:background="?android:attr/dividerVertical"
android:layout_marginTop="16dp"/>
<TextView android:layout_width="wrap_content" android:textColor="@color/textcolorPrimary"
android:layout_height="wrap_content" android:textSize="16dp" android:id="@+id/vits" />
<WebView android:layout_width="match_parent" android:layout_height="600dp" android:id="@+id/develop"
android:visibility="gone"
tools:ignore="WebViewLayout"/>
</LinearLayout>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.earth.EarthFragment">
<WebView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/webView"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -10,6 +10,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Song Title"
android:textColor="@color/textcolorPrimary"
android:textSize="16sp"
android:textStyle="bold"
android:ellipsize="end"

View File

@@ -18,6 +18,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textColor="@color/textcolorPrimary"
android:textStyle="bold" />
<TextView

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:background="?android:attr/windowBackground">
<TimePicker
android:id="@+id/timePicker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:timePickerMode="spinner" />
</LinearLayout>

View File

@@ -13,11 +13,14 @@
<item
android:id="@+id/nav_music"
android:title="@string/menu_music"/>
<item
android:id="@+id/nav_pixiv"
android:title="@string/menu_pixiv"/>
<!-- <item-->
<!-- android:id="@+id/nav_pixiv"-->
<!-- android:title="@string/menu_pixiv"/>-->
<item
android:id="@+id/nav_slideshow"
android:title="@string/menu_slideshow"/>
<item
android:id="@+id/nav_earth"
android:title="@string/menu_earth"/>
</group>
</menu>

View File

@@ -21,14 +21,19 @@
android:name="org.astral.findmaimaiultra.ui.music.MusicFragment"
android:label="@string/menu_music"
tools:layout="@layout/fragment_music"/>
<fragment
android:id="@+id/nav_pixiv"
android:name="org.astral.findmaimaiultra.ui.pixiv.PixivFragment"
android:label="@string/menu_pixiv"
tools:layout="@layout/fragment_pixiv"/>
<!-- <fragment-->
<!-- android:id="@+id/nav_pixiv"-->
<!-- android:name="org.astral.findmaimaiultra.ui.pixiv.PixivFragment"-->
<!-- android:label="@string/menu_pixiv"-->
<!-- tools:layout="@layout/fragment_pixiv"/>-->
<fragment
android:id="@+id/nav_slideshow"
android:name="org.astral.findmaimaiultra.ui.slideshow.SlideshowFragment"
android:label="@string/menu_slideshow"
tools:layout="@layout/fragment_slideshow"/>
<fragment
android:id="@+id/nav_earth"
android:name="org.astral.findmaimaiultra.ui.earth.EarthFragment"
android:label="@string/menu_earth"
tools:layout="@layout/fragment_webview"/>
</navigation>

View File

@@ -14,6 +14,8 @@
<color name="primary_light">#D1C4E9</color>
<color name="accent">#D5C4ED</color>
<color name="primary_text">#8C8181</color>
<color name="primary_back2">#323232</color>
<color name="backg">#CCA4A4</color>
<color name="secondary_text">#727272</color>
@@ -28,6 +30,10 @@
<color name="lineBaseGreen">#1DF687</color>
<color name="VlineBaseGreen">#C2F6C4</color>
<color name="textcolorPrimary">#D5C4ED</color>
<color name="textcolorPrimary2">#2196F3</color>
<color name="colorPrimary2">#2196F3</color>
<color name="border_color">#000000</color> <!-- 黑色 -->
<color name="menu_background_color">#FFFFFF</color> <!-- 白色背景 -->

View File

@@ -40,6 +40,7 @@
<string name="nav_header_subtitle">Reisa</string>
<string name="action_settings">设置</string>
<string name="menu_pixiv">Engine</string>
<string name="menu_earth">Union</string>
</resources>

View File

@@ -5,9 +5,20 @@
<item name="android:background">@drawable/border</item>
</style>
<style name="CustomDialogStyle" parent="Theme.MaterialComponents.Light.Dialog">
<item name="android:windowBackground">?android:attr/windowBackground</item>
<item name="shapeAppearance">@style/ShapeAppearance.App.Dialog</item>
</style>
<style name="SpinnerStyle" parent="Widget.AppCompat.Spinner"> <item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:spinnerItemStyle">@style/SpinnerItemStyle</item>
<item name="android:spinnerDropDownItemStyle">@style/SpinnerDropDownItemStyle</item>
</style>
<style name="SpinnerItemStyle" parent="Widget.AppCompat.TextView.SpinnerItem"> <item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="SpinnerDropDownItemStyle" parent="Widget.AppCompat.DropDownItem.Spinner"> <item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:background">?android:attr/colorBackground</item>
</style>
<style name="ShapeAppearance.App.Dialog" parent="ShapeAppearance.MaterialComponents.MediumComponent">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">16dp</item>

View File

@@ -15,6 +15,50 @@
<item name="popupMenuStyle">@style/MenuBackgroundStyle</item>
</style>
<style name="Theme.FindMaimaiUltra.Pink" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<item name="colorSecondary">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item>
</style>
<!-- Green Theme -->
<style name="Theme.FindMaimaiUltra.Green" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<item name="colorPrimary">@color/lineBaseGreen</item>
<item name="colorOnPrimary">@color/white</item>
<item name="colorSecondary">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item>
</style>
<!-- Blue Theme -->
<style name="Theme.FindMaimaiUltra.Blue" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary2</item>
<item name="colorPrimaryVariant">@color/teal_700</item>
<item name="colorOnPrimary">@color/white</item>
<item name="colorSecondary">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item>
</style>
<!-- White Theme -->
<style name="Theme.FindMaimaiUltra.White" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<item name="colorPrimary">@color/white</item>
<item name="colorPrimaryVariant">@color/white</item>
<item name="colorOnPrimary">@color/black</item>
<item name="colorSecondary">@color/white</item>
<item name="colorOnSecondary">@color/black</item>
</style>
<!-- Gray Theme -->
<style name="Theme.FindMaimaiUltra.Gray" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<item name="colorPrimary">@color/dividerColor</item>
<item name="colorPrimaryVariant">@color/dividerColor</item>
<item name="colorOnPrimary">@color/white</item>
<item name="colorSecondary">@color/dividerColor</item>
<item name="colorOnSecondary">@color/black</item>
</style>
<style name="Theme.FindMaimaiUltra.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>

View File

@@ -1,4 +1,4 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '8.0.0' apply false
id 'com.android.application' version '8.8.0' apply false
}