diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..17076f1 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,16 @@ +*.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/src/main/java/com/pushdeer/os/AppKeys.kt diff --git a/android/.idea/.gitignore b/android/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/android/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/android/.idea/compiler.xml b/android/.idea/compiler.xml new file mode 100644 index 0000000..fb7f4a8 --- /dev/null +++ b/android/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/android/.idea/gradle.xml b/android/.idea/gradle.xml new file mode 100644 index 0000000..00ecf65 --- /dev/null +++ b/android/.idea/gradle.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/android/.idea/inspectionProfiles/Project_Default.xml b/android/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..2842237 --- /dev/null +++ b/android/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/android/.idea/misc.xml b/android/.idea/misc.xml new file mode 100644 index 0000000..0dc9c62 --- /dev/null +++ b/android/.idea/misc.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/vcs.xml b/android/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/android/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/android/Readme.md b/android/Readme.md new file mode 100644 index 0000000..cd6582e --- /dev/null +++ b/android/Readme.md @@ -0,0 +1,93 @@ +# PushDeer for Android + + + +### 适配进度 + +* MiPush(状态:已调通、已接入) + * miui 12.5.6 正常 + * 原生、类原生 可成功注册,无法收到推送,可能和地区识别有关,待解决 + +### TODO + +* ~~调通 MiPush~~ +* 接入 MiPush +* 完善 log ~~采集~~ 回传机制,便于调试 +* 测试不同厂商设备上的通知推送效果 +* ~~接入 PushDeer~~ +* ~~界面设计:BottomBar+Navigation(Device Key Message Setting)~~ +* 调整 KeyList MessageList 等处的自定义绘制 +* ~~增加 DeviceList 外的侧滑手势~~ +* ~~增加侧滑手势相关动作~~ +* 增加各种操作前的二次确认弹窗,包括自动登陆 +* 增加非Miui设备的权限获取 + +### 日志 + +* 2021-12-25 + * 文件夹已经创建完毕 + * MiPush + * 小米官方文档写的像shit,陈年旧注释都不删 + * 测试环境推送消息不推送 + * 增加了 channel 参数,默认属于 运营,推送数量太少,需要申请新 channel + * 因为不是所有SDK都支持透传,暂时选择跳过这个功能 + +* 2022-01-01 + * 增加图片、颜色等资源,抽取字符串 + * 增加登陆界面 + * 重新组织主题 + * 使用"纯自研" TopBar 替换 TopAppBar + * PushDeer api已调通,未接入 + +* 2022-01-04 + * PushDeer /user/info 响应类型去掉列表 + * 为 MessageList 界面适配右上角箭头icon、增加输入框及其切换动画 + * 适配设置界面不显示右上角icon + * MessageList、KeyList item适配 + * DeviceList 增加侧滑手势 + * 抽象 item 相关 ui,提高代码复用率 + +* 2022-01-05 + * 从 AppDatabase.kt 删除 message + * 调通user/info device/list key/list message/list四个路由 + * 去掉message相关的各种repository、viewmodel等 + * 一些小修改 + +* 2022-01-06 + * 完善device/key/message的删除逻辑 + * 创建 KeyListPage.kt + * 一些小修改 + +* 2022-01-08 + * 增加二维码扫描组件 + * 重绘设置界面 + * 增加日志列表界面 + * 调整滑动删除界面绘制逻辑和操作逻辑 + * 调整列表界面绘制逻辑 + * 增加消息列表对Markdown类型消息的渲染支持 + * 增加消息列表对Image类型消息的显示支持(demo级别,硬编码) + * 调整Key列表中项目绘制逻辑 + * 一些细节变化 + +* 2022-01-09 + * 增加列表尾部占位符 + * 微调Key和Message列表项的构图 + * 微调滑动删除图标背景 + * 调整列表项阴影 + * 调整设置界面的按钮颜色 + * 界面整体配色几近处理完善 + * Markdown: + * 增加链接解析支持 + * 增加图片显示支持 + * 增加html解析支持 + * 增加task-list解析支持 + * 增加table解析支持 + +* 2022-01-15 + * 增加 Message 的数据库表 + * 调整项目文件结构 + * 将数据库用作message列表的直接数据来源 + +### 感谢 + +https://github.com/taoweiji/MixPush \ No newline at end of file diff --git a/android/app/.gitignore b/android/app/.gitignore new file mode 100644 index 0000000..7ded95a --- /dev/null +++ b/android/app/.gitignore @@ -0,0 +1,2 @@ +/build +/src/main/java/com/pushdeer/os/values/AppKeys.kt diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..5baf670 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,111 @@ +plugins { + id 'com.android.application' + id 'kotlin-android' + id 'kotlin-kapt' +} + +android { + compileSdk 31 + + defaultConfig { + applicationId "com.pushdeer.os" + minSdk 21 + targetSdk 31 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary true + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + useIR = true + } + buildFeatures { + compose true + } + composeOptions { + kotlinCompilerExtensionVersion compose_version + kotlinCompilerVersion '1.5.31' + } + packagingOptions { + resources { + excludes += '/META-INF/{AL2.0,LGPL2.1}' + } + } +} + +dependencies { + + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.appcompat:appcompat:1.4.0' + implementation 'com.google.android.material:material:1.4.0' + implementation "androidx.compose.ui:ui:$compose_version" + implementation "androidx.compose.material:material:$compose_version" + implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0' + implementation 'androidx.activity:activity-compose:1.4.0' + implementation files('libs/MiPush_SDK_Client_4_9_0.jar') + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" + debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" + + implementation project(path: ':common') + implementation project(path: ':compose') + + // navigation + implementation "androidx.navigation:navigation-compose:2.4.0-rc01" + + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.google.code.gson:gson:2.8.9' + implementation 'com.squareup.retrofit2:converter-gson:2.3.0' + + + def accompanist_version = "0.22.0-rc" +// implementation "com.google.accompanist:accompanist-pager:$accompanist_version" +// implementation "com.google.accompanist:accompanist-pager-indicators:$accompanist_version" +// implementation "com.google.accompanist:accompanist-navigation-material:$accompanist_version" + implementation "com.google.accompanist:accompanist-systemuicontroller:$accompanist_version" + implementation "com.google.accompanist:accompanist-insets:$accompanist_version" +// implementation "com.google.accompanist:accompanist-insets-ui:$accompanist_version" + + // room + def room_version = "2.4.0" + implementation "androidx.room:room-ktx:$room_version" + implementation "androidx.room:room-runtime:$room_version" + kapt "androidx.room:room-compiler:$room_version" + + implementation 'com.google.zxing:core:3.2.1' + implementation 'cn.bingoogolapple:bga-qrcodecore:1.1.7@aar' + implementation 'cn.bingoogolapple:bga-zxing:1.1.7@aar' + + implementation 'androidx.constraintlayout:constraintlayout:2.1.2' + + final def markwon_version = '4.6.2' + implementation "io.noties.markwon:core:$markwon_version" + implementation "io.noties.markwon:ext-tables:$markwon_version" + implementation "io.noties.markwon:ext-tasklist:$markwon_version" + implementation "io.noties.markwon:image-coil:$markwon_version" + implementation "io.noties.markwon:linkify:$markwon_version" + implementation "io.noties.markwon:html:$markwon_version" + + implementation "io.coil-kt:coil:1.4.0" +// implementation 'com.github.bumptech.glide:glide:4.12.0' +// annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' +} \ No newline at end of file diff --git a/android/app/libs/MiPush_SDK_Client_4_9_0.jar b/android/app/libs/MiPush_SDK_Client_4_9_0.jar new file mode 100644 index 0000000..b2adcbd Binary files /dev/null and b/android/app/libs/MiPush_SDK_Client_4_9_0.jar differ diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/android/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/android/app/src/androidTest/java/com/pushdeer/os/ExampleInstrumentedTest.kt b/android/app/src/androidTest/java/com/pushdeer/os/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..afb510d --- /dev/null +++ b/android/app/src/androidTest/java/com/pushdeer/os/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.pushdeer.os + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.pushdeer.os", appContext.packageName) + } +} \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1bd89e6 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/App.kt b/android/app/src/main/java/com/pushdeer/os/App.kt new file mode 100644 index 0000000..f03b233 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/App.kt @@ -0,0 +1,80 @@ +package com.pushdeer.os + +import android.app.ActivityManager +import android.app.Application +import android.os.Process +import android.util.Log +import com.pushdeer.os.data.api.PushDeerApi +import com.pushdeer.os.data.database.AppDatabase +import com.pushdeer.os.factory.ViewModelFactory +import com.pushdeer.os.keeper.RepositoryKeeper +import com.pushdeer.os.keeper.StoreKeeper +import com.pushdeer.os.values.AppKeys +import com.xiaomi.channel.commonutils.logger.LoggerInterface +import com.xiaomi.mipush.sdk.Logger +import com.xiaomi.mipush.sdk.MiPushClient +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.scalars.ScalarsConverterFactory + +class App : Application() { + + val storeKeeper by lazy { StoreKeeper(this) } + val database by lazy { AppDatabase.getDatabase(this) } + val repositoryKeeper by lazy { RepositoryKeeper(database) } + val pushDeerService by lazy { + Retrofit.Builder() + .baseUrl("http://0.0.0.0:8800") + .addConverterFactory(ScalarsConverterFactory.create()) + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(PushDeerApi::class.java) + } + val viewModelFactory by lazy { + ViewModelFactory( + repositoryKeeper, + storeKeeper, + pushDeerService + ) + } + + override fun onCreate() { + super.onCreate() + //初始化push推送服务 + if (shouldInit()) { + MiPushClient.registerPush(this, AppKeys.MiPush_Id, AppKeys.MiPush_Key) + } + //打开Log + val newLogger: LoggerInterface = object : LoggerInterface { + override fun setTag(tag: String) { + // ignore + } + + override fun log(content: String, t: Throwable) { + Log.d(TAG, content, t) + } + + override fun log(content: String) { + Log.d(TAG, content) + } + } + Logger.setLogger(this, newLogger) + } + + private fun shouldInit(): Boolean { + val am = getSystemService(ACTIVITY_SERVICE) as ActivityManager + val processInfoList = am.runningAppProcesses + val mainProcessName = applicationInfo.processName + val myPid = Process.myPid() + for (info in processInfoList) { + if (info.pid == myPid && mainProcessName == info.processName) { + return true + } + } + return false + } + + companion object { + const val TAG = "TAG" + } +} diff --git a/android/app/src/main/java/com/pushdeer/os/MainActivity.kt b/android/app/src/main/java/com/pushdeer/os/MainActivity.kt new file mode 100644 index 0000000..b1e6e31 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/MainActivity.kt @@ -0,0 +1,169 @@ +package com.pushdeer.os + +import android.content.ClipboardManager +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.text.util.Linkify +import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.viewModels +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.core.view.WindowCompat +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import coil.ImageLoader +import com.google.accompanist.insets.ProvideWindowInsets +import com.google.accompanist.insets.statusBarsPadding +import com.google.accompanist.systemuicontroller.rememberSystemUiController +import com.pushdeer.os.activity.QrScanActivity +import com.pushdeer.os.data.api.data.request.DeviceInfo +import com.pushdeer.os.holder.RequestHolder +import com.pushdeer.os.store.SettingStore +import com.pushdeer.os.ui.compose.page.LogDaoPage +import com.pushdeer.os.ui.compose.page.LoginPage +import com.pushdeer.os.ui.compose.page.main.MainPage +import com.pushdeer.os.ui.theme.PushDeerTheme +import com.pushdeer.os.util.SystemUtil +import com.pushdeer.os.viewmodel.LogDogViewModel +import com.pushdeer.os.viewmodel.MessageViewModel +import com.pushdeer.os.viewmodel.PushDeerViewModel +import com.pushdeer.os.viewmodel.UiViewModel +import com.wh.common.util.UiUtils +import io.noties.markwon.Markwon +import io.noties.markwon.image.coil.CoilImagesPlugin +import io.noties.markwon.linkify.LinkifyPlugin +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import java.util.* + + +class MainActivity : ComponentActivity(), RequestHolder { + + private val viewModelFactory by lazy { (application as App).viewModelFactory } + private val repositoryKeeper by lazy { (application as App).repositoryKeeper } + private val miPushRepository by lazy { repositoryKeeper.miPushRepository } + override val uiViewModel: UiViewModel by viewModels { viewModelFactory } + override val pushDeerViewModel: PushDeerViewModel by viewModels { viewModelFactory } + override val logDogViewModel: LogDogViewModel by viewModels { viewModelFactory } + override val messageViewModel: MessageViewModel by viewModels { viewModelFactory } + override val settingStore: SettingStore by lazy { (application as App).storeKeeper.settingStore } + + override val coilImageLoader: ImageLoader by lazy { + ImageLoader.Builder(this) + .apply { + availableMemoryPercentage(0.5) + bitmapPoolPercentage(0.5) + crossfade(true) + } + .build() + } + + override val markdown: Markwon by lazy { + Markwon.builder(this) + .usePlugin(CoilImagesPlugin.create(this, coilImageLoader)) + .usePlugin(LinkifyPlugin.create(Linkify.WEB_URLS)) + .build(); + } + + override lateinit var globalNavController: NavHostController + override lateinit var coroutineScope: CoroutineScope + override lateinit var myActivity: ComponentActivity + override val clipboardManager by lazy { getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager } + + override lateinit var activityOpener: ActivityResultLauncher + + @ExperimentalAnimationApi + @ExperimentalMaterialApi + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + myActivity = this + activityOpener = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + Toast.makeText( + this, + "${result.data?.getStringExtra(QrScanActivity.DataKey)}", + Toast.LENGTH_SHORT + ).show() + } + + UiUtils.keepScreenOn(window) + setContent { + globalNavController = rememberNavController() + coroutineScope = rememberCoroutineScope() + val useDarkIcons = MaterialTheme.colors.isLight + val systemUiController = rememberSystemUiController() + when { + SystemUtil.isMiui() -> { + systemUiController.setStatusBarColor(Color.Transparent, useDarkIcons) + } + else -> { + systemUiController.setSystemBarsColor(Color.Transparent, useDarkIcons) + } + } + WindowCompat.setDecorFitsSystemWindows(window, true) + miPushRepository.regId.observe(this) { + // 这个操作放到注册成功后进行 + settingStore.thisDeviceId = it + coroutineScope.launch { + if (pushDeerViewModel.shouldRegDevice()) { + pushDeerViewModel.deviceReg(DeviceInfo().apply { + this.name = SystemUtil.getDeviceModel() + this.device_id = it + this.is_clip = 0 + }) + } + } + } + + SideEffect { + coroutineScope.launch { + pushDeerViewModel.login().also { + pushDeerViewModel.userInfo() + pushDeerViewModel.keyList() + pushDeerViewModel.deviceList() + pushDeerViewModel.messageList() + + globalNavController.navigate("main") { + globalNavController.popBackStack() + } + } + } + } + + PushDeerTheme { + ProvideWindowInsets { + Surface(color = MaterialTheme.colors.background) { + NavHost( + navController = globalNavController, + startDestination = "login", + modifier = Modifier.statusBarsPadding() + ) { + composable("login") { + LoginPage(requestHolder = this@MainActivity) + } + composable("logdog") { + LogDaoPage(requestHolder = this@MainActivity) + } + composable("main") { + MainPage(requestHolder = this@MainActivity) + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/activity/QrScanActivity.kt b/android/app/src/main/java/com/pushdeer/os/activity/QrScanActivity.kt new file mode 100644 index 0000000..5aaaae1 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/activity/QrScanActivity.kt @@ -0,0 +1,66 @@ +package com.pushdeer.os.activity + + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import cn.bingoogolapple.qrcode.core.QRCodeView +import com.pushdeer.os.R + + +class QrScanActivity : AppCompatActivity(), QRCodeView.Delegate { + + private val TAG = "WH_" + javaClass.simpleName + private lateinit var qrCode: QRCodeView + + companion object { + val RequestCode_get_scan_result = 436 + val DataKey = "qr_scan_result" + + fun forScanResultIntent(context: Context): Intent { + return Intent(context, QrScanActivity::class.java).apply { + putExtra(DataKey, 1) + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_qr_scan) + qrCode = findViewById(R.id.qrcode1) + qrCode.setDelegate(this) + } + + override fun onStart() { + super.onStart() + Log.d(TAG, "onStart") + qrCode.startSpotAndShowRect() + } + + override fun onStop() { + Log.d(TAG, "onStop") + qrCode.stopCamera() + super.onStop() + } + + override fun onDestroy() { + qrCode.onDestroy() + super.onDestroy() + } + + override fun onScanQRCodeSuccess(result: String?) { + Log.d(TAG, "onScanQRCodeSuccess: $result") + qrCode.stopCamera() + val intent = Intent() + intent.putExtra(DataKey, result) + setResult(RequestCode_get_scan_result, intent) + finish() + } + + override fun onScanQRCodeOpenCameraError() { + Log.e(TAG, "onScanQRCodeOpenCameraError") + qrCode.startSpotAndShowRect() + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/data/api/PushDeerApi.kt b/android/app/src/main/java/com/pushdeer/os/data/api/PushDeerApi.kt new file mode 100644 index 0000000..cf8c7a2 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/data/api/PushDeerApi.kt @@ -0,0 +1,53 @@ +package com.pushdeer.os.data.api + +import com.pushdeer.os.data.api.data.response.* +import retrofit2.http.* + +interface PushDeerApi { + @GET("/login/fake") + suspend fun fakeLogin(): ReturnData + + @FormUrlEncoded + @POST("/user/info") + suspend fun userInfo(@Field("token") token: String): ReturnData + + @FormUrlEncoded + @POST("/device/reg") + suspend fun deviceReg(@FieldMap data: Map): ReturnData + + @FormUrlEncoded + @POST("/device/list") + suspend fun deviceList(@Field("token") token: String): ReturnData + + @FormUrlEncoded + @POST("/device/remove") + suspend fun deviceRemove(@Field("token") token: String,@Field("id") id:Int): String + + @FormUrlEncoded + @POST("/key/gen") + suspend fun keyGen(@Field("token") token: String): ReturnData + + @FormUrlEncoded + @POST("/key/regen") + suspend fun keyRegen(@FieldMap data: Map): String + + @FormUrlEncoded + @POST("/key/list") + suspend fun keyList(@Field("token") token: String): ReturnData + + @FormUrlEncoded + @POST("/key/remove") + suspend fun keyRemove(@FieldMap data: Map): String + + @FormUrlEncoded + @POST("/message/push") + suspend fun messagePush(@FieldMap data: Map): String + + @FormUrlEncoded + @POST("/message/list") + suspend fun messageList(@Field("token") token: String): ReturnData + + @FormUrlEncoded + @POST("/message/remove") + suspend fun messageRemove(@Field("token")token:String,@Field("id")id:Int): String +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/data/api/data/request/DeviceInfo.kt b/android/app/src/main/java/com/pushdeer/os/data/api/data/request/DeviceInfo.kt new file mode 100644 index 0000000..9da0b94 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/data/api/data/request/DeviceInfo.kt @@ -0,0 +1,23 @@ +package com.pushdeer.os.data.api.data.request + +class DeviceInfo { + var id:Int = 0 + var uid:String = "" + var name:String = "" + var type:String = "" + var device_id: String = "" + var is_clip: Int = 0 + + fun toRequestMap(token:String): Map { + return mapOf( + "token" to token, + "name" to name, + "device_id" to device_id, + "is_clip" to is_clip.toString() + ) + } + + override fun toString(): String { + return "id:$id uid:$uid name:$name type:$type device_id:$device_id is_clip:$is_clip" + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/data/api/data/response/DeviceInfo.kt b/android/app/src/main/java/com/pushdeer/os/data/api/data/response/DeviceInfo.kt new file mode 100644 index 0000000..517f4ac --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/data/api/data/response/DeviceInfo.kt @@ -0,0 +1,12 @@ +package com.pushdeer.os.data.api.data.response + +import com.pushdeer.os.data.api.data.request.DeviceInfo + + +class DeviceInfoList{ + var devices:List = emptyList() + + override fun toString(): String { + return "devices:$devices" + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/data/api/data/response/DeviceRemove.kt b/android/app/src/main/java/com/pushdeer/os/data/api/data/response/DeviceRemove.kt new file mode 100644 index 0000000..d0658ef --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/data/api/data/response/DeviceRemove.kt @@ -0,0 +1,13 @@ +package com.pushdeer.os.data.api.data.response + +class DeviceRemove { + var token: String = "" + var id = "" + + fun toMap():Map { + return mapOf( + "token" to token, + "id" to id + ) + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/data/api/data/response/Message.kt b/android/app/src/main/java/com/pushdeer/os/data/api/data/response/Message.kt new file mode 100644 index 0000000..8cd09af --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/data/api/data/response/Message.kt @@ -0,0 +1,20 @@ +package com.pushdeer.os.data.api.data.response + +import com.pushdeer.os.data.database.entity.MessageEntity + +class Message : MessageEntity() { + fun toMessageEntity(): MessageEntity { + return MessageEntity().apply { + this.id = this@Message.id + this.uid = this@Message.uid + this.text = this@Message.text + this.desp = this@Message.desp + this.type = this@Message.type + this.created_at = this@Message.created_at + } + } +} + +class MessageList { + var messages = emptyList() +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/data/api/data/response/PushKey.kt b/android/app/src/main/java/com/pushdeer/os/data/api/data/response/PushKey.kt new file mode 100644 index 0000000..ade64d5 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/data/api/data/response/PushKey.kt @@ -0,0 +1,20 @@ +package com.pushdeer.os.data.api.data.response + +class PushKey { + var id:String = "" + var key: String = "" + var name: String = "" + var created_at = "" + + override fun toString(): String { + return "id:$id key:$key name:$name created_at:$created_at" + } +} + +class PushKeyList { + var keys: List = emptyList() + + override fun toString(): String { + return "keys:$keys" + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/data/api/data/response/ReturnData.kt b/android/app/src/main/java/com/pushdeer/os/data/api/data/response/ReturnData.kt new file mode 100644 index 0000000..7df7be7 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/data/api/data/response/ReturnData.kt @@ -0,0 +1,15 @@ +package com.pushdeer.os.data.api.data.response + +class ReturnData { + var code: Int = 0 + var content: T?=null + var error:String = "" + + override fun toString(): String { + return "code:${code} error:${error} content:${content.toString()}" + } +} + +class TokenOnly{ + var token:String = "" +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/data/api/data/response/UserInfo.kt b/android/app/src/main/java/com/pushdeer/os/data/api/data/response/UserInfo.kt new file mode 100644 index 0000000..1358c4e --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/data/api/data/response/UserInfo.kt @@ -0,0 +1,23 @@ +package com.pushdeer.os.data.api.data.response + +class UserInfo { + var id: String = "" + var name: String = "" + var email: String = "" + var app_id: String = "" + var wechat_id: String = "" + var level: Int = 1 + var created_at: String = "" + var updated_at: String = "" + + override fun toString(): String { + return "id:$id\n" + + "name:$name\n" + + "email:$email\n" + + "app_id:$app_id\n" + + "wechat_id:$wechat_id\n" + + "level:$level\n" + + "created:$created_at\n" + + "updated:$updated_at" + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/data/database/AppDatabase.kt b/android/app/src/main/java/com/pushdeer/os/data/database/AppDatabase.kt new file mode 100644 index 0000000..e254e6e --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/data/database/AppDatabase.kt @@ -0,0 +1,33 @@ +package com.pushdeer.os.data.database + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import com.pushdeer.os.data.database.dao.LogDogDao +import com.pushdeer.os.data.database.dao.MessageDao +import com.pushdeer.os.data.database.entity.LogDog +import com.pushdeer.os.data.database.entity.MessageEntity + +@Database(entities = [LogDog::class,MessageEntity::class], version = 1, exportSchema = false) +abstract class AppDatabase : RoomDatabase() { + abstract fun logDogDao(): LogDogDao + abstract fun messageDao():MessageDao + + companion object { + @Volatile + private var INSTANCE: AppDatabase? = null + + fun getDatabase(context: Context): AppDatabase { + return INSTANCE ?: synchronized(this) { + val instance = + Room.databaseBuilder( + context.applicationContext, + AppDatabase::class.java, "app-database" + ).build() + INSTANCE = instance + instance + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/data/database/dao/LogDogDao.kt b/android/app/src/main/java/com/pushdeer/os/data/database/dao/LogDogDao.kt new file mode 100644 index 0000000..da0ae3a --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/data/database/dao/LogDogDao.kt @@ -0,0 +1,20 @@ +package com.pushdeer.os.data.database.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import com.pushdeer.os.data.database.entity.LogDog +import kotlinx.coroutines.flow.Flow + +@Dao +interface LogDogDao { + + @get:Query("select * from LogDog Order by id desc") + val all:Flow> + + @Insert + suspend fun insert(vararg logDog: LogDog) + + @Query("delete from LogDog") + suspend fun clear() +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/data/database/dao/MessageDao.kt b/android/app/src/main/java/com/pushdeer/os/data/database/dao/MessageDao.kt new file mode 100644 index 0000000..1452d09 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/data/database/dao/MessageDao.kt @@ -0,0 +1,22 @@ +package com.pushdeer.os.data.database.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy.REPLACE +import androidx.room.Query +import com.pushdeer.os.data.database.entity.MessageEntity +import kotlinx.coroutines.flow.Flow + +@Dao +interface MessageDao { + + @get:Query("select * from message order by id desc") + val all: Flow> + + @Delete + suspend fun delete(vararg messageEntity: MessageEntity) + + @Insert(onConflict = REPLACE) + suspend fun insert(vararg messageEntity: MessageEntity) +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/data/database/entity/LogDog.kt b/android/app/src/main/java/com/pushdeer/os/data/database/entity/LogDog.kt new file mode 100644 index 0000000..d25fb2a --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/data/database/entity/LogDog.kt @@ -0,0 +1,74 @@ +package com.pushdeer.os.data.database.entity + +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.wh.common.typeExt.toTimestamp + +@Entity +class LogDog { + @PrimaryKey(autoGenerate = true) + var id:Int = 0 + var level:String = "d" + var entity:String = "" + var event:String = "" + var log:String = "" + var timestamp:Long = System.currentTimeMillis() + + override fun toString(): String { + return "id:$id level:$level entity:$entity\nlog:$log time:${timestamp.toTimestamp()}" + } + +// @Composable +// fun resolve( +// d: @Composable () -> Unit, +// e: @Composable () -> Unit, +// i: @Composable () -> Unit, +// w: @Composable () -> Unit +// ) { +// when(this.level){ +// "d" -> d() +// "e" -> e() +// "i" -> i() +// "w" -> w() +// } +// +// } + + companion object{ + fun logd(entity: String,event:String,log:String): LogDog { + return LogDog().apply { + level = "d" + this.entity = entity + this.event = event + this.log = log + } + } + + fun loge(entity: String,event:String,log:String): LogDog { + return LogDog().apply { + level = "e" + this.entity = entity + this.event = event + this.log = log + } + } + + fun logi(entity: String,event:String,log:String): LogDog { + return LogDog().apply { + level = "i" + this.entity = entity + this.event = event + this.log = log + } + } + + fun logw(entity: String,event:String,log:String): LogDog { + return LogDog().apply { + level = "w" + this.entity = entity + this.event = event + this.log = log + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/data/database/entity/MessageEntity.java b/android/app/src/main/java/com/pushdeer/os/data/database/entity/MessageEntity.java new file mode 100644 index 0000000..70d3c63 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/data/database/entity/MessageEntity.java @@ -0,0 +1,29 @@ +package com.pushdeer.os.data.database.entity; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +import com.pushdeer.os.data.api.data.response.Message; + +@Entity(tableName = "message") +public class MessageEntity { + @PrimaryKey + public int id; + public String uid; + public String text; + public String desp; + public String type; + public String created_at; + + + public Message toMessage(){ + Message m = new Message(); + m.id = id; + m.uid = uid; + m.created_at = created_at; + m.desp = desp; + m.text = text; + m.type = type; + return m; + } +} diff --git a/android/app/src/main/java/com/pushdeer/os/data/repository/LogDogRepository.kt b/android/app/src/main/java/com/pushdeer/os/data/repository/LogDogRepository.kt new file mode 100644 index 0000000..c3492aa --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/data/repository/LogDogRepository.kt @@ -0,0 +1,42 @@ +package com.pushdeer.os.data.repository + +import com.pushdeer.os.data.database.dao.LogDogDao +import com.pushdeer.os.data.database.entity.LogDog +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class LogDogRepository(private val logDogDao: LogDogDao) { + val all = logDogDao.all + + suspend fun clear(){ + logDogDao.clear() + } + + suspend fun insert(vararg logDog: LogDog) { + logDogDao.insert(*logDog) + } + + suspend fun logd(entity: String, event: String, log: String) { + withContext(Dispatchers.IO) { + insert(LogDog.logd(entity, event, log)) + } + } + + suspend fun loge(entity: String, event: String, log: String) { + withContext(Dispatchers.IO) { + insert(LogDog.loge(entity, event, log)) + } + } + + suspend fun logi(entity: String, event: String, log: String) { + withContext(Dispatchers.IO) { + insert(LogDog.logi(entity, event, log)) + } + } + + suspend fun logw(entity: String, event: String, log: String) { + withContext(Dispatchers.IO) { + insert(LogDog.logw(entity, event, log)) + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/data/repository/MessageRepository.kt b/android/app/src/main/java/com/pushdeer/os/data/repository/MessageRepository.kt new file mode 100644 index 0000000..06e5ba6 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/data/repository/MessageRepository.kt @@ -0,0 +1,25 @@ +package com.pushdeer.os.data.repository + +import com.pushdeer.os.data.api.data.response.Message +import com.pushdeer.os.data.database.dao.MessageDao +import com.pushdeer.os.data.database.entity.MessageEntity + +class MessageRepository(private val messageDao: MessageDao) { + val all = messageDao.all + + suspend fun insert(vararg message: Message) { + message.forEach { + messageDao.insert(it.toMessageEntity()) + } + } + + suspend fun delete(vararg message: Message) { + message.forEach { + messageDao.delete(it.toMessageEntity()) + } + } + + suspend fun delete(vararg messageEntity: MessageEntity) { + messageDao.delete(*messageEntity) + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/data/repository/MiPushRepository.kt b/android/app/src/main/java/com/pushdeer/os/data/repository/MiPushRepository.kt new file mode 100644 index 0000000..34e25b6 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/data/repository/MiPushRepository.kt @@ -0,0 +1,7 @@ +package com.pushdeer.os.data.repository + +import androidx.lifecycle.MutableLiveData + +class MiPushRepository { + val regId = MutableLiveData("") +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/factory/ViewModelFactory.kt b/android/app/src/main/java/com/pushdeer/os/factory/ViewModelFactory.kt new file mode 100644 index 0000000..41b4859 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/factory/ViewModelFactory.kt @@ -0,0 +1,48 @@ +package com.pushdeer.os.factory + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.pushdeer.os.data.api.PushDeerApi +import com.pushdeer.os.keeper.RepositoryKeeper +import com.pushdeer.os.keeper.StoreKeeper +import com.pushdeer.os.viewmodel.LogDogViewModel +import com.pushdeer.os.viewmodel.MessageViewModel +import com.pushdeer.os.viewmodel.PushDeerViewModel +import com.pushdeer.os.viewmodel.UiViewModel + +class ViewModelFactory( + private val repositoryKeeper: RepositoryKeeper, + private val storeKeeper: StoreKeeper, + private val pushDeerService: PushDeerApi +) : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + when { + modelClass.isAssignableFrom(UiViewModel::class.java) -> { + @Suppress("UNCHECKED_CAST") + return UiViewModel(settingStore = storeKeeper.settingStore) as T + } + modelClass.isAssignableFrom(PushDeerViewModel::class.java) -> { + @Suppress("UNCHECKED_CAST") + return PushDeerViewModel( + settingStore = storeKeeper.settingStore, + logDogRepository = repositoryKeeper.logDogRepository, + pushDeerService = pushDeerService, + messageRepository = repositoryKeeper.messageRepository + ) as T + } + modelClass.isAssignableFrom(LogDogViewModel::class.java) -> { + @Suppress("UNCHECKED_CAST") + return LogDogViewModel(repositoryKeeper.logDogRepository) as T + } + modelClass.isAssignableFrom(MessageViewModel::class.java) -> { + @Suppress("UNCHECKED_CAST") + return MessageViewModel( + messageRepository = repositoryKeeper.messageRepository, + pushDeerService = pushDeerService, + settingStore = storeKeeper.settingStore + ) as T + } + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/holder/DataHolder.kt b/android/app/src/main/java/com/pushdeer/os/holder/DataHolder.kt new file mode 100644 index 0000000..46906c9 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/holder/DataHolder.kt @@ -0,0 +1,4 @@ +package com.pushdeer.os.holder + +interface DataHolder { +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/holder/RequestHolder.kt b/android/app/src/main/java/com/pushdeer/os/holder/RequestHolder.kt new file mode 100644 index 0000000..0af3822 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/holder/RequestHolder.kt @@ -0,0 +1,115 @@ +package com.pushdeer.os.holder + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Intent +import android.util.Log +import androidx.activity.ComponentActivity +import androidx.activity.result.ActivityResultLauncher +import androidx.navigation.NavHostController +import coil.ImageLoader +import com.pushdeer.os.activity.QrScanActivity +import com.pushdeer.os.data.api.data.request.DeviceInfo +import com.pushdeer.os.data.api.data.response.Message +import com.pushdeer.os.data.api.data.response.PushKey +import com.pushdeer.os.store.SettingStore +import com.pushdeer.os.viewmodel.LogDogViewModel +import com.pushdeer.os.viewmodel.MessageViewModel +import com.pushdeer.os.viewmodel.PushDeerViewModel +import com.pushdeer.os.viewmodel.UiViewModel +import io.noties.markwon.Markwon +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +interface RequestHolder { + val uiViewModel: UiViewModel + val pushDeerViewModel: PushDeerViewModel + val logDogViewModel: LogDogViewModel + val messageViewModel:MessageViewModel + val settingStore: SettingStore + val globalNavController: NavHostController + val coroutineScope: CoroutineScope + val myActivity: ComponentActivity + val markdown: Markwon + val activityOpener: ActivityResultLauncher + val coilImageLoader: ImageLoader + + val clipboardManager: ClipboardManager + + fun copyPlainString(str: String) { + clipboardManager.setPrimaryClip(ClipData.newPlainText("pushdeer-pushkey", str)) + } + + fun startQrScanActivity() { + activityOpener.launch(QrScanActivity.forScanResultIntent(myActivity)) + } + + fun keyGen() { + coroutineScope.launch { + pushDeerViewModel.keyGen() + } + } + + fun keyRegen(keyId: String) { + coroutineScope.launch { + pushDeerViewModel.keyRegen(keyId) + } + } + + fun keyRemove(pushKey: PushKey) { + coroutineScope.launch { + pushDeerViewModel.keyList.remove(pushKey) + pushDeerViewModel.keyRemove(pushKey.id) + } + } + + fun deviceReg(deviceInfo: DeviceInfo){ + coroutineScope.launch { + pushDeerViewModel.deviceReg(deviceInfo) + } + } + + fun deviceRemove(deviceInfo: DeviceInfo) { + coroutineScope.launch { + pushDeerViewModel.deviceList.remove(deviceInfo) + pushDeerViewModel.deviceRemove(deviceInfo.id) + } + } + + fun messagePush(text: String, desp: String, type: String, pushkey: String) { + coroutineScope.launch { + pushDeerViewModel.messagePush(text, desp, type, pushkey) + } + } + + fun messagePushTest(desp: String) { + if (pushDeerViewModel.keyList.isNotEmpty()) { + messagePush("pushtest", desp, "markdown", pushDeerViewModel.keyList[0].key) + coroutineScope.launch { + delay(1000) + pushDeerViewModel.messageList() + } + } else + Log.d("WH_", "messagePushTest: keylist is empty") + } + + fun messageRemove(message: Message,onDone:()->Unit={}) { + coroutineScope.launch { +// pushDeerViewModel.messageList.remove(message) + pushDeerViewModel.messageRemove(message.id) + onDone() + } + } + + fun toggleMessageSender() { + settingStore.showMessageSender = settingStore.showMessageSender.not() + uiViewModel.showMessageSender = settingStore.showMessageSender + } + + fun clearLogDog() { + coroutineScope.launch { + logDogViewModel.clear() + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/keeper/RepositoryKeeper.kt b/android/app/src/main/java/com/pushdeer/os/keeper/RepositoryKeeper.kt new file mode 100644 index 0000000..cd01fd2 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/keeper/RepositoryKeeper.kt @@ -0,0 +1,12 @@ +package com.pushdeer.os.keeper + +import com.pushdeer.os.data.database.AppDatabase +import com.pushdeer.os.data.repository.LogDogRepository +import com.pushdeer.os.data.repository.MessageRepository +import com.pushdeer.os.data.repository.MiPushRepository + +class RepositoryKeeper(database: AppDatabase) { + val miPushRepository = MiPushRepository() + val logDogRepository = LogDogRepository(database.logDogDao()) + val messageRepository = MessageRepository(database.messageDao()) +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/keeper/StoreKeeper.kt b/android/app/src/main/java/com/pushdeer/os/keeper/StoreKeeper.kt new file mode 100644 index 0000000..d152fd4 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/keeper/StoreKeeper.kt @@ -0,0 +1,8 @@ +package com.pushdeer.os.keeper + +import android.content.Context +import com.pushdeer.os.store.SettingStore + +class StoreKeeper(context: Context) { + val settingStore = SettingStore(context) +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/receiver/MessageReceiver.kt b/android/app/src/main/java/com/pushdeer/os/receiver/MessageReceiver.kt new file mode 100644 index 0000000..aab03cd --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/receiver/MessageReceiver.kt @@ -0,0 +1,123 @@ +package com.pushdeer.os.receiver + +import android.content.Context +import android.text.TextUtils +import com.pushdeer.os.App +import com.pushdeer.os.data.repository.MiPushRepository +import com.pushdeer.os.keeper.RepositoryKeeper +import com.xiaomi.mipush.sdk.* + +class MessageReceiver : PushMessageReceiver() { + private var mRegId: String? = null + private val mResultCode: Long = -1 + private val mReason: String? = null + private val mCommand: String? = null + private var mMessage: String? = null + private var mTopic: String? = null + private var mAlias: String? = null + private var mUserAccount: String? = null + private var mStartTime: String? = null + private var mEndTime: String? = null + + private var repositoryKeeper:RepositoryKeeper?=null + private var miPushRepository: MiPushRepository?=null + + fun init(context: Context){ + if (repositoryKeeper==null){ + repositoryKeeper = (context.applicationContext as App).repositoryKeeper + } + if (miPushRepository==null){ + miPushRepository = repositoryKeeper!!.miPushRepository + } + } + + override fun onReceivePassThroughMessage(context: Context, message: MiPushMessage) { + init(context) +// Log.d("WH_", "onReceivePassThroughMessage: $message") + mMessage = message.content + if (!TextUtils.isEmpty(message.topic)) { + mTopic = message.topic + } else if (!TextUtils.isEmpty(message.alias)) { + mAlias = message.alias + } else if (!TextUtils.isEmpty(message.userAccount)) { + mUserAccount = message.userAccount + } + } + + override fun onNotificationMessageClicked(context: Context, message: MiPushMessage) { + init(context) +// Log.d("WH_", "onNotificationMessageClicked: $message") + mMessage = message.content + if (!TextUtils.isEmpty(message.topic)) { + mTopic = message.topic + } else if (!TextUtils.isEmpty(message.alias)) { + mAlias = message.alias + } else if (!TextUtils.isEmpty(message.userAccount)) { + mUserAccount = message.userAccount + } + } + + override fun onNotificationMessageArrived(context: Context, message: MiPushMessage) { + init(context) +// Log.d("WH_", "onNotificationMessageArrived: $message") + mMessage = message.content + if (!TextUtils.isEmpty(message.topic)) { + mTopic = message.topic + } else if (!TextUtils.isEmpty(message.alias)) { + mAlias = message.alias + } else if (!TextUtils.isEmpty(message.userAccount)) { + mUserAccount = message.userAccount + } + } + + override fun onCommandResult(context: Context, message: MiPushCommandMessage) { + init(context) +// Log.d("WH_", "onCommandResult: $message") + val command = message.command + val arguments = message.commandArguments + val cmdArg1 = if (arguments != null && arguments.size > 0) arguments[0] else null + val cmdArg2 = if (arguments != null && arguments.size > 1) arguments[1] else null + if (MiPushClient.COMMAND_REGISTER == command) { + if (message.resultCode == ErrorCode.SUCCESS.toLong()) { + + mRegId = cmdArg1 + } + } else if (MiPushClient.COMMAND_SET_ALIAS == command) { + if (message.resultCode == ErrorCode.SUCCESS.toLong()) { + mAlias = cmdArg1 + } + } else if (MiPushClient.COMMAND_UNSET_ALIAS == command) { + if (message.resultCode == ErrorCode.SUCCESS.toLong()) { + mAlias = cmdArg1 + } + } else if (MiPushClient.COMMAND_SUBSCRIBE_TOPIC == command) { + if (message.resultCode == ErrorCode.SUCCESS.toLong()) { + mTopic = cmdArg1 + } + } else if (MiPushClient.COMMAND_UNSUBSCRIBE_TOPIC == command) { + if (message.resultCode == ErrorCode.SUCCESS.toLong()) { + mTopic = cmdArg1 + } + } else if (MiPushClient.COMMAND_SET_ACCEPT_TIME == command) { + if (message.resultCode == ErrorCode.SUCCESS.toLong()) { + mStartTime = cmdArg1 + mEndTime = cmdArg2 + } + } + } + + override fun onReceiveRegisterResult(context: Context, message: MiPushCommandMessage) { + init(context) +// Log.d("WH_", "onReceiveRegisterResult: $message") + val command = message.command + val arguments = message.commandArguments + val cmdArg1 = if (arguments != null && arguments.size > 0) arguments[0] else null + val cmdArg2 = if (arguments != null && arguments.size > 1) arguments[1] else null + if (MiPushClient.COMMAND_REGISTER == command) { + if (message.resultCode == ErrorCode.SUCCESS.toLong()) { + mRegId = cmdArg1 + miPushRepository!!.regId.postValue(mRegId) + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/sss/MyActivityResultContract.kt b/android/app/src/main/java/com/pushdeer/os/sss/MyActivityResultContract.kt new file mode 100644 index 0000000..ef8f91b --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/sss/MyActivityResultContract.kt @@ -0,0 +1,19 @@ +//package com.pushdeer.os.sss +// +//import android.content.Context +//import android.content.Intent +//import androidx.activity.result.contract.ActivityResultContract +//import com.pushdeer.os.activity.QrScanActivity +// +//class MyActivityResultContract : ActivityResultContract() { +// override fun createIntent(context: Context, input: String): Intent { +// return QrScanActivity.forScanResultIntent(context) +// } +// +// override fun parseResult(resultCode: Int, intent: Intent?): String { +// intent?.let { +// return it.getStringExtra(QrScanActivity.DataKey).toString() +// } +// return "" +// } +//} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/store/SettingStore.kt b/android/app/src/main/java/com/pushdeer/os/store/SettingStore.kt new file mode 100644 index 0000000..508ac8c --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/store/SettingStore.kt @@ -0,0 +1,21 @@ +package com.pushdeer.os.store + +import android.content.Context +import com.wh.common.store.Store + +class SettingStore(context:Context) { + val store = Store.create(context,"setting") + + var userToken by store.string("user-token","") + var deviceName by store.string("device-name","My Dear Deer") + var useRecv by store.boolean("use-recv",false) // 启用接收 + var useSend by store.boolean("use-send",false) + var useSendNotification by store.boolean("use-send-notification",false) + var notificationPackages by store.stringSet("notification-packages", emptySet()) + var useSendMissedCall by store.boolean("use-send=missed-call",false) + var useSendSMS by store.boolean("use-send-sms",false) + + var showMessageSender by store.boolean("show-message-sender",true) + var thisPushSdk by store.string("this-push-sdk","mi-push") + var thisDeviceId by store.string("this-device-id","") +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/typeExt/Flow.kt b/android/app/src/main/java/com/pushdeer/os/typeExt/Flow.kt new file mode 100644 index 0000000..cf17130 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/typeExt/Flow.kt @@ -0,0 +1,8 @@ +package com.pushdeer.os.typeExt + +import androidx.lifecycle.LifecycleOwner +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch + diff --git a/android/app/src/main/java/com/pushdeer/os/ui/compose/componment/Item.kt b/android/app/src/main/java/com/pushdeer/os/ui/compose/componment/Item.kt new file mode 100644 index 0000000..d0fa3dd --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/compose/componment/Item.kt @@ -0,0 +1,146 @@ +package com.pushdeer.os.ui.compose.componment + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.Image +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.pushdeer.os.R +import com.pushdeer.os.ui.theme.MBlue +import com.pushdeer.os.ui.theme.MainBlue + +@ExperimentalMaterialApi +@Composable +fun CardItemSingleLineWithIcon( + onClick: () -> Unit = {}, + @DrawableRes resId: Int = R.drawable.iphone2x, + text: String = "Easy's iPhone" +) { + Card( + onClick = onClick, + shape = RoundedCornerShape(4.dp), + modifier = Modifier + .border( + width = 1.dp, + color = MainBlue, + shape = RoundedCornerShape(4.dp) + ), + elevation = 5.dp + + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .height(60.dp) + .padding(start = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = painterResource(id = resId), + contentDescription = "", + colorFilter = ColorFilter.tint(color = MaterialTheme.colors.MBlue), + modifier = Modifier.size(28.dp) + ) + Text( + text = text, + color = MainBlue, + textAlign = TextAlign.Center, + modifier = Modifier.padding(start = 8.dp) + ) + } + } +} + +@ExperimentalMaterialApi +@Composable +fun CardItemMultiLine( + onClick: () -> Unit = {}, + @DrawableRes resId: Int = R.drawable.iphone2x, + text: String = "Easy's iPhone" +) { + Card( + onClick = onClick, + shape = RoundedCornerShape(4.dp), + modifier = Modifier +// .padding(bottom = 16.dp) + .border( + width = 1.dp, + color = MainBlue, + shape = RoundedCornerShape(4.dp) + ), + elevation = 5.dp + + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .height(60.dp) + .padding(start = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = painterResource(id = resId), + contentDescription = "", + colorFilter = ColorFilter.tint(color = MaterialTheme.colors.MBlue), + modifier = Modifier.size(28.dp) + ) + Text( + text = text, + color = MainBlue, + textAlign = TextAlign.Center, + modifier = Modifier.padding(start = 8.dp) + ) + } + } +} + +@ExperimentalMaterialApi +@Composable +fun CardItemWithContent(onClick: () -> Unit = {}, content: @Composable () -> Unit = {}) { + Card( + onClick = onClick, + shape = RoundedCornerShape(4.dp), + modifier = Modifier + .border( + width = 1.dp, + color = MainBlue, + shape = RoundedCornerShape(4.dp) + ), + content = content, + elevation = 5.dp + ) +} + +@Composable +fun ListBottomBlankItem() { + Row( + modifier = Modifier + .fillMaxWidth() + .height(60.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.Top + ) { +// Text(text = "End of List",color = Color.Gray) + } +} + +@ExperimentalMaterialApi +@Preview(showBackground = true) +@Composable +fun IP() { + CardItemWithContent { + Text(text = "aaa") + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/compose/componment/KeyItem.kt b/android/app/src/main/java/com/pushdeer/os/ui/compose/componment/KeyItem.kt new file mode 100644 index 0000000..ae2d65f --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/compose/componment/KeyItem.kt @@ -0,0 +1,134 @@ +package com.pushdeer.os.ui.compose.componment + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.Image +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.DateRange +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.pushdeer.os.R +import com.pushdeer.os.data.api.data.response.PushKey +import com.pushdeer.os.holder.RequestHolder +import com.pushdeer.os.ui.theme.MBlue +import com.wh.common.util.TimeUtils + +@ExperimentalMaterialApi +@Composable +fun KeyItem(key: PushKey, requestHolder: RequestHolder) { + CardItemWithContent { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.Bottom, + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 20.dp), + ) { + Row(verticalAlignment = Alignment.Bottom) { + Image( + painter = painterResource(id = R.drawable.ic_deer_head_with_mail), + contentDescription = "", + modifier = Modifier.size(36.dp) + ) + Text( + text = key.name, + fontSize = 16.sp, + color = MaterialTheme.colors.MBlue, + modifier = Modifier.padding(start = 10.dp) + ) + } + Row(verticalAlignment = Alignment.CenterVertically) { + Icon( + imageVector = Icons.Default.DateRange, + contentDescription = "", + tint = Color.Gray, + modifier = Modifier + .size(20.dp) + .padding(end = 4.dp) + ) + Text( + text = TimeUtils.getFormattedTime( + TimeUtils.utcTS2ms( + key.created_at, + "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'" + ), "MM/dd HH:mm" + ), + color = Color.Gray, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + fontSize = 12.sp + ) + } + } + Text( + text = key.key, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = Color.Gray, + fontSize = 14.sp, + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 14.dp) + .border( + width = 1.dp, + color = Color.Gray, + shape = RoundedCornerShape(4.dp) + ) + .padding(horizontal = 14.dp, vertical = 8.dp) + ) +// Canvas(modifier = Modifier +// .fillMaxWidth() +// .height(16.dp), onDraw = { +// val linePath = Path() +// val linePaint = Paint() +// linePaint.pathEffect = PathEffect.dashPathEffect(FloatArray(10),10F) +// drawIntoCanvas { +// it.drawPath(linePath, linePaint) +// } +// }) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + OutlinedButton( + onClick = { requestHolder.keyRegen(key.id) }, + colors = ButtonDefaults.outlinedButtonColors( + backgroundColor = Color.Transparent, + contentColor = MaterialTheme.colors.MBlue + ), + border = BorderStroke(1.dp, MaterialTheme.colors.MBlue), + shape = RoundedCornerShape(6.dp) + ) { + Text(text = "Reset") + } + Button( + onClick = { + requestHolder.copyPlainString(key.key) + }, + colors = ButtonDefaults.buttonColors( + backgroundColor = MaterialTheme.colors.MBlue, + contentColor = Color.White + ), + shape = RoundedCornerShape(6.dp) + ) { + Text(text = "Copy") + } + } + } + + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/compose/componment/MessageItem.kt b/android/app/src/main/java/com/pushdeer/os/ui/compose/componment/MessageItem.kt new file mode 100644 index 0000000..a2a5d1e --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/compose/componment/MessageItem.kt @@ -0,0 +1,168 @@ +package com.pushdeer.os.ui.compose.componment + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.viewinterop.AndroidView +import com.pushdeer.os.R +import com.pushdeer.os.data.database.entity.MessageEntity +import com.pushdeer.os.holder.RequestHolder +import com.pushdeer.os.ui.theme.MBlue +import com.pushdeer.os.util.CurrentTimeUtil +import com.pushdeer.os.values.ConstValues + + +@ExperimentalMaterialApi +@Composable +fun PlainTextMessageItem(message: MessageEntity) { + Column( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(4.dp)) + .background(color = MaterialTheme.colors.surface) + ) { + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = painterResource(id = R.drawable.ic_deer_head_with_mail), + contentDescription = "", + modifier = Modifier.size(40.dp) + ) + Text( + text = "${message.text}·${ + CurrentTimeUtil.resolveUTCTimeAndNow( + message.created_at, + System.currentTimeMillis() + ) + }" + ) + } + + CardItemWithContent() { + Text( + text = message.desp, + overflow = TextOverflow.Visible, + lineHeight = 24.sp, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) + } + } +} + +@ExperimentalMaterialApi +@Composable +fun ImageMessageItem(message: MessageEntity) { + Column( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(4.dp)) + .background (color = MaterialTheme.colors.surface) + ) { + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = ConstValues.MainPageSidePadding) + .padding(bottom = 12.dp), + ) { + Image( + painter = painterResource(id = R.drawable.ic_deer_head_with_mail), + contentDescription = "", + modifier = Modifier.size(40.dp) + ) + Text( + text = "${message.text}·${ + CurrentTimeUtil.resolveUTCTimeAndNow( + message.created_at, + System.currentTimeMillis() + ) + }" + ) + } + Card(modifier = Modifier.fillMaxWidth(), onClick = {}) { + Image( + painter = painterResource(id = R.drawable.logo_com_x2), + contentDescription = "", + contentScale = ContentScale.FillWidth + ) + } + } +} + +@ExperimentalMaterialApi +@Composable +fun MarkdownMessageItem(message: MessageEntity, requestHolder: RequestHolder) { + Column( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(4.dp)) + .background(color = MaterialTheme.colors.surface) + ) { + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 12.dp), verticalAlignment = Alignment.CenterVertically + ) { + Box { + Image( + painter = painterResource(id = R.drawable.ic_deer_head_with_mail), + contentDescription = "", + modifier = Modifier.size(40.dp) + ) + Icon( + painter = painterResource(id = R.drawable.ic_markdown), + contentDescription = "", + tint = MaterialTheme.colors.MBlue, + modifier = Modifier + .size(20.dp) + .align(alignment = Alignment.BottomCenter) + ) + } + + Text( + text = "${message.text}·${ + CurrentTimeUtil.resolveUTCTimeAndNow( + message.created_at, + System.currentTimeMillis() + ) + }" + ) + } + + CardItemWithContent { + AndroidView( + factory = { ctx -> + android.widget.TextView(ctx).apply { + this.post { +// requestHolder.markdown.configuration().theme(). + requestHolder.markdown.setMarkdown(this, message.desp) + } + } + + }, modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/compose/componment/SettingItem.kt b/android/app/src/main/java/com/pushdeer/os/ui/compose/componment/SettingItem.kt new file mode 100644 index 0000000..05a8498 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/compose/componment/SettingItem.kt @@ -0,0 +1,54 @@ +package com.pushdeer.os.ui.compose.componment + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.pushdeer.os.ui.theme.MBlue + +@ExperimentalMaterialApi +@Composable +fun SettingItem(text: String, buttonString: String, onClick: () -> Unit) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp) + ) { + CardItemWithContent() { + Row( + modifier = Modifier + .fillMaxWidth() +// .padding(vertical = 10.dp) + .padding(start = 20.dp, end = 16.dp, top = 10.dp, bottom = 10.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = text, + fontSize = 16.sp, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + ) + Button( + onClick = onClick, + shape = RoundedCornerShape(8.dp), + colors = ButtonDefaults.buttonColors( + backgroundColor = MaterialTheme.colors.MBlue, + contentColor = Color.White + ) + ) { + Text( + text = buttonString, + fontSize = 15.sp + ) + } + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/compose/componment/SwipeToDismissItem.kt b/android/app/src/main/java/com/pushdeer/os/ui/compose/componment/SwipeToDismissItem.kt new file mode 100644 index 0000000..6e910d0 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/compose/componment/SwipeToDismissItem.kt @@ -0,0 +1,80 @@ +package com.pushdeer.os.ui.compose.componment + +import androidx.compose.animation.animateColorAsState +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.Done +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.pushdeer.os.values.ConstValues + +@ExperimentalMaterialApi +@Composable +fun SwipeToDismissItem( + onDismiss: () -> Unit, + sidePadding: Boolean = false, + content: @Composable RowScope.() -> Unit +) { + val dismissState = rememberDismissState() + if (dismissState.isDismissed(DismissDirection.EndToStart)) { + onDismiss() + } + Column(modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp)) { + SwipeToDismiss( + state = dismissState, + background = { + val direction = dismissState.dismissDirection ?: return@SwipeToDismiss + + val color by animateColorAsState( + when (dismissState.targetValue) { + DismissValue.DismissedToEnd -> Color.Green + DismissValue.DismissedToStart -> Color.Red + else -> Color.Gray + } + ) + + val alignment = when (direction) { + DismissDirection.StartToEnd -> Alignment.CenterStart + DismissDirection.EndToStart -> Alignment.CenterEnd + } + + val icon = when (direction) { + DismissDirection.StartToEnd -> Icons.Default.Done + DismissDirection.EndToStart -> Icons.Default.Delete + } + + Box( + contentAlignment = alignment, + modifier = Modifier + .fillMaxSize() + .clip(RoundedCornerShape(4.dp)) + .background(color) + .padding(end = 32.dp) + ) { + Icon( + imageVector = icon, + contentDescription = "", +// tint = Color.Red + ) + } + }, + directions = setOf(DismissDirection.EndToStart, DismissDirection.EndToStart), + dismissThresholds = { direction -> + FractionalThreshold(if (direction == DismissDirection.EndToStart) 0.45f else 0.57f) + }, + dismissContent = content, + modifier = Modifier.padding(horizontal = if (sidePadding) ConstValues.MainPageSidePadding else 0.dp) + ) + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/compose/page/LogDaoPage.kt b/android/app/src/main/java/com/pushdeer/os/ui/compose/page/LogDaoPage.kt new file mode 100644 index 0000000..9fba3f5 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/compose/page/LogDaoPage.kt @@ -0,0 +1,75 @@ +package com.pushdeer.os.ui.compose.page + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Delete +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import com.pushdeer.os.R +import com.pushdeer.os.data.database.entity.LogDog +import com.pushdeer.os.holder.RequestHolder +import com.pushdeer.os.ui.compose.page.main.MainPageFrame + +@ExperimentalMaterialApi +@Composable +fun LogDaoPage(requestHolder: RequestHolder) { + MainPageFrame( + titleStringId = R.string.global_logdog, + sideIcon = Icons.Default.Delete, + onSideIconClick = { + requestHolder.clearLogDog() + }) { + val logDogs by requestHolder.logDogViewModel.all.collectAsState(initial = emptyList()) + + Scaffold(modifier = Modifier.fillMaxSize()) { + if (logDogs.isEmpty()) { + Row( + modifier = Modifier + .fillMaxSize(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Image( + painter = painterResource(id = R.drawable.logo_half), + contentDescription = "", + ) + } + } else { + LazyColumn( + content = { + items(logDogs, key = { item: LogDog -> item.id }) { logDog: LogDog -> + Card( + onClick = { /*TODO*/ }, + elevation = 5.dp, + shape = RoundedCornerShape(4.dp), + modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Text(text = logDog.toString()) + } + } + } + }, + modifier = Modifier.fillMaxWidth() + ) + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/compose/page/LoginPage.kt b/android/app/src/main/java/com/pushdeer/os/ui/compose/page/LoginPage.kt new file mode 100644 index 0000000..187047f --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/compose/page/LoginPage.kt @@ -0,0 +1,76 @@ +package com.pushdeer.os.ui.compose.page + +import androidx.compose.foundation.Image +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.pushdeer.os.R +import com.pushdeer.os.holder.RequestHolder +import com.pushdeer.os.ui.theme.MainBlue +import com.pushdeer.os.ui.theme.MainGreen + +@ExperimentalMaterialApi +@Composable +fun LoginPage(requestHolder: RequestHolder) { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Image( + painter = painterResource(R.drawable.logo_com_x2), + contentDescription = "big push deer logo" + ) + Card( + onClick = { /*TODO*/ }, + shape = RoundedCornerShape(4.dp), + modifier = Modifier + .padding(bottom = 16.dp) + .border( + width = 1.dp, + color = MainBlue, + shape = RoundedCornerShape(4.dp) + ) + ) { + Text( + text = "Sign in with Apple", + color = MainBlue, + textAlign = TextAlign.Center, + modifier = Modifier + .padding(vertical = 16.dp) + .fillMaxWidth(0.6F) + + ) + } + Card( + onClick = {}, + shape = RoundedCornerShape(4.dp), + modifier = Modifier.border( + width = 1.dp, + color = MainGreen, + shape = RoundedCornerShape(4.dp) + ) + ) { + Text( + text = "Sign in with WeChat", + color = MainGreen, + textAlign = TextAlign.Center, + modifier = Modifier + .padding(vertical = 16.dp) + .fillMaxWidth(0.6F) + ) + } + } + +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/DeviceListPage.kt b/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/DeviceListPage.kt new file mode 100644 index 0000000..5e1f9a7 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/DeviceListPage.kt @@ -0,0 +1,109 @@ +package com.pushdeer.os.ui.compose.page.main + +import android.os.Build +import android.text.TextUtils +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.Card +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.pushdeer.os.R +import com.pushdeer.os.data.api.data.request.DeviceInfo +import com.pushdeer.os.data.api.data.response.UserInfo +import com.pushdeer.os.holder.RequestHolder +import com.pushdeer.os.ui.compose.componment.CardItemSingleLineWithIcon +import com.pushdeer.os.ui.compose.componment.ListBottomBlankItem +import com.pushdeer.os.ui.compose.componment.SwipeToDismissItem +import com.pushdeer.os.ui.navigation.Page +import com.pushdeer.os.util.SystemUtil + + +@ExperimentalMaterialApi +@Composable +fun DeviceListPage(requestHolder: RequestHolder) { + MainPageFrame( + titleStringId = Page.Devices.labelStringId, + onSideIconClick = { + requestHolder.deviceReg( + deviceInfo = DeviceInfo().apply { + name = System.currentTimeMillis().toString() + device_id = "sdsdf" + is_clip = 0 + } + ) + } + ) { + val state = rememberLazyListState() + LazyColumn(state = state) { + items( + items = requestHolder.pushDeerViewModel.deviceList, + key = { item: DeviceInfo -> item.id }) { deviceInfo: DeviceInfo -> + SwipeToDismissItem(onDismiss = { requestHolder.deviceRemove(deviceInfo) }) { + CardItemSingleLineWithIcon( + onClick = {}, + resId = R.drawable.ipad_landscape2x, + text = if (deviceInfo.device_id == requestHolder.settingStore.thisDeviceId) "${deviceInfo.name} (this device)" else deviceInfo.name + ) + } + } + item { + ListBottomBlankItem() + } + } + } +} + +@ExperimentalMaterialApi +@Composable +fun DeviceListPage(userInfo: UserInfo, token: String, regid: String) { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 8.dp) + .padding(top = 8.dp) + ) { + item { + Card(elevation = 5.dp, onClick = {}, modifier = Modifier.fillMaxWidth()) { + Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 14.dp)) { + Text(text = "当前版本 Android ${SystemUtil.getSystemVersion()}") + Text(text = "本机品牌 ${SystemUtil.getDeviceBrand()}") + Text(text = "本机型号 ${SystemUtil.getDeviceModel()}") + Text(text = "产品名称 ${Build.PRODUCT}") + MiuiVersion() + } + } + } + item { + Card(elevation = 5.dp, onClick = {}, modifier = Modifier.fillMaxWidth()) { + Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 14.dp)) { + Text(text = "id ${userInfo.id}") + Text(text = "name ${userInfo.name}") + Text(text = "email ${userInfo.email}") + Text(text = "app_id ${userInfo.app_id}") + Text(text = "wechat_id ${userInfo.wechat_id}") + Text(text = "created_at ${userInfo.created_at}") + Text(text = "updated_at ${userInfo.updated_at}") + Text(text = "level ${userInfo.level}") + Text(text = "token $token") + Text(text = "regid $regid") + } + } + } + } +} + +@Composable +fun MiuiVersion() { + val v = SystemUtil.getMiuiVersion() + if (!TextUtils.isEmpty(v)) { + Text(text = "Miui 版本 ${v}") + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/KeyListPage.kt b/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/KeyListPage.kt new file mode 100644 index 0000000..39429a1 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/KeyListPage.kt @@ -0,0 +1,37 @@ +package com.pushdeer.os.ui.compose.page.main + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.pushdeer.os.data.api.data.response.PushKey +import com.pushdeer.os.holder.RequestHolder +import com.pushdeer.os.ui.compose.componment.KeyItem +import com.pushdeer.os.ui.compose.componment.ListBottomBlankItem +import com.pushdeer.os.ui.compose.componment.SwipeToDismissItem +import com.pushdeer.os.ui.navigation.Page + +@ExperimentalMaterialApi +@Composable +fun KeyListPage(requestHolder: RequestHolder) { + MainPageFrame( + titleStringId = Page.Keys.labelStringId, + onSideIconClick = { requestHolder.keyGen() } + ) { + LazyColumn(modifier = Modifier.fillMaxWidth()) { + items( + requestHolder.pushDeerViewModel.keyList, + key = { item: PushKey -> item.id }) { pushKey: PushKey -> + SwipeToDismissItem(onDismiss = { requestHolder.keyRemove(pushKey) } + ) { + KeyItem(key = pushKey, requestHolder = requestHolder) + } + } + item { + ListBottomBlankItem() + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/MainPage.kt b/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/MainPage.kt new file mode 100644 index 0000000..50c1f5d --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/MainPage.kt @@ -0,0 +1,109 @@ +package com.pushdeer.os.ui.compose.page.main + +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.size +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavDestination.Companion.hierarchy +import androidx.navigation.NavGraph.Companion.findStartDestination +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.currentBackStackEntryAsState +import androidx.navigation.compose.rememberNavController +import com.pushdeer.os.R +import com.pushdeer.os.holder.RequestHolder +import com.pushdeer.os.ui.navigation.Page +import com.pushdeer.os.ui.navigation.pageList +import com.pushdeer.os.ui.theme.mainBottomBtn + +@ExperimentalAnimationApi +@ExperimentalMaterialApi +@Composable +fun MainPage(requestHolder: RequestHolder) { + + var titleStringId by remember { + mutableStateOf(Page.Devices.labelStringId) + } + val navController = rememberNavController() + Scaffold( + scaffoldState = rememberScaffoldState(), + bottomBar = { + BottomNavigation(backgroundColor = MaterialTheme.colors.surface) { + val navBackStackEntry by requestHolder.globalNavController.currentBackStackEntryAsState() + val currentDestination = navBackStackEntry?.destination + pageList.forEach { page -> + val selected = page.labelStringId == titleStringId + BottomNavigationItem( + icon = { + Icon( + painter = painterResource(id = page.id), + contentDescription = stringResource(id = titleStringId), + modifier = Modifier.size(23.dp), + tint = MaterialTheme.colors.mainBottomBtn(selected = selected) + ) + }, + label = { + Text( + stringResource(id = page.labelStringId), + color = MaterialTheme.colors.mainBottomBtn(selected = selected) + ) + }, + selected = currentDestination?.hierarchy?.any { it.route == page.route } == true, + onClick = { + navController.navigate(page.route) { + titleStringId = page.labelStringId + popUpTo(navController.graph.findStartDestination().id) { + saveState = true + } + launchSingleTop = true + restoreState = true + } + } + ) + } + } + }, + snackbarHost = { }, + content = { + Box(modifier = Modifier.fillMaxSize()) { + Image( + painter = painterResource(id = R.drawable.ic_logo_svg_1), + contentDescription = "", + alpha = 0.05F, colorFilter = ColorFilter.tint(color = Color.Gray), + modifier = Modifier + .align(alignment = Alignment.BottomCenter) + .size(500.dp) + .offset(x = (-180).dp, y = 140.dp), + ) + NavHost( + navController = navController, + startDestination = Page.Devices.route, + ) { + composable(Page.Devices.route) { + DeviceListPage(requestHolder = requestHolder) + } + composable(Page.Keys.route) { + KeyListPage(requestHolder = requestHolder) + } + composable(Page.Messages.route) { + MessageListPage(requestHolder = requestHolder) + } + composable(Page.Settings.route) { + SettingPage(requestHolder = requestHolder) + } + } + } + }, + ) +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/MainPageFrame.kt b/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/MainPageFrame.kt new file mode 100644 index 0000000..40af90b --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/MainPageFrame.kt @@ -0,0 +1,70 @@ +package com.pushdeer.os.ui.compose.page.main + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.pushdeer.os.values.ConstValues + +@Composable +fun MainPageFrame( + titleStringId: Int, + showSideIcon: Boolean = true, + sideIcon: ImageVector = Icons.Default.Add, + onSideIconClick: () -> Unit = {}, + sidePadding: Boolean = true, + content: @Composable BoxScope.() -> Unit +) { +// val sizePaddingValue = if (sidePadding) PaddingValues() + Box(modifier = Modifier.fillMaxSize()) { + Column( + modifier = Modifier + .fillMaxSize() +// .padding(horizontal = if (sidePadding) 37.dp else 0.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp, top = 27.dp) + .padding(horizontal = ConstValues.MainPageSidePadding) + .background(color = Color.Transparent), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = stringResource(id = titleStringId), fontSize = 32.sp, + fontWeight = FontWeight.W400, + ) +// if (showSideIcon) { + IconButton(onClick = onSideIconClick,modifier = Modifier.alpha(if (showSideIcon)1F else 0F)) { + Icon( + imageVector = sideIcon, + contentDescription = "", + tint = Color.LightGray + ) + } +// } + } + Box( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = if (sidePadding) ConstValues.MainPageSidePadding else 0.dp) + ) { + content() + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/MessageListPage.kt b/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/MessageListPage.kt new file mode 100644 index 0000000..3f3658c --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/MessageListPage.kt @@ -0,0 +1,113 @@ +package com.pushdeer.os.ui.compose.page.main + +import androidx.compose.animation.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.KeyboardArrowDown +import androidx.compose.material.icons.filled.KeyboardArrowUp +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.pushdeer.os.data.database.entity.MessageEntity +import com.pushdeer.os.holder.RequestHolder +import com.pushdeer.os.ui.compose.componment.* +import com.pushdeer.os.ui.navigation.Page +import com.pushdeer.os.ui.theme.MBlue +import com.pushdeer.os.values.ConstValues + + +@ExperimentalAnimationApi +@ExperimentalMaterialApi +@Composable +fun MessageListPage(requestHolder: RequestHolder) { + MainPageFrame( + titleStringId = Page.Messages.labelStringId, + sideIcon = if (requestHolder.uiViewModel.showMessageSender) Icons.Default.KeyboardArrowUp else Icons.Default.KeyboardArrowDown, + onSideIconClick = { requestHolder.toggleMessageSender() }, + sidePadding = false + ) { + var s by remember { + mutableStateOf("") + } + val messageList by requestHolder.messageViewModel.all.collectAsState(initial = emptyList()) + + LazyColumn(content = { + item { + AnimatedVisibility( + visible = requestHolder.uiViewModel.showMessageSender, + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically() + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp) + .padding(horizontal = ConstValues.MainPageSidePadding) + + ) { + OutlinedTextField( + value = s, + onValueChange = { s = it }, + shape = RoundedCornerShape(4.dp), + colors = TextFieldDefaults.outlinedTextFieldColors( + backgroundColor = Color.Transparent, + focusedBorderColor = MaterialTheme.colors.MBlue, + focusedLabelColor = Color.Transparent, + unfocusedBorderColor = MaterialTheme.colors.MBlue, + unfocusedLabelColor = Color.Transparent, + ), + maxLines = 5, + singleLine = false, + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp), + ) + Button( + onClick = { + requestHolder.messagePushTest(s) + }, + colors = ButtonDefaults.buttonColors( + backgroundColor = MaterialTheme.colors.MBlue, + contentColor = Color.White + ), + ) { + Text(text = "Send") + } + } + } + } + items( + items = messageList, + key = { item: MessageEntity -> item.id }) { message: MessageEntity -> + SwipeToDismissItem( + onDismiss = { + requestHolder.messageRemove(message.toMessage(), onDone = { + requestHolder.messageViewModel.delete(message) + }) + }, +// sidePadding = false + sidePadding = message.type != "image" + ) { +// ImageMessageItem(message) + when (message.type) { + "markdown" -> MarkdownMessageItem(message, requestHolder) + "text" -> PlainTextMessageItem(message) + "image" -> ImageMessageItem(message) + } + } + } + + item { + ListBottomBlankItem() + } + }) + + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/SettingPage.kt b/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/SettingPage.kt new file mode 100644 index 0000000..77baab1 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/compose/page/main/SettingPage.kt @@ -0,0 +1,72 @@ +package com.pushdeer.os.ui.compose.page.main + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import com.pushdeer.os.holder.RequestHolder +import com.pushdeer.os.ui.compose.componment.SettingItem +import com.pushdeer.os.ui.navigation.Page + +@ExperimentalMaterialApi +@Composable +fun SettingPage(requestHolder: RequestHolder) { + MainPageFrame( + titleStringId = Page.Settings.labelStringId, + showSideIcon = false + ) { + LazyColumn( + modifier = Modifier.fillMaxSize() + ) { + item { + SettingItem( + text = "Hi ${requestHolder.pushDeerViewModel.userInfo.name} !", + buttonString = "Logout" + ) { + requestHolder.settingStore.userToken = "" + // logout 操作: + // 从服务器删除本设备 + // 删除保存的 token + } + } + item { + SettingItem( + text = "Customize Server", + buttonString = "Scan QR" + ) { + requestHolder.startQrScanActivity() + } + } + item { + SettingItem( + text = "Do you like PushDeer ?", + buttonString = "Like" + ) { + } + } + + item { + SettingItem( + text = "LogDog", + buttonString = "Open" + ) { + requestHolder.globalNavController.navigate("logdog") + } + } + } + } +} + +@Composable +fun TogglePreferenceItem(label: String) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxSize() + ) { + Text(text = label) + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/navigation/Page.kt b/android/app/src/main/java/com/pushdeer/os/ui/navigation/Page.kt new file mode 100644 index 0000000..b30da0f --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/navigation/Page.kt @@ -0,0 +1,16 @@ +package com.pushdeer.os.ui.navigation + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import com.pushdeer.os.R + +sealed class Page(val route: String, @StringRes val labelStringId:Int, @DrawableRes val id : Int) { + object Devices : Page("device",R.string.main_device, R.drawable.ipad_and_iphon2x) + object Keys:Page("key",R.string.main_key,R.drawable.key2x) + object Messages:Page("message",R.string.main_message,R.drawable.message2x) + object Settings:Page("setting",R.string.main_setting,R.drawable.gearshape2x) +} + +val pageList = listOf( + Page.Devices,Page.Keys,Page.Messages,Page.Settings +) \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/theme/Color.kt b/android/app/src/main/java/com/pushdeer/os/ui/theme/Color.kt new file mode 100644 index 0000000..b55e96a --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/theme/Color.kt @@ -0,0 +1,12 @@ +package com.pushdeer.os.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple200 = Color(0xFFBB86FC) +val Purple500 = Color(0xFF6200EE) +val Purple700 = Color(0xFF3700B3) +val Teal200 = Color(0xFF03DAC5) + +val MainBlue = Color(0xFF3B4789) +val MainGreen = Color(0xFF296C05) +val MainBottomBtn = Color(0xFF8E8E8E) \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/theme/Shape.kt b/android/app/src/main/java/com/pushdeer/os/ui/theme/Shape.kt new file mode 100644 index 0000000..ba2e23b --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/theme/Shape.kt @@ -0,0 +1,11 @@ +package com.pushdeer.os.ui.theme + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Shapes +import androidx.compose.ui.unit.dp + +val Shapes = Shapes( + small = RoundedCornerShape(4.dp), + medium = RoundedCornerShape(4.dp), + large = RoundedCornerShape(0.dp) +) \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/theme/Theme.kt b/android/app/src/main/java/com/pushdeer/os/ui/theme/Theme.kt new file mode 100644 index 0000000..659c72e --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/theme/Theme.kt @@ -0,0 +1,71 @@ +package com.pushdeer.os.ui.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material.Colors +import androidx.compose.material.MaterialTheme +import androidx.compose.material.darkColors +import androidx.compose.material.lightColors +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color + +private val DarkColorPalette = darkColors( + primary = Purple200, + primaryVariant = Purple700, + secondary = Teal200 +) + +private val LightColorPalette = lightColors( + primary = Purple500, + primaryVariant = Purple700, + secondary = Teal200 + + /* Other default colors to override + background = Color.White, + surface = Color.White, + onPrimary = Color.White, + onSecondary = Color.Black, + onBackground = Color.Black, + onSurface = Color.Black, + */ +) + +val Colors.MBlue: Color + @Composable get() = if (isLight) MainBlue else MainBlue + +val Colors.MBottomBtn: Color + @Composable get() = MainBottomBtn + +val Colors.MBottomBarBgc: Color + @Composable get() = if (isLight) Color.White else Color.White + +//val Colors.thingNormal: Color +// @Composable get() = if (isLight) Green400 else Green700 +// +//val Colors.thingLost: Color +// @Composable get() = if (isLight) Red400 else Red700 +// +//val Colors.thingEnd: Color +// @Composable get() = if (isLight) Color.LightGray else Color.DarkGray + +@Composable +fun Colors.mainBottomBtn(selected: Boolean): Color { + return if (selected) MBlue else MBottomBtn +} + +@Composable +fun PushDeerTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) { + val colors = if (darkTheme) { + DarkColorPalette + } else { + LightColorPalette + } + + LightColorPalette + + MaterialTheme( + colors = colors, + typography = Typography, + shapes = Shapes, + content = content + ) +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/ui/theme/Type.kt b/android/app/src/main/java/com/pushdeer/os/ui/theme/Type.kt new file mode 100644 index 0000000..14ded5a --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/ui/theme/Type.kt @@ -0,0 +1,28 @@ +package com.pushdeer.os.ui.theme + +import androidx.compose.material.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + body1 = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp + ) + /* Other default text styles to override + button = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W500, + fontSize = 14.sp + ), + caption = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 12.sp + ) + */ +) \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/util/CurrentTimeUtil.kt b/android/app/src/main/java/com/pushdeer/os/util/CurrentTimeUtil.kt new file mode 100644 index 0000000..cc90e26 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/util/CurrentTimeUtil.kt @@ -0,0 +1,47 @@ +package com.pushdeer.os.util + +import android.annotation.SuppressLint +import java.text.SimpleDateFormat +import java.util.* +import kotlin.math.abs + +object CurrentTimeUtil { + @SuppressLint("SimpleDateFormat") + val ymdFmt = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") + + @SuppressLint("SimpleDateFormat") + val ymdthmssFmt = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'") + + private val currentTimeZone: TimeZone = TimeZone.getDefault() + + private val tz2utcMSOffset = currentTimeZone.getOffset(System.currentTimeMillis()) + + fun utcTS2ms(utcTS: String): Long { + val calendar = Calendar.getInstance(currentTimeZone) + val date = ymdthmssFmt.parse(utcTS)!! + calendar.time = date + return calendar.time.time + tz2utcMSOffset + } + + fun msTSDis(now: Long, then: Long): String { + val dis = abs(now - then) + return when { + dis < 60_000 -> { + (dis / 1_000).toString() + "s ago" + } + dis < 3_600_000 -> { + (dis / 60_000).toString() + "min ago" + } + dis < 86_400_000 -> { + (dis / 3_600_000).toString() + "h ago" + } + else -> { + ymdFmt.format(Date(then)) + } + } + } + + fun resolveUTCTimeAndNow(utcTS: String, now: Long): String { + return msTSDis(now, utcTS2ms(utcTS)) + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/util/StatusBarUtils.java b/android/app/src/main/java/com/pushdeer/os/util/StatusBarUtils.java new file mode 100644 index 0000000..beb9a56 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/util/StatusBarUtils.java @@ -0,0 +1,329 @@ +package com.pushdeer.os.util; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Color; +import android.os.Build; +import android.util.Log; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * Created zmm + *

+ * Functions: 设置状态栏透明并改变状态栏颜色为深色工具类 + */ + +public class StatusBarUtils { + + public static int getStatusBarHeight(Resources resources,Context context) { + int statusBarHeight = 0; + int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + statusBarHeight = resources.getDimensionPixelSize(resourceId); + } + Log.d("CompatToolbar", "状态栏高度:" + px2dp(statusBarHeight,context) + "dp"); + return statusBarHeight; + } + + public static float px2dp(float pxVal, Context context) { + final float scale = context.getResources().getDisplayMetrics().density; + return (pxVal / scale); + } + + + public static void setStatusBarFontIconDark(Window window,boolean dark) { + // 小米MIUI + try { + Class clazz = window.getClass(); + Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams"); + Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE"); + int darkModeFlag = field.getInt(layoutParams); + Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class); + if (dark) { //状态栏亮色且黑色字体 + extraFlagField.invoke(window, darkModeFlag, darkModeFlag); + } else { //清除黑色字体 + extraFlagField.invoke(window, 0, darkModeFlag); + } + } catch (Exception e) { + e.printStackTrace(); + } + + // 魅族FlymeUI + try { + WindowManager.LayoutParams lp = window.getAttributes(); + Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON"); + Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags"); + darkFlag.setAccessible(true); + meizuFlags.setAccessible(true); + int bit = darkFlag.getInt(null); + int value = meizuFlags.getInt(lp); + if (dark) { + value |= bit; + } else { + value &= ~bit; + } + meizuFlags.setInt(lp, value); + window.setAttributes(lp); + } catch (Exception e) { + e.printStackTrace(); + } + // android6.0+系统 + // 这个设置和在xml的style文件中用这个true属性是一样的 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (dark) { + window.getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + } + } + } + + + /** + * 设置魅族手机状态栏图标颜色风格 + *

+ * 可以用来判断是否为Flyme用户 + * + * @param window 需要设置的窗口 + * @param dark 是否把状态栏字体及图标颜色设置为深色 + * @return boolean 成功执行返回true + */ + + public static boolean FlymeSetStatusBarLightMode(Window window, boolean dark) { + + boolean result = false; + + if (window != null) { + + try { + + WindowManager.LayoutParams lp = window.getAttributes(); + + Field darkFlag = WindowManager.LayoutParams.class + + .getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON"); + + Field meizuFlags = WindowManager.LayoutParams.class + + .getDeclaredField("meizuFlags"); + + darkFlag.setAccessible(true); + + meizuFlags.setAccessible(true); + + int bit = darkFlag.getInt(null); + + int value = meizuFlags.getInt(lp); + + if (dark) { + + value |= bit; + + } else { + + value &= ~bit; + + } + + meizuFlags.setInt(lp, value); + + window.setAttributes(lp); + + result = true; + + } catch (Exception e) { + + } + + } + + return result; + + } + + /** + * 设置小米手机状态栏字体图标颜色模式,需要MIUIV6以上 + * + * @param window 需要设置的窗口 + * @param dark 是否把状态栏字体及图标颜色设置为深色 + * @return boolean 成功执行返回true + */ + + public static boolean MIUISetStatusBarLightMode(Window window, boolean dark) { + + boolean result = false; + + if (window != null) { + + Class clazz = window.getClass(); + + try { + + int darkModeFlag = 0; + + Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams"); + + Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE"); + + darkModeFlag = field.getInt(layoutParams); + + Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class); + + if (dark) {//状态栏透明且黑色字体 + + extraFlagField.invoke(window, darkModeFlag, darkModeFlag); + + } else {//清除黑色字体 + + extraFlagField.invoke(window, 0, darkModeFlag); + + } + + result = true; + + } catch (Exception e) { + + } + + } + + return result; + + } + + /** + * 在不知道手机系统的情况下尝试设置状态栏字体模式为深色 + *

+ * 也可以根据此方法判断手机系统类型 + *

+ * 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android + * + * @param activity + * @return 1:MIUUI 2:Flyme 3:android6.0 0:设置失败 + */ + + public static void statusBarLightMode(Activity activity) { + + int result = 0; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + + if (MIUISetStatusBarLightMode(activity.getWindow(), true)) { + +//result = 1; + + StatusBarLightMode(activity, 1); + + } else if (FlymeSetStatusBarLightMode(activity.getWindow(), true)) { + +//result = 2; + + StatusBarLightMode(activity, 2); + + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + +//activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + +//result = 3; + + StatusBarLightMode(activity, 3); + + } + + } + + } + + /** + * 已知系统类型时,设置状态栏字体图标为深色。 + *

+ * 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android + * + * @param activity + * @param type 1:MIUUI 2:Flyme 3:android6.0 + */ + + public static void StatusBarLightMode(Activity activity, int type) { + + if (type == 1) { + + MIUISetStatusBarLightMode(activity.getWindow(), true); + + } else if (type == 2) { + + FlymeSetStatusBarLightMode(activity.getWindow(), true); + + } else if (type == 3) { + + Window window = activity.getWindow(); + + window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + + } + + } + + /** + * 已知系统类型时,清除MIUI或flyme或6.0以上版本状态栏字体深色模式 + * + * @param activity + * @param type 1:MIUUI 2:Flyme 3:android6.0 + */ + + public static void StatusBarDarkMode(Activity activity, int type) { + + if (type == 1) { + + MIUISetStatusBarLightMode(activity.getWindow(), false); + + } else if (type == 2) { + + FlymeSetStatusBarLightMode(activity.getWindow(), false); + + } else if (type == 3) { + + activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + + } + + } + + /** + * 状态栏背景透明 + * + * @param activity + */ + + public static void StatusBarTransport(Activity activity) { + + Window window = activity.getWindow(); + + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS + + | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + + window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + + window.setStatusBarColor(Color.TRANSPARENT); + + window.setNavigationBarColor(Color.TRANSPARENT); + + } + + } + +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/util/SystemUtil.java b/android/app/src/main/java/com/pushdeer/os/util/SystemUtil.java new file mode 100644 index 0000000..05d8aab --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/util/SystemUtil.java @@ -0,0 +1,97 @@ +package com.pushdeer.os.util; + +import android.text.TextUtils; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Locale; + +public class SystemUtil { + + /** + * 获取当前手机系统语言。 + * + * @return 返回当前系统语言。例如:当前设置的是“中文-中国”,则返回“zh-CN” + */ + public static String getSystemLanguage() { + return Locale.getDefault().getLanguage(); + } + + /** + * 获取当前系统上的语言列表(Locale列表) + * + * @return 语言列表 + */ + public static Locale[] getSystemLanguageList() { + return Locale.getAvailableLocales(); + } + + /** + * 获取当前手机系统版本号 + * + * @return 系统版本号 + */ + public static String getSystemVersion() { + return android.os.Build.VERSION.RELEASE; + } + + /** + * 获取手机型号 + * + * @return 手机型号 + */ + public static String getDeviceModel() { + return android.os.Build.MODEL; + } + + /** + * 获取手机厂商 + * + * @return 手机厂商 + */ + public static String getDeviceBrand() { + return android.os.Build.BRAND; + } + + public static String getSystemProperty(String propName){ + String line; + BufferedReader input = null; + try + { + Process p = Runtime.getRuntime().exec("getprop " + propName); + input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024); + line = input.readLine(); + input.close(); + } + catch (IOException ex) + { + Log.e("WH_", "Unable to read sysprop " + propName, ex); + return null; + } + finally + { + if(input != null) + { + try + { + input.close(); + } + catch (IOException e) + { + Log.e("WH_", "Exception while closing InputStream", e); + } + } + } + return line; + } + + public static String getMiuiVersion(){ + return getSystemProperty("ro.miui.ui.version.name"); + } + + public static boolean isMiui(){ + return !TextUtils.isEmpty(getMiuiVersion()); + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/values/ConstValues.kt b/android/app/src/main/java/com/pushdeer/os/values/ConstValues.kt new file mode 100644 index 0000000..33fc4fe --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/values/ConstValues.kt @@ -0,0 +1,12 @@ +package com.pushdeer.os.values + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.ui.unit.dp + +object ConstValues { + val MainPageSidePadding = 37.dp + val MainPageSidePaddings = PaddingValues(horizontal = 37.dp) + + val bigRoundCorner = RoundedCornerShape(8.dp) +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/viewmodel/LogDogViewModel.kt b/android/app/src/main/java/com/pushdeer/os/viewmodel/LogDogViewModel.kt new file mode 100644 index 0000000..c373db0 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/viewmodel/LogDogViewModel.kt @@ -0,0 +1,12 @@ +package com.pushdeer.os.viewmodel + +import androidx.lifecycle.ViewModel +import com.pushdeer.os.data.repository.LogDogRepository + +class LogDogViewModel(private val logDogRepository: LogDogRepository): ViewModel() { + val all = logDogRepository.all + + suspend fun clear(){ + logDogRepository.clear() + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/viewmodel/MessageViewModel.kt b/android/app/src/main/java/com/pushdeer/os/viewmodel/MessageViewModel.kt new file mode 100644 index 0000000..987674e --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/viewmodel/MessageViewModel.kt @@ -0,0 +1,37 @@ +package com.pushdeer.os.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.pushdeer.os.data.api.PushDeerApi +import com.pushdeer.os.data.api.data.response.Message +import com.pushdeer.os.data.database.entity.MessageEntity +import com.pushdeer.os.data.repository.MessageRepository +import com.pushdeer.os.store.SettingStore +import kotlinx.coroutines.launch + + +class MessageViewModel( + private val messageRepository: MessageRepository, + private val settingStore: SettingStore, + private val pushDeerService: PushDeerApi +) : ViewModel() { + val all = messageRepository.all + + fun insert(vararg message: Message) { + viewModelScope.launch { + messageRepository.insert(*message) + } + } + + fun delete(vararg message: Message) { + viewModelScope.launch { + messageRepository.delete(*message) + } + } + + fun delete(vararg messageEntity: MessageEntity){ + viewModelScope.launch { + messageRepository.delete(*messageEntity) + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/viewmodel/PushDeerViewModel.kt b/android/app/src/main/java/com/pushdeer/os/viewmodel/PushDeerViewModel.kt new file mode 100644 index 0000000..8645b01 --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/viewmodel/PushDeerViewModel.kt @@ -0,0 +1,236 @@ +package com.pushdeer.os.viewmodel + +import android.util.Log +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import com.pushdeer.os.data.api.PushDeerApi +import com.pushdeer.os.data.api.data.request.DeviceInfo +import com.pushdeer.os.data.api.data.response.PushKey +import com.pushdeer.os.data.api.data.response.UserInfo +import com.pushdeer.os.data.repository.LogDogRepository +import com.pushdeer.os.data.repository.MessageRepository +import com.pushdeer.os.store.SettingStore +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class PushDeerViewModel( + private val settingStore: SettingStore, + private val logDogRepository: LogDogRepository, + private val pushDeerService:PushDeerApi, + private val messageRepository: MessageRepository +) : ViewModel() { + private val TAG = "WH_" + + var token: String by mutableStateOf(settingStore.userToken) + var userInfo: UserInfo by mutableStateOf(UserInfo()) + var deviceList = mutableStateListOf() + val keyList = mutableStateListOf() +// var messageList = mutableStateListOf() + + suspend fun login(onReturn: (String) -> Unit = {}) { + withContext(Dispatchers.IO) { + if (token == "") { + try { + pushDeerService.fakeLogin().let { + it.content?.let { tokenOnly -> + settingStore.userToken = tokenOnly.token + token = tokenOnly.token + } + } + } catch (e: Exception) { + Log.d(TAG, "login: ${e.localizedMessage}") + logDogRepository.loge("login", "", e.toString()) + return@withContext + } + logDogRepository.logi("login","normally","nothing happened") + } +// Log.d(TAG, "login: token $token") + } + } + + suspend fun userInfo(onReturn: (UserInfo) -> Unit = {}) { + withContext(Dispatchers.IO) { + try { + pushDeerService.userInfo(token).let { + it.content?.let { ita -> + userInfo = ita + } + } + } catch (e: Exception) { + Log.d(TAG, "userInfo: ${e.localizedMessage}") + logDogRepository.loge("userInfo", "", e.toString()) + } + } + } + + suspend fun deviceReg(deviceInfo: DeviceInfo, onReturn: (DeviceInfo) -> Unit = {}) { + withContext(Dispatchers.IO) { + try { + pushDeerService.deviceReg(deviceInfo.toRequestMap(token)).let { + it.content?.let { + deviceList.clear() + deviceList.addAll(it.devices) + deviceList.filter { + it.device_id == deviceInfo.device_id + }.let { dis -> + if (dis.isNotEmpty()) { + withContext(Dispatchers.Default) { + onReturn(dis.first()) + } + } + } + } + } + } catch (e: Exception) { + Log.d(TAG, "deviceReg: ${e.localizedMessage}") + logDogRepository.loge("deviceReg", "", e.toString()) + } + } + } + + suspend fun deviceList(onReturn: (List) -> Unit = {}) { + withContext(Dispatchers.IO) { + try { + pushDeerService.deviceList(token).let { + it.content?.let { + deviceList.clear() + deviceList.addAll(it.devices) + } + } + } catch (e: Exception) { + Log.d(TAG, "deviceList: ${e.localizedMessage}") + } + } + } + + fun shouldRegDevice(): Boolean { +// Log.d(TAG, "isDeviceReged: current device id ${settingStore.thisDeviceId}") + return deviceList.none { it.device_id == settingStore.thisDeviceId } + } + + suspend fun deviceRemove(deviceId: Int) { + withContext(Dispatchers.IO) { + try { + pushDeerService.deviceRemove(token, deviceId).let { + deviceList() + Log.d(TAG, "deviceRemove: $it") + } + } catch (e: Exception) { + Log.d(TAG, "deviceRemove: ${e.localizedMessage}") + } + } + } + + suspend fun keyGen() { + withContext(Dispatchers.IO) { + try { + pushDeerService.keyGen(token).let { + it.content?.let { + keyList.clear() + keyList.addAll(it.keys) + } + } + } catch (e: Exception) { + Log.d(TAG, "keyGen: ${e.localizedMessage}") + } + } + } + + suspend fun keyRegen(keyId: String) { + withContext(Dispatchers.IO) { + try { + pushDeerService.keyRegen( + mapOf( + "token" to token, + "id" to keyId + ) + ).let { + keyList() + } + } catch (e: Exception) { + Log.d(TAG, "keyRegen: ${e.localizedMessage}") + } + } + } + + suspend fun keyList() { + withContext(Dispatchers.IO) { + try { + pushDeerService.keyList(token).let { + it.content?.let { + keyList.clear() + keyList.addAll(it.keys) + } + } + } catch (e: Exception) { + Log.d(TAG, "keyList: ${e.localizedMessage}") + } + } + } + + suspend fun keyRemove(keyId: String) { + withContext(Dispatchers.IO) { + try { + pushDeerService.keyRemove(mapOf("token" to token, "id" to keyId)).let { + keyList() + } + } catch (e: Exception) { + Log.d(TAG, "keyRemove: ${e.localizedMessage}") + } + } + + } + + suspend fun messagePush( + text: String, + desp: String, + type: String = "markdown", + pushkey: String + ) { + withContext(Dispatchers.IO) { + try { + pushDeerService.messagePush( + mapOf( + "pushkey" to pushkey, + "text" to text, + "desp" to desp, + "type" to type + ) + ).let { + messageList() + } + } catch (e: Exception) { + Log.d(TAG, "messagePush: ${e.localizedMessage}") + } + } + } + + suspend fun messageList() { + withContext(Dispatchers.IO) { + try { + pushDeerService.messageList(token).let { + it.content?.let { + messageRepository.insert(*(it.messages.toTypedArray())) + } + } + } catch (e: Exception) { + Log.d(TAG, "messageList: ${e.localizedMessage}") + } + } + } + + suspend fun messageRemove(messageId: Int) { + withContext(Dispatchers.IO) { + try { + pushDeerService.messageRemove(token, messageId).let { + messageList() + } + } catch (e: Exception) { + Log.d(TAG, "keyRemove: ${e.localizedMessage}") + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/pushdeer/os/viewmodel/UiViewModel.kt b/android/app/src/main/java/com/pushdeer/os/viewmodel/UiViewModel.kt new file mode 100644 index 0000000..b6c459b --- /dev/null +++ b/android/app/src/main/java/com/pushdeer/os/viewmodel/UiViewModel.kt @@ -0,0 +1,11 @@ +package com.pushdeer.os.viewmodel + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import com.pushdeer.os.store.SettingStore + +class UiViewModel(private val settingStore: SettingStore): ViewModel() { + var showMessageSender by mutableStateOf(settingStore.showMessageSender) +} \ No newline at end of file diff --git a/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable/deer_placeholder.png b/android/app/src/main/res/drawable/deer_placeholder.png new file mode 100644 index 0000000..ed7df69 Binary files /dev/null and b/android/app/src/main/res/drawable/deer_placeholder.png differ diff --git a/android/app/src/main/res/drawable/gearshape2x.png b/android/app/src/main/res/drawable/gearshape2x.png new file mode 100644 index 0000000..4f91229 Binary files /dev/null and b/android/app/src/main/res/drawable/gearshape2x.png differ diff --git a/android/app/src/main/res/drawable/ic_deer_head_with_mail.xml b/android/app/src/main/res/drawable/ic_deer_head_with_mail.xml new file mode 100644 index 0000000..1ee908d --- /dev/null +++ b/android/app/src/main/res/drawable/ic_deer_head_with_mail.xml @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/res/drawable/ic_logo_svg_1.xml b/android/app/src/main/res/drawable/ic_logo_svg_1.xml new file mode 100644 index 0000000..e22ce72 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_logo_svg_1.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/android/app/src/main/res/drawable/ic_markdown.xml b/android/app/src/main/res/drawable/ic_markdown.xml new file mode 100644 index 0000000..054618a --- /dev/null +++ b/android/app/src/main/res/drawable/ic_markdown.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/ipad_and_iphon2x.png b/android/app/src/main/res/drawable/ipad_and_iphon2x.png new file mode 100644 index 0000000..edc9ba3 Binary files /dev/null and b/android/app/src/main/res/drawable/ipad_and_iphon2x.png differ diff --git a/android/app/src/main/res/drawable/ipad_landscape2x.png b/android/app/src/main/res/drawable/ipad_landscape2x.png new file mode 100644 index 0000000..a6c9d0a Binary files /dev/null and b/android/app/src/main/res/drawable/ipad_landscape2x.png differ diff --git a/android/app/src/main/res/drawable/iphone2x.png b/android/app/src/main/res/drawable/iphone2x.png new file mode 100644 index 0000000..bab68a1 Binary files /dev/null and b/android/app/src/main/res/drawable/iphone2x.png differ diff --git a/android/app/src/main/res/drawable/key2x.png b/android/app/src/main/res/drawable/key2x.png new file mode 100644 index 0000000..9cc3c7e Binary files /dev/null and b/android/app/src/main/res/drawable/key2x.png differ diff --git a/android/app/src/main/res/drawable/logo_com_x2.png b/android/app/src/main/res/drawable/logo_com_x2.png new file mode 100644 index 0000000..ec37ebc Binary files /dev/null and b/android/app/src/main/res/drawable/logo_com_x2.png differ diff --git a/android/app/src/main/res/drawable/logo_half.png b/android/app/src/main/res/drawable/logo_half.png new file mode 100644 index 0000000..a4118b2 Binary files /dev/null and b/android/app/src/main/res/drawable/logo_half.png differ diff --git a/android/app/src/main/res/drawable/message2x.png b/android/app/src/main/res/drawable/message2x.png new file mode 100644 index 0000000..13b1582 Binary files /dev/null and b/android/app/src/main/res/drawable/message2x.png differ diff --git a/android/app/src/main/res/layout/activity_qr_scan.xml b/android/app/src/main/res/layout/activity_qr_scan.xml new file mode 100644 index 0000000..9594e0c --- /dev/null +++ b/android/app/src/main/res/layout/activity_qr_scan.xml @@ -0,0 +1,46 @@ + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/android/app/src/main/res/values-night/themes.xml b/android/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..a1d6fd0 --- /dev/null +++ b/android/app/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..8867171 --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,14 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + + #3B4789 + #296C05 + #8E8E8E + \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..65eca7d --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + PushDeer + Device + Key + Message + Setting + LogDog + diff --git a/android/app/src/main/res/values/themes.xml b/android/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..0767fa7 --- /dev/null +++ b/android/app/src/main/res/values/themes.xml @@ -0,0 +1,27 @@ + + + + + + +