diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..5a570f6 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +hhu \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..2e2a914 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,124 @@ + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/dictionaries/Mr_right.xml b/.idea/dictionaries/Mr_right.xml new file mode 100644 index 0000000..c805775 --- /dev/null +++ b/.idea/dictionaries/Mr_right.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 53ba2e5..7aa2f2d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,29 +1,28 @@ plugins { id("com.android.application") id("kotlin-android") + id("kotlin-kapt") } - android { - val androidTargetSdkVersion: Int by rootProject.extra - val androidMinSdkVersion: Int by rootProject.extra + val targetSdkVersion: Int by rootProject.extra + val minSdkVersion: Int by rootProject.extra val androidBuildToolsVersion: String by rootProject.extra - val androidCompileSdkVersion: Int by rootProject.extra - val androidCompileNdkVersion: String by rootProject.extra - val defaultManagerPackageName: String by rootProject.extra + val compileSdkVersion: Int by rootProject.extra + val compileNdkVersion: String by rootProject.extra + val packageName: String by rootProject.extra val verCode: Int by rootProject.extra val verName: String by rootProject.extra - - compileSdk = androidCompileSdkVersion - ndkVersion = androidCompileNdkVersion + compileSdk = compileSdkVersion + ndkVersion = compileNdkVersion buildToolsVersion = androidBuildToolsVersion defaultConfig { - applicationId = defaultManagerPackageName - minSdk = androidMinSdkVersion - targetSdk = androidTargetSdkVersion + applicationId = packageName + minSdk = minSdkVersion + targetSdk = targetSdkVersion versionCode = verCode versionName = verName @@ -69,6 +68,14 @@ dependencies { implementation(libs.bundles.accompanist) implementation(libs.bundles.lifecycle) implementation(libs.bundles.retrofit) + implementation(libs.bundles.room) implementation(libs.bundles.thirdparty) + kapt("androidx.room:room-compiler:2.3.0") + + implementation("androidx.appcompat:appcompat:1.3.1") + + debugImplementation("androidx.compose.ui:ui-tooling:1.0.2") + androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.0.2") + } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2d2cfa1..943fc2d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,20 +13,23 @@ android:supportsRtl="true" android:theme="@style/Theme.Hhu.NoActionBar" android:usesCleartextTraffic="true"> - + diff --git a/app/src/main/java/github/sukieva/hhu/data/entity/Website.kt b/app/src/main/java/github/sukieva/hhu/data/entity/Website.kt new file mode 100644 index 0000000..3a194be --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/data/entity/Website.kt @@ -0,0 +1,17 @@ +package github.sukieva.hhu.data.entity + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Index +import androidx.room.PrimaryKey + +@Entity(indices = [Index(value = ["site_name"], unique = true)]) +data class Website( + @ColumnInfo(name = "site_name") + var siteName: String, + @ColumnInfo(name = "site_address") + var siteAddress: String +) { + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/github/sukieva/hhu/data/bean/Course.kt b/app/src/main/java/github/sukieva/hhu/data/entity/bean/Course.kt similarity index 86% rename from app/src/main/java/github/sukieva/hhu/data/bean/Course.kt rename to app/src/main/java/github/sukieva/hhu/data/entity/bean/Course.kt index 5eb68c4..d77cd35 100644 --- a/app/src/main/java/github/sukieva/hhu/data/bean/Course.kt +++ b/app/src/main/java/github/sukieva/hhu/data/entity/bean/Course.kt @@ -1,4 +1,4 @@ -package github.sukieva.hhu.data.bean +package github.sukieva.hhu.data.entity.bean data class Course( diff --git a/app/src/main/java/github/sukieva/hhu/data/bean/LoginData.kt b/app/src/main/java/github/sukieva/hhu/data/entity/bean/LoginData.kt similarity index 78% rename from app/src/main/java/github/sukieva/hhu/data/bean/LoginData.kt rename to app/src/main/java/github/sukieva/hhu/data/entity/bean/LoginData.kt index a619663..8e1745f 100644 --- a/app/src/main/java/github/sukieva/hhu/data/bean/LoginData.kt +++ b/app/src/main/java/github/sukieva/hhu/data/entity/bean/LoginData.kt @@ -1,12 +1,13 @@ -package github.sukieva.hhu.data.bean +package github.sukieva.hhu.data.entity.bean + import java.math.BigInteger import java.security.MessageDigest + data class LoginData( var username: String, - var password: String, - var captcha: String + var password: String ) { init { // md5 加密(小写字母+数字) val md = MessageDigest.getInstance("MD5") diff --git a/app/src/main/java/github/sukieva/hhu/data/entity/bean/PostData.kt b/app/src/main/java/github/sukieva/hhu/data/entity/bean/PostData.kt new file mode 100644 index 0000000..067d064 --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/data/entity/bean/PostData.kt @@ -0,0 +1,15 @@ +package github.sukieva.hhu.data.entity.bean + + +data class PostData( + var maccount: String, + var mwid: String, + var mname: String, + var maid: String, + var minstitute: String, + var mgrade: String, + var mclass: String, + var mbuilding: String, + var mroom: String, + var mphone: String +) diff --git a/app/src/main/java/github/sukieva/hhu/data/bean/Rank.kt b/app/src/main/java/github/sukieva/hhu/data/entity/bean/Rank.kt similarity index 84% rename from app/src/main/java/github/sukieva/hhu/data/bean/Rank.kt rename to app/src/main/java/github/sukieva/hhu/data/entity/bean/Rank.kt index be5b4d9..eae309e 100644 --- a/app/src/main/java/github/sukieva/hhu/data/bean/Rank.kt +++ b/app/src/main/java/github/sukieva/hhu/data/entity/bean/Rank.kt @@ -1,4 +1,4 @@ -package github.sukieva.hhu.data.bean +package github.sukieva.hhu.data.entity.bean data class Rank( var name: String, diff --git a/app/src/main/java/github/sukieva/hhu/data/local/AppDatabase.kt b/app/src/main/java/github/sukieva/hhu/data/local/AppDatabase.kt new file mode 100644 index 0000000..b63c521 --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/data/local/AppDatabase.kt @@ -0,0 +1,32 @@ +package github.sukieva.hhu.data.local + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import github.sukieva.hhu.data.entity.Website +import github.sukieva.hhu.data.local.dao.FavDao + +@Database(version = 1, exportSchema = false, entities = [Website::class]) +abstract class AppDatabase : RoomDatabase() { + + abstract fun favDao(): FavDao + + + companion object { // 全局只创建一个数据库实例 + private var instance: AppDatabase? = null + + @Synchronized + fun getDatabase(context: Context): AppDatabase { + instance?.let { + return it + } + return Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database") + .fallbackToDestructiveMigration() // 数据库升级时销毁,测试用 + .build().apply { + instance = this + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/data/local/dao/FavDao.kt b/app/src/main/java/github/sukieva/hhu/data/local/dao/FavDao.kt new file mode 100644 index 0000000..f9b49a0 --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/data/local/dao/FavDao.kt @@ -0,0 +1,24 @@ +package github.sukieva.hhu.data.local.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import github.sukieva.hhu.data.entity.Website + +@Dao +interface FavDao { + + @Insert + suspend fun insertWeb(website: Website): Long + + + @Query("select * from Website") + suspend fun loadAllWebs(): List + + @Query("delete from Website") + suspend fun deleteAllWebs() + + @Query("delete from Website where site_name = :siteName") + suspend fun deleteWebBySiteName(siteName: String): Int +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/data/remote/EasyOkhttp.kt b/app/src/main/java/github/sukieva/hhu/data/remote/EasyOkhttp.kt new file mode 100644 index 0000000..8d41ffb --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/data/remote/EasyOkhttp.kt @@ -0,0 +1,59 @@ +package github.sukieva.hhu.data.remote + +import okhttp3.* +import java.io.IOException +import java.util.concurrent.TimeUnit +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +object EasyOkhttp { + + val sessionCookieJar = SessionCookieJar() + + val okHttpClient = OkHttpClient().newBuilder() + .connectTimeout(10, TimeUnit.SECONDS) + .readTimeout(10, TimeUnit.SECONDS) + .cookieJar(sessionCookieJar) + .build() + + fun sendHttpRequest(address: String, body: RequestBody? = null, callback: Callback) { + val request = + if (body != null) Request.Builder().url(address).post(body).build() + else Request.Builder().url(address).build() + okHttpClient.newCall(request).enqueue(callback) + } + + + + suspend fun request(address: String, body: RequestBody? = null): String { + return suspendCoroutine { + sendHttpRequest(address, body, object : Callback { + override fun onFailure(call: Call, e: IOException) { + it.resumeWithException(e) + } + + override fun onResponse(call: Call, response: Response) { + val resBody = response.body()?.string() + if (resBody != null) it.resume(resBody) + else it.resumeWithException(RuntimeException("response body is null")) + } + + }) + } + } + + + // https://stackoverflow.com/questions/38418809/add-cookies-to-retrofit-2-request + class SessionCookieJar : CookieJar { + private val cookieStore: HashMap> = HashMap() + override fun saveFromResponse(url: HttpUrl, cookies: List) { + cookieStore[url.host()] = cookies + } + + override fun loadForRequest(url: HttpUrl): List { + val cookies = cookieStore[url.host()] + return cookies ?: ArrayList() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/network/JwxtService.kt b/app/src/main/java/github/sukieva/hhu/data/remote/retrofit/Api.kt similarity index 52% rename from app/src/main/java/github/sukieva/hhu/network/JwxtService.kt rename to app/src/main/java/github/sukieva/hhu/data/remote/retrofit/Api.kt index 357cf53..983db18 100644 --- a/app/src/main/java/github/sukieva/hhu/network/JwxtService.kt +++ b/app/src/main/java/github/sukieva/hhu/data/remote/retrofit/Api.kt @@ -1,25 +1,25 @@ -package github.sukieva.hhu.network +package github.sukieva.hhu.data.remote.retrofit -import github.sukieva.hhu.data.bean.LoginData +import github.sukieva.hhu.data.entity.bean.LoginData import retrofit2.Call import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST -interface JwxtService { +interface Api { @POST("j_spring_security_check") - fun login(@Body data: LoginData): Call + fun login(@Body data: LoginData): Call @GET("img/captcha.jpg") - fun getCaptchaPic(): Call + fun getCaptchaPic(): Call @GET("student/integratedQuery/scoreQuery/schemeScores/callback") - fun getGrades(): Call + fun getGrades(): Call @GET("student/integratedQuery/gpaRankingQuery/index/jdpmcx") - fun getRank(): Call + fun getRank(): Call } \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/network/JwxtNetwork.kt b/app/src/main/java/github/sukieva/hhu/data/remote/retrofit/Network.kt similarity index 64% rename from app/src/main/java/github/sukieva/hhu/network/JwxtNetwork.kt rename to app/src/main/java/github/sukieva/hhu/data/remote/retrofit/Network.kt index 646a78e..05b23f5 100644 --- a/app/src/main/java/github/sukieva/hhu/network/JwxtNetwork.kt +++ b/app/src/main/java/github/sukieva/hhu/data/remote/retrofit/Network.kt @@ -1,24 +1,24 @@ -package github.sukieva.hhu.network +package github.sukieva.hhu.data.remote.retrofit import retrofit2.Call import retrofit2.Callback import retrofit2.Response -import github.sukieva.hhu.data.bean.LoginData +import github.sukieva.hhu.data.entity.bean.LoginData import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine -object JwxtNetwork { +object Network { - private val jwxtService = ServiceCreator.create() + private val service = ServiceCreator.create() - suspend fun login(data: LoginData) = jwxtService.login(data).await() + suspend fun login(data: LoginData) = service.login(data).await() - suspend fun getCaptchaPic() = jwxtService.getCaptchaPic().await() + suspend fun getCaptchaPic() = service.getCaptchaPic().await() - suspend fun getGrades() = jwxtService.getGrades().await() + suspend fun getGrades() = service.getGrades().await() - suspend fun getRank() = jwxtService.getRank().await() + suspend fun getRank() = service.getRank().await() private suspend fun Call.await(): T { diff --git a/app/src/main/java/github/sukieva/hhu/data/remote/retrofit/ServiceCreator.kt b/app/src/main/java/github/sukieva/hhu/data/remote/retrofit/ServiceCreator.kt new file mode 100644 index 0000000..4d183c6 --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/data/remote/retrofit/ServiceCreator.kt @@ -0,0 +1,29 @@ +package github.sukieva.hhu.data.remote.retrofit + +import github.sukieva.hhu.data.remote.EasyOkhttp.sessionCookieJar +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + + +object ServiceCreator { + private const val BASE_URL = "http://jwxs.hhu.edu.cn/" + + private val retrofit = Retrofit.Builder() + .baseUrl(BASE_URL) + .client( + OkHttpClient().newBuilder() + .cookieJar(sessionCookieJar) + .build() + ) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + + fun create(serviceClass: Class): T = retrofit.create(serviceClass) + + inline fun create(): T = create(T::class.java) + + +} + diff --git a/app/src/main/java/github/sukieva/hhu/data/repository/LocalRepository.kt b/app/src/main/java/github/sukieva/hhu/data/repository/LocalRepository.kt new file mode 100644 index 0000000..424b824 --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/data/repository/LocalRepository.kt @@ -0,0 +1,34 @@ +package github.sukieva.hhu.data.repository + +import github.sukieva.hhu.MyApp +import github.sukieva.hhu.data.entity.Website +import github.sukieva.hhu.data.local.AppDatabase +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.* + +object LocalRepository { + + private val favDao = AppDatabase.getDatabase(MyApp.context).favDao() + + fun getWebsites(): Flow> = flow { + val websites = favDao.loadAllWebs() + emit(websites) + }.flowOn(Dispatchers.IO) // 通过 flowOn 切换到 IO 线程 + + suspend fun resetWebs() { + favDao.deleteAllWebs() + favDao.insertWeb(Website("奥蓝系统", "http://smst.hhu.edu.cn/Mobile/login.aspx")) + favDao.insertWeb(Website("教务系统", "http://jwxs.hhu.edu.cn/")) + favDao.insertWeb(Website("信息门户", "http://my.hhu.edu.cn/portal-web/html/index.html")) + } + + suspend fun insertWeb(webSite: Website) = favDao.insertWeb(webSite) + + suspend fun deleteWebByName(siteName: String) = favDao.deleteWebBySiteName(siteName) + + suspend fun updateWeb(webSite: Website) { + deleteWebByName(webSite.siteName) + favDao.insertWeb(webSite) + } + +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/data/repository/RemoteRepository.kt b/app/src/main/java/github/sukieva/hhu/data/repository/RemoteRepository.kt new file mode 100644 index 0000000..b12cad1 --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/data/repository/RemoteRepository.kt @@ -0,0 +1,57 @@ +package github.sukieva.hhu.data.repository + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import coil.request.ImageRequest +import github.sukieva.hhu.data.remote.EasyOkhttp +import github.sukieva.hhu.utils.LogUtil +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn +import okhttp3.Call +import okhttp3.Callback +import okhttp3.Request +import okhttp3.Response +import java.io.IOException +import java.io.InputStream +import kotlin.concurrent.thread + +object RemoteRepository { + + private const val BASE_URL = "http://jwxs.hhu.edu.cn/" + private const val captchaPicUrl = BASE_URL + "img/captcha.jpg" + private const val loginUrl = BASE_URL + "j_spring_security_check" + private const val gradesUrl = BASE_URL + "student/integratedQuery/scoreQuery/schemeScores/callback" + private const val rankUrl = BASE_URL + "student/integratedQuery/gpaRankingQuery/index/jdpmcx" + private const val TAG = "HTTP" + + + fun getCaptchaPic(): Bitmap? { + val request = Request.Builder().url(captchaPicUrl).build() + try { + val response = EasyOkhttp.okHttpClient.newCall(request).execute() + if (!response.isSuccessful) response.close() + if (response.body() != null) { + val `is`: InputStream? = response.body()!!.byteStream() + LogUtil.d(TAG, "==> Successfully get captchaPic") + return BitmapFactory.decodeStream(`is`) + } + } catch (e: Exception) { + LogUtil.d(TAG, "==> Fail to get captchaPic") + } + return null + } + + + fun getHitokoto() = flow { + try { + val hitoko = EasyOkhttp.request("https://v1.hitokoto.cn/?encode=text&c=i") + LogUtil.d("Hitokoto", "==> Hitokoto is $hitoko") + emit(hitoko) + } catch (e: Exception) { + e.printStackTrace() + LogUtil.d("Hitokoto", "Fail to get Hitokoto") + } + }.flowOn(Dispatchers.IO) + +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/data/repository/Repository.kt b/app/src/main/java/github/sukieva/hhu/data/repository/Repository.kt new file mode 100644 index 0000000..01cd387 --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/data/repository/Repository.kt @@ -0,0 +1,8 @@ +package github.sukieva.hhu.data.repository + +interface Repository { + + fun getWebsites() = LocalRepository.getWebsites() + + +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/network/ServiceCreator.kt b/app/src/main/java/github/sukieva/hhu/network/ServiceCreator.kt deleted file mode 100644 index a95e4ed..0000000 --- a/app/src/main/java/github/sukieva/hhu/network/ServiceCreator.kt +++ /dev/null @@ -1,42 +0,0 @@ -package github.sukieva.hhu.network - -import okhttp3.Cookie -import okhttp3.CookieJar -import okhttp3.HttpUrl -import okhttp3.OkHttpClient -import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory - - -object ServiceCreator { - private const val BASE_URL = "http://jwxs.hhu.edu.cn/" - - private val retrofit = Retrofit.Builder() - .baseUrl(BASE_URL) - .client( - OkHttpClient().newBuilder() - .cookieJar(SessionCookieJar()) - .build() - ) - .addConverterFactory(GsonConverterFactory.create()) - .build() - - fun create(serviceClass: Class): T = retrofit.create(serviceClass) - - inline fun create(): T = create(T::class.java) - - // https://stackoverflow.com/questions/38418809/add-cookies-to-retrofit-2-request - class SessionCookieJar : CookieJar { - private val cookieStore: HashMap> = HashMap() - override fun saveFromResponse(url: HttpUrl, cookies: List) { - cookieStore[url.host()] = cookies - } - - override fun loadForRequest(url: HttpUrl): List { - val cookies = cookieStore[url.host()] - return cookies ?: ArrayList() - } - } - -} - diff --git a/app/src/main/java/github/sukieva/hhu/ui/activity/MainActivity.kt b/app/src/main/java/github/sukieva/hhu/ui/activity/MainActivity.kt deleted file mode 100644 index bf25c67..0000000 --- a/app/src/main/java/github/sukieva/hhu/ui/activity/MainActivity.kt +++ /dev/null @@ -1,39 +0,0 @@ -package github.sukieva.hhu.ui.activity - -import android.os.Bundle -import androidx.activity.compose.setContent -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.ui.Modifier -import github.sukieva.hhu.ui.activity.base.BaseActivity -import github.sukieva.hhu.ui.activity.base.InitView -import github.sukieva.hhu.ui.components.* -import github.sukieva.hhu.utils.errorToast -import github.sukieva.hhu.utils.infoToast - -class MainActivity : BaseActivity() { - - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContent { - InitView { - HomeAppBar() - CardCheckIn() - CardGradeQuery() - CardSchedule() - ListCardFavourite() - ListCardConfig() - } - } - } - - - override fun onResume() { - super.onResume() - "开始交互".errorToast() - } - - -} - diff --git a/app/src/main/java/github/sukieva/hhu/ui/activity/AboutActivity.kt b/app/src/main/java/github/sukieva/hhu/ui/activity/about/AboutActivity.kt similarity index 96% rename from app/src/main/java/github/sukieva/hhu/ui/activity/AboutActivity.kt rename to app/src/main/java/github/sukieva/hhu/ui/activity/about/AboutActivity.kt index f066ec5..4e9a3b5 100644 --- a/app/src/main/java/github/sukieva/hhu/ui/activity/AboutActivity.kt +++ b/app/src/main/java/github/sukieva/hhu/ui/activity/about/AboutActivity.kt @@ -1,4 +1,4 @@ -package github.sukieva.hhu.ui.activity +package github.sukieva.hhu.ui.activity.about import android.widget.ImageView import android.widget.TextView diff --git a/app/src/main/java/github/sukieva/hhu/ui/activity/base/BaseActivity.kt b/app/src/main/java/github/sukieva/hhu/ui/activity/base/BaseActivity.kt index 692c919..064dba2 100644 --- a/app/src/main/java/github/sukieva/hhu/ui/activity/base/BaseActivity.kt +++ b/app/src/main/java/github/sukieva/hhu/ui/activity/base/BaseActivity.kt @@ -2,13 +2,9 @@ package github.sukieva.hhu.ui.activity.base import android.os.Bundle import androidx.activity.ComponentActivity -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier import github.sukieva.hhu.ui.theme.MyTheme import github.sukieva.hhu.utils.ActivityCollector @@ -30,11 +26,11 @@ open class BaseActivity : ComponentActivity() { @Composable fun InitView(content: @Composable () -> Unit) { + MyTheme { Surface(color = MaterialTheme.colors.background) { - Column(modifier = Modifier.fillMaxHeight()) { - content() - } + content() } } + } \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/ui/activity/ConfigActivity.kt b/app/src/main/java/github/sukieva/hhu/ui/activity/config/ConfigActivity.kt similarity index 57% rename from app/src/main/java/github/sukieva/hhu/ui/activity/ConfigActivity.kt rename to app/src/main/java/github/sukieva/hhu/ui/activity/config/ConfigActivity.kt index f42ffb4..28fa1fe 100644 --- a/app/src/main/java/github/sukieva/hhu/ui/activity/ConfigActivity.kt +++ b/app/src/main/java/github/sukieva/hhu/ui/activity/config/ConfigActivity.kt @@ -1,18 +1,17 @@ -package github.sukieva.hhu.ui.activity +package github.sukieva.hhu.ui.activity.config import android.os.Bundle import androidx.activity.compose.setContent +import androidx.compose.material.ExperimentalMaterialApi import github.sukieva.hhu.ui.activity.base.BaseActivity -import github.sukieva.hhu.ui.activity.base.InitView -import github.sukieva.hhu.ui.components.MaterialTopAppBar class ConfigActivity : BaseActivity() { + @ExperimentalMaterialApi override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + setContent { - InitView { - MaterialTopAppBar() - } + ConfigView() } } } \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/ui/activity/config/ConfigView.kt b/app/src/main/java/github/sukieva/hhu/ui/activity/config/ConfigView.kt new file mode 100644 index 0000000..f0c42f1 --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/ui/activity/config/ConfigView.kt @@ -0,0 +1,211 @@ +package github.sukieva.hhu.ui.activity.config + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Clear +import androidx.compose.material.icons.outlined.CheckCircleOutline +import androidx.compose.material.icons.outlined.Save +import androidx.compose.material.icons.rounded.ManageSearch +import androidx.compose.material.icons.rounded.PendingActions +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import com.google.accompanist.insets.ProvideWindowInsets +import github.sukieva.hhu.R +import github.sukieva.hhu.ui.activity.base.InitView +import github.sukieva.hhu.ui.components.ExpandableCard +import github.sukieva.hhu.ui.components.ListCard +import github.sukieva.hhu.ui.components.MaterialTopAppBar +import github.sukieva.hhu.ui.components.MyScaffold +import github.sukieva.hhu.ui.theme.fontHead +import github.sukieva.hhu.utils.LogUtil +import github.sukieva.hhu.utils.browse + + +@ExperimentalMaterialApi +@Composable +fun ConfigView() { + val model: ConfigViewModel = viewModel() + model.showConfig() + ProvideWindowInsets(windowInsetsAnimationsEnabled = true) { + InitView { + MyScaffold(topBar = { ConfigAppBar() }) { + LazyColumn(modifier = Modifier.fillMaxHeight()) { + item { GradeItem() } + item { CheckInItem() } + item { ScheduleItem() } + } + } + } + } +} + +@Composable +fun ScheduleItem() { + ListCard( + title = stringResource(id = R.string.home_card_schedule), + icon = Icons.Rounded.PendingActions, + modifier = Modifier + .padding(start = 22.dp, top = 12.dp) + .height(50.dp) + .clickable { + browse("https://www.coolapk.com/apk/com.suda.yzune.wakeupschedule") + } + ) +} + +@ExperimentalMaterialApi +@Composable +fun GradeItem() { + ExpandableCard( + title = stringResource(id = R.string.home_card_results), + icon = Icons.Rounded.ManageSearch, + content = { + val model: ConfigViewModel = viewModel() + val accountState = remember { model.account } + val passwordState = remember { model.password } + Column { + MyTextField( + state = accountState, + label = "学号" + ) + MyTextField( + state = passwordState, + label = "教务系统密码" + ) + } + } + ) + +} + +@ExperimentalMaterialApi +@Composable +fun CheckInItem() { + ExpandableCard( + title = stringResource(id = R.string.home_card_checkin), + icon = Icons.Outlined.CheckCircleOutline, + content = { + val model: ConfigViewModel = viewModel() + val wid = remember { model.wid } + val name = remember { model.name } + val aid = remember { model.aid } + val institute = remember { model.institute } + val grade = remember { model.grade } + val mclass = remember { model.mclass } + val building = remember { model.building } + val room = remember { model.room } + val phone = remember { model.phone } + Text( + text = "请对照健康打卡填写\n还需填写上面的学号", + color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium), + style = MaterialTheme.typography.caption, + modifier = Modifier.padding(start = 35.dp) + ) + Column { + MyTextField( + state = wid, + label = "wid(无需修改 自动填写)", + readOnly = true + ) + MyTextField( + state = name, + label = "姓名" + ) + MyTextField( + state = aid, + label = "身份证号" + ) + MyTextField( + state = institute, + label = "学院" + ) + MyTextField( + state = grade, + label = "年级" + ) + MyTextField( + state = mclass, + label = "班级" + ) + MyTextField( + state = building, + label = "宿舍楼" + ) + MyTextField( + state = room, + label = "宿舍号" + ) + MyTextField( + state = phone, + label = "电话号码" + ) + } + } + ) + +} + + +@Composable +fun MyTextField( + state: MutableState, + label: String, + readOnly: Boolean = false +) { + Row( + modifier = Modifier + .padding(top = 10.dp) + .fillMaxWidth(), + horizontalArrangement = Arrangement.Center + ) { + OutlinedTextField( + value = state.value, + readOnly = readOnly, + onValueChange = { state.value = it }, + label = { Text(label) }, + textStyle = TextStyle(color = MaterialTheme.colors.fontHead), + trailingIcon = @Composable { + Image(imageVector = Icons.Filled.Clear, // 清除图标 + contentDescription = null, + modifier = Modifier.clickable { state.value = "" }) // 给图标添加点击事件,点击就清空text + }, + modifier = Modifier.width(300.dp) + ) + } + +} + +@Composable +fun ConfigAppBar() { + val model: ConfigViewModel = viewModel() + MaterialTopAppBar( + title = stringResource(id = R.string.home_list_config), + actions = { + IconButton(onClick = { + model.saveConfig() + LogUtil.d("ConfigAppBar", "Save config") + }) { + Icon(Icons.Outlined.Save, contentDescription = "Save config") + } + } + ) +} + + +@ExperimentalMaterialApi +@Preview +@Composable +fun ConfigViewPreview() { + ConfigView() +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/ui/activity/config/ConfigViewModel.kt b/app/src/main/java/github/sukieva/hhu/ui/activity/config/ConfigViewModel.kt new file mode 100644 index 0000000..106ba83 --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/ui/activity/config/ConfigViewModel.kt @@ -0,0 +1,68 @@ +package github.sukieva.hhu.ui.activity.config + +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import github.sukieva.hhu.ui.components.InJavaScriptLocalObj +import github.sukieva.hhu.utils.DataManager +import github.sukieva.hhu.utils.infoToast +import kotlinx.coroutines.launch + +class ConfigViewModel : ViewModel() { + + + var account = mutableStateOf("") + var password = mutableStateOf("") + var wid = mutableStateOf("") + var name = mutableStateOf("") + var aid = mutableStateOf("") + var institute = mutableStateOf("") + var grade = mutableStateOf("") + var mclass = mutableStateOf("") + var building = mutableStateOf("") + var room = mutableStateOf("") + var phone = mutableStateOf("") + val configUrl = "http://dailyreport.hhu.edu.cn/pdc/formDesign/showFormFilled?selfFormWid=A335B048C8456F75E0538101600A6A04&lwUserId=${account.value}" + + fun saveConfig() { + viewModelScope.launch { + DataManager.saveData("account", account.value) + DataManager.saveData("password", password.value) + DataManager.saveData("wid", wid.value) + DataManager.saveData("name", name.value) + DataManager.saveData("aid", aid.value) + DataManager.saveData("institute", institute.value) + DataManager.saveData("grade", grade.value) + DataManager.saveData("mclass", mclass.value) + DataManager.saveData("building", building.value) + DataManager.saveData("room", room.value) + DataManager.saveData("phone", phone.value) + } + "已保存配置".infoToast() + } + + fun showConfig() { + viewModelScope.launch { + account.value = DataManager.readData("account", "") + password.value = DataManager.readData("password", "") + wid.value = DataManager.readData("wid", "") + name.value = DataManager.readData("name", "") + aid.value = DataManager.readData("aid", "") + institute.value = DataManager.readData("institute", "") + grade.value = DataManager.readData("grade", "") + mclass.value = DataManager.readData("mclass", "") + building.value = DataManager.readData("building", "") + room.value = DataManager.readData("room", "") + phone.value = DataManager.readData("phone", "") + if (wid.value == "") wid.value = "A335B048C8456F75E0538101600A6A04" + return@launch + } + } + + fun getHtml() { + val html = InJavaScriptLocalObj.webHtml + println(html) + } + + +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/ui/activity/FavouriteActivity.kt b/app/src/main/java/github/sukieva/hhu/ui/activity/favourite/FavouriteActivity.kt similarity index 50% rename from app/src/main/java/github/sukieva/hhu/ui/activity/FavouriteActivity.kt rename to app/src/main/java/github/sukieva/hhu/ui/activity/favourite/FavouriteActivity.kt index 4122ca9..2c8a25d 100644 --- a/app/src/main/java/github/sukieva/hhu/ui/activity/FavouriteActivity.kt +++ b/app/src/main/java/github/sukieva/hhu/ui/activity/favourite/FavouriteActivity.kt @@ -1,24 +1,21 @@ -package github.sukieva.hhu.ui.activity +package github.sukieva.hhu.ui.activity.favourite import android.os.Bundle import androidx.activity.compose.setContent import github.sukieva.hhu.ui.activity.base.BaseActivity -import github.sukieva.hhu.ui.activity.base.InitView -import github.sukieva.hhu.ui.components.MaterialTopAppBar -import github.sukieva.hhu.ui.components.MyWebView class FavouriteActivity : BaseActivity() { + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + + val siteAddress: String? = intent.getStringExtra("siteAddress") + val siteName: String? = intent.getStringExtra("siteName") setContent { - InitView { - MaterialTopAppBar() - MyWebView() - } + FavouriteView(siteAddress, siteName) } } - } \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/ui/activity/favourite/FavouriteView.kt b/app/src/main/java/github/sukieva/hhu/ui/activity/favourite/FavouriteView.kt new file mode 100644 index 0000000..34e7858 --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/ui/activity/favourite/FavouriteView.kt @@ -0,0 +1,203 @@ +package github.sukieva.hhu.ui.activity.favourite + +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.OpenInBrowser +import androidx.compose.material.icons.rounded.Add +import androidx.compose.material.icons.rounded.Done +import androidx.compose.material.icons.rounded.RestartAlt +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel +import github.sukieva.hhu.MyApp +import github.sukieva.hhu.R +import github.sukieva.hhu.data.entity.Website +import github.sukieva.hhu.ui.activity.base.InitView +import github.sukieva.hhu.ui.components.* +import github.sukieva.hhu.ui.theme.fontHead +import github.sukieva.hhu.utils.* + + +@Composable +fun FavouriteView(siteAddress: String? = null, siteName: String? = null) { + val model: FavouriteViewModel = viewModel() + if (siteAddress != null && siteName != null) { + LogUtil.d("FavouriteActivity", "==>$siteAddress") + InitView { + MyScaffold(topBar = { FavWebAppBar(siteAddress, siteName) }) { + MyWebView(siteAddress) + } + } + } else { + model.getWebs() + val websites = model.websites + InitView { + MyScaffold(topBar = { FavAppBar() }) { + LazyColumn(modifier = Modifier.fillMaxHeight()) { + items(websites) { website -> + FavListItem(website) + } + } + } + } + } +} + +@Composable +fun FavListItem(website: Website) { + val model: FavouriteViewModel = viewModel() + ListCard( + title = website.siteName, + modifier = Modifier + .padding(start = 5.dp, end = 5.dp, top = 12.dp, bottom = 12.dp) + .fillMaxWidth() + .height(50.dp) + .pointerInput(Unit) { + detectTapGestures( + onLongPress = { + alert( + message = MyApp.context.getString(R.string.dialog_press_message), + positive = MyApp.context.getString(R.string.dialog_press_confirm), + showNegative = true, + negative = MyApp.context.getString(R.string.dialog_press_cancel), + onPositiveClick = { + model.deleteWebByName(website.siteName) + model.refreshUI() + } + ) + }, + onTap = { + start { + putExtra("siteAddress", website.siteAddress) + putExtra("siteName", website.siteName) + } + } + ) + } + ) +} + +@Composable +fun FavWebAppBar(siteAddress: String, siteName: String) { + MaterialTopAppBar( + title = siteName, + actions = { + IconButton(onClick = { browse(siteAddress) }) { + Icon(Icons.Outlined.OpenInBrowser, contentDescription = "Browse by system") + } + } + ) + if (siteName == "信息门户") + "信息门户请在浏览器中打开哦".warningToast() +} + + +@Composable +fun FavAppBar() { + val model: FavouriteViewModel = viewModel() + val openDialog = remember { model.isOpenDialog } + MaterialTopAppBar( + title = stringResource(id = R.string.home_list_favourite), + actions = { + IconButton(onClick = { + model.isOpenDialog.value = true + openDialog.value = model.isOpenDialog.value + LogUtil.d("FavAppBar", "Add a new website") + }) { + Icon(Icons.Rounded.Add, contentDescription = "Add a website") + } + IconButton(onClick = { + MyApp.context.getString(R.string.appbar_reset).infoToast() + model.resetWeb() + model.refreshUI() + LogUtil.d("FavAppBar", "Reset websites") + }) { + Icon(Icons.Rounded.RestartAlt, contentDescription = "Reset websites") + } + } + ) + if (openDialog.value) AddWebDialog() +} + +@Composable +fun AddWebDialog() { + val model: FavouriteViewModel = viewModel() + val webNameState = remember { model.webName } + val webUrlState = remember { model.webAddress } + AlertDialog( + onDismissRequest = { + model.isOpenDialog.value = false + model.clearDialog() + }, + text = { + Column { + Text( + text = stringResource(id = R.string.dialog_title), + color = MaterialTheme.colors.secondary, + fontSize = 20.sp + ) + Divider( + Modifier + .fillMaxWidth() + .padding(top = 10.dp) + ) + Spacer( + modifier = Modifier + .fillMaxWidth() + .height(10.dp) + ) + OutlinedTextField( + value = webNameState.value, + onValueChange = { model.webName.value = it }, + label = { Text(stringResource(id = R.string.dialog_web_name)) }, + textStyle = TextStyle(color = MaterialTheme.colors.fontHead), + maxLines = 1 + ) + OutlinedTextField( + value = webUrlState.value, + onValueChange = { model.webAddress.value = it }, + label = { Text(stringResource(id = R.string.dialog_web_url)) }, + textStyle = TextStyle(color = MaterialTheme.colors.fontHead), + placeholder = { + Text("https://baidu.com") + } + ) + } + }, + confirmButton = { + IconButton(onClick = { + if (model.checkNull() && model.checkUrl()) { + LogUtil.d("FavDialog", "Add Url ==> ${webUrlState.value}") + model.addWeb() + model.isOpenDialog.value = false + model.clearDialog() + model.refreshUI() + } + }) { + Icon( + Icons.Rounded.Done, + contentDescription = "Confirm button", + tint = MaterialTheme.colors.secondary, + modifier = Modifier.size(30.dp) + ) + } + } + ) +} + +@Preview +@Composable +fun FavouriteViewPreview() { + FavouriteView() +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/ui/activity/favourite/FavouriteViewModel.kt b/app/src/main/java/github/sukieva/hhu/ui/activity/favourite/FavouriteViewModel.kt new file mode 100644 index 0000000..923ceba --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/ui/activity/favourite/FavouriteViewModel.kt @@ -0,0 +1,85 @@ +package github.sukieva.hhu.ui.activity.favourite + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import github.sukieva.hhu.MyApp +import github.sukieva.hhu.R +import github.sukieva.hhu.data.entity.Website +import github.sukieva.hhu.data.repository.LocalRepository +import github.sukieva.hhu.utils.ActivityCollector +import github.sukieva.hhu.utils.errorToast +import github.sukieva.hhu.utils.start +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch +import java.util.regex.Pattern + +class FavouriteViewModel : ViewModel() { + + private val scope = viewModelScope + private val URL_VALIDATION_REGEX = "^((https|http)?://).*$" + var isOpenDialog = mutableStateOf(false) + var webName = mutableStateOf("") + var webAddress = mutableStateOf("") + var websites: List by mutableStateOf(emptyList()) + + fun getWebs() { + scope.launch { + LocalRepository.getWebsites().collect { + websites = it + } + } + } + + fun addWeb() { + scope.launch { + val newWeb = Website(webName.value, webAddress.value) + for (website in websites) { + if (website.siteName == webName.value) { + LocalRepository.updateWeb(newWeb) + return@launch + } + } + LocalRepository.insertWeb(newWeb) + } + } + + fun resetWeb() { + scope.launch { + LocalRepository.resetWebs() + } + } + + fun clearDialog() { + webName.value = "" + webAddress.value = "" + } + + fun deleteWebByName(name: String) { + scope.launch { + LocalRepository.deleteWebByName(name) + } + } + + fun checkUrl(): Boolean { + if (Pattern.matches(URL_VALIDATION_REGEX, webAddress.value)) + return true + MyApp.context.getString(R.string.dialog_error_not_valid).errorToast() + return false + } + + fun checkNull(): Boolean { + if (webName.value == "" || webAddress.value == "") { + MyApp.context.getString(R.string.dialog_error_null).errorToast() + return false + } + return true + } + + fun refreshUI() { + start() + ActivityCollector.finishTopActivity() + } +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/ui/activity/home/HomeActivity.kt b/app/src/main/java/github/sukieva/hhu/ui/activity/home/HomeActivity.kt new file mode 100644 index 0000000..1517ead --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/ui/activity/home/HomeActivity.kt @@ -0,0 +1,20 @@ +package github.sukieva.hhu.ui.activity.home + +import android.os.Bundle +import androidx.activity.compose.setContent +import github.sukieva.hhu.ui.activity.base.BaseActivity +import github.sukieva.hhu.utils.errorToast + +class HomeActivity : BaseActivity() { + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + HomeView() + } + } + + +} + diff --git a/app/src/main/java/github/sukieva/hhu/ui/activity/home/HomeView.kt b/app/src/main/java/github/sukieva/hhu/ui/activity/home/HomeView.kt new file mode 100644 index 0000000..58754b5 --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/ui/activity/home/HomeView.kt @@ -0,0 +1,155 @@ +package github.sukieva.hhu.ui.activity.home + +import android.content.Intent +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Bookmarks +import androidx.compose.material.icons.outlined.Extension +import androidx.compose.material.icons.rounded.ManageSearch +import androidx.compose.material.icons.rounded.PendingActions +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import github.sukieva.hhu.MyApp +import github.sukieva.hhu.R +import github.sukieva.hhu.data.repository.RemoteRepository +import github.sukieva.hhu.ui.activity.base.InitView +import github.sukieva.hhu.ui.activity.config.ConfigActivity +import github.sukieva.hhu.ui.activity.favourite.FavouriteActivity +import github.sukieva.hhu.ui.activity.results.ResultsActivity +import github.sukieva.hhu.ui.components.* +import github.sukieva.hhu.ui.theme.Teal200 +import github.sukieva.hhu.utils.* + + +@Composable +fun HomeView() { + InitView { + MyScaffold( + topBar = { HomeAppBar() }, + content = { + CardCheckIn() + CardGradeQuery() + CardSchedule() + ListCardFavourite() + ListCardConfig() + } + ) + } +} + + +@Composable +fun CardCheckIn() { + CardItem( + isLarge = true, + isActive = true, + onClick = { "点击了".infoToast() }, + title = stringResource(id = R.string.home_card_checkin), + body = "已经打卡" + ) +} + +@Composable +fun CardGradeQuery() { + CardItem( + title = stringResource(id = R.string.home_card_results), + body = "加油", + icon = Icons.Rounded.ManageSearch, + onClick = { + start() + } + ) +} + +@Composable +fun CardSchedule() { + CardItem( + title = stringResource(id = R.string.home_card_schedule), + body = getDate(), + icon = Icons.Rounded.PendingActions, + onClick = { + try { + val intent = Intent(Intent.ACTION_VIEW) + intent.setClassName("com.suda.yzune.wakeupschedule", "com.suda.yzune.wakeupschedule.SplashActivity") + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + MyApp.context.startActivity(intent) + } catch (e: Exception) { + "Wakeup Not Installed!".errorToast() + LogUtil.d("CardSchedule", "Schedule Error: not installed") + e.printStackTrace() + } + } + ) +} + + +@Composable +fun ListCardFavourite() { + ListCardItem( + title = stringResource(id = R.string.home_list_favourite), + icon = Icons.Outlined.Bookmarks, + onClick = { + start() + } + ) +} + +@Composable +fun ListCardConfig() { + ListCardItem( + title = stringResource(id = R.string.home_list_config), + icon = Icons.Outlined.Extension, + onClick = { + start() + } + ) +} + +@Composable +fun HomeAppBar() { + val hitoko by RemoteRepository.getHitokoto().collectAsState(initial = stringResource(id = R.string.home_subtitle)) + MaterialAppBar( + modifier = Modifier.padding(top = 20.dp, bottom = 20.dp), + title = { + Column { + Text(text = stringResource(id = R.string.app_name), style = MaterialTheme.typography.h6) + Spacer(modifier = Modifier.height(10.dp)) + Text( + text = hitoko, + color = Teal200, + style = MaterialTheme.typography.subtitle2, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + }, + navigationIcon = { + Image( + painter = painterResource(R.drawable.home_logo), + contentDescription = "Home logo" + ) + }, + actions = { + HomeMenu() + } + ) +} + +@Preview +@Composable +fun HomeViewPreview() { + HomeView() +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/ui/activity/results/ResultsActivity.kt b/app/src/main/java/github/sukieva/hhu/ui/activity/results/ResultsActivity.kt new file mode 100644 index 0000000..55d2922 --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/ui/activity/results/ResultsActivity.kt @@ -0,0 +1,23 @@ +package github.sukieva.hhu.ui.activity.results + +import android.os.Bundle +import androidx.activity.compose.setContent +import github.sukieva.hhu.ui.activity.base.BaseActivity + +class ResultsActivity : BaseActivity() { + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val isLogin = intent.getBooleanExtra("isLogin", false) + + setContent { + ResultsView() + } + + + } + + +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/ui/activity/results/ResultsView.kt b/app/src/main/java/github/sukieva/hhu/ui/activity/results/ResultsView.kt new file mode 100644 index 0000000..0522370 --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/ui/activity/results/ResultsView.kt @@ -0,0 +1,38 @@ +package github.sukieva.hhu.ui.activity.results + +import androidx.compose.foundation.Image +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.tooling.preview.Preview +import androidx.lifecycle.viewmodel.compose.viewModel +import github.sukieva.hhu.ui.activity.base.InitView +import github.sukieva.hhu.ui.components.MaterialTopAppBar +import github.sukieva.hhu.ui.components.MyScaffold + + +@Composable +fun ResultsView() { + InitView { + MyScaffold(topBar = { MaterialTopAppBar() }) { + //CaptchaPic() + } + } +} + +@Composable +fun CaptchaPic() { + val model: ResultsViewModel = viewModel() + if (!model.bitmapFlag) + model.getCaptchaPic() + val pic = remember { model.bitmap } + Image( + bitmap = pic.value, + contentDescription = "CaptchaPic" + ) +} + +@Composable +@Preview +fun ResultsViewPreview() { + ResultsView() +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/ui/activity/results/ResultsViewModel.kt b/app/src/main/java/github/sukieva/hhu/ui/activity/results/ResultsViewModel.kt new file mode 100644 index 0000000..56671ac --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/ui/activity/results/ResultsViewModel.kt @@ -0,0 +1,27 @@ +package github.sukieva.hhu.ui.activity.results + +import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.asImageBitmap +import androidx.lifecycle.ViewModel +import github.sukieva.hhu.data.repository.RemoteRepository +import kotlin.concurrent.thread + +class ResultsViewModel : ViewModel() { + + var bitmapFlag: Boolean = false + var bitmap = mutableStateOf(ImageBitmap(20, 20)) + + fun getCaptchaPic() { + thread { + RemoteRepository.getCaptchaPic()?.let { + bitmap.value = it.asImageBitmap() + } + } + bitmapFlag = true + } + + +} + + diff --git a/app/src/main/java/github/sukieva/hhu/ui/components/ToolBar.kt b/app/src/main/java/github/sukieva/hhu/ui/components/AppBar.kt similarity index 65% rename from app/src/main/java/github/sukieva/hhu/ui/components/ToolBar.kt rename to app/src/main/java/github/sukieva/hhu/ui/components/AppBar.kt index 5bee6c4..1f1377f 100644 --- a/app/src/main/java/github/sukieva/hhu/ui/components/ToolBar.kt +++ b/app/src/main/java/github/sukieva/hhu/ui/components/AppBar.kt @@ -1,7 +1,7 @@ package github.sukieva.hhu.ui.components -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme @@ -11,18 +11,14 @@ import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.google.accompanist.insets.LocalWindowInsets import com.google.accompanist.insets.rememberInsetsPaddingValues import com.google.accompanist.insets.ui.TopAppBar -import github.sukieva.hhu.R -import github.sukieva.hhu.ui.activity.base.InitView -import github.sukieva.hhu.ui.theme.Teal200 import github.sukieva.hhu.utils.ActivityCollector + @Composable fun MaterialTopAppBar( title: String = "Title", @@ -42,28 +38,6 @@ fun MaterialTopAppBar( ) } -@Composable -fun HomeAppBar() { - MaterialAppBar( - modifier = Modifier.padding(top = 20.dp, bottom = 20.dp), - title = { - Column { - Text(text = stringResource(id = R.string.app_name), style = MaterialTheme.typography.h6) - Spacer(modifier = Modifier.height(10.dp)) - Text(text = stringResource(id = R.string.home_subtitle), color = Teal200, style = MaterialTheme.typography.subtitle2) - } - }, - navigationIcon = { - Image( - painter = painterResource(R.drawable.home_logo), - contentDescription = "Home logo" - ) - }, - actions = { - HomeMenu() - } - ) -} @Composable fun MaterialAppBar( @@ -93,8 +67,12 @@ fun MaterialAppBar( @Preview @Composable -fun AppBarPreview() { - InitView { - HomeAppBar() - } +fun MaterialTopAppBarPreview() { + MaterialTopAppBar() } + +@Preview +@Composable +fun MaterialAppBarPreview() { + MaterialAppBar() +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/ui/components/Card.kt b/app/src/main/java/github/sukieva/hhu/ui/components/Card.kt index 62b1d07..ef889df 100644 --- a/app/src/main/java/github/sukieva/hhu/ui/components/Card.kt +++ b/app/src/main/java/github/sukieva/hhu/ui/components/Card.kt @@ -8,49 +8,14 @@ import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.CheckCircleOutline -import androidx.compose.material.icons.rounded.ManageSearch -import androidx.compose.material.icons.rounded.PendingActions import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import github.sukieva.hhu.ui.activity.base.InitView import github.sukieva.hhu.ui.theme.* -import github.sukieva.hhu.utils.infoToast -import github.sukieva.hhu.R - -@Composable -fun CardCheckIn() { - CardItem( - isLarge = true, - isActive = true, - onClick = { "点击了".infoToast() }, - title = stringResource(id = R.string.home_card_checkin), - body = "已经打卡" - ) -} - -@Composable -fun CardGradeQuery() { - CardItem( - title = stringResource(id = R.string.home_card_results), - body = "加油", - icon = Icons.Rounded.ManageSearch - ) -} - -@Composable -fun CardSchedule() { - CardItem( - title = stringResource(id = R.string.home_card_schedule), - body = "今天无课", - icon = Icons.Rounded.PendingActions - ) -} @Composable @@ -119,8 +84,6 @@ fun CardItem( @Preview @Composable -fun CardPreview() { - InitView { - CardItem() - } +fun CardItemPreview() { + CardItem() } \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/ui/components/ExpandableCard.kt b/app/src/main/java/github/sukieva/hhu/ui/components/ExpandableCard.kt new file mode 100644 index 0000000..b3d1a6c --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/ui/components/ExpandableCard.kt @@ -0,0 +1,79 @@ +package github.sukieva.hhu.ui.components + +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.LinearOutSlowInEasing +import androidx.compose.animation.core.tween +import androidx.compose.foundation.layout.* +import androidx.compose.material.Card +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.MaterialTheme +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.TravelExplore +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + + +@ExperimentalMaterialApi +@Composable +fun ExpandableCard( + title: String = "Title", + icon: ImageVector = Icons.Outlined.TravelExplore, + shape: Shape = MaterialTheme.shapes.medium, + content: @Composable () -> Unit = {}, + padding: Dp = 12.dp +) { + var expandedState by remember { mutableStateOf(false) } + + Card( + modifier = Modifier + .fillMaxWidth() + .animateContentSize( + animationSpec = tween( + durationMillis = 300, + easing = LinearOutSlowInEasing + ) + ), + shape = shape, + onClick = { + expandedState = !expandedState + }, + backgroundColor = MaterialTheme.colors.background + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(padding) + ) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + ListCard( + modifier = Modifier + .padding(start = 10.dp, top = 12.dp) + .height(50.dp), + title = title, + icon = icon + ) + } + if (expandedState) { + content() + } + } + } +} + + +@ExperimentalMaterialApi +@Composable +@Preview +fun ExpandableCardPreview() { + ExpandableCard( + title = "My Title" + ) +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/ui/components/List.kt b/app/src/main/java/github/sukieva/hhu/ui/components/List.kt index 54f32f3..e403de4 100644 --- a/app/src/main/java/github/sukieva/hhu/ui/components/List.kt +++ b/app/src/main/java/github/sukieva/hhu/ui/components/List.kt @@ -7,61 +7,44 @@ import androidx.compose.material.Card import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.Bookmarks -import androidx.compose.material.icons.outlined.CheckCircleOutline -import androidx.compose.material.icons.outlined.Extension +import androidx.compose.material.icons.outlined.TravelExplore import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import github.sukieva.hhu.R -import github.sukieva.hhu.ui.activity.ConfigActivity -import github.sukieva.hhu.ui.activity.FavouriteActivity -import github.sukieva.hhu.ui.activity.base.InitView import github.sukieva.hhu.ui.theme.fontBody import github.sukieva.hhu.ui.theme.fontHead -import github.sukieva.hhu.utils.start - - -@Composable -fun ListCardFavourite() { - ListCardItem( - title = stringResource(id = R.string.home_list_favourite), - icon = Icons.Outlined.Bookmarks, - onClick = { - start() - } - ) -} - -@Composable -fun ListCardConfig() { - ListCardItem( - title = stringResource(id = R.string.home_list_config), - icon = Icons.Outlined.Extension, - onClick = { - start() - } - ) -} @Composable fun ListCardItem( modifier: Modifier = Modifier, title: String = "Title", - icon: ImageVector = Icons.Outlined.CheckCircleOutline, + icon: ImageVector = Icons.Outlined.TravelExplore, onClick: () -> Unit = {}, ) { - Card( + ListCard( modifier = modifier .padding(start = 25.dp, end = 25.dp, top = 20.dp) .fillMaxWidth() .height(50.dp) .clickable { onClick() }, + title = title, + icon = icon + ) +} + + +@Composable +fun ListCard( + modifier: Modifier = Modifier, + title: String = "Title", + icon: ImageVector = Icons.Outlined.TravelExplore +) { + Card( + modifier = modifier, shape = MaterialTheme.shapes.small, backgroundColor = MaterialTheme.colors.background, elevation = 0.dp @@ -94,14 +77,10 @@ fun ListCardItem( } } } - - } @Preview @Composable -fun ListPreview() { - InitView { - ListCardItem() - } +fun ListCardItemPreview() { + ListCardItem() } \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/ui/components/Menu.kt b/app/src/main/java/github/sukieva/hhu/ui/components/Menu.kt index d3e317b..bf9749c 100644 --- a/app/src/main/java/github/sukieva/hhu/ui/components/Menu.kt +++ b/app/src/main/java/github/sukieva/hhu/ui/components/Menu.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import github.sukieva.hhu.R -import github.sukieva.hhu.ui.activity.AboutActivity +import github.sukieva.hhu.ui.activity.about.AboutActivity import github.sukieva.hhu.ui.theme.fontBody import github.sukieva.hhu.utils.ActivityCollector import github.sukieva.hhu.utils.browse @@ -29,14 +29,23 @@ fun HomeMenu() { expanded = expanded, onDismissRequest = { expanded = false } ) { - DropdownMenuItem(onClick = { ActivityCollector.finishAllActivity() }) { + DropdownMenuItem(onClick = { + ActivityCollector.finishAllActivity() + expanded = false + }) { MaterialFont(stringResource(id = R.string.home_menu_exit)) } Divider() - DropdownMenuItem(onClick = { browse("https://github.com/SukiEva/Myhhu/issues") }) { + DropdownMenuItem(onClick = { + browse("https://github.com/SukiEva/Myhhu/issues") + expanded = false + }) { MaterialFont(stringResource(id = R.string.home_menu_issue)) } - DropdownMenuItem(onClick = { start() }) { + DropdownMenuItem(onClick = { + start() + expanded = false + }) { MaterialFont(stringResource(id = R.string.home_menu_about)) } } diff --git a/app/src/main/java/github/sukieva/hhu/ui/components/Scaffold.kt b/app/src/main/java/github/sukieva/hhu/ui/components/Scaffold.kt new file mode 100644 index 0000000..976b7ea --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/ui/components/Scaffold.kt @@ -0,0 +1,21 @@ +package github.sukieva.hhu.ui.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.material.Scaffold +import androidx.compose.runtime.Composable + + +@Composable +fun MyScaffold( + topBar: @Composable () -> Unit, + content: @Composable () -> Unit +) { + Scaffold( + topBar = topBar, + content = { + Column { + content() + } + } + ) +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/ui/components/WebView.kt b/app/src/main/java/github/sukieva/hhu/ui/components/WebView.kt index 2607a97..88c444d 100644 --- a/app/src/main/java/github/sukieva/hhu/ui/components/WebView.kt +++ b/app/src/main/java/github/sukieva/hhu/ui/components/WebView.kt @@ -24,7 +24,7 @@ import github.sukieva.hhu.utils.LogUtil import github.sukieva.hhu.utils.errorToast import kotlinx.coroutines.launch -@SuppressLint("SetJavaScriptEnabled") +@SuppressLint("JavascriptInterface", "SetJavaScriptEnabled") @Composable fun MyWebView( url: String = "https://github.com/SukiEva" @@ -48,7 +48,7 @@ fun MyWebView( //缩放操作 setSupportZoom(true) builtInZoomControls = true - displayZoomControls = true + displayZoomControls = false //是否支持通过JS打开新窗口 javaScriptCanOpenWindowsAutomatically = true //不加载缓存内容 @@ -105,6 +105,11 @@ fun CustomWebView( override fun onPageFinished(view: WebView?, url: String?) { super.onPageFinished(view, url) onProgressChange(100) + view!!.loadUrl( + "javascript:window.java_obj.getSource(" + + "document.getElementsByTagName('html')[0].innerHTML);" + ) + //println(InJavaScriptLocalObj.webHtml) } override fun shouldOverrideUrlLoading( @@ -150,6 +155,7 @@ fun CustomWebView( this.webChromeClient = webViewChromeClient //回调webSettings供调用方设置webSettings的相关配置 initSettings(this.settings) + addJavascriptInterface(InJavaScriptLocalObj, "java_obj") webView = this loadUrl(url) } @@ -162,3 +168,13 @@ fun CustomWebView( } } } + +object InJavaScriptLocalObj { + var webHtml: String? = null + + @JavascriptInterface + fun getSource(html: String?) { + webHtml = html + } +} + diff --git a/app/src/main/java/github/sukieva/hhu/ui/theme/Color.kt b/app/src/main/java/github/sukieva/hhu/ui/theme/Color.kt index 9425c08..b655b11 100644 --- a/app/src/main/java/github/sukieva/hhu/ui/theme/Color.kt +++ b/app/src/main/java/github/sukieva/hhu/ui/theme/Color.kt @@ -4,9 +4,7 @@ import androidx.compose.material.Colors import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color -val Purple200 = Color(0xFFBB86FC) -val Purple500 = Color(0xFF6200EE) -val Purple700 = Color(0xFF3700B3) +val Purple700 = Color(0xC83700B3) val Teal200 = Color(0xFF03DAC5) val White = Color(0xffffffff) diff --git a/app/src/main/java/github/sukieva/hhu/ui/theme/Theme.kt b/app/src/main/java/github/sukieva/hhu/ui/theme/Theme.kt index 66ad36d..d08b047 100644 --- a/app/src/main/java/github/sukieva/hhu/ui/theme/Theme.kt +++ b/app/src/main/java/github/sukieva/hhu/ui/theme/Theme.kt @@ -10,17 +10,21 @@ import androidx.compose.ui.graphics.Color import com.google.accompanist.systemuicontroller.rememberSystemUiController private val DarkColorPalette = darkColors( - primary = Black, + primary = Teal200, primaryVariant = Purple700, secondary = Teal200, - background = ui_background_night + background = ui_background_night, + onBackground = White, + onSurface = White ) private val LightColorPalette = lightColors( - primary = White, + primary = Purple700, primaryVariant = Purple700, secondary = Teal200, - background = ui_background + background = ui_background, + onBackground = Black, + onSurface = Black /* Other default colors to override background = Color.White, diff --git a/app/src/main/java/github/sukieva/hhu/utils/ActivityCollector.kt b/app/src/main/java/github/sukieva/hhu/utils/ActivityCollector.kt index c91414e..e6477fc 100644 --- a/app/src/main/java/github/sukieva/hhu/utils/ActivityCollector.kt +++ b/app/src/main/java/github/sukieva/hhu/utils/ActivityCollector.kt @@ -6,13 +6,15 @@ object ActivityCollector { private val activities = ArrayList() + fun addActivity(activity: Activity) = activities.add(activity) fun removeActivity(activity: Activity) = activities.remove(activity) + fun topActivity(): Activity = activities[activities.lastIndex] fun finishTopActivity() { - val topActivity = activities[activities.lastIndex] + val topActivity = topActivity() if (!topActivity.isFinishing) topActivity.finish() removeActivity(topActivity) } diff --git a/app/src/main/java/github/sukieva/hhu/utils/AppHelper.kt b/app/src/main/java/github/sukieva/hhu/utils/AppHelper.kt new file mode 100644 index 0000000..072ffc3 --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/utils/AppHelper.kt @@ -0,0 +1,89 @@ +package github.sukieva.hhu.utils + + +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.net.Uri +import android.widget.Button +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import es.dmoral.toasty.Toasty +import github.sukieva.hhu.MyApp +import github.sukieva.hhu.R +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + + +fun browse(url: String) { + val intent = Intent(Intent.ACTION_VIEW) + intent.data = Uri.parse(url) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + MyApp.context.startActivity(intent) +} + +fun alert( + context: Context = ActivityCollector.topActivity(), + title: String = "(/ ̄ー ̄)/~~☆’.・.・:★’.・.・:☆", + message: String = "消息", + positive: String = "确认", + onPositiveClick: () -> Unit = {}, + showNegative: Boolean = false, + negative: String = "取消", + onNegativeClick: () -> Unit = {} +) { + AlertDialog.Builder(context, R.style.AlertDialogStyle).apply { + setTitle(title) + setMessage(message) + setCancelable(false) + setPositiveButton(positive) { _, _ -> + onPositiveClick() + } + if (showNegative) { + setNegativeButton(negative) { _, _ -> + onNegativeClick() + } + } + setCancelable(true) + show() + } +} + +/* 范型实化 + start(){ + putExtra() + putExtra() + } + */ +inline fun start(block: Intent.() -> Unit = {}) { + val context = MyApp.context + val intent = Intent(context, T::class.java) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + intent.block() + context.startActivity(intent) +} + +fun String.showToast(duration: Int = Toast.LENGTH_SHORT) = + Toasty.normal(MyApp.context, this, duration).show() + + +fun String.infoToast(duration: Int = Toast.LENGTH_SHORT) = + Toasty.info(MyApp.context, this, duration, true).show() + + +fun String.successToast(duration: Int = Toast.LENGTH_SHORT) = + Toasty.success(MyApp.context, this, duration, true).show() + + +fun String.warningToast(duration: Int = Toast.LENGTH_SHORT) = + Toasty.warning(MyApp.context, this, duration, true).show() + + +fun String.errorToast(duration: Int = Toast.LENGTH_SHORT) = + Toasty.error(MyApp.context, this, duration, true).show() + + +fun getDate(): String { + val formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd") + return LocalDateTime.now().format(formatter) +} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/utils/DataManager.kt b/app/src/main/java/github/sukieva/hhu/utils/DataManager.kt new file mode 100644 index 0000000..c53185c --- /dev/null +++ b/app/src/main/java/github/sukieva/hhu/utils/DataManager.kt @@ -0,0 +1,50 @@ +package github.sukieva.hhu.utils + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.* +import androidx.datastore.preferences.preferencesDataStore +import github.sukieva.hhu.MyApp +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map + +val Context.dataStore: DataStore by preferencesDataStore(name = "DataStore") + +object DataManager { + + private var dataStore: DataStore = MyApp.context.dataStore + + + // 异步写入数据 + suspend fun saveData(key: String, value: T) { + when (value) { + is Int -> dataStore.edit { it[intPreferencesKey(key)] = value } + is Long -> dataStore.edit { it[longPreferencesKey(key)] = value } + is String -> dataStore.edit { it[stringPreferencesKey(key)] = value } + is Boolean -> dataStore.edit { it[booleanPreferencesKey(key)] = value } + is Float -> dataStore.edit { it[floatPreferencesKey(key)] = value } + else -> throw IllegalArgumentException("This type can't be saved into DataStore") + } + } + + // 异步获得数据 + @Suppress("UNCHECKED_CAST") + suspend fun readData(key: String, default: T): T { + val data = when (default) { + is Int -> dataStore.data.map { it[intPreferencesKey(key)] ?: 0 } + is Long -> dataStore.data.map { it[longPreferencesKey(key)] ?: 0L } + is String -> dataStore.data.map { it[stringPreferencesKey(key)] ?: "" } + is Boolean -> dataStore.data.map { it[booleanPreferencesKey(key)] ?: Boolean } + is Float -> dataStore.data.map { it[floatPreferencesKey(key)] ?: 0f } + else -> throw IllegalArgumentException("This type can't be read") + } + //scope.launch { data.first() } + return data.first() as T + } + + suspend fun clearData() { + dataStore.edit { + it.clear() + } + } +} diff --git a/app/src/main/java/github/sukieva/hhu/utils/LogUtil.kt b/app/src/main/java/github/sukieva/hhu/utils/LogUtil.kt index 93506ff..900916a 100644 --- a/app/src/main/java/github/sukieva/hhu/utils/LogUtil.kt +++ b/app/src/main/java/github/sukieva/hhu/utils/LogUtil.kt @@ -13,7 +13,7 @@ object LogUtil { private const val ERROR = 5 - private const val level = ERROR + private const val level = VERBOSE fun v(tag: String, msg: String) { if (level <= VERBOSE) Log.v(tag, msg) diff --git a/app/src/main/java/github/sukieva/hhu/utils/Reified.kt b/app/src/main/java/github/sukieva/hhu/utils/Reified.kt deleted file mode 100644 index a91596f..0000000 --- a/app/src/main/java/github/sukieva/hhu/utils/Reified.kt +++ /dev/null @@ -1,29 +0,0 @@ -package github.sukieva.hhu.utils - - -import android.content.Intent -import android.net.Uri -import github.sukieva.hhu.MyApp - - -fun browse(url: String) { - val intent = Intent(Intent.ACTION_VIEW) - intent.data = Uri.parse(url) - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK - MyApp.context.startActivity(intent) -} - - -/* 范型实化 - start(){ - putExtra() - putExtra() - } - */ -inline fun start(block: Intent.() -> Unit = {}) { - val context = MyApp.context - val intent = Intent(context, T::class.java) - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK - intent.block() - context.startActivity(intent) -} \ No newline at end of file diff --git a/app/src/main/java/github/sukieva/hhu/utils/Toasty.kt b/app/src/main/java/github/sukieva/hhu/utils/Toasty.kt deleted file mode 100644 index 238c01b..0000000 --- a/app/src/main/java/github/sukieva/hhu/utils/Toasty.kt +++ /dev/null @@ -1,38 +0,0 @@ -package github.sukieva.hhu.utils - -import android.widget.Toast -import es.dmoral.toasty.Toasty -import github.sukieva.hhu.MyApp - - -fun String.showToast(duration: Int = Toast.LENGTH_SHORT) = - Toasty.normal(MyApp.context, this, duration).show() - - -fun String.infoToast(duration: Int = Toast.LENGTH_SHORT) = - Toasty.info(MyApp.context, this, duration, true).show() - - -fun String.successToast(duration: Int = Toast.LENGTH_SHORT) = - Toasty.success(MyApp.context, this, duration, true).show() - - -fun String.warningToast(duration: Int = Toast.LENGTH_SHORT) = - Toasty.warning(MyApp.context, this, duration, true).show() - - -fun String.errorToast(duration: Int = Toast.LENGTH_SHORT) = - Toasty.error(MyApp.context, this, duration, true).show() - - - - - - - - - - - - - diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index c19eb2b..f484723 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -24,4 +24,10 @@ @color/about_bg_dark @color/white + + \ No newline at end of file diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index e5fd65e..dd5fb63 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -13,6 +13,17 @@ 收藏 配置 + + 重置为默认值 + 添加网址 + 名称 + 网址 + 不能为空 + 网址无效 + \n确定移除网址? + 确定 + 取消 + 关于 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d8ceac4..870986a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,6 +13,17 @@ Favorites Config + + Reset to default + Add a new website + Name + Url + Can\'t be empty + Url is invalid + \nSure to remove the URL? + Confirm + Cancel + About A third-party app for hhuers @@ -20,4 +31,5 @@ Can\'t find related apps Something went wrong + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 56ea02f..636f75e 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -33,4 +33,19 @@ @color/about_bg_light @color/white + + + + + +