commit 37f69aa2445cac3498070b061a06db6e50b29a05 Author: nyaco Date: Wed Jun 3 19:09:28 2026 +0800 重新建库,开源大部分代码,脱敏处理 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c65388 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties +/app/release/ +.idea +/.idea/ +/app/src/main/jniLibs/arm64-v8a/libJniQrCodeSign.so diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..2593569 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,63 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace = "cn.nahco3awa.naouc" + compileSdk = 36 + + defaultConfig { + applicationId = "cn.nahco3awa.naouc" + minSdk = 26 + targetSdk = 36 + versionCode = 1 + versionName = "0.1.5" + + ndk { + abiFilters.add("arm64-v8a") + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + buildFeatures { + viewBinding = true + } + + sourceSets { + getByName("main") { + jniLibs.srcDirs("src/main/jniLibs") + } + } +} + + + +dependencies { + implementation(libs.gson) + implementation(libs.okhttp) + implementation(libs.appcompat) + implementation(libs.material) + implementation(libs.constraintlayout) + implementation(libs.lifecycle.livedata.ktx) + implementation(libs.lifecycle.viewmodel.ktx) + implementation(libs.navigation.fragment) + implementation(libs.navigation.ui) + implementation(libs.activity) + implementation(libs.zxing.android.embedded) + implementation(libs.recyclerview) + testImplementation(libs.junit) + androidTestImplementation(libs.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +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 + +# 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 *; +#} + +# 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 \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8feb6f5 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/cn/nahco3awa/naouc/MainActivity.java b/app/src/main/java/cn/nahco3awa/naouc/MainActivity.java new file mode 100644 index 0000000..0f6a818 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/MainActivity.java @@ -0,0 +1,43 @@ +package cn.nahco3awa.naouc; + +import static android.widget.Toast.LENGTH_SHORT; + +import android.os.Bundle; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; +import androidx.navigation.ui.AppBarConfiguration; +import androidx.navigation.ui.NavigationUI; + +import com.google.android.material.bottomnavigation.BottomNavigationView; + +import cn.nahco3awa.naouc.databinding.ActivityMainBinding; + +public class MainActivity extends AppCompatActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + MyCrashHandler crashHandler = MyCrashHandler.newInstance(); + crashHandler.init(this); + Thread.setDefaultUncaughtExceptionHandler(crashHandler); + + ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + BottomNavigationView navView = findViewById(R.id.nav_view); + AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder( + R.id.navigation_ouc, R.id.navigation_weouc) + .build(); + NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main); + NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); + NavigationUI.setupWithNavController(binding.navView, navController); + } + + private void sorryNo() { + Toast.makeText(this, "× 抱歉,不行。", LENGTH_SHORT).show(); + finish(); + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/nahco3awa/naouc/MyCrashHandler.java b/app/src/main/java/cn/nahco3awa/naouc/MyCrashHandler.java new file mode 100644 index 0000000..6157a3c --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/MyCrashHandler.java @@ -0,0 +1,93 @@ +package cn.nahco3awa.naouc; + +import static android.widget.Toast.LENGTH_LONG; + +import android.content.Context; +import android.os.Environment; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; + +public class MyCrashHandler implements Thread.UncaughtExceptionHandler { + private Thread.UncaughtExceptionHandler mDefaultHandler; + private Context mcontext; + private static MyCrashHandler myCrashHandler; + + private MyCrashHandler() {} + + public static synchronized MyCrashHandler newInstance() { + if (myCrashHandler == null) + myCrashHandler = new MyCrashHandler(); + return myCrashHandler; + } + + public void init(Context context) { + mcontext = context; + mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); + Thread.setDefaultUncaughtExceptionHandler(this); + } + + @Override + public void uncaughtException(@NonNull Thread t, Throwable e) { + new AlertDialog.Builder(mcontext) + .setTitle("未捕获的异常!") + .setMessage(e.getMessage()) + .show(); + Toast.makeText(mcontext, e.getMessage(), LENGTH_LONG).show(); + if (!handleExample(e) && mDefaultHandler != null) { + mDefaultHandler.uncaughtException(t, e); + } else { + try { + Thread.sleep(3000); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + android.os.Process.killProcess(android.os.Process.myPid()); + System.exit(1); + } + } + + private boolean handleExample(Throwable ex) { + if(ex == null) + return false; + saveCrashInfoToFile(ex); + return true; + } + + private void saveCrashInfoToFile(Throwable ex) { + Writer writer = new StringWriter(); + PrintWriter printWriter = new PrintWriter(writer); + ex.printStackTrace(printWriter); + Throwable exCause = ex.getCause(); + while (exCause != null) { + exCause.printStackTrace(printWriter); + exCause = exCause.getCause(); + } + printWriter.close(); + + long timeMillis = System.currentTimeMillis(); + String fileName = "crash-" + timeMillis + ".log"; + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + String path = "/storage/emulated/0/crash_logInfo/"; + File fl = new File(path); + if (!fl.exists()) { + boolean ignored = fl.mkdirs(); + } + try { + FileOutputStream fileOutputStream = new FileOutputStream(path + fileName); + fileOutputStream.write(writer.toString().getBytes()); + fileOutputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/OUCRequestSender.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/OUCRequestSender.java new file mode 100644 index 0000000..7cc53d0 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/OUCRequestSender.java @@ -0,0 +1,368 @@ +package cn.nahco3awa.naouc.network.ouc; + +import androidx.annotation.NonNull; + +import java.io.IOException; + +import cn.nahco3awa.naouc.network.ouc.request.AccountPayAliPayOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.GetBarCodePayOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.GetCardAccInfoOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.GetInfoByTokenOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.GetMyBillOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.GetPhotoBySnoOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.GetRsaOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.GetValidateCodeOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.LoginOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.NetGdcOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.OUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.TsmOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.WaterApiAccUseHzWatchRequest; +import cn.nahco3awa.naouc.network.ouc.request.WaterApiBookCodeCancelRequest; +import cn.nahco3awa.naouc.network.ouc.request.WaterApiBookCodeRequest; +import cn.nahco3awa.naouc.network.ouc.response.AccountPayOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.GetBarCodePayOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.GetCardAccInfoOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.GetInfoByTokenOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.GetMyBillOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.GetPhotoBySnoOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.GetRsaKeyOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.GetValidateCodeOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.LoginOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.NetGdcOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.OUCCallback; +import cn.nahco3awa.naouc.network.ouc.response.TsmOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.WaterApiAccUseHzWatchResponse; +import cn.nahco3awa.naouc.network.ouc.response.WaterApiBookCodeCancelResponse; +import cn.nahco3awa.naouc.network.ouc.response.WaterApiBookCodeResponse; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +public class OUCRequestSender { + public static OUCRequestSender INSTANCE = null; + private String sessionId; + private final String imeiTicket; + private String sourceTypeTicket; + private String jSessionId; + private final OkHttpClient httpClient; + private OUCRequestSender(String imeiTicket) { + this.imeiTicket = imeiTicket; + this.sourceTypeTicket = "0"; + this.sessionId = ""; + this.jSessionId = ""; + httpClient = new OkHttpClient(); + } + + public static void init(String imeiTicket) { + INSTANCE = new OUCRequestSender(imeiTicket); + } + + public static OUCRequestSender getInstance() { + return INSTANCE; + } + + public String getImeiTicket() { + return imeiTicket; + } + + public String getSessionId() { + return sessionId; + } + + public String getSourceTypeTicket() { + return sourceTypeTicket; + } + + public Request.Builder setAspHeaders(Request.Builder request) { + return request.header("Cookie", "JSESSIONID=" + jSessionId + "; ASP.NET_SessionId=" + getSessionId() + "; imeiticket=" + getImeiTicket() + "; sourcetypeticket=" + getSourceTypeTicket()) + .header("X-Requested-With", "XMLHttpRequest") + .header("Origin", "https://vcard.ouc.edu.cn") + .header("Sec-Fetch-Site", "same-origin") + .header("Sec-Fetch-Mode", "cors") + .header("Sec-Fetch-Dest", "empty") + .header("Referer", "https://vcard.ouc.edu.cn//Phone/Login") + .header("User-Agent", "Mozilla/5.0 (Linux; Android 12; ALA-AN70 Build/HONORALA-AN70; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Mobile Safari/537.36"); + } + + public void waterApiBookCodeCancel(WaterApiBookCodeCancelRequest request, OUCCallback callback) { + sendRequest(request, new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + callback.onFailure(e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + try { + callback.onSuccess(new WaterApiBookCodeCancelResponse(response)); + } catch (Exception e) { + callback.onFailure(e); + } + } + }); + } + + public void waterApiBookCode(WaterApiBookCodeRequest request, OUCCallback callback) { + sendRequest(request, new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + callback.onFailure(e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + try { + callback.onSuccess(new WaterApiBookCodeResponse(response)); + } catch (Exception e) { + callback.onFailure(e); + } + } + }); + } + + public void waterApiAccUseHzWatch(WaterApiAccUseHzWatchRequest request, OUCCallback callback) { + sendRequest(request, new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + callback.onFailure(e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + try { + callback.onSuccess(new WaterApiAccUseHzWatchResponse(response)); + } catch (Exception e) { + callback.onFailure(e); + } + } + }); + } + + public void netGdc(NetGdcOUCRequest request, OUCCallback callback) { + sendRequest(request, new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + callback.onFailure(e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + try { + callback.onSuccess(new NetGdcOUCResponse(response)); + } catch (Exception e) { + callback.onFailure(e); + } + } + }); + } + + public void tsm(TsmOUCRequest request, OUCCallback callback) { + sendRequest(request, new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + callback.onFailure(e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + try { + callback.onSuccess(new TsmOUCResponse(response)); + } catch (Exception e) { + callback.onFailure(e); + } + } + }); + } + + public void getMyBill(GetMyBillOUCRequest request, OUCCallback callback) { + sendRequest(request, new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + callback.onFailure(e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + try { + callback.onSuccess(new GetMyBillOUCResponse(response)); + } catch (Exception e) { + callback.onFailure(e); + } + } + }); + } + + public void accountPayAlipay(AccountPayAliPayOUCRequest request, OUCCallback callback) { + sendRequest(request, new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + callback.onFailure(e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + try { + callback.onSuccess(new AccountPayOUCResponse(response)); + } catch (Exception e) { + callback.onFailure(e); + } + } + }); + } + + public void getCardAccInfo(GetCardAccInfoOUCRequest request, OUCCallback callback) { + sendRequest(request, new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + callback.onFailure(e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { + callback.onSuccess(new GetCardAccInfoOUCResponse(response)); + } + }); + } + + public void getBarCodePay(GetBarCodePayOUCRequest request, OUCCallback callback) { + sendRequest(request, new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + callback.onFailure(e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + GetBarCodePayOUCResponse response1; + try { + response1 = new GetBarCodePayOUCResponse(response); + callback.onSuccess(response1); + } catch (Exception e) { + callback.onFailure(e); + } + } + }); + } + + public void getPhotoBySno(GetPhotoBySnoOUCRequest request, OUCCallback callback) { + sendRequest(request, new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + callback.onFailure(e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + callback.onSuccess(new GetPhotoBySnoOUCResponse(response)); + } + }); + } + + public void getInfoByToken(GetInfoByTokenOUCRequest request, OUCCallback callback) { + sendRequest(request, new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + callback.onFailure(e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + try { + callback.onSuccess(new GetInfoByTokenOUCResponse(response)); + } catch (Exception e) { + callback.onFailure(e); + } + } + }); + } + + public void getRsaKey(GetRsaOUCRequest request, OUCCallback callback) { + sendRequest(request, new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + callback.onFailure(e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + callback.onSuccess(new GetRsaKeyOUCResponse(response)); + } + }); + } + + public void login(LoginOUCRequest request, OUCCallback callback) { + sendRequest(request, new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + callback.onFailure(e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + try { + callback.onSuccess(new LoginOUCResponse(response)); + } catch (Exception e) { + callback.onFailure(e); + } + } + }); + } + + public void getValidateCode(GetValidateCodeOUCRequest request, OUCCallback callback) { + sendRequest(request, new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + callback.onFailure(e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + callback.onSuccess(new GetValidateCodeOUCResponse(response)); + } + }); + } + + public void sendRequest(OUCRequest request, Callback callback) { + Request req = request.makeRequest(this); + Call call = httpClient.newCall(req); + call.enqueue(new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + callback.onFailure(call, e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { + if (response.isSuccessful()) { + String setCookie = response.header("Set-Cookie"); + if (setCookie != null) { + for (String cookie : setCookie.split(";")) { + String[] kv = cookie.split("="); + if (kv[0].equals("ASP.NET_SessionId")) { + OUCRequestSender.this.sessionId = kv[1]; + } + if (kv[0].equals("JSESSIONID")) { + OUCRequestSender.this.jSessionId = kv[1]; + } + } + } + } + callback.onResponse(call, response); + } + }); + } + + public String getjSessionId() { + return jSessionId; + } + + public void setSessionId(String sessionId) { + this.sessionId = sessionId; + } + + public void setSourceTypeTicket(String sourceTypeTicket) { + this.sourceTypeTicket = sourceTypeTicket; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/AccountPayAliPayOUCRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/AccountPayAliPayOUCRequest.java new file mode 100644 index 0000000..d4aed02 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/AccountPayAliPayOUCRequest.java @@ -0,0 +1,34 @@ +package cn.nahco3awa.naouc.network.ouc.request; + +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import okhttp3.FormBody; +import okhttp3.Request; + +public class AccountPayAliPayOUCRequest implements OUCRequest { + public static final String URL = "https://vcard.ouc.edu.cn/User/Account_Pay"; + private final String account; + private final String tranamt; + + public AccountPayAliPayOUCRequest(String account, String tranamt) { + this.account = account; + this.tranamt = tranamt; + } + + @Override + public Request makeRequest(OUCRequestSender sender) { + return sender.setAspHeaders(new Request.Builder() + .url(URL) + .post(new FormBody.Builder() + .addEncoded("account", account) + .addEncoded("acctype", "001") + .addEncoded("tranamt", tranamt) + .addEncoded("objtype", "own") + .addEncoded("paytype", "alipay") + .addEncoded("client_type", "wap") + .addEncoded("paymethod", "4") + .addEncoded("iacctype", "acc") + .addEncoded("iswap", "0") + .addEncoded("json", "true") + .build())).build(); + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetBarCodePayOUCRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetBarCodePayOUCRequest.java new file mode 100644 index 0000000..9be8a8a --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetBarCodePayOUCRequest.java @@ -0,0 +1,118 @@ +package cn.nahco3awa.naouc.network.ouc.request; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; + +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import cn.nahco3awa.naouc.util.QrCodeSign; +import cn.nahco3awa.naouc.util.RSASign; +import cn.nahco3awa.naouc.util.SignUtils; +import okhttp3.FormBody; +import okhttp3.Request; + +public class GetBarCodePayOUCRequest implements OUCRequest { + public static final String URL = "https://vcard.ouc.edu.cn:20085/PhonePay/MobilePayCommon"; + private final String account; + private final String cardId; + private final String imei; + private final String sourceType; + + public GetBarCodePayOUCRequest(String account, String cardId, String imei, String sourceType) { + this.account = account; + this.cardId = cardId; + this.imei = imei; + this.sourceType = sourceType; + } + + + /* + method + timestamp + v + registerid + request + sign + // md5string + clientType + versionName + versionCode + sourcetype + imei + MD5string + */ + public static final String UTF_8 = "UTF-8"; + @Override + public Request makeRequest(OUCRequestSender sender) { + HashMap map = new HashMap<>(); + + JsonObject jsonObject = new JsonObject(); + JsonObject jsonObject2 = new JsonObject(); + + jsonObject.addProperty("account", account); + jsonObject.addProperty("acctype", "001"); + jsonObject.addProperty("sourcetype", sourceType); + jsonObject.addProperty("flag", "00"); + jsonObject.addProperty("cardid", cardId); + jsonObject2.add("synjones.pay.getbarcode", jsonObject); + + JsonArray jsonArray = new JsonArray(); + jsonArray.add(jsonObject2); + + String request; + map.put("method", "synjones.pay.getbarcode"); + String timestamp; + timestamp = URLEncoder.encode(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(System.currentTimeMillis())), StandardCharsets.UTF_8); + map.put("timestamp", timestamp); + map.put("v", "1.0"); + map.put("registerid", "mobileby"); + request = new SignUtils().sign(jsonArray.get(0).toString(), "qJzGEh6hESZDVJeCnFPGuxzaiB7NLQM5", "DESede"); + String nr = URLEncoder.encode(request, StandardCharsets.UTF_8); + map.put("request", nr); + String sign = URLEncoder.encode(RSASign.sign(map), StandardCharsets.UTF_8); + map.put("sign", sign); + + String signStatus = QrCodeSign.getSign(map); + + if (signStatus != null) { + if (signStatus.equals("sign_error")) { + throw new RuntimeException("验证签名失败!"); + } + + map.put("clientType", "1"); + map.put("versionName", "1.5.5"); + map.put("versionCode", "10505"); + map.put("sourcetype", sourceType); + map.put("imei", imei); + + signStatus = QrCodeSign.getSign(map); + String md5 = QrCodeSign.getBase64(signStatus).toLowerCase(); + + FormBody formBody = new FormBody.Builder() + .add("method", "synjones.pay.getbarcode") + .add("timestamp", timestamp) + .add("v", "1.0") + .add("registerid", "mobileby") + .add("request", nr) + .add("sign", sign) + .add("md5string", md5) + .add("clientType", "1") + .add("versionName", "1.5.5") + .add("versionCode", "10505") + .add("sourcetype", sourceType) + .add("imei", imei) + .build(); + + return sender.setAspHeaders(new Request.Builder() + .url(URL) + .post(formBody)).build(); + } + throw new RuntimeException("签名为空!"); + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetCardAccInfoOUCRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetCardAccInfoOUCRequest.java new file mode 100644 index 0000000..61e4689 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetCardAccInfoOUCRequest.java @@ -0,0 +1,24 @@ +package cn.nahco3awa.naouc.network.ouc.request; + +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import okhttp3.FormBody; +import okhttp3.Request; + +public class GetCardAccInfoOUCRequest implements OUCRequest { + public static final String URL = "https://vcard.ouc.edu.cn/User/GetCardAccInfo"; + private final String account; + + public GetCardAccInfoOUCRequest(String account) { + this.account = account; + } + + @Override + public Request makeRequest(OUCRequestSender sender) { + return sender.setAspHeaders(new Request.Builder() + .post(new FormBody.Builder() + .addEncoded("acc", account) + .addEncoded("json", "true") + .build()) + .url(URL)).build(); + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetInfoByTokenOUCRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetInfoByTokenOUCRequest.java new file mode 100644 index 0000000..bb80d3a --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetInfoByTokenOUCRequest.java @@ -0,0 +1,35 @@ +package cn.nahco3awa.naouc.network.ouc.request; + +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import okhttp3.FormBody; +import okhttp3.Request; + +public class GetInfoByTokenOUCRequest implements OUCRequest { + public static final String URL = "https://vcard.ouc.edu.cn/NoBase/GetInfoByToken"; + private final String imei; + private final String sourceType; + private final String token; + + public GetInfoByTokenOUCRequest(String imei, String sourceType, String token) { + this.imei = imei; + this.sourceType = sourceType; + this.token = token; + } + + @Override + public Request makeRequest(OUCRequestSender sender) { + return sender.setAspHeaders(new Request.Builder()) + .url(URL) + .post(new FormBody.Builder() + .addEncoded("clientType", "1") + .addEncoded("imei", imei) + .addEncoded("sourcetype", sourceType) + .addEncoded("versionName", "1.5.5") + .addEncoded("versionCode", "10505") + .addEncoded("token", token) + .build()) + .build(); + } +} + +// 5760548 \ No newline at end of file diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetMyBillOUCRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetMyBillOUCRequest.java new file mode 100644 index 0000000..89dedef --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetMyBillOUCRequest.java @@ -0,0 +1,28 @@ +package cn.nahco3awa.naouc.network.ouc.request; + +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import okhttp3.FormBody; +import okhttp3.Request; + +public class GetMyBillOUCRequest implements OUCRequest { + public static final String URL = "https://vcard.ouc.edu.cn/Report/GetMyBill"; + private final String account; + private final int page; + + public GetMyBillOUCRequest(String account, int page) { + this.account = account; + this.page = page; + } + + @Override + public Request makeRequest(OUCRequestSender sender) { + return sender.setAspHeaders(new Request.Builder()) + .url(URL) + .post(new FormBody.Builder() + .addEncoded("account", account) + .addEncoded("page", String.valueOf(page)) + .addEncoded("json", "true") + .build()) + .build(); + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetPhotoBySnoOUCRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetPhotoBySnoOUCRequest.java new file mode 100644 index 0000000..6b53628 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetPhotoBySnoOUCRequest.java @@ -0,0 +1,34 @@ +package cn.nahco3awa.naouc.network.ouc.request; + +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import okhttp3.FormBody; +import okhttp3.Request; + +public class GetPhotoBySnoOUCRequest implements OUCRequest { + public static final String URL = "https://vcard.ouc.edu.cn/NoBase/GetPhotoBySno"; + private final String imei; + private final String sourceType; + private final String sno; + + public GetPhotoBySnoOUCRequest(String imei, String sourceType, String sno) { + this.imei = imei; + this.sourceType = sourceType; + this.sno = sno; + } + + @Override + public Request makeRequest(OUCRequestSender sender) { + return sender.setAspHeaders(new Request.Builder()) + .url(URL) + .post(new FormBody.Builder() + .addEncoded("accesstype", sourceType) + .addEncoded("clientType", "1") + .addEncoded("sno", sno) + .addEncoded("imei", imei) + .addEncoded("sourcetype", sourceType) + .addEncoded("versionName", "1.5.5") + .addEncoded("versionCode", "10505") + .build()) + .build(); + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetRsaOUCRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetRsaOUCRequest.java new file mode 100644 index 0000000..d2a9a60 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetRsaOUCRequest.java @@ -0,0 +1,18 @@ +package cn.nahco3awa.naouc.network.ouc.request; + +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import okhttp3.FormBody; +import okhttp3.Request; + +public class GetRsaOUCRequest implements OUCRequest { + public static final String URL = "https://vcard.ouc.edu.cn/Common/GetRsaKey"; + @Override + public Request makeRequest(OUCRequestSender sender) { + return sender.setAspHeaders(new Request.Builder()) + .url(URL) + .post(new FormBody.Builder() + .addEncoded("json", "true") + .build()) + .build(); + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetValidateCodeOUCRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetValidateCodeOUCRequest.java new file mode 100644 index 0000000..75c8e13 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/GetValidateCodeOUCRequest.java @@ -0,0 +1,28 @@ +package cn.nahco3awa.naouc.network.ouc.request; + +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import okhttp3.Request; + +public class GetValidateCodeOUCRequest implements OUCRequest { + public static final String URL = "https://vcard.ouc.edu.cn/Phone/GetValidateCode"; + private long time; + public GetValidateCodeOUCRequest(long time) { + this.time = time; + } + + public long getTime() { + return time; + } + + public void setTime(long time) { + this.time = time; + } + + @Override + public Request makeRequest(OUCRequestSender sender) { + return sender.setAspHeaders(new Request.Builder() + .url(URL + "?time=" + getTime()) + .get()) + .build(); + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/LoginOUCRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/LoginOUCRequest.java new file mode 100644 index 0000000..a1e679b --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/LoginOUCRequest.java @@ -0,0 +1,40 @@ +package cn.nahco3awa.naouc.network.ouc.request; + +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import okhttp3.FormBody; +import okhttp3.Request; + +public class LoginOUCRequest implements OUCRequest { + public static final String URL = "https://vcard.ouc.edu.cn/Phone/Login"; + private final String sno; + private final String pwd; + private final boolean remember; + private final String uclass; + private final String yzm; + private final String key; + + public LoginOUCRequest(String sno, String pwd, boolean remember, String uClass, String yzm, String key) { + this.sno = sno; + this.pwd = pwd; + this.remember = remember; + this.uclass = uClass; + this.yzm = yzm; + this.key = key; + } + + @Override + public Request makeRequest(OUCRequestSender sender) { + return sender.setAspHeaders(new Request.Builder() + .url(URL) + .post(new FormBody.Builder() + .addEncoded("sno", sno) + .addEncoded("pwd", pwd) + .addEncoded("remember", remember ? "1" : "0") + .addEncoded("uclass", uclass) + .addEncoded("yzm", yzm) + .addEncoded("key", key) + .addEncoded("json", "true") + .build())) + .build(); + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/NetCheckOUCRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/NetCheckOUCRequest.java new file mode 100644 index 0000000..7c98846 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/NetCheckOUCRequest.java @@ -0,0 +1,15 @@ +package cn.nahco3awa.naouc.network.ouc.request; + +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import okhttp3.Request; + +public class NetCheckOUCRequest implements OUCRequest { + public static final String URL = "https://vcard.ouc.edu.cn:8988/web/common/check.html?"; + @Override + public Request makeRequest(OUCRequestSender sender) { + return sender.setAspHeaders(new Request.Builder()) + .url(URL + "ticket=" + sender.getSourceTypeTicket()) + .get() + .build(); + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/NetGdcOUCRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/NetGdcOUCRequest.java new file mode 100644 index 0000000..02b48f5 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/NetGdcOUCRequest.java @@ -0,0 +1,32 @@ +package cn.nahco3awa.naouc.network.ouc.request; + +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import okhttp3.FormBody; +import okhttp3.Request; + +public class NetGdcOUCRequest implements OUCRequest { + public static final String URL = "https://vcard.ouc.edu.cn:8988/web/NetWork/NetGdc.html"; + private final String account; + private final String netAccount; + private final int tran; + + public NetGdcOUCRequest(String account, String netAccount, int tran) { + this.account = account; + this.netAccount = netAccount; + this.tran = tran; + } + + @Override + public Request makeRequest(OUCRequestSender sender) { + return sender.setAspHeaders(new Request.Builder()) + .url(URL) + .post(new FormBody.Builder() + .addEncoded("aid", "0030000000000301") + .addEncoded("account", account) + .addEncoded("tran", String.valueOf(tran)) + .addEncoded("netacc", netAccount) + .addEncoded("acctype", "###") + .build()) + .build(); + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/OUCRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/OUCRequest.java new file mode 100644 index 0000000..7543a9e --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/OUCRequest.java @@ -0,0 +1,8 @@ +package cn.nahco3awa.naouc.network.ouc.request; + +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import okhttp3.Request; + +public interface OUCRequest { + Request makeRequest(OUCRequestSender sender); +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/TsmOUCRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/TsmOUCRequest.java new file mode 100644 index 0000000..3f66f23 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/TsmOUCRequest.java @@ -0,0 +1,36 @@ +package cn.nahco3awa.naouc.network.ouc.request; + +import com.google.gson.JsonObject; + +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import okhttp3.FormBody; +import okhttp3.Request; + +public class TsmOUCRequest implements OUCRequest { + public static final String URL = "https://vcard.ouc.edu.cn:8988/web/Common/Tsm.html"; + private final String account; + private final String payAccount; + + public TsmOUCRequest(String account, String payAccount) { + this.account = account; + this.payAccount = payAccount; + } + + @Override + public Request makeRequest(OUCRequestSender sender) { + JsonObject jsonObject = new JsonObject(); + JsonObject queryInfo = new JsonObject(); + queryInfo.addProperty("aid", "0030000000000301"); + queryInfo.addProperty("account", account); + queryInfo.addProperty("payacc", payAccount); + jsonObject.add("query_net_info", queryInfo); + return sender.setAspHeaders(new Request.Builder()) + .url(URL) + .post(new FormBody.Builder() + .addEncoded("jsondata", jsonObject.toString()) + .addEncoded("funname", "synjones.onecard.query.net.info") + .addEncoded("json", "true") + .build()) + .build(); + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/WaterApiAccUseHzWatchRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/WaterApiAccUseHzWatchRequest.java new file mode 100644 index 0000000..ca896fb --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/WaterApiAccUseHzWatchRequest.java @@ -0,0 +1,43 @@ +package cn.nahco3awa.naouc.network.ouc.request; + +import com.google.gson.JsonObject; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import okhttp3.Request; + +public class WaterApiAccUseHzWatchRequest implements OUCRequest { + public static final String URL = "http://222.195.158.17:5003/waterapi/api/AccUseHzWatch"; + public static final byte[] PUBLIC_KEY = Base64.getDecoder().decode("3n4DdO47LWH2Co/WfpbdyA=="); + public static final String AES_MODE = "AES/ECB/PKCS5Padding"; + public static final String AES = "AES"; + private final String account; + public WaterApiAccUseHzWatchRequest(String account) { + this.account = account; + } + + @Override + public Request makeRequest(OUCRequestSender sender) { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("ano", account); + try { + byte[] raw = jsonObject.toString().getBytes(StandardCharsets.UTF_8); + SecretKeySpec keySpec = new SecretKeySpec(PUBLIC_KEY, AES); + Cipher cipher = Cipher.getInstance(AES_MODE); + cipher.init(Cipher.ENCRYPT_MODE, keySpec); + byte[] encryptedInfo = cipher.doFinal(raw); + String info = Base64.getEncoder().encodeToString(encryptedInfo).replaceAll("=", "%3D"); + return new Request.Builder() + .url(URL + "?info=" + info + "&token=" + sender.getSourceTypeTicket()) + .get() + .build(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/WaterApiBookCodeCancelRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/WaterApiBookCodeCancelRequest.java new file mode 100644 index 0000000..e1787d2 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/WaterApiBookCodeCancelRequest.java @@ -0,0 +1,47 @@ +package cn.nahco3awa.naouc.network.ouc.request; + +import com.google.gson.JsonObject; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import okhttp3.Request; + +public class WaterApiBookCodeCancelRequest implements OUCRequest { + public static final String URL = "http://222.195.158.17:5003/waterapi/api/BookCodeReqCancel"; + public static final byte[] PUBLIC_KEY = Base64.getDecoder().decode("3n4DdO47LWH2Co/WfpbdyA=="); + public static final String AES_MODE = "AES/ECB/PKCS5Padding"; + public static final String AES = "AES"; + private final String account; + private final int classNo; + + public WaterApiBookCodeCancelRequest(String account, int classNo) { + this.account = account; + this.classNo = classNo; + } + + @Override + public Request makeRequest(OUCRequestSender sender) { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("ano", account); + jsonObject.addProperty("classno", classNo); + try { + byte[] raw = jsonObject.toString().getBytes(StandardCharsets.UTF_8); + SecretKeySpec keySpec = new SecretKeySpec(PUBLIC_KEY, AES); + Cipher cipher = Cipher.getInstance(AES_MODE); + cipher.init(Cipher.ENCRYPT_MODE, keySpec); + byte[] encryptedInfo = cipher.doFinal(raw); + String info = Base64.getEncoder().encodeToString(encryptedInfo).replaceAll("=", "%3D"); + return new Request.Builder() + .url(URL + "?info=" + info + "&token=" + sender.getSourceTypeTicket()) + .get() + .build(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/WaterApiBookCodeRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/WaterApiBookCodeRequest.java new file mode 100644 index 0000000..a0921fc --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/request/WaterApiBookCodeRequest.java @@ -0,0 +1,47 @@ +package cn.nahco3awa.naouc.network.ouc.request; + +import com.google.gson.JsonObject; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import okhttp3.Request; + +public class WaterApiBookCodeRequest implements OUCRequest { + public static final String URL = "http://222.195.158.17:5003/waterapi/api/BookCodeReq"; + public static final byte[] PUBLIC_KEY = Base64.getDecoder().decode("3n4DdO47LWH2Co/WfpbdyA=="); + public static final String AES_MODE = "AES/ECB/PKCS5Padding"; + public static final String AES = "AES"; + private final String account; + private final int classNo; + + public WaterApiBookCodeRequest(String account, int classNo) { + this.account = account; + this.classNo = classNo; + } + + @Override + public Request makeRequest(OUCRequestSender sender) { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("ano", account); + jsonObject.addProperty("classno", classNo); + try { + byte[] raw = jsonObject.toString().getBytes(StandardCharsets.UTF_8); + SecretKeySpec keySpec = new SecretKeySpec(PUBLIC_KEY, AES); + Cipher cipher = Cipher.getInstance(AES_MODE); + cipher.init(Cipher.ENCRYPT_MODE, keySpec); + byte[] encryptedInfo = cipher.doFinal(raw); + String info = Base64.getEncoder().encodeToString(encryptedInfo).replaceAll("=", "%3D"); + return new Request.Builder() + .url(URL + "?info=" + info + "&token=" + sender.getSourceTypeTicket()) + .get() + .build(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/AccountPayOUCResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/AccountPayOUCResponse.java new file mode 100644 index 0000000..06637a1 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/AccountPayOUCResponse.java @@ -0,0 +1,34 @@ +package cn.nahco3awa.naouc.network.ouc.response; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import okhttp3.Response; + +public class AccountPayOUCResponse extends OUCResponse { + private final String url; + private final String method; + private final String orderId; + public AccountPayOUCResponse(Response response) { + super(response); + JsonObject jsonObject = JsonParser.parseReader(response.body().charStream()).getAsJsonObject(); + if (!jsonObject.get("IsSucceed").getAsBoolean()) { + throw new RuntimeException(jsonObject.get("Msg").getAsString()); + } + url = jsonObject.get("Msg").getAsString(); + method = jsonObject.get("Obj").getAsString(); + orderId = jsonObject.get("OrderID").getAsString(); + } + + public String getUrl() { + return url; + } + + public String getMethod() { + return method; + } + + public String getOrderId() { + return orderId; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetBarCodePayOUCResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetBarCodePayOUCResponse.java new file mode 100644 index 0000000..c144acf --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetBarCodePayOUCResponse.java @@ -0,0 +1,42 @@ +package cn.nahco3awa.naouc.network.ouc.response; + +import android.util.Log; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import okhttp3.Response; + +public class GetBarCodePayOUCResponse extends OUCResponse { + private final int account; + private final long expires; + private final String[] barcode; + public GetBarCodePayOUCResponse(Response response) { + super(response); + JsonObject object = JsonParser.parseReader(response.body().charStream()).getAsJsonObject(); + if (!object.get("retcode").getAsString().equals("0")) { + Log.e("GBCP", object.toString()); + throw new RuntimeException(object.get("errmsg").getAsString()); + } + JsonObject obj = object.getAsJsonObject("obj"); + account = Integer.parseInt(obj.get("ACCOUNT").getAsString()); + expires = Long.parseLong(obj.get("EXPIRES").getAsString()); + barcode = new String[4]; + var awa = obj.get("BARCODE").getAsJsonArray(); + for (int i = 0; i < 4; i++) { + barcode[i] = awa.get(i).getAsString(); + } + } + + public int getAccount() { + return account; + } + + public long getExpires() { + return expires; + } + + public String[] getBarcode() { + return barcode; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetCardAccInfoOUCResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetCardAccInfoOUCResponse.java new file mode 100644 index 0000000..c4e524d --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetCardAccInfoOUCResponse.java @@ -0,0 +1,26 @@ +package cn.nahco3awa.naouc.network.ouc.response; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import java.io.IOException; + +import okhttp3.Response; + +public class GetCardAccInfoOUCResponse extends OUCResponse { + private final int balance; // 我们假定海大没有富哥 + public GetCardAccInfoOUCResponse(Response response) throws IOException { + super(response); + JsonObject jsonObject = JsonParser.parseString(response.body().string()).getAsJsonObject(); + String msg = jsonObject.get("Msg").getAsString(); + JsonObject queryObject = JsonParser.parseString(msg).getAsJsonObject().get("query_accinfo").getAsJsonObject(); + if (!queryObject.get("retcode").getAsString().equals("0")) { + throw new RuntimeException(queryObject.get("errmsg").getAsString()); + } + balance = Integer.parseInt(queryObject.get("accinfo").getAsJsonArray().get(0).getAsJsonObject().get("balance").getAsString()); + } + + public int getBalance() { + return balance; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetInfoByTokenOUCResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetInfoByTokenOUCResponse.java new file mode 100644 index 0000000..126357f --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetInfoByTokenOUCResponse.java @@ -0,0 +1,138 @@ +package cn.nahco3awa.naouc.network.ouc.response; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import okhttp3.Response; + +public class GetInfoByTokenOUCResponse extends OUCResponse { + private final String no; + private final String name; + private final String sex; + private final String sno; + private final String lastUpdate; + private final String account; + private final String cardId; + private final String password; + private final String bmmc; + private final String subjectCode; + private final String subjectName; + private final String gradeName; + private final String schoolName; + private final String pidName; + private final String pidCode; + private final String classCode; + private final String expDate; + private final String areaCode; + private final String deptCode; + private final String className; + + public GetInfoByTokenOUCResponse(Response response) { + super(response); + JsonObject object = JsonParser.parseReader(response.body().charStream()).getAsJsonObject(); + if (!object.get("IsSucceed").getAsBoolean()) { + throw new RuntimeException(object.get("Msg").getAsString()); + } + JsonObject obj2 = object.getAsJsonObject("Obj"); + no = obj2.get("NO").getAsString(); + name = obj2.get("NAME").getAsString(); + sex = obj2.get("SEX").getAsString(); + sno = obj2.get("SNO").getAsString(); + lastUpdate = obj2.get("LASTUPDATE").getAsString(); + account = obj2.get("ACCOUNT").getAsString(); + cardId = obj2.get("CARDID").getAsString(); + subjectName = obj2.get("ZYMC").getAsString(); + subjectCode = obj2.get("ZYDM").getAsString(); + gradeName = obj2.get("NJMC").getAsString(); + schoolName = obj2.get("XQMC").getAsString(); + pidName = obj2.get("PIDNAME").getAsString(); + pidCode = obj2.get("PIDCODE").getAsString(); + classCode = obj2.get("BJDM").getAsString(); + expDate = obj2.get("EXPDATE").getAsString(); + areaCode = obj2.get("AREACODE").getAsString(); + deptCode = obj2.get("DEPTCODE").getAsString(); + className = obj2.get("BJMC").getAsString(); + password = obj2.get("PASSWORD").getAsString(); + bmmc = obj2.get("BMMC").getAsString(); + } + + public String getClassName() { + return className; + } + + public String getDeptCode() { + return deptCode; + } + + public String getAreaCode() { + return areaCode; + } + + public String getExpDate() { + return expDate; + } + + public String getClassCode() { + return classCode; + } + + public String getPidCode() { + return pidCode; + } + + public String getPidName() { + return pidName; + } + + public String getSchoolName() { + return schoolName; + } + + public String getGradeName() { + return gradeName; + } + + public String getSubjectName() { + return subjectName; + } + + public String getSubjectCode() { + return subjectCode; + } + + public String getBmmc() { + return bmmc; + } + + public String getPassword() { + return password; + } + + public String getCardId() { + return cardId; + } + + public String getAccount() { + return account; + } + + public String getLastUpdate() { + return lastUpdate; + } + + public String getSno() { + return sno; + } + + public String getSex() { + return sex; + } + + public String getName() { + return name; + } + + public String getNo() { + return no; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetMyBillOUCResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetMyBillOUCResponse.java new file mode 100644 index 0000000..85f5cb1 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetMyBillOUCResponse.java @@ -0,0 +1,30 @@ +package cn.nahco3awa.naouc.network.ouc.response; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import java.util.ArrayList; +import java.util.List; + +import cn.nahco3awa.naouc.network.ouc.response.data.SingleBillData; +import okhttp3.Response; + +public class GetMyBillOUCResponse extends OUCResponse { + private final List billData; + public GetMyBillOUCResponse(Response response) { + super(response); + JsonObject jsonObject = JsonParser.parseReader(response.body().charStream()).getAsJsonObject(); + var arr = jsonObject.get("rows").getAsJsonArray(); + billData = new ArrayList<>(arr.size()); + Gson gson = new Gson(); + for (JsonElement jsonElement : arr) { + billData.add(gson.fromJson(jsonElement, SingleBillData.class)); + } + } + + public List getBillData() { + return billData; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetPhotoBySnoOUCResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetPhotoBySnoOUCResponse.java new file mode 100644 index 0000000..92315a2 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetPhotoBySnoOUCResponse.java @@ -0,0 +1,29 @@ +package cn.nahco3awa.naouc.network.ouc.response; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import java.util.Base64; + +import okhttp3.Response; + +public class GetPhotoBySnoOUCResponse extends OUCResponse { + private final Bitmap image; + public GetPhotoBySnoOUCResponse(Response response) { + super(response); + JsonObject object = JsonParser.parseReader(response.body().charStream()).getAsJsonObject(); + if (!object.get("IsSucceed").getAsBoolean()) { + throw new RuntimeException(object.get("Msg").getAsString()); + } + String imageBase64 = object.get("Obj").getAsString(); + byte[] bs = Base64.getDecoder().decode(imageBase64); + image = BitmapFactory.decodeByteArray(bs, 0, bs.length); + } + + public Bitmap getImage() { + return image; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetRsaKeyOUCResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetRsaKeyOUCResponse.java new file mode 100644 index 0000000..b903f11 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetRsaKeyOUCResponse.java @@ -0,0 +1,32 @@ +package cn.nahco3awa.naouc.network.ouc.response; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import okhttp3.Response; + +public class GetRsaKeyOUCResponse extends OUCResponse { + private final String msg; + private final String e; + private final String mod; + public GetRsaKeyOUCResponse(Response response) { + super(response); + JsonObject object = JsonParser.parseReader(response.body().charStream()).getAsJsonObject(); + msg = object.get("Msg").getAsString(); + String[] keys = object.get("Obj").getAsString().split(","); + e = keys[0]; + mod = keys[1]; + } + + public String getMod() { + return mod; + } + + public String getMsg() { + return msg; + } + + public String getE() { + return e; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetValidateCodeOUCResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetValidateCodeOUCResponse.java new file mode 100644 index 0000000..0174198 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/GetValidateCodeOUCResponse.java @@ -0,0 +1,19 @@ +package cn.nahco3awa.naouc.network.ouc.response; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +import okhttp3.Response; + +public class GetValidateCodeOUCResponse extends OUCResponse { + private final Bitmap image; + + public GetValidateCodeOUCResponse(Response response) { + super(response); + image = BitmapFactory.decodeStream(response.body().byteStream()); + } + + public Bitmap getImage() { + return image; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/LoginOUCResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/LoginOUCResponse.java new file mode 100644 index 0000000..0fd78f0 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/LoginOUCResponse.java @@ -0,0 +1,144 @@ +package cn.nahco3awa.naouc.network.ouc.response; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import okhttp3.Response; + +public class LoginOUCResponse extends OUCResponse { + private final String rescouseType; + private final String no; + private final String name; + private final String sex; + private final String sno; + private final String lastUpdate; + private final String account; + private final String cardId; + private final String tel; + private final String subjectName; + private final String subjectCode; + private final String gradeName; + private final String schoolName; + private final String pidName; + private final String pidCode; + private final String classCode; + private final String expDate; + private final String areaCode; + private final String deptCode; + private final String className; + private final String createdDate; + public LoginOUCResponse(Response response) { + super(response); + JsonObject object = JsonParser.parseReader(response.body().charStream()).getAsJsonObject(); + if (!object.get("IsSucceed").getAsBoolean()) { + throw new RuntimeException(object.get("Msg").getAsString()); + } + JsonObject obj2 = object.getAsJsonObject("Obj2"); + rescouseType = obj2.get("RescouseType").getAsString(); + no = obj2.get("NO").getAsString(); + name = obj2.get("NAME").getAsString(); + sex = obj2.get("SEX").getAsString(); + sno = obj2.get("SNO").getAsString(); + lastUpdate = obj2.get("LASTUPDATE").getAsString(); + account = obj2.get("ACCOUNT").getAsString(); + cardId = obj2.get("CARDID").getAsString(); + tel = obj2.get("TEL").getAsString(); + subjectName = obj2.get("ZYMC").getAsString(); + subjectCode = obj2.get("ZYDM").getAsString(); + gradeName = obj2.get("NJMC").getAsString(); + schoolName = obj2.get("XQMC").getAsString(); + pidName = obj2.get("PIDNAME").getAsString(); + pidCode = obj2.get("PIDCODE").getAsString(); + classCode = obj2.get("BJDM").getAsString(); + expDate = obj2.get("EXPDATE").getAsString(); + areaCode = obj2.get("AREACODE").getAsString(); + deptCode = obj2.get("DEPTCODE").getAsString(); + className = obj2.get("BJMC").getAsString(); + createdDate = obj2.get("CREATEDATE").getAsString(); + } + + + public String getCreatedDate() { + return createdDate; + } + + public String getClassName() { + return className; + } + + public String getDeptCode() { + return deptCode; + } + + public String getAreaCode() { + return areaCode; + } + + public String getExpDate() { + return expDate; + } + + public String getClassCode() { + return classCode; + } + + public String getPidCode() { + return pidCode; + } + + public String getPidName() { + return pidName; + } + + public String getSchoolName() { + return schoolName; + } + + public String getGradeName() { + return gradeName; + } + + public String getSubjectCode() { + return subjectCode; + } + + public String getSubjectName() { + return subjectName; + } + + public String getTel() { + return tel; + } + + public String getCardId() { + return cardId; + } + + public String getAccount() { + return account; + } + + public String getLastUpdate() { + return lastUpdate; + } + + public String getSno() { + return sno; + } + + public String getSex() { + return sex; + } + + public String getName() { + return name; + } + + public String getNo() { + return no; + } + + public String getRescouseType() { + return rescouseType; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/NetGdcOUCResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/NetGdcOUCResponse.java new file mode 100644 index 0000000..acce135 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/NetGdcOUCResponse.java @@ -0,0 +1,49 @@ +package cn.nahco3awa.naouc.network.ouc.response; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import okhttp3.Response; + +public class NetGdcOUCResponse extends OUCResponse { + private final int retCode; + private final String errMessage; + private final String aid; + private final String account; + private final String accountType; + private final int tranAmount; + public NetGdcOUCResponse(Response response) { + super(response); + JsonObject jsonObject = JsonParser.parseReader(response.body().charStream()).getAsJsonObject().getAsJsonObject("pay_net_gdc"); + retCode = Integer.parseInt(jsonObject.get("retcode").getAsString()); + errMessage = jsonObject.get("errmsg").getAsString(); + aid = jsonObject.get("aid").getAsString(); + account = jsonObject.get("account").getAsString(); + accountType = jsonObject.get("acctype").getAsString(); + tranAmount = Integer.parseInt(jsonObject.get("tranamt").getAsString()); + } + + public int getRetCode() { + return retCode; + } + + public int getTranAmount() { + return tranAmount; + } + + public String getAccount() { + return account; + } + + public String getAccountType() { + return accountType; + } + + public String getAid() { + return aid; + } + + public String getErrMessage() { + return errMessage; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/OUCCallback.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/OUCCallback.java new file mode 100644 index 0000000..dd0e589 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/OUCCallback.java @@ -0,0 +1,6 @@ +package cn.nahco3awa.naouc.network.ouc.response; + +public interface OUCCallback { + void onSuccess(R response); + void onFailure(Throwable e); +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/OUCResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/OUCResponse.java new file mode 100644 index 0000000..dc10d93 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/OUCResponse.java @@ -0,0 +1,14 @@ +package cn.nahco3awa.naouc.network.ouc.response; + +import okhttp3.Response; + +public abstract class OUCResponse { + private final Response response; + public OUCResponse(Response response) { + this.response = response; + } + + public Response getResponse() { + return response; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/TsmOUCResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/TsmOUCResponse.java new file mode 100644 index 0000000..2af9cb0 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/TsmOUCResponse.java @@ -0,0 +1,62 @@ +package cn.nahco3awa.naouc.network.ouc.response; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import okhttp3.Response; + +public class TsmOUCResponse extends OUCResponse { + private final String msg; + private final String aid; + private final String account; + private final String netAccount; + private final float balance; + private final boolean isFrozen; + private final boolean hasLost; + private final String retcode; + + public TsmOUCResponse(Response response) { + super(response); + JsonObject jsonObject = JsonParser.parseReader(response.body().charStream()).getAsJsonObject().getAsJsonObject("query_net_info"); + if (!jsonObject.get("retcode").getAsString().equals("0")) { + throw new RuntimeException(jsonObject.get("errmsg").getAsString()); + } + retcode = jsonObject.get("retcode").getAsString(); + msg = jsonObject.get("errmsg").getAsString(); + aid = jsonObject.get("aid").getAsString(); + account = jsonObject.get("account").getAsString(); + JsonObject netAccountObject = jsonObject.getAsJsonObject("netacc"); + netAccount = netAccountObject.get("netacc").getAsString(); + balance = Integer.parseInt(netAccountObject.get("bal").getAsString()) / 100.0f; + isFrozen = netAccountObject.get("freezeflag").getAsString().equals("1"); + hasLost = netAccountObject.get("lostflag").getAsString().equals("1"); + } + + public String getMsg() { + return msg; + } + + public String getAid() { + return aid; + } + + public String getAccount() { + return account; + } + + public String getNetAccount() { + return netAccount; + } + + public float getBalance() { + return balance; + } + + public boolean isFrozen() { + return isFrozen; + } + + public boolean hasLost() { + return hasLost; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/WaterApiAccUseHzWatchResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/WaterApiAccUseHzWatchResponse.java new file mode 100644 index 0000000..0a2dceb --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/WaterApiAccUseHzWatchResponse.java @@ -0,0 +1,52 @@ +package cn.nahco3awa.naouc.network.ouc.response; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import java.util.ArrayList; +import java.util.List; + +import cn.nahco3awa.naouc.network.ouc.response.data.SingleWaterHzWatchData; +import okhttp3.Response; + +public class WaterApiAccUseHzWatchResponse extends OUCResponse { + private final int retNo; + private final String description; + private final List data; + public WaterApiAccUseHzWatchResponse(Response response) { + super(response); + JsonObject jsonObject = JsonParser.parseReader(response.body().charStream()).getAsJsonObject(); + retNo = jsonObject.get("RetNo").getAsInt(); + description = jsonObject.get("RetDsp").getAsString(); + if (retNo != 0) { + throw new RuntimeException(description); + } + data = new ArrayList<>(); + for (JsonElement single : jsonObject.getAsJsonArray("List")) { + JsonObject singleData = single.getAsJsonObject(); + data.add(new SingleWaterHzWatchData( + singleData.get("ClassNo").getAsInt(), + singleData.get("ClassName").getAsString(), + singleData.get("PosNum").getAsInt(), + singleData.get("WarnPosNum").getAsInt(), + singleData.get("UseFreeRate").getAsInt(), + singleData.get("BookRate").getAsInt(), + singleData.get("Actkind").getAsInt(), + singleData.get("BookCode").getAsString() + )); + } + } + + public List getData() { + return data; + } + + public int getRetNo() { + return retNo; + } + + public String getDescription() { + return description; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/WaterApiBookCodeCancelResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/WaterApiBookCodeCancelResponse.java new file mode 100644 index 0000000..b9625ef --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/WaterApiBookCodeCancelResponse.java @@ -0,0 +1,31 @@ +package cn.nahco3awa.naouc.network.ouc.response; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import okhttp3.Response; + +public class WaterApiBookCodeCancelResponse extends OUCResponse { + private final int retNo; + private final String description; + private final String bookCode; + public WaterApiBookCodeCancelResponse(Response response) { + super(response); + JsonObject jsonObject = JsonParser.parseReader(response.body().charStream()).getAsJsonObject(); + retNo = jsonObject.get("RetNo").getAsInt(); + description = jsonObject.get("RetDsp").getAsString(); + bookCode = jsonObject.get("BookCode").getAsString(); + } + + public String getBookCode() { + return bookCode; + } + + public int getRetNo() { + return retNo; + } + + public String getDescription() { + return description; + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/WaterApiBookCodeResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/WaterApiBookCodeResponse.java new file mode 100644 index 0000000..4e943fa --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/WaterApiBookCodeResponse.java @@ -0,0 +1,31 @@ +package cn.nahco3awa.naouc.network.ouc.response; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import okhttp3.Response; + +public class WaterApiBookCodeResponse extends OUCResponse { + private final int retNo; + private final String description; + private final String bookCode; + public WaterApiBookCodeResponse(Response response) { + super(response); + JsonObject jsonObject = JsonParser.parseReader(response.body().charStream()).getAsJsonObject(); + retNo = jsonObject.get("RetNo").getAsInt(); + description = jsonObject.get("RetDsp").getAsString(); + bookCode = jsonObject.get("BookCode").getAsString(); + } + + public String getBookCode() { + return bookCode; + } + + public int getRetNo() { + return retNo; + } + + public String getDescription() { + return description; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/data/SingleBillData.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/data/SingleBillData.java new file mode 100644 index 0000000..9b55f35 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/data/SingleBillData.java @@ -0,0 +1,19 @@ +package cn.nahco3awa.naouc.network.ouc.response.data; + +public class SingleBillData { + public int RO; // 行数 + public String OCCTIME; // 支出事件 + public String EFFECTDATE; // 生效时间 + public String XQ; // 星期 + public String MERCNAME; // 消费地点 + public float TRANAMT; // 金额 + public String TRANNAME; // 支付方式 + public float CARDBAL; // 余额 + public String JDESC; // 流水简介 + public int JNUM; + public String MACCOUNT; + public String TRANCODE; + public String F1; + public String F2; + public String F3; +} \ No newline at end of file diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/data/SingleWaterHzWatchData.java b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/data/SingleWaterHzWatchData.java new file mode 100644 index 0000000..50c78fa --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/ouc/response/data/SingleWaterHzWatchData.java @@ -0,0 +1,107 @@ +package cn.nahco3awa.naouc.network.ouc.response.data; + +import java.util.Objects; + +public final class SingleWaterHzWatchData { + private final int ClassNo; + private final String ClassName; + private final int PosNum; + private final int WarnPosNum; + private final int UseFreeRate; + private final int BookRate; + private int Actkind; + private String BookCode; + + public SingleWaterHzWatchData( + int ClassNo, // 单位编号 + String ClassName, // 单位名称 + int PosNum, // 设备量 + int WarnPosNum, // ??? + int UseFreeRate, // 空闲率 + int BookRate, // 预约比例(66为0.66%) + int Actkind, // 0未预约 1已预约 + String BookCode // 预约编号 + ) { + this.ClassNo = ClassNo; + this.ClassName = ClassName; + this.PosNum = PosNum; + this.WarnPosNum = WarnPosNum; + this.UseFreeRate = UseFreeRate; + this.BookRate = BookRate; + this.Actkind = Actkind; + this.BookCode = BookCode; + } + + public int ClassNo() { + return ClassNo; + } + + public String ClassName() { + return ClassName; + } + + public int PosNum() { + return PosNum; + } + + public int WarnPosNum() { + return WarnPosNum; + } + + public int UseFreeRate() { + return UseFreeRate; + } + + public int BookRate() { + return BookRate; + } + + public int Actkind() { + return Actkind; + } + + public String BookCode() { + return BookCode; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (SingleWaterHzWatchData) obj; + return this.ClassNo == that.ClassNo && + Objects.equals(this.ClassName, that.ClassName) && + this.PosNum == that.PosNum && + this.WarnPosNum == that.WarnPosNum && + this.UseFreeRate == that.UseFreeRate && + this.BookRate == that.BookRate && + this.Actkind == that.Actkind && + Objects.equals(this.BookCode, that.BookCode); + } + + @Override + public int hashCode() { + return Objects.hash(ClassNo, ClassName, PosNum, WarnPosNum, UseFreeRate, BookRate, Actkind, BookCode); + } + + @Override + public String toString() { + return "SingleWaterHzWatchData[" + + "ClassNo=" + ClassNo + ", " + + "ClassName=" + ClassName + ", " + + "PosNum=" + PosNum + ", " + + "WarnPosNum=" + WarnPosNum + ", " + + "UseFreeRate=" + UseFreeRate + ", " + + "BookRate=" + BookRate + ", " + + "Actkind=" + Actkind + ", " + + "BookCode=" + BookCode + ']'; + } + + public void setActkind(int actkind) { + Actkind = actkind; + } + + public void setBookCode(String bookCode) { + BookCode = bookCode; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/weouc/LoginType.java b/app/src/main/java/cn/nahco3awa/naouc/network/weouc/LoginType.java new file mode 100644 index 0000000..04ee100 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/weouc/LoginType.java @@ -0,0 +1,13 @@ +package cn.nahco3awa.naouc.network.weouc; + +public enum LoginType { + UNDERGRADUATE("undergraduate"); + String name; + LoginType(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/weouc/WeOUCRequestSender.java b/app/src/main/java/cn/nahco3awa/naouc/network/weouc/WeOUCRequestSender.java new file mode 100644 index 0000000..0f12054 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/weouc/WeOUCRequestSender.java @@ -0,0 +1,12 @@ +package cn.nahco3awa.naouc.network.weouc; + +import okhttp3.OkHttpClient; + +public class WeOUCRequestSender { + private final OkHttpClient client; + private WeOUCRequestSender() { + client = new OkHttpClient(); + } + + +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/weouc/request/KbWeOUCRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/weouc/request/KbWeOUCRequest.java new file mode 100644 index 0000000..43949c4 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/weouc/request/KbWeOUCRequest.java @@ -0,0 +1,37 @@ +package cn.nahco3awa.naouc.network.weouc.request; + +import cn.nahco3awa.naouc.network.weouc.LoginType; +import cn.nahco3awa.naouc.network.weouc.WeOUCRequestSender; +import okhttp3.FormBody; +import okhttp3.Request; + +public class KbWeOUCRequest implements WeOUCRequest { + public static final String URL = "https://api.weouc.com/api/jw/kb"; + private final int xn; + private final int xq; + private final String sno; + private final LoginType loginType; + private final String password; + + public KbWeOUCRequest(int xn, int xq, String sno, LoginType loginType, String password) { + this.xn = xn; + this.xq = xq; + this.sno = sno; + this.loginType = loginType; + this.password = password; + } + + @Override + public Request makeRequest(WeOUCRequestSender sender) { + return new Request.Builder() + .url(URL) + .post(new FormBody.Builder() + .addEncoded("xn", String.valueOf(xn)) + .addEncoded("xq", String.valueOf(xq)) + .addEncoded("sno", sno) + .addEncoded("loginType", loginType.getName()) + .addEncoded("password", password) + .build()) + .build(); + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/weouc/request/WeOUCRequest.java b/app/src/main/java/cn/nahco3awa/naouc/network/weouc/request/WeOUCRequest.java new file mode 100644 index 0000000..40224d7 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/weouc/request/WeOUCRequest.java @@ -0,0 +1,8 @@ +package cn.nahco3awa.naouc.network.weouc.request; + +import cn.nahco3awa.naouc.network.weouc.WeOUCRequestSender; +import okhttp3.Request; + +public interface WeOUCRequest { + Request makeRequest(WeOUCRequestSender sender); +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/weouc/response/KbWeOUCResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/weouc/response/KbWeOUCResponse.java new file mode 100644 index 0000000..1e247dd --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/weouc/response/KbWeOUCResponse.java @@ -0,0 +1,40 @@ +package cn.nahco3awa.naouc.network.weouc.response; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import java.util.List; + +import cn.nahco3awa.naouc.network.weouc.response.data.kb.ClassData; +import okhttp3.Response; + +public class KbWeOUCResponse extends WeOUCResponse { + private final List classes; + private final boolean fromCache; + private final String message; + private final int status; + public KbWeOUCResponse(Response response) { + super(response); + JsonObject jsonObject = JsonParser.parseReader(response.body().charStream()).getAsJsonObject(); + fromCache = jsonObject.get("from_cache").getAsBoolean(); + message = jsonObject.get("message").getAsString(); + status = jsonObject.get("status").getAsInt(); + classes = ClassData.parseClassesData(jsonObject.get("data").getAsJsonArray()); + } + + public int getStatus() { + return status; + } + + public List getClasses() { + return classes; + } + + public String getMessage() { + return message; + } + + public boolean isFromCache() { + return fromCache; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/weouc/response/WeOUCCallback.java b/app/src/main/java/cn/nahco3awa/naouc/network/weouc/response/WeOUCCallback.java new file mode 100644 index 0000000..1a4ea5a --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/weouc/response/WeOUCCallback.java @@ -0,0 +1,6 @@ +package cn.nahco3awa.naouc.network.weouc.response; + +public interface WeOUCCallback { + void onSuccess(R response); + void onFailure(Throwable e); +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/weouc/response/WeOUCResponse.java b/app/src/main/java/cn/nahco3awa/naouc/network/weouc/response/WeOUCResponse.java new file mode 100644 index 0000000..a662699 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/weouc/response/WeOUCResponse.java @@ -0,0 +1,14 @@ +package cn.nahco3awa.naouc.network.weouc.response; + +import okhttp3.Response; + +public abstract class WeOUCResponse { + private final Response response; + public WeOUCResponse(Response response) { + this.response = response; + } + + public Response getResponse() { + return response; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/network/weouc/response/data/kb/ClassData.java b/app/src/main/java/cn/nahco3awa/naouc/network/weouc/response/data/kb/ClassData.java new file mode 100644 index 0000000..cf12450 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/network/weouc/response/data/kb/ClassData.java @@ -0,0 +1,134 @@ +package cn.nahco3awa.naouc.network.weouc.response.data.kb; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ClassData { + private final String name; + private final String classRoom; + private final String credit; + private final String origin; + private final String status; + private final String teacher; + private final int begin; + private final int num; + private final int day; + private final List weeks; + + public static final Map DAY_OF_WEEK_MAP = new HashMap<>(); + public static final int[] CLASS_MINUTES = { + 0, + 8 * 60, + 9 * 60, + 10 * 60 + 10, + 11 * 60 + 10, + // rest + 13 * 60 + 30, + 14 * 60 + 30, + 15 * 60 + 30, + 16 * 60 + 30, + 17 * 60 + 30, + 18 * 60 + 30, + 19 * 60 + 30, + 20 * 60 + 30 + }; + + public ClassData(String name, String classRoom, String credit, String origin, String status, String teacher, int begin, int num, int day, List weeks) { + this.name = name; + this.classRoom = classRoom; + this.credit = credit; + this.origin = origin; + this.status = status; + this.teacher = teacher; + this.begin = begin; + this.num = num; + this.day = day; + this.weeks = weeks; + } + + public List getWeeks() { + return weeks; + } + + public int getDay() { + return day; + } + + public int getNum() { + return num; + } + + public int getBegin() { + return begin; + } + + public String getTeacher() { + return teacher; + } + + public String getStatus() { + return status; + } + + public String getOrigin() { + return origin; + } + + public String getCredit() { + return credit; + } + + public String getClassRoom() { + return classRoom; + } + + public String getName() { + return name; + } + + public int getMinutesOfDay() { + return CLASS_MINUTES[getBegin()]; + } + + public static ClassData fromJson(JsonObject jsonObject) { + return new Gson().fromJson(jsonObject, ClassData.class); + } + + public static List parseClassesData(JsonArray data) { + List ret = new ArrayList<>(); + for (JsonElement datum : data) { + for (JsonElement classes : datum.getAsJsonObject().getAsJsonArray("classes")) { + if (classes.isJsonArray()) { + for (JsonElement classData : classes.getAsJsonArray()) { + if (classData.isJsonObject()) { + ret.add(fromJson(classData.getAsJsonObject())); + } + } + } + } + } + return ret; + } + + public static int getClassDayByDayOfWeek(int dayOfWeek) { + return DAY_OF_WEEK_MAP.get(dayOfWeek); + } + + static { + DAY_OF_WEEK_MAP.put(Calendar.SUNDAY, 6); + DAY_OF_WEEK_MAP.put(Calendar.MONDAY, 0); + DAY_OF_WEEK_MAP.put(Calendar.TUESDAY, 1); + DAY_OF_WEEK_MAP.put(Calendar.WEDNESDAY, 2); + DAY_OF_WEEK_MAP.put(Calendar.THURSDAY, 3); + DAY_OF_WEEK_MAP.put(Calendar.FRIDAY, 4); + DAY_OF_WEEK_MAP.put(Calendar.SATURDAY, 5); + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/OUCFragment.java b/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/OUCFragment.java new file mode 100644 index 0000000..2f4ff48 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/OUCFragment.java @@ -0,0 +1,385 @@ +package cn.nahco3awa.naouc.ui.ouc; + +import static android.content.Context.MODE_PRIVATE; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.PorterDuff; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.journeyapps.barcodescanner.BarcodeEncoder; + +import java.io.IOException; +import java.util.Locale; +import java.util.Random; + +import cn.nahco3awa.naouc.R; +import cn.nahco3awa.naouc.databinding.FragmentOucBinding; +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import cn.nahco3awa.naouc.network.ouc.request.GetBarCodePayOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.GetCardAccInfoOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.GetInfoByTokenOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.NetCheckOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.TsmOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.WaterApiAccUseHzWatchRequest; +import cn.nahco3awa.naouc.network.ouc.response.GetBarCodePayOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.GetCardAccInfoOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.GetInfoByTokenOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.OUCCallback; +import cn.nahco3awa.naouc.network.ouc.response.TsmOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.WaterApiAccUseHzWatchResponse; +import cn.nahco3awa.naouc.ui.ouc.activity.OucBalanceActivity; +import cn.nahco3awa.naouc.ui.ouc.activity.OucLoginMainActivity; +import cn.nahco3awa.naouc.ui.ouc.activity.OucNetActivity; +import cn.nahco3awa.naouc.ui.ouc.activity.OucWaterActivity; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Response; + +public class OUCFragment extends Fragment { + private SharedPreferences preferences; + private FragmentOucBinding binding; + private String sourceType = null; + private Button loginButton; + private TextView welcomeTextView; + private ActivityResultLauncher loginLauncher; + private ImageView barcodeImageView; + private ImageView qrCodeImageView; + private TextView balanceTextView; + private TextView netBalanceTextView; + private TextView waterAvailableTextView; + private ImageView netBalanceImageView; + + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + binding = FragmentOucBinding.inflate(inflater, container, false); + View root = binding.getRoot(); + Context context = root.getContext(); + + preferences = context.getSharedPreferences("ouc", MODE_PRIVATE); + if (!preferences.contains("imei")) { + StringBuilder stringBuilder = new StringBuilder(); + Random random = new Random(); + for (int i = 0; i < 32; i++) { + stringBuilder.append(random.nextInt(10)); + } + preferences.edit() + .putString("imei", stringBuilder.toString()) + .apply(); + } + OUCRequestSender.init(preferences.getString("imei", "1145141919810deadbeef52013149922")); + + loginLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { + if (result.getResultCode() == Activity.RESULT_OK) { + refreshLogonState(); + } + }); + + loginButton = root.findViewById(R.id.oucMainLoginButton); + welcomeTextView = root.findViewById(R.id.oucMainWelcomeTextView); + barcodeImageView = root.findViewById(R.id.oucBarcodeImageView); + qrCodeImageView = root.findViewById(R.id.qrCodeImageView); + balanceTextView = root.findViewById(R.id.balanceTextView); + netBalanceTextView = root.findViewById(R.id.netBalanceTextView); + netBalanceImageView = root.findViewById(R.id.netImage); + waterAvailableTextView = root.findViewById(R.id.waterBalanceTextView); + + loginButton.setOnClickListener(this::onClickLogin); + welcomeTextView.setOnClickListener(this::onClickWelcomeText); + barcodeImageView.setOnClickListener(this::onClickRefreshPayCode); + qrCodeImageView.setOnClickListener(this::onClickRefreshPayCode); + root.findViewById(R.id.cashButton).setOnClickListener(this::onClickBalance); + root.findViewById(R.id.netButton).setOnClickListener(this::onClickNet); + root.findViewById(R.id.waterButton).setOnClickListener(this::onClickWater); + + refreshLogonState(); + + return root; + } + + private GetInfoByTokenOUCResponse infoResponse = null; + + public void refreshLogonState() { + if (isLogon()) { + loginButton.setText("登出"); + sourceType = preferences.getString("source_ticket", ""); + OUCRequestSender.getInstance().setSourceTypeTicket(sourceType); + OUCRequestSender.getInstance().getInfoByToken(new GetInfoByTokenOUCRequest(OUCRequestSender.getInstance().getImeiTicket(), sourceType, sourceType), new OUCCallback<>() { + @Override + public void onSuccess(GetInfoByTokenOUCResponse response) { + infoResponse = response; + requireActivity().runOnUiThread(() -> { + refreshWelcomeText(); + refreshPayCode(); + refreshBalance(); + refreshNetBalance(); + refreshAvailableWater(); + }); + } + + @Override + public void onFailure(Throwable e) { + requireActivity().runOnUiThread(() -> { + Toast.makeText(getActivity(), "获取个人信息失败!", Toast.LENGTH_SHORT).show(); + welcomeTextView.setText("可能需要重新登录?"); + if (e.getMessage() != null && e.getMessage().equals("令牌失效")) { + preferences.edit() + .putBoolean("logon", false) + .apply(); + refreshLogonState(); + } + }); + } + }); + } else { + loginButton.setText("登录"); + welcomeTextView.setText("尚未登录"); + } + } + + private void onClickRefreshPayCode(View view) { + if (isLogon() && infoResponse != null) { + refreshPayCode(); + refreshBalance(); + refreshNetBalance(); + refreshAvailableWater(); + } + } + + @Override + public void onResume() { + super.onResume(); + refreshLogonState(); + } + + private void refreshAvailableWater() { + waterAvailableTextView.setText("..."); + OUCRequestSender.getInstance().waterApiAccUseHzWatch(new WaterApiAccUseHzWatchRequest(infoResponse.getAccount()), new OUCCallback<>() { + @Override + public void onSuccess(WaterApiAccUseHzWatchResponse response) { + requireActivity().runOnUiThread(() -> waterAvailableTextView.setText(response.getData().size() + " 可用")); + } + + @Override + public void onFailure(Throwable e) { + requireActivity().runOnUiThread(() -> waterAvailableTextView.setText("获取失败")); + } + }); + } + + private void refreshNetBalance() { + netBalanceTextView.setText("..."); + if (OUCRequestSender.getInstance().getjSessionId().isEmpty()) { + OUCRequestSender.getInstance().sendRequest(new NetCheckOUCRequest(), new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + requireActivity().runOnUiThread(() -> netBalanceTextView.setText("获取失败")); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + refreshNetByTsm(); + } + }); + } else { + refreshNetByTsm(); + } + } + + private void refreshNetByTsm() { + String account = infoResponse.getAccount(); + OUCRequestSender.getInstance().tsm(new TsmOUCRequest(account, infoResponse.getSno()), new OUCCallback<>() { + @Override + public void onSuccess(TsmOUCResponse response) { + requireActivity().runOnUiThread(() -> { + int redColor = 0xFFF44336; + if (response.isFrozen()) { + netBalanceTextView.setText("停机"); + netBalanceImageView.setColorFilter(redColor, PorterDuff.Mode.MULTIPLY); + } else { + netBalanceTextView.setText(String.format(Locale.SIMPLIFIED_CHINESE, "%.2f", response.getBalance())); + netBalanceImageView.setColorFilter(null); + } + }); + } + + @Override + public void onFailure(Throwable e) { + requireActivity().runOnUiThread(() -> { + netBalanceTextView.setText("获取失败"); + new androidx.appcompat.app.AlertDialog.Builder(requireActivity()) + .setTitle("网费详情获取失败") + .setMessage(e.getMessage()) + .show(); + }); + } + }); + } + + private void refreshBalance() { + String account = infoResponse.getAccount(); + balanceTextView.setText("..."); + OUCRequestSender.getInstance().getCardAccInfo(new GetCardAccInfoOUCRequest(account), new OUCCallback<>() { + @Override + public void onSuccess(GetCardAccInfoOUCResponse response) { + requireActivity().runOnUiThread(() -> balanceTextView.setText(String.format(Locale.SIMPLIFIED_CHINESE, "%.2f", response.getBalance() / 100.0))); + } + + @Override + public void onFailure(Throwable e) { + requireActivity().runOnUiThread(() -> balanceTextView.setText("获取失败")); + } + }); + } + + private void refreshPayCode() { + String account = infoResponse.getAccount(); + + try { + GetBarCodePayOUCRequest getBarCodePayOUCRequest = new GetBarCodePayOUCRequest(account, "1", OUCRequestSender.getInstance().getImeiTicket(), OUCRequestSender.getInstance().getSourceTypeTicket()); + OUCRequestSender.getInstance().getBarCodePay(getBarCodePayOUCRequest, new OUCCallback<>() { + @Override + public void onSuccess(GetBarCodePayOUCResponse response) { + try { + requireActivity().runOnUiThread(() -> { + try { + BarcodeFormat barcodeFormat = BarcodeFormat.CODE_128; + BarcodeEncoder barcodeEncoder = new BarcodeEncoder(); + BitMatrix bitMatrix = barcodeEncoder.encode(response.getBarcode()[0], barcodeFormat, barcodeImageView.getWidth(), barcodeImageView.getHeight()); + Bitmap barcodeBitmap = barcodeEncoder.createBitmap(bitMatrix); + barcodeImageView.setImageBitmap(barcodeBitmap); + + bitMatrix = barcodeEncoder.encode(response.getBarcode()[0], BarcodeFormat.QR_CODE, qrCodeImageView.getWidth(), qrCodeImageView.getHeight()); + Bitmap qrBitmap = barcodeEncoder.createBitmap(bitMatrix); + qrCodeImageView.setImageBitmap(qrBitmap); + } catch (WriterException e) { + requireActivity().runOnUiThread(() -> new AlertDialog.Builder(requireActivity()) + .setTitle("显示支付码错误!") + .setMessage(e.getMessage()) + .setNegativeButton("蒿", null) + .show()); + } + + }); + } catch (Exception e) { + requireActivity().runOnUiThread(() -> new AlertDialog.Builder(requireActivity()) + .setTitle("显示支付码错误!") + .setMessage(e.getMessage()) + .setNegativeButton("蒿", null) + .show()); + } + } + + @Override + public void onFailure(Throwable e) { + requireActivity().runOnUiThread(() -> new AlertDialog.Builder(requireActivity()) + .setTitle("获取支付码错误!") + .setMessage(e.getMessage()) + .setNegativeButton("蒿", null) + .show()); + } + }); + } catch (Exception e) { + requireActivity().runOnUiThread(() -> new AlertDialog.Builder(requireActivity()) + .setTitle("获取支付码错误!") + .setMessage(e.getMessage()) + .setNegativeButton("蒿", null) + .show()); + } + } + + private void onClickWater(View view) { + if (isLogon() && infoResponse != null) { + Intent intent = new Intent(getActivity(), OucWaterActivity.class); + intent.putExtra("account", infoResponse.getAccount()); + startActivity(intent); + } + } + + private void onClickBalance(View view) { + if (isLogon() && infoResponse != null) { + Intent intent = new Intent(getActivity(), OucBalanceActivity.class); + intent.putExtra("account", infoResponse.getAccount()); + startActivity(intent); + } + } + + private void onClickNet(View view) { + if (isLogon() && infoResponse != null) { + Intent intent = new Intent(getActivity(), OucNetActivity.class); + intent.putExtra("account", infoResponse.getAccount()); + intent.putExtra("sno", infoResponse.getSno()); + startActivity(intent); + } + } + + private void refreshWelcomeText() { + if (isLogon()) { + welcomeTextView.setText("欢迎回来," + getName() + "~"); + } else { + welcomeTextView.setText("尚未登录"); + } + } + + private String getName() { + return preferences.getString("nickname", infoResponse == null ? "" : infoResponse.getName()); + } + + public boolean isLogon() { + return preferences.getBoolean("logon",false); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; + } + + public void onClickLogin(View view) { + if (isLogon()) { + preferences.edit() + .putBoolean("logon", false) + .apply(); + refreshLogonState(); + } else { + loginLauncher.launch(new Intent(getActivity(), OucLoginMainActivity.class)); + } + } + + private void onClickWelcomeText(View view) { + EditText editText = new EditText(requireContext()); + editText.setHint("输出昵称..."); + String nick = preferences.getString("nickname", infoResponse == null ? "" : infoResponse.getName()); + editText.setText(nick); + new androidx.appcompat.app.AlertDialog.Builder(requireActivity()) + .setTitle("设置昵称:") + .setView(editText) + .setNegativeButton("取消", null) + .setPositiveButton("应用", (dialogInterface, i) -> { + preferences.edit() + .putString("nickname", editText.getText().toString()) + .apply(); + refreshWelcomeText(); + }) + .show(); + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/activity/OucBalanceActivity.java b/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/activity/OucBalanceActivity.java new file mode 100644 index 0000000..71661ed --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/activity/OucBalanceActivity.java @@ -0,0 +1,123 @@ +package cn.nahco3awa.naouc.ui.ouc.activity; + +import static android.view.View.INVISIBLE; +import static android.view.View.VISIBLE; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.text.InputType; +import android.view.View; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.Locale; + +import cn.nahco3awa.naouc.R; +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import cn.nahco3awa.naouc.network.ouc.request.AccountPayAliPayOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.GetCardAccInfoOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.GetMyBillOUCRequest; +import cn.nahco3awa.naouc.network.ouc.response.AccountPayOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.GetCardAccInfoOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.GetMyBillOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.OUCCallback; +import cn.nahco3awa.naouc.ui.ouc.view.ItemMyBillAdapter; + +public class OucBalanceActivity extends AppCompatActivity { + private String account; + private TextView balanceTextView; + private RecyclerView myBillView; + private ItemMyBillAdapter adapt = new ItemMyBillAdapter(new ArrayList<>()); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_ouc_balance); + + Intent intent = getIntent(); + account = intent.getStringExtra("account"); + + balanceTextView = findViewById(R.id.balanceBigTextView); + + myBillView = findViewById(R.id.myBillListView); + myBillView.setVisibility(INVISIBLE); + LinearLayoutManager layoutManager = new LinearLayoutManager(this); + layoutManager.setOrientation(LinearLayoutManager.VERTICAL); + myBillView.setLayoutManager(layoutManager); + myBillView.setAdapter(adapt); + + refreshBalance(); + refreshMyBill(); + } + + private void refreshMyBill() { + myBillView.setVisibility(INVISIBLE); + OUCRequestSender.getInstance().getMyBill(new GetMyBillOUCRequest(account, 1), new OUCCallback<>() { + @Override + public void onSuccess(GetMyBillOUCResponse response) { + runOnUiThread(() -> { + adapt = new ItemMyBillAdapter(response.getBillData()); + myBillView.setAdapter(adapt); + myBillView.setVisibility(VISIBLE); + }); + } + + @Override + public void onFailure(Throwable e) { + myBillView.setVisibility(INVISIBLE); + } + }); + } + + private void refreshBalance() { + OUCRequestSender.getInstance().getCardAccInfo(new GetCardAccInfoOUCRequest(account), new OUCCallback<>() { + @Override + public void onSuccess(GetCardAccInfoOUCResponse response) { + runOnUiThread(() -> balanceTextView.setText(String.format(Locale.SIMPLIFIED_CHINESE, "%.2f", response.getBalance() / 100.0))); + } + + @Override + public void onFailure(Throwable e) { + runOnUiThread(() -> Toast.makeText(OucBalanceActivity.this, "获取余额失败!", Toast.LENGTH_SHORT).show()); + } + }); + } + + public void onClickPay(View view) { + EditText editText = new EditText(this); + editText.setHint("请输入金额..."); + editText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL); + new AlertDialog.Builder(this) + .setTitle("充值") + .setView(editText) + .setNegativeButton("取消", null) + .setPositiveButton("充值", (dialogInterface, i) -> { + String amountString = editText.getText().toString(); + double amount = Double.parseDouble(amountString) * 100; + String parsedAmount = String.valueOf((int)amount); + OUCRequestSender.getInstance().accountPayAlipay(new AccountPayAliPayOUCRequest(account, parsedAmount), new OUCCallback<>() { + @Override + public void onSuccess(AccountPayOUCResponse response) { + runOnUiThread(() -> { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(response.getUrl())); + startActivity(intent); + }); + } + + @Override + public void onFailure(Throwable e) { + runOnUiThread(() -> Toast.makeText(OucBalanceActivity.this, "下单失败!", Toast.LENGTH_SHORT).show()); + } + }); + }) + .show(); + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/activity/OucLoginMainActivity.java b/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/activity/OucLoginMainActivity.java new file mode 100644 index 0000000..f752e23 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/activity/OucLoginMainActivity.java @@ -0,0 +1,172 @@ +package cn.nahco3awa.naouc.ui.ouc.activity; + +import static android.widget.Toast.LENGTH_SHORT; + +import android.app.Activity; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.Toast; + +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.spec.RSAPublicKeySpec; + +import javax.crypto.Cipher; + +import cn.nahco3awa.naouc.R; +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import cn.nahco3awa.naouc.network.ouc.request.GetRsaOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.GetValidateCodeOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.LoginOUCRequest; +import cn.nahco3awa.naouc.network.ouc.response.GetRsaKeyOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.GetValidateCodeOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.LoginOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.OUCCallback; + +public class OucLoginMainActivity extends AppCompatActivity { + + private SharedPreferences preferences; + private EditText snoEdit; + private EditText passwordEdit; + private EditText ysmEdit; + private CheckBox rememberCheck; + private ImageView ysmImageView; + private Button loginButton; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_ouc_login_main); + + snoEdit = findViewById(R.id.editTextOucSno); + passwordEdit = findViewById(R.id.editTextOucPassword); + ysmEdit = findViewById(R.id.editTextOucYsm); + rememberCheck = findViewById(R.id.checkBoxOucRemember); + ysmImageView = findViewById(R.id.oucYsmImageView); + loginButton = findViewById(R.id.buttonOucLogin); + + preferences = getSharedPreferences("ouc", MODE_PRIVATE); + if (preferences.getBoolean("mem", false)) { + snoEdit.setText(preferences.getString("mem_sno", "")); + passwordEdit.setText(preferences.getString("mem_pwd", "")); + rememberCheck.setChecked(true); + } + + getLoginNeeds(); + } + + private void getLoginNeeds() { + loginButton.setEnabled(false); + refreshYsmCode(); + } + + private void refreshYsmCode() { + OUCRequestSender.getInstance().getValidateCode(new GetValidateCodeOUCRequest(System.currentTimeMillis() + 1000 * 60 * 5), new OUCCallback<>() { + @Override + public void onSuccess(GetValidateCodeOUCResponse response) { + runOnUiThread(() -> { + ysmImageView.setImageBitmap(response.getImage()); + loginButton.setEnabled(true); + }); + } + + @Override + public void onFailure(Throwable e) { + runOnUiThread(() -> { + Toast.makeText(OucLoginMainActivity.this, "网络连接有点问题,请重试", LENGTH_SHORT).show(); + new AlertDialog.Builder(OucLoginMainActivity.this) + .setTitle("错误") + .setMessage("网络错误:\n" + e.getMessage()) + .setNegativeButton("好", null) + .show(); + }); + + } + }); + } + + private void onLoginFailed(Throwable e) { + new AlertDialog.Builder(this) + .setTitle("登录失败……") + .setMessage("中国海洋大学门户登录失败,请稍后再试。\n" + e.getMessage()) + .setNegativeButton("好", null) + .show(); + refreshYsmCode(); + } + + public void onClickLogin(View view) { + loginButton.setEnabled(false); + OUCRequestSender.getInstance().getRsaKey(new GetRsaOUCRequest(), new OUCCallback<>() { + @Override + public void onSuccess(GetRsaKeyOUCResponse response) { + try { + String pwd = passwordEdit.getText().toString(); + BigInteger bigExpo = new BigInteger(response.getE(), 16); + BigInteger bigModulus = new BigInteger(response.getMod(), 16); + RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigModulus, bigExpo); + KeyFactory factory = KeyFactory.getInstance("RSA"); + PublicKey publicKey = factory.generatePublic(keySpec); + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] light = pwd.getBytes(StandardCharsets.UTF_8); + byte[] encrypted = cipher.doFinal(light); + StringBuilder builder = new StringBuilder(encrypted.length * 2); + for (byte b : encrypted) { + String hexB = Integer.toHexString(b & 0xFF); + if (hexB.length() == 1) { + hexB = "0" + hexB; + } + builder.append(hexB); + } + String encryptedHex = builder.toString(); + OUCRequestSender.getInstance().login(new LoginOUCRequest(snoEdit.getText().toString(), encryptedHex, rememberCheck.isChecked(), "8898", ysmEdit.getText().toString(), response.getMsg()), new OUCCallback<>() { + @Override + public void onSuccess(LoginOUCResponse loginResponse) { + runOnUiThread(() -> { + if (rememberCheck.isChecked()) { + preferences.edit() + .putString("mem_sno", loginResponse.getSno()) + .putString("mem_pwd", pwd) + .apply(); + } + preferences.edit() + .putString("source_ticket", loginResponse.getRescouseType()) + .putBoolean("mem", rememberCheck.isChecked()) + .putBoolean("logon", true) + .putString("sno", loginResponse.getSno()) + .apply(); + String name = preferences.getString("nickname", loginResponse.getName()); + Toast.makeText(OucLoginMainActivity.this, name + ",欢迎回来~", LENGTH_SHORT).show(); + setResult(Activity.RESULT_OK); + finish(); + }); + } + + @Override + public void onFailure(Throwable e) { + runOnUiThread(() -> OucLoginMainActivity.this.onLoginFailed(e)); + } + }); + } catch (Exception e) { + runOnUiThread(() -> OucLoginMainActivity.this.onLoginFailed(e)); + Log.e("OUCLogin", "Login failed", e); + } + } + + @Override + public void onFailure(Throwable e) { + runOnUiThread(() -> OucLoginMainActivity.this.onLoginFailed(e)); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/activity/OucNetActivity.java b/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/activity/OucNetActivity.java new file mode 100644 index 0000000..2ddcb9a --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/activity/OucNetActivity.java @@ -0,0 +1,195 @@ +package cn.nahco3awa.naouc.ui.ouc.activity; + +import static android.widget.Toast.LENGTH_SHORT; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; +import android.net.wifi.WifiManager; +import android.os.Bundle; +import android.text.InputType; +import android.view.View; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import java.io.IOException; +import java.util.Locale; + +import cn.nahco3awa.naouc.R; +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import cn.nahco3awa.naouc.network.ouc.request.NetGdcOUCRequest; +import cn.nahco3awa.naouc.network.ouc.request.TsmOUCRequest; +import cn.nahco3awa.naouc.network.ouc.response.NetGdcOUCResponse; +import cn.nahco3awa.naouc.network.ouc.response.OUCCallback; +import cn.nahco3awa.naouc.network.ouc.response.TsmOUCResponse; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +public class OucNetActivity extends AppCompatActivity { + private TextView netStatusTextView; + private TextView netBalanceTextView; + private String account; + private String sno; + private SharedPreferences preferences; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_ouc_net); + Intent intent = getIntent(); + account = intent.getStringExtra("account"); + sno = intent.getStringExtra("sno"); + netStatusTextView = findViewById(R.id.netStatusTextView); + netBalanceTextView = findViewById(R.id.netBalanceBigTextView); + refreshBalance(); + preferences = getSharedPreferences("net", MODE_PRIVATE); + } + + private void refreshBalance() { + OUCRequestSender.getInstance().tsm(new TsmOUCRequest(account, sno), new OUCCallback<>() { + @Override + public void onSuccess(TsmOUCResponse response) { + runOnUiThread(() -> { + netBalanceTextView.setText(String.format(Locale.SIMPLIFIED_CHINESE, "%.2f", response.getBalance())); + netStatusTextView.setText(response.isFrozen() ? "校园网停机" : "校园网余额"); + if (response.isFrozen()) netStatusTextView.setTextColor(0xFFF44336); + }); + } + + @Override + public void onFailure(Throwable e) { + runOnUiThread(() -> { + netStatusTextView.setText("查询失败"); + netBalanceTextView.setText(""); + }); + } + }); + } + + public void onClickPay(View view) { + EditText editText = new EditText(this); + editText.setHint("请输入金额(元):"); + editText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL); + new AlertDialog.Builder(this) + .setTitle("网费缴纳") + .setView(editText) + .setNegativeButton("取消", null) + .setPositiveButton("缴纳", (dialog, which) -> { + String amountString = editText.getText().toString(); + double amount = Double.parseDouble(amountString) * 100; + OUCRequestSender.getInstance().netGdc(new NetGdcOUCRequest(account, sno, (int) amount), new OUCCallback<>() { + @Override + public void onSuccess(NetGdcOUCResponse response) { + runOnUiThread(() -> { + if (response.getRetCode() == 0) { + Toast.makeText(OucNetActivity.this, "缴费成功~", LENGTH_SHORT).show(); + refreshBalance(); + } else { + Toast.makeText(OucNetActivity.this, "缴费失败:" + response.getErrMessage(), LENGTH_SHORT).show(); + } + }); + } + + @Override + public void onFailure(Throwable e) { + runOnUiThread(() -> Toast.makeText(OucNetActivity.this, "缴费失败!", LENGTH_SHORT).show()); + } + }); + }) + .show(); + } + + public void onClickLogin(View view) { + String password = preferences.getString("password", ""); + if (password.isEmpty()) { + EditText editText = new EditText(this); + editText.setHint("请输入校园网密码..."); + editText.setAutofillHints(View.AUTOFILL_HINT_PASSWORD); + editText.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD); + new AlertDialog.Builder(this) + .setTitle("校园网认证") + .setView(editText) + .setNegativeButton("登录", (dialogInterface, i) -> { + preferences.edit() + .putString("password", editText.getText().toString()) + .apply(); + onClickLogin(null); + }) + .show(); + return; + } + WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE); + int iip = wifiManager.getConnectionInfo().getIpAddress(); + String ip = String.format( + Locale.SIMPLIFIED_CHINESE, + "%d.%d.%d.%d", + (iip & 0xff), + (iip >> 8 & 0xff), + (iip >> 16 & 0xff), + (iip >> 24 & 0xff) + ); + OkHttpClient client = new OkHttpClient(); + var request = new Request.Builder() + .url(String.format( + Locale.SIMPLIFIED_CHINESE, + "https://wxrz.ouc.edu.cn:802/eportal/portal/login?&callback=dr1003&login_method=1&user_account=%s&user_password=%s&wlan_user_ip=%s&wlan_user_ipv6=&wlan_user_mac=000000000000&wlan_ac_ip=&wlan_ac_name=&jsVersion=4.1&terminal_type=1&lang=zh-cn&v=3288&lang=zh", + sno, password, ip + )) + .get() + .build(); + client.newCall(request).enqueue(new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + Toast.makeText(OucNetActivity.this, "登录失败,请检查校园网连接!", LENGTH_SHORT).show(); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { + var resp = response.body().string(); + + if (!resp.startsWith("dr1003(") || !resp.endsWith(");")) { + Toast.makeText(OucNetActivity.this, "登录失败,响应异常!", LENGTH_SHORT).show(); + return; + } + + resp = resp.substring(7, resp.length() - 2); + JsonObject jsonObject = JsonParser.parseString(resp).getAsJsonObject(); + + int result = jsonObject.get("result").getAsInt(); + String msg = jsonObject.get("msg").getAsString(); + if (result == 1) { + Toast.makeText(OucNetActivity.this, "校园网登录成功~", LENGTH_SHORT).show(); + return; + } + + int code = jsonObject.get("ret_code").getAsInt(); + if (code == 2) { + Toast.makeText(OucNetActivity.this, "您已经在线啦~", LENGTH_SHORT).show(); + return; + } + + new AlertDialog.Builder(OucNetActivity.this) + .setTitle("校园网登录失败...") + .setMessage(msg) + .setNegativeButton("好吧", null) + .setPositiveButton("再试一次", (dialogInterface, i) -> onClickLogin(null)) + .show(); + } + }); + } + + public void onClickOfficial(View view) { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://222.195.158.36/Self/")); + startActivity(intent); + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/activity/OucWaterActivity.java b/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/activity/OucWaterActivity.java new file mode 100644 index 0000000..5fe6c91 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/activity/OucWaterActivity.java @@ -0,0 +1,74 @@ +package cn.nahco3awa.naouc.ui.ouc.activity; + +import static android.view.View.INVISIBLE; +import static android.view.View.VISIBLE; +import static android.widget.Toast.LENGTH_SHORT; + +import android.content.Intent; +import android.os.Bundle; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; + +import cn.nahco3awa.naouc.R; +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import cn.nahco3awa.naouc.network.ouc.request.WaterApiAccUseHzWatchRequest; +import cn.nahco3awa.naouc.network.ouc.response.OUCCallback; +import cn.nahco3awa.naouc.network.ouc.response.WaterApiAccUseHzWatchResponse; +import cn.nahco3awa.naouc.ui.ouc.view.ItemWaterAdapter; + +public class OucWaterActivity extends AppCompatActivity { + private String account = null; + private RecyclerView waterListView; + private ItemWaterAdapter adapter; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_ouc_water); + + Intent intent = getIntent(); + account = intent.getStringExtra("account"); + adapter = new ItemWaterAdapter(new ArrayList<>(), account, this); + + waterListView = findViewById(R.id.waterListView); + waterListView.setVisibility(INVISIBLE); + LinearLayoutManager layoutManager = new LinearLayoutManager(this); + layoutManager.setOrientation(LinearLayoutManager.VERTICAL); + waterListView.setLayoutManager(layoutManager); + waterListView.setAdapter(adapter); + + refreshWaterList(); + } + + private void refreshWaterList() { + waterListView.setVisibility(INVISIBLE); + OUCRequestSender.getInstance().waterApiAccUseHzWatch(new WaterApiAccUseHzWatchRequest(account), new OUCCallback<>() { + @Override + public void onSuccess(WaterApiAccUseHzWatchResponse response) { + runOnUiThread(() -> { + adapter = new ItemWaterAdapter(response.getData(), account, OucWaterActivity.this); + waterListView.setAdapter(adapter); + waterListView.setVisibility(VISIBLE); + }); + } + + @Override + public void onFailure(Throwable e) { + runOnUiThread(() -> { + waterListView.setVisibility(INVISIBLE); + Toast.makeText(OucWaterActivity.this, "刷新列表失败:" + e.getMessage(), LENGTH_SHORT).show(); + }); + } + }); + } + + @Override + protected void onResume() { + super.onResume(); + refreshWaterList(); + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/view/ItemMyBillAdapter.java b/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/view/ItemMyBillAdapter.java new file mode 100644 index 0000000..9f06e8e --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/view/ItemMyBillAdapter.java @@ -0,0 +1,96 @@ +package cn.nahco3awa.naouc.ui.ouc.view; + +import android.graphics.PorterDuff; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.List; +import java.util.Locale; + +import cn.nahco3awa.naouc.R; +import cn.nahco3awa.naouc.network.ouc.response.data.SingleBillData; + +public class ItemMyBillAdapter extends RecyclerView.Adapter { + private final List billData; + + public ItemMyBillAdapter(List billData) { + this.billData = billData; + } + + @NonNull @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_my_bill, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + SingleBillData data = billData.get(position); + holder.setData(data); + } + + public void addBillData(SingleBillData data) { + billData.add(data); + } + + @Override + public int getItemCount() { + return billData.size(); + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + private final ImageView iconImageView; + private final TextView tranNameTextView; + private final TextView mercnNameTextView; + private final TextView amountTextView; + private final TextView balanceTextView; + private SingleBillData data = null; + public ViewHolder(@NonNull View itemView) { + super(itemView); + iconImageView = itemView.findViewById(R.id.cellMyBillImageView); + tranNameTextView = itemView.findViewById(R.id.cellMyBillTranNameTextView); + mercnNameTextView = itemView.findViewById(R.id.cellMyBillMercnNameTextView); + amountTextView = itemView.findViewById(R.id.cellMyBillAmountTextView); + balanceTextView = itemView.findViewById(R.id.cellMyBillBalanceTextView); + + itemView.setOnClickListener(view -> { + if (data != null) { + new AlertDialog.Builder(view.getContext()) + .setTitle("消费详情") + .setMessage("订单时间:" + data.OCCTIME + + "\n生效时间:" + data.EFFECTDATE + + "\n流水金额:" + getFormat(data.TRANAMT) + + "\n账户余额:" + getFormat(data.CARDBAL) + + "\n支付地点:" + data.MERCNAME + + "\n\n" + data.JDESC) + .setNegativeButton("好",null) + .show(); + } + }); + } + + private void setData(SingleBillData data) { + this.data = data; + float amt = data.TRANAMT; + int color = amt < 0 ? 0xFFF44336 : 0xFF8BC34A; + iconImageView.setColorFilter(color, PorterDuff.Mode.MULTIPLY); + amountTextView.setTextColor(color); + amountTextView.setText(getFormat(amt)); + tranNameTextView.setText(data.TRANNAME); + mercnNameTextView.setText(data.MERCNAME); + balanceTextView.setText("余额:" + getFormat(data.CARDBAL)); + } + + @NonNull + private static String getFormat(float amt) { + return String.format(Locale.SIMPLIFIED_CHINESE, "%.2f", amt); + } + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/view/ItemWaterAdapter.java b/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/view/ItemWaterAdapter.java new file mode 100644 index 0000000..b81c2ee --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/ui/ouc/view/ItemWaterAdapter.java @@ -0,0 +1,124 @@ +package cn.nahco3awa.naouc.ui.ouc.view; + +import static android.widget.Toast.LENGTH_SHORT; + +import android.app.Activity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.List; +import java.util.Locale; + +import cn.nahco3awa.naouc.R; +import cn.nahco3awa.naouc.network.ouc.OUCRequestSender; +import cn.nahco3awa.naouc.network.ouc.request.WaterApiBookCodeCancelRequest; +import cn.nahco3awa.naouc.network.ouc.request.WaterApiBookCodeRequest; +import cn.nahco3awa.naouc.network.ouc.response.OUCCallback; +import cn.nahco3awa.naouc.network.ouc.response.WaterApiBookCodeCancelResponse; +import cn.nahco3awa.naouc.network.ouc.response.WaterApiBookCodeResponse; +import cn.nahco3awa.naouc.network.ouc.response.data.SingleWaterHzWatchData; + +public class ItemWaterAdapter extends RecyclerView.Adapter { + private final List data; + private final String account; + private final Activity activity; + public ItemWaterAdapter(List data, String account, Activity activity) { + this.data = data; + this.account = account; + this.activity = activity; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_ouc_water, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + SingleWaterHzWatchData datum = data.get(position); + holder.setData(datum, account, activity); + } + + @Override + public int getItemCount() { + return data.size(); + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + private final TextView nameTextView; + private final TextView statusTextView; + private final TextView bookTextView; + private SingleWaterHzWatchData data = null; + private String account = null; + private boolean active; + private Activity activity; + public ViewHolder(@NonNull View itemView) { + super(itemView); + active = true; + nameTextView = itemView.findViewById(R.id.cellOucWaterNameTextView); + statusTextView = itemView.findViewById(R.id.cellOucWaterStatusTextView); + bookTextView = itemView.findViewById(R.id.cellOucWaterBookTextView); + + itemView.setOnClickListener(view -> { + if (data != null && active) { + active = false; + if (data.Actkind() == 0) { + // 预约请求 + OUCRequestSender.getInstance().waterApiBookCode(new WaterApiBookCodeRequest(account, data.ClassNo()), new OUCCallback<>() { + @Override + public void onSuccess(WaterApiBookCodeResponse response) { + activity.runOnUiThread(() -> { + data.setActkind(1); + data.setBookCode(response.getBookCode()); + setData(data, account, activity); + }); + } + + @Override + public void onFailure(Throwable e) { + activity.runOnUiThread(() -> Toast.makeText(view.getContext(), "预约失败", LENGTH_SHORT).show()); + } + }); + } else { + // 取消预约 + OUCRequestSender.getInstance().waterApiBookCodeCancel(new WaterApiBookCodeCancelRequest(account, data.ClassNo()), new OUCCallback<>() { + @Override + public void onSuccess(WaterApiBookCodeCancelResponse response) { + activity.runOnUiThread(() -> { + data.setActkind(0); + data.setBookCode(""); + setData(data, account, activity); + }); + } + + @Override + public void onFailure(Throwable e) { + activity.runOnUiThread(() -> Toast.makeText(view.getContext(), "取消预约失败", LENGTH_SHORT).show()); + } + }); + } + } + }); + } + + private void setData(SingleWaterHzWatchData data, String account, Activity activity) { + this.account = account; + this.data = data; + this.activity = activity; + active = true; + int color = (data.Actkind() == 1 || data.UseFreeRate() <= 0) ? 0xFFF44336 : 0xFF8BC34A; + nameTextView.setText(data.ClassName()); + statusTextView.setText(String.format(Locale.SIMPLIFIED_CHINESE, "%.2f", data.UseFreeRate() / 100.0f) + "% 可用"); + bookTextView.setTextColor(color); + bookTextView.setText(data.Actkind() == 1 ? data.BookCode() : (data.UseFreeRate() > 0 ? "可预约" : "不可用")); + } + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/ui/weouc/WeOUCFragment.java b/app/src/main/java/cn/nahco3awa/naouc/ui/weouc/WeOUCFragment.java new file mode 100644 index 0000000..560dbe9 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/ui/weouc/WeOUCFragment.java @@ -0,0 +1,41 @@ +package cn.nahco3awa.naouc.ui.weouc; + +import static android.content.Context.MODE_PRIVATE; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import cn.nahco3awa.naouc.databinding.FragmentWeoucBinding; + +public class WeOUCFragment extends Fragment { + private FragmentWeoucBinding binding; + private Activity activity; + private Context context; + private View root; + private SharedPreferences preferences; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + binding = FragmentWeoucBinding.inflate(inflater, container, true); + activity = requireActivity(); + context = requireContext(); + root = binding.getRoot(); + + preferences = context.getSharedPreferences("weouc", MODE_PRIVATE); + + + return root; + } + + +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/util/QrCodeSign.java b/app/src/main/java/cn/nahco3awa/naouc/util/QrCodeSign.java new file mode 100644 index 0000000..41e1eef --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/util/QrCodeSign.java @@ -0,0 +1,24 @@ +package cn.nahco3awa.naouc.util; + +import android.util.Base64; + +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import synjones.commerce.utils.JniQrCodeSign; + +public class QrCodeSign { + private static final JniQrCodeSign jniQrCodeSign = new JniQrCodeSign(); + + public static String getSign(Map map) { + return jniQrCodeSign.GetSign(map); + } + + public static String getSign(String str) { + return jniQrCodeSign.GetOfflineQrCode(str); + } + + public static String getBase64(String str) { + return new String(Base64.decode(str, 0), StandardCharsets.UTF_8); + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/nahco3awa/naouc/util/RSASign.java b/app/src/main/java/cn/nahco3awa/naouc/util/RSASign.java new file mode 100644 index 0000000..3117fff --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/util/RSASign.java @@ -0,0 +1,26 @@ +package cn.nahco3awa.naouc.util; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashMap; + +public class RSASign { + private static final String f16254a = "SHA1withRSA"; + private static final String f16256c = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDil3PZhXSwuPZZ9M4oRARVCyvNjV6Ogw2ndE+1uIzt4SAsI5SlWwp3EL37es++FWd73lLX74h5NYxrzUo+yLPc1E0eqIw6MANPtd+mAFY+SsA2FdjJN91iNorqL+HKzYGzMn3yQ8BbiNWtX3L692smTRcVsimeeNm2/EdOAddrpQIDAQAB"; + private static final String f16257d = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOKXc9mFdLC49ln0zihEBFULK82NXo6DDad0T7W4jO3hICwjlKVbCncQvft6z74VZ3veUtfviHk1jGvNSj7Is9zUTR6ojDowA0+136YAVj5KwDYV2Mk33WI2iuov4crNgbMyffJDwFuI1a1fcvr3ayZNFxWyKZ542bb8R04B12ulAgMBAAECgYAwiEnq/DerJmK1j8acPz1CTds68p2fHpjNFg+Al5+vz7lJWvGanS5XpEFc3MgkKYd5s3vA/nAXrg1+hYDyg6BqM+lI6OHRZxPynaeQQeOiMDdTvR+pZ3b8uSx386riD1DM5d1oGRg9tMvJxQVCQU0bLrxViv2+KJf44dT3yqLJ3QJBAOwmsjpZi1BT+13IREjmdwEuTZRf7Ksffa1SvNyI+EoIpHZrM6V1w6vhRmJsENWe75qvJYy3hhem3i77O6z84oMCQQD1ow/fPTNxtM5qQKxMux3BEixp/j23ib+MIkoswmd+ARGtUEKqwnjXJWoXneXfWhMQTTJhBb5REwOEjOmv8IC3AkB09dlyMuVgHKgz07uWS6cHS7Ka2UOzoX4yePcXVzN6H3utNv02ZvRJzeJ5XsKbuwM7HqI/ZqogTsJejIoK7JkXAkAODxoudctG+8lArZju/1qxnT+rhWC0645qD+Bc9XeE77y6Rbi7G0xdTAfpeCEbCoXCzhhPE0wUSdlOsd4CMuq7AkBUD8WlEup0Ypa7oXTPGIdCOPZ+gxIWfEKBMAvi1paik+HIRfBdeaIPG1PyXrYDg8QWcZ3IrzMqLd9+jZ7XdYUF"; + public static String sign(HashMap map) { + String str = ""; + Object[] array = map.keySet().toArray(); + Arrays.sort(array); + for (Object obj : array) { + str = (obj.equals("request") || obj.equals("timestamp")) ? str + obj + URLDecoder.decode(map.get(obj), StandardCharsets.UTF_8) : str + obj + map.get(obj); + } + System.out.println("sourcestr=" + str); + String strB = new SignUtils().getPrivateKey(str, f16257d, f16254a); + System.out.println("sign=" + strB); + return strB; + + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/util/SignUtils.java b/app/src/main/java/cn/nahco3awa/naouc/util/SignUtils.java new file mode 100644 index 0000000..74c04ec --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/util/SignUtils.java @@ -0,0 +1,33 @@ +package cn.nahco3awa.naouc.util; + +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; + +public class SignUtils { + public String getPrivateKey(String data, String key, String alg) { + try { + return new String(Base64.getEncoder().encode(SignatureUtil.getPrivateSign(data.getBytes(), Base64.getDecoder().decode(key), alg))); + } catch (GeneralSecurityException e) { + return ""; + } + } + public String sign(String s, String key, String mode) { + Base64.Encoder encoder = Base64.getEncoder(); + try { + return new String(encoder.encode(SymmetricCryptoUtil.encrypt(s.getBytes(StandardCharsets.UTF_8), Base64.getDecoder().decode(key), mode, Cipher.ENCRYPT_MODE))); + } catch (NoSuchPaddingException | NoSuchAlgorithmException | + InvalidAlgorithmParameterException | InvalidKeyException | + IllegalBlockSizeException | BadPaddingException e) { + return ""; + } + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/util/SignatureUtil.java b/app/src/main/java/cn/nahco3awa/naouc/util/SignatureUtil.java new file mode 100644 index 0000000..170cd90 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/util/SignatureUtil.java @@ -0,0 +1,26 @@ +package cn.nahco3awa.naouc.util; + +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.spec.PKCS8EncodedKeySpec; + +public class SignatureUtil { + public static byte[] getPrivateSign(byte[] bs, byte[] bs2, String alg) throws GeneralSecurityException { + PrivateKey privateKeyA = a(bs2, alg); + Signature signature = Signature.getInstance(alg); + signature.initSign(privateKeyA); + signature.update(bs); + return signature.sign(); + } + + public static PrivateKey a(byte[] bArr, String str) throws GeneralSecurityException { + return KeyFactory.getInstance(a(str, "with")).generatePrivate(new PKCS8EncodedKeySpec(bArr)); + } + + public static String a(String str, String str2) { + int iIndexOf; + return (str2 == null || (iIndexOf = str.indexOf(str2)) == -1) ? "" : str.substring(iIndexOf + str2.length()); + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/util/SymmetricCryptoUtil.java b/app/src/main/java/cn/nahco3awa/naouc/util/SymmetricCryptoUtil.java new file mode 100644 index 0000000..7b6dcf1 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/util/SymmetricCryptoUtil.java @@ -0,0 +1,61 @@ +package cn.nahco3awa.naouc.util; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class SymmetricCryptoUtil { + public static byte[] encrypt(byte[] data, byte[] key, String algorithm, int mode) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + String finalAlgorithm = algorithm + "/CBC/PKCS5Padding"; + byte[] iv = createZeroBlock(finalAlgorithm); + return encrypt(key, iv, finalAlgorithm, "CBC", mode).doFinal(data); + } + + public static Cipher encrypt(byte[] key, byte[] iv, String transformation, String algorithm, int mode) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException { + Cipher cipher = Cipher.getInstance(transformation); + SecretKeySpec secretKeySpec = new SecretKeySpec(key, a(transformation, "/")); + if (isEquals(algorithm, "CBC")) { + cipher.init(mode, secretKeySpec, new IvParameterSpec(iv)); + } else { + cipher.init(mode, secretKeySpec); + } + return cipher; + } + + public static byte[] createZeroBlock(String transformation) throws NoSuchPaddingException, NoSuchAlgorithmException { + int blockSize = Cipher.getInstance(transformation).getBlockSize(); + return new byte[blockSize]; + } + + public static boolean isEquals(CharSequence charSequence, CharSequence charSequence2) { + if (charSequence == charSequence2) { + return true; + } + if (charSequence == null || charSequence2 == null) { + return false; + } + if ((charSequence instanceof String) && (charSequence2 instanceof String)) { + return charSequence.equals(charSequence2); + } + return charSequence.equals(charSequence2); + } + + public static String a(String str, String str2) { + if (str == null || str2 == null) { + return str; + } + if (str2.isEmpty()) { + return ""; + } + int iIndexOf = str.indexOf(str2); + return iIndexOf == -1 ? str : str.substring(0, iIndexOf); + } + +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/util/passwd/BarrettMu.java b/app/src/main/java/cn/nahco3awa/naouc/util/passwd/BarrettMu.java new file mode 100644 index 0000000..729a0ef --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/util/passwd/BarrettMu.java @@ -0,0 +1,56 @@ +package cn.nahco3awa.naouc.util.passwd; + +public class BarrettMu { + public BigInt modulus; + public int k; + public BigInt mu; + public BigInt bkplus1; + + public BarrettMu(BigInt m) { + modulus = m.biCopy(); + k = modulus.biHighIndex() + 1; + BigInt b2k = new BigInt(); + b2k.digits[2 * k] = 1; + mu = BigInt.biDivide(b2k, modulus); + bkplus1 = new BigInt(); + bkplus1.digits[k + 1] = 1; + } + + public BigInt modulo(BigInt x) { + var q1 = BigInt.biDivideByRadixPower(x, this.k - 1); + var q2 = BigInt.biMultiply(q1, this.mu); + var q3 = BigInt.biDivideByRadixPower(q2, this.k + 1); + var r1 = BigInt.biModuloByRadixPower(x, this.k + 1); + var r2term = BigInt.biMultiply(q3, this.modulus); + var r2 = BigInt.biModuloByRadixPower(r2term, this.k + 1); + var r = BigInt.biSubtract(r1, r2); + if (r.isNeg) { + r = BigInt.biAdd(r, this.bkplus1); + } + var rgtem = BigInt.biCompare(r, this.modulus) >= 0; + while (rgtem) { + r = BigInt.biSubtract(r, this.modulus); + rgtem = BigInt.biCompare(r, this.modulus) >= 0; + } + return r; + } + + public BigInt multiplyMod(BigInt x, BigInt y) { + var xy = BigInt.biMultiply(x, y); + return this.modulo(xy); + } + + public BigInt powMod(BigInt x, BigInt y) { + var result = new BigInt(); + result.digits[0] = 1; + var a = x; + var k = y; + while (true) { + if ((k.digits[0] & 1) != 0) result = this.multiplyMod(result, a); + k = k.biShiftRight(1); + if (k.digits[0] == 0 && k.biHighIndex() == 0) break; + a = this.multiplyMod(a, a); + } + return result; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/util/passwd/BigInt.java b/app/src/main/java/cn/nahco3awa/naouc/util/passwd/BigInt.java new file mode 100644 index 0000000..8b58468 --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/util/passwd/BigInt.java @@ -0,0 +1,444 @@ +package cn.nahco3awa.naouc.util.passwd; + +public class BigInt { + public boolean isNeg; + public int[] digits; + public static int maxDigits; + public static int[] zeroArray; + public static BigInt bigZero; + public static BigInt bigOne; + public static final int BI_RADIX = 1 << 16; + public static final int BI_RADIX_BASE = 2; + public static final int BI_RADIX_BITS = 16; + public static final int BI_HALF_RADIX = BI_RADIX >>> 1; + public static final long BI_RADIX_SQUARED = (long) BI_RADIX * BI_RADIX; + public static final int MAX_DIGIT_VAL = BI_RADIX - 1; + public static final long MAX_INTEGER = 9_999_999_999_999_998L; + public static final int[] LOW_BIT_MASKS = new int[] { + 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, + 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, + 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF + }; + public static final int[] HIGH_BIT_MASKS = new int[] { + 0x0000, 0x8000, 0xC000, 0xE000, 0xF000, 0xF800, + 0xFC00, 0xFE00, 0xFF00, 0xFF80, 0xFFC0, 0xFFE0, + 0xFFF0, 0xFFF8, 0xFFFC, 0xFFFE, 0xFFFF + }; + public static final char[] HEX_TO_CHAR = new char[] { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f' + }; + public static final char[] hexatrigesimalToChar = new char[] { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z' + }; + + public static void setMaxDigits(int value) { + maxDigits = value; + zeroArray = new int[maxDigits]; + for (int i = 0; i < maxDigits; i++) { + zeroArray[i] = 0; + } + bigZero = new BigInt(); + bigOne = new BigInt(); + bigOne.digits[0] = 1; + } + + public BigInt() { + digits = zeroArray.clone(); + isNeg = false; + } + + public BigInt(boolean flag) { + if (flag) { + digits = null; + } else { + digits = zeroArray.clone(); + } + isNeg = false; + } + + public static int charToHex(char c) { + int zero = 48; + int nine = zero + 9; + int lA = 97; + int lZ = lA + 25; + int bA = 65; + int bZ = bA + 25; + + if (c >= zero && c <= nine) { + return c - zero; + } else if (c >= bA && c <= bZ) { + return 10 + c - bA; + } else if (c >= lA && c <= lZ) { + return 10 + c - lA; + } else { + return 0; + } + } + + public static int hexToDigit(String s) { + int result = 0; + int sl = Math.min(s.length(), 4); + for (int i = 0; i < sl; i++) { + result <<= 4; + result |= charToHex(s.charAt(i)); + } + return result; + } + + public static BigInt biFromHex(String s) { + BigInt result = new BigInt(); + int sl = s.length(); + for (int i = sl, j = 0; i > 0; i -= 4, ++j) { + int ia = Math.max(i - 4, 0); + int ib = Math.min(i, 4); + result.digits[j] = hexToDigit(s.substring(Math.min(ia, ib), Math.max(ia, ib))); + } + return result; + } + + public static BigInt biDivideByRadixPower(BigInt x, int n) { + var ret = new BigInt(); + arrayCopy(x.digits, n, ret.digits, 0, ret.digits.length - n); + return ret; + } + + public static BigInt biMultiply(BigInt x, BigInt y) { + var result = new BigInt(); + int c; + var n = x.biHighIndex(); + var t = y.biHighIndex(); + int uv, k; + + for (int i = 0; i <= t; ++i) { + c = 0; + k = i; + for (int j = 0; j <= n; ++j, ++k) { + uv = result.digits[k] + x.digits[j] * y.digits[i] + c; + result.digits[k] = uv & MAX_DIGIT_VAL; + c = uv >>> BI_RADIX_BITS; + //c = Math.floor(uv / biRadix); + } + result.digits[i + n + 1] = c; + } + // Someone give me a logical xor, please. + result.isNeg = x.isNeg != y.isNeg; + return result; + } + + public static BigInt biModuloByRadixPower(BigInt x, int n) { + var result = new BigInt(); + arrayCopy(x.digits, 0, result.digits, 0, n); + return result; + } + + public static String biToHex(BigInt x) { + StringBuilder result = new StringBuilder(); + for (var i = x.biHighIndex(); i > -1; --i) { + result.append(digitToHex(x.digits[i])); + } + return result.toString(); + } + + + public static String reverseStr(String s) + { + StringBuilder result = new StringBuilder(); + for (var i = s.length() - 1; i > -1; --i) { + result.append(s.charAt(i)); + } + return result.toString(); + } + + private static String digitToHex(int n) { + var mask = 0xf; + StringBuilder result = new StringBuilder(); + for (int i = 0; i < 4; ++i) { + result.append(HEX_TO_CHAR[n & mask]); + n >>>= 4; + } + return reverseStr(result.toString()); + } + + public static String biToString(BigInt x, int radix) { + var b = new BigInt(); + b.digits[0] = radix; + var qr = biDivideModulo(x, b); + StringBuilder result = new StringBuilder(hexatrigesimalToChar[qr[1].digits[0]]); + while (biCompare(qr[0], bigZero) == 1) { + qr = biDivideModulo(qr[0], b); + // [???] digit = qr[1].digits[0]; + result.append(hexatrigesimalToChar[qr[1].digits[0]]); + } + return (x.isNeg ? "-" : "") + reverseStr(result.toString()); + } + + public int biHighIndex() { + int result = digits.length - 1; + while (result > 0 && digits[result] == 0) -- result; + return result; + } + + public BigInt biCopy() { + BigInt result = new BigInt(true); + result.digits = digits.clone(); + result.isNeg = isNeg; + return result; + } + + public static final int BITS_PER_DIGIT = 16; + public int biNumBits() { + int n = biHighIndex(); + int d = digits[n]; + int m = (n + 1) * BITS_PER_DIGIT; + int result; + for (result = m; result > m - BITS_PER_DIGIT; --result) { + if ((d & 0x8000) != 0) break; + d <<= 1; + } + return result; + } + + public static BigInt biAdd(BigInt x, BigInt y) { + BigInt result; + + if (x.isNeg != y.isNeg) { + y.isNeg = !y.isNeg; + result = biSubtract(x, y); + y.isNeg = !y.isNeg; + } + else { + result = new BigInt(); + var c = 0; + int n; + for (var i = 0; i < x.digits.length; ++i) { + n = x.digits[i] + y.digits[i] + c; + result.digits[i] = n % BI_RADIX; + c = (n >= BI_RADIX) ? 1 : 0; + } + result.isNeg = x.isNeg; + } + return result; + } + + + public static BigInt biSubtract(BigInt x, BigInt y) { + BigInt result; + if (x.isNeg != y.isNeg) { + y.isNeg = !y.isNeg; + result = biAdd(x, y); + y.isNeg = !y.isNeg; + } else { + result = new BigInt(); + int n, c; + c = 0; + for (var i = 0; i < x.digits.length; ++i) { + n = x.digits[i] - y.digits[i] + c; + result.digits[i] = n % BI_RADIX; + // Stupid non-conforming modulus operation. + if (result.digits[i] < 0) result.digits[i] += BI_RADIX; + c = -((n < 0) ? 1 : 0); + } + // Fix up the negative sign, if any. + if (c == -1) { + c = 0; + for (var i = 0; i < x.digits.length; ++i) { + n = -result.digits[i] + c; + result.digits[i] = n % BI_RADIX; + // Stupid non-conforming modulus operation. + if (result.digits[i] < 0) result.digits[i] += BI_RADIX; + c = -((n < 0) ? 1 : 0); + } + // Result is opposite sign of arguments. + result.isNeg = !x.isNeg; + } else { + // Result is same sign. + result.isNeg = x.isNeg; + } + } + return result; + } + + public static void arrayCopy(int[] src, int srcStart, int[] dest, int destStart, int n) + { + var m = Math.min(srcStart + n, src.length); + for (int i = srcStart, j = destStart; i < m; ++i, ++j) { + dest[j] = src[i]; + } + } + + public BigInt biShiftLeft(int n) { + int digitCount = (int) Math.floor((double) n / BITS_PER_DIGIT); + var result = new BigInt(); + + arrayCopy(digits, 0, result.digits, digitCount, + result.digits.length - digitCount); + + var bits = n % BITS_PER_DIGIT; + var rightBits = BITS_PER_DIGIT - bits; + int i, i1; + for (i = result.digits.length - 1, i1 = i - 1; i > 0; --i, --i1) { + result.digits[i] = ((result.digits[i] << bits) & MAX_DIGIT_VAL) | + ((result.digits[i1] & HIGH_BIT_MASKS[bits]) >>> + (rightBits)); + } + result.digits[0] = ((result.digits[i] << bits) & MAX_DIGIT_VAL); + result.isNeg = isNeg; + return result; + } + + public static int biCompare(BigInt x, BigInt y) + { + if (x.isNeg != y.isNeg) { + return 1 - 2 * (x.isNeg ? 1 : 0); + } + for (var i = x.digits.length - 1; i >= 0; --i) { + if (x.digits[i] != y.digits[i]) { + if (x.isNeg) { + return 1 - 2 * (x.digits[i] > y.digits[i] ? 1 : 0); + } else { + return 1 - 2 * (x.digits[i] < y.digits[i] ? 1 : 0); + } + } + } + return 0; + } + + public BigInt biShiftRight(int n) + { + int digitCount = (int) Math.floor((double) n / BITS_PER_DIGIT); + var result = new BigInt(); + arrayCopy(digits, digitCount, result.digits, 0, + digits.length - digitCount); + var bits = n % BITS_PER_DIGIT; + var leftBits = BITS_PER_DIGIT - bits; + for (int i = 0, i1 = i + 1; i < result.digits.length - 1; ++i, ++i1) { + result.digits[i] = (result.digits[i] >>> bits) | + ((result.digits[i1] & LOW_BIT_MASKS[bits]) << leftBits); + } + result.digits[result.digits.length - 1] >>>= bits; + result.isNeg = isNeg; + return result; + } + + public static BigInt biMultiplyDigit(BigInt x, int y) + { + int n, c, uv; + + BigInt result = new BigInt(); + n = x.biHighIndex(); + c = 0; + for (var j = 0; j <= n; ++j) { + uv = result.digits[j] + x.digits[j] * y + c; + result.digits[j] = uv & MAX_DIGIT_VAL; + c = uv >>> BI_RADIX_BITS; + //c = Math.floor(uv / biRadix); + } + result.digits[1 + n] = c; + return result; + } + + public static BigInt biMultiplyByRadixPower(BigInt x, int n) + { + var result = new BigInt(); + arrayCopy(x.digits, 0, result.digits, n, result.digits.length - n); + return result; + } + + public static BigInt[] biDivideModulo(BigInt x, BigInt y) { + int nb = x.biNumBits(); + int tb = y.biNumBits(); + boolean origYIsNeg = y.isNeg; + BigInt q, r; + if (nb < tb) { + if (x.isNeg) { + q = bigOne.biCopy(); + q.isNeg = !y.isNeg; + x.isNeg = false; + y.isNeg = false; + r = biSubtract(y, x); + // Restore signs, 'cause they're references. + x.isNeg = true; + y.isNeg = origYIsNeg; + } else { + q = new BigInt(); + r = x.biCopy(); + } + return new BigInt[]{q, r}; + } + + q = new BigInt(); + r = x; + + // Normalize Y. + int t = (int) (Math.ceil((double) tb / BITS_PER_DIGIT) - 1); + var lambda = 0; + while (y.digits[t] < BI_HALF_RADIX) { + y = y.biShiftLeft(1); + ++lambda; + ++tb; + t = (int) (Math.ceil((double) tb / BITS_PER_DIGIT) - 1); + } + // Shift r over to keep the quotient constant. We'll shift the + // remainder back at the end. + r = r.biShiftLeft(lambda); + nb += lambda; // Update the bit count for x. + int n = (int) (Math.ceil((double) nb / BITS_PER_DIGIT) - 1); + + var b = biMultiplyByRadixPower(y, n - t); + while (biCompare(r, b) != -1) { + ++q.digits[n - t]; + r = biSubtract(r, b); + } + for (var i = n; i > t; --i) { + var ri = (i >= r.digits.length) ? 0 : r.digits[i]; + var ri1 = (i - 1 >= r.digits.length) ? 0 : r.digits[i - 1]; + var ri2 = (i - 2 >= r.digits.length) ? 0 : r.digits[i - 2]; + var yt = (t >= y.digits.length) ? 0 : y.digits[t]; + var yt1 = (t - 1 >= y.digits.length) ? 0 : y.digits[t - 1]; + if (ri == yt) { + q.digits[i - t - 1] = MAX_DIGIT_VAL; + } else { + q.digits[i - t - 1] = (int) Math.floor((double) (ri * BI_RADIX + ri1) / yt); + } + + var c1 = q.digits[i - t - 1] * ((yt * BI_RADIX) + yt1); + var c2 = (ri * BI_RADIX_SQUARED) + (((long) ri1 * BI_RADIX) + ri2); + while (c1 > c2) { + --q.digits[i - t - 1]; + c1 = q.digits[i - t - 1] * ((yt * BI_RADIX) | yt1); + c2 = ((long) ri * BI_RADIX * BI_RADIX) + (((long) ri1 * BI_RADIX) + ri2); + } + + b = biMultiplyByRadixPower(y, i - t - 1); + r = biSubtract(r, biMultiplyDigit(b, q.digits[i - t - 1])); + if (r.isNeg) { + r = biAdd(r, b); + --q.digits[i - t - 1]; + } + } + r = r.biShiftRight(lambda); + // Fiddle with the signs and stuff to make sure that 0 <= r < y. + q.isNeg = x.isNeg != origYIsNeg; + if (x.isNeg) { + if (origYIsNeg) { + q = biAdd(q, bigOne); + } else { + q = biSubtract(q, bigOne); + } + y = y.biShiftRight(lambda); + r = biSubtract(y, r); + } + // Check for the unbelievably stupid degenerate case of r == -0. + if (r.digits[0] == 0 && r.biHighIndex() == 0) r.isNeg = false; + + return new BigInt[]{q, r}; + } + + public static BigInt biDivide(BigInt x, BigInt y) { + return biDivideModulo(x, y)[0]; + } +} diff --git a/app/src/main/java/cn/nahco3awa/naouc/util/passwd/RSA.java b/app/src/main/java/cn/nahco3awa/naouc/util/passwd/RSA.java new file mode 100644 index 0000000..20da4ef --- /dev/null +++ b/app/src/main/java/cn/nahco3awa/naouc/util/passwd/RSA.java @@ -0,0 +1,94 @@ +package cn.nahco3awa.naouc.util.passwd; + +public class RSA { + public static class RSAKeyPair { + public BigInt e; + public BigInt d; + public BigInt m; + public int digitSize; + public int chunkSize; + public int radix; + public BarrettMu barrett; + + public RSAKeyPair(String encryptionExponent, String decryptionExponent, String modulus) { + this.e = BigInt.biFromHex(encryptionExponent); + this.d = BigInt.biFromHex(decryptionExponent); + this.m = BigInt.biFromHex(modulus); + + ////////////////////////////////// TYF + this.digitSize = 2 * m.biHighIndex() + 2; + this.chunkSize = this.digitSize - 11; + ////////////////////////////////// TYF + + this.radix = 16; + this.barrett = new BarrettMu(this.m); + } + } + public static String encryptedString(RSAKeyPair key, String s) { + ////////////////////////////////// TYF + if (key.chunkSize > key.digitSize - 11) + { + return "Error"; + } + ////////////////////////////////// TYF + + + var a = new int[s.length()]; + var sl = s.length(); + + var i = 0; + while (i < sl) { + a[i] = s.charAt(i); + i++; + } + + var al = a.length; + StringBuilder result = new StringBuilder(); + int j, k; + BigInt block; + for (i = 0; i < al; i += key.chunkSize) { + block = new BigInt(); + j = 0; + + //for (k = i; k < i + key.chunkSize; ++j) { + // block.digits[j] = a[k++]; + // block.digits[j] += a[k++] << 8; + //} + + ////////////////////////////////// TYF + // Add PKCS#1 v1.5 padding + // 0x00 || 0x02 || PseudoRandomNonZeroBytes || 0x00 || Message + // Variable a before padding must be of at most digitSize-11 + // That is for 3 marker bytes plus at least 8 random non-zero bytes + int x; + var msgLength = (i+key.chunkSize)>al ? al%key.chunkSize : key.chunkSize; + var paddedSize = Math.max(8, key.digitSize - 3 - msgLength); + // Variable b with 0x00 || 0x02 at the highest index. + int[] b = new int[Math.max(msgLength+1+paddedSize, key.digitSize)]; + for (x=0; x + + diff --git a/app/src/main/res/drawable-anydpi/ic_action_coin.xml b/app/src/main/res/drawable-anydpi/ic_action_coin.xml new file mode 100644 index 0000000..4c488d6 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_action_coin.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable-anydpi/ic_action_money.xml b/app/src/main/res/drawable-anydpi/ic_action_money.xml new file mode 100644 index 0000000..5099c55 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_action_money.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable-anydpi/ic_action_public.xml b/app/src/main/res/drawable-anydpi/ic_action_public.xml new file mode 100644 index 0000000..7ff6c6c --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_action_public.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable-anydpi/ic_action_study.xml b/app/src/main/res/drawable-anydpi/ic_action_study.xml new file mode 100644 index 0000000..5e000ec --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_action_study.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable-anydpi/ic_action_water.xml b/app/src/main/res/drawable-anydpi/ic_action_water.xml new file mode 100644 index 0000000..c3f6c8e --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_action_water.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable-hdpi/ic_action_card.png b/app/src/main/res/drawable-hdpi/ic_action_card.png new file mode 100644 index 0000000..867e463 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_card.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_coin.png b/app/src/main/res/drawable-hdpi/ic_action_coin.png new file mode 100644 index 0000000..652a0e3 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_coin.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_money.png b/app/src/main/res/drawable-hdpi/ic_action_money.png new file mode 100644 index 0000000..359e676 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_money.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_public.png b/app/src/main/res/drawable-hdpi/ic_action_public.png new file mode 100644 index 0000000..205fcce Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_public.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_study.png b/app/src/main/res/drawable-hdpi/ic_action_study.png new file mode 100644 index 0000000..650fae1 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_study.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_water.png b/app/src/main/res/drawable-hdpi/ic_action_water.png new file mode 100644 index 0000000..101b525 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_water.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_card.png b/app/src/main/res/drawable-mdpi/ic_action_card.png new file mode 100644 index 0000000..be4db39 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_card.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_coin.png b/app/src/main/res/drawable-mdpi/ic_action_coin.png new file mode 100644 index 0000000..0fcbf15 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_coin.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_money.png b/app/src/main/res/drawable-mdpi/ic_action_money.png new file mode 100644 index 0000000..fe5340a Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_money.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_public.png b/app/src/main/res/drawable-mdpi/ic_action_public.png new file mode 100644 index 0000000..e7231fa Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_public.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_study.png b/app/src/main/res/drawable-mdpi/ic_action_study.png new file mode 100644 index 0000000..b60b4a4 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_study.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_water.png b/app/src/main/res/drawable-mdpi/ic_action_water.png new file mode 100644 index 0000000..72d1b61 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_water.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_card.png b/app/src/main/res/drawable-xhdpi/ic_action_card.png new file mode 100644 index 0000000..aa63af9 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_card.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_coin.png b/app/src/main/res/drawable-xhdpi/ic_action_coin.png new file mode 100644 index 0000000..4c519d8 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_coin.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_money.png b/app/src/main/res/drawable-xhdpi/ic_action_money.png new file mode 100644 index 0000000..8cbb68e Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_money.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_public.png b/app/src/main/res/drawable-xhdpi/ic_action_public.png new file mode 100644 index 0000000..33ba26d Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_public.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_study.png b/app/src/main/res/drawable-xhdpi/ic_action_study.png new file mode 100644 index 0000000..50bbc94 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_study.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_water.png b/app/src/main/res/drawable-xhdpi/ic_action_water.png new file mode 100644 index 0000000..75f319d Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_water.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_card.png b/app/src/main/res/drawable-xxhdpi/ic_action_card.png new file mode 100644 index 0000000..24be06f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_card.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_coin.png b/app/src/main/res/drawable-xxhdpi/ic_action_coin.png new file mode 100644 index 0000000..6f5c673 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_coin.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_money.png b/app/src/main/res/drawable-xxhdpi/ic_action_money.png new file mode 100644 index 0000000..d2217f3 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_money.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_public.png b/app/src/main/res/drawable-xxhdpi/ic_action_public.png new file mode 100644 index 0000000..adf4bb0 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_public.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_study.png b/app/src/main/res/drawable-xxhdpi/ic_action_study.png new file mode 100644 index 0000000..e0fc866 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_study.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_water.png b/app/src/main/res/drawable-xxhdpi/ic_action_water.png new file mode 100644 index 0000000..a9ae066 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_water.png differ diff --git a/app/src/main/res/drawable/ic_dashboard_black_24dp.xml b/app/src/main/res/drawable/ic_dashboard_black_24dp.xml new file mode 100644 index 0000000..46fc8de --- /dev/null +++ b/app/src/main/res/drawable/ic_dashboard_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_home_black_24dp.xml b/app/src/main/res/drawable/ic_home_black_24dp.xml new file mode 100644 index 0000000..f8bb0b5 --- /dev/null +++ b/app/src/main/res/drawable/ic_home_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_notifications_black_24dp.xml b/app/src/main/res/drawable/ic_notifications_black_24dp.xml new file mode 100644 index 0000000..78b75c3 --- /dev/null +++ b/app/src/main/res/drawable/ic_notifications_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ouc_login.png b/app/src/main/res/drawable/ouc_login.png new file mode 100644 index 0000000..3a213b8 Binary files /dev/null and b/app/src/main/res/drawable/ouc_login.png differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..bad97d6 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,32 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_ouc_balance.xml b/app/src/main/res/layout/activity_ouc_balance.xml new file mode 100644 index 0000000..715e65a --- /dev/null +++ b/app/src/main/res/layout/activity_ouc_balance.xml @@ -0,0 +1,57 @@ + + + + + + + +