Merge pull request #22 from alone-wolf/main

主要修改:接入appleId,当前处于几乎可用状态
This commit is contained in:
Easy 2022-01-21 08:37:05 +08:00 committed by GitHub
commit 330554a55b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 334 additions and 167 deletions

View File

@ -7,11 +7,12 @@
* MiPush状态已调通、已接入 * MiPush状态已调通、已接入
* miui 12.5.6 正常 * miui 12.5.6 正常
* 原生、类原生 可成功注册,无法收到推送,可能和地区识别有关,待解决 * 原生、类原生 可成功注册,无法收到推送,可能和地区识别有关,待解决
* miui下处于"几乎可用"的状态,已接入 appleId
### TODO ### TODO
* ~~调通 MiPush~~ * ~~调通 MiPush~~
* 接入 MiPush * ~~接入 MiPush~~
* 完善 log ~~采集~~ 回传机制,便于调试 * 完善 log ~~采集~~ 回传机制,便于调试
* 测试不同厂商设备上的通知推送效果 * 测试不同厂商设备上的通知推送效果
* ~~接入 PushDeer~~ * ~~接入 PushDeer~~
@ -21,6 +22,11 @@
* ~~增加侧滑手势相关动作~~ * ~~增加侧滑手势相关动作~~
* 增加各种操作前的二次确认弹窗,包括自动登陆 * 增加各种操作前的二次确认弹窗,包括自动登陆
* 增加非Miui设备的权限获取 * 增加非Miui设备的权限获取
* 增加手动修改服务器地址/退出登陆的逻辑(并放置到登陆界面)
* 增加登陆过程中的图形提示
* 增加对PushKey重命名的逻辑
* 增加对设备重命名的逻辑
* 增加长按复制消息内容的逻辑
### 日志 ### 日志
@ -88,6 +94,14 @@
* 调整项目文件结构 * 调整项目文件结构
* 将数据库用作message列表的直接数据来源 * 将数据库用作message列表的直接数据来源
* 2022-01-20
* 修改 /device/reg post参数增加 type=android
* 集成 SignIn With Apple
* 调整设备列表加号的显示和操作逻辑
* 将自动注册设备修改为手动点击加号注册设备
* 当前app几乎处于可以使用的状态
### 感谢 ### 感谢
https://github.com/taoweiji/MixPush https://github.com/taoweiji/MixPush

View File

@ -9,7 +9,7 @@ android {
defaultConfig { defaultConfig {
applicationId "com.pushdeer.os" applicationId "com.pushdeer.os"
minSdk 21 minSdk 22
targetSdk 31 targetSdk 31
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
@ -106,6 +106,8 @@ dependencies {
implementation "io.noties.markwon:html:$markwon_version" implementation "io.noties.markwon:html:$markwon_version"
implementation "io.coil-kt:coil:1.4.0" implementation "io.coil-kt:coil:1.4.0"
implementation 'com.github.vishalkumarsinghvi:sign-in-with-apple-button-android:0.6'
// implementation 'com.github.bumptech.glide:glide:4.12.0' // implementation 'com.github.bumptech.glide:glide:4.12.0'
// annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' // annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
} }

View File

@ -24,7 +24,7 @@ class App : Application() {
val repositoryKeeper by lazy { RepositoryKeeper(database) } val repositoryKeeper by lazy { RepositoryKeeper(database) }
val pushDeerService by lazy { val pushDeerService by lazy {
Retrofit.Builder() Retrofit.Builder()
.baseUrl("http://0.0.0.0:8800") .baseUrl(PushDeerApi.baseUrl)
.addConverterFactory(ScalarsConverterFactory.create()) .addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create())
.build() .build()
@ -45,7 +45,7 @@ class App : Application() {
MiPushClient.registerPush(this, AppKeys.MiPush_Id, AppKeys.MiPush_Key) MiPushClient.registerPush(this, AppKeys.MiPush_Id, AppKeys.MiPush_Key)
} }
//打开Log //打开Log
val newLogger: LoggerInterface = object : LoggerInterface { Logger.setLogger(this, object : LoggerInterface {
override fun setTag(tag: String) { override fun setTag(tag: String) {
// ignore // ignore
} }
@ -57,8 +57,7 @@ class App : Application() {
override fun log(content: String) { override fun log(content: String) {
Log.d(TAG, content) Log.d(TAG, content)
} }
} })
Logger.setLogger(this, newLogger)
} }
private fun shouldInit(): Boolean { private fun shouldInit(): Boolean {

View File

@ -11,6 +11,7 @@ import androidx.activity.compose.setContent
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
@ -19,6 +20,7 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.fragment.app.FragmentManager
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
@ -28,10 +30,10 @@ import com.google.accompanist.insets.ProvideWindowInsets
import com.google.accompanist.insets.statusBarsPadding import com.google.accompanist.insets.statusBarsPadding
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.pushdeer.os.activity.QrScanActivity 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.holder.RequestHolder
import com.pushdeer.os.store.SettingStore import com.pushdeer.os.store.SettingStore
import com.pushdeer.os.ui.compose.page.LogDaoPage import com.pushdeer.os.ui.compose.componment.MyAlertDialog
import com.pushdeer.os.ui.compose.page.LogDogPage
import com.pushdeer.os.ui.compose.page.LoginPage import com.pushdeer.os.ui.compose.page.LoginPage
import com.pushdeer.os.ui.compose.page.main.MainPage import com.pushdeer.os.ui.compose.page.main.MainPage
import com.pushdeer.os.ui.theme.PushDeerTheme import com.pushdeer.os.ui.theme.PushDeerTheme
@ -49,7 +51,7 @@ import kotlinx.coroutines.launch
import java.util.* import java.util.*
class MainActivity : ComponentActivity(), RequestHolder { class MainActivity : AppCompatActivity(), RequestHolder {
private val viewModelFactory by lazy { (application as App).viewModelFactory } private val viewModelFactory by lazy { (application as App).viewModelFactory }
private val repositoryKeeper by lazy { (application as App).repositoryKeeper } private val repositoryKeeper by lazy { (application as App).repositoryKeeper }
@ -59,6 +61,7 @@ class MainActivity : ComponentActivity(), RequestHolder {
override val logDogViewModel: LogDogViewModel by viewModels { viewModelFactory } override val logDogViewModel: LogDogViewModel by viewModels { viewModelFactory }
override val messageViewModel: MessageViewModel by viewModels { viewModelFactory } override val messageViewModel: MessageViewModel by viewModels { viewModelFactory }
override val settingStore: SettingStore by lazy { (application as App).storeKeeper.settingStore } override val settingStore: SettingStore by lazy { (application as App).storeKeeper.settingStore }
override val fragmentManager: FragmentManager by lazy { this.supportFragmentManager }
override val coilImageLoader: ImageLoader by lazy { override val coilImageLoader: ImageLoader by lazy {
ImageLoader.Builder(this) ImageLoader.Builder(this)
@ -69,6 +72,7 @@ class MainActivity : ComponentActivity(), RequestHolder {
} }
.build() .build()
} }
override val alert: RequestHolder.AlertRequest = object : RequestHolder.AlertRequest() {}
override val markdown: Markwon by lazy { override val markdown: Markwon by lazy {
Markwon.builder(this) Markwon.builder(this)
@ -106,40 +110,22 @@ class MainActivity : ComponentActivity(), RequestHolder {
val useDarkIcons = MaterialTheme.colors.isLight val useDarkIcons = MaterialTheme.colors.isLight
val systemUiController = rememberSystemUiController() val systemUiController = rememberSystemUiController()
when { when {
SystemUtil.isMiui() -> { SystemUtil.isMiui() -> systemUiController.setStatusBarColor(Color.Transparent, useDarkIcons)
systemUiController.setStatusBarColor(Color.Transparent, useDarkIcons) else -> systemUiController.setSystemBarsColor(Color.Transparent, useDarkIcons)
}
else -> {
systemUiController.setSystemBarsColor(Color.Transparent, useDarkIcons)
}
} }
WindowCompat.setDecorFitsSystemWindows(window, true) WindowCompat.setDecorFitsSystemWindows(window, true)
miPushRepository.regId.observe(this) { miPushRepository.regId.observe(this) {
// 这个操作放到注册成功后进行 // 这个操作放到注册成功后进行
settingStore.thisDeviceId = it 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 { SideEffect {
coroutineScope.launch { coroutineScope.launch {
pushDeerViewModel.login().also { pushDeerViewModel.login(onReturn = {
pushDeerViewModel.userInfo()
pushDeerViewModel.keyList()
pushDeerViewModel.deviceList()
pushDeerViewModel.messageList()
globalNavController.navigate("main") { globalNavController.navigate("main") {
globalNavController.popBackStack() globalNavController.popBackStack()
} }
} })
} }
} }
@ -155,13 +141,14 @@ class MainActivity : ComponentActivity(), RequestHolder {
LoginPage(requestHolder = this@MainActivity) LoginPage(requestHolder = this@MainActivity)
} }
composable("logdog") { composable("logdog") {
LogDaoPage(requestHolder = this@MainActivity) LogDogPage(requestHolder = this@MainActivity)
} }
composable("main") { composable("main") {
MainPage(requestHolder = this@MainActivity) MainPage(requestHolder = this@MainActivity)
} }
} }
} }
MyAlertDialog(alertRequest = alert)
} }
} }
} }

View File

@ -4,6 +4,14 @@ import com.pushdeer.os.data.api.data.response.*
import retrofit2.http.* import retrofit2.http.*
interface PushDeerApi { interface PushDeerApi {
companion object {
val baseUrl = "https://api2.pushdeer.com"
}
@FormUrlEncoded
@POST("/login/idtoken")
suspend fun loginIdToken(@Field("idToken") idToken: String): ReturnData<TokenOnly>
@GET("/login/fake") @GET("/login/fake")
suspend fun fakeLogin(): ReturnData<TokenOnly> suspend fun fakeLogin(): ReturnData<TokenOnly>
@ -21,7 +29,7 @@ interface PushDeerApi {
@FormUrlEncoded @FormUrlEncoded
@POST("/device/remove") @POST("/device/remove")
suspend fun deviceRemove(@Field("token") token: String,@Field("id") id:Int): String suspend fun deviceRemove(@Field("token") token: String, @Field("id") id: Int): String
@FormUrlEncoded @FormUrlEncoded
@POST("/key/gen") @POST("/key/gen")
@ -49,5 +57,5 @@ interface PushDeerApi {
@FormUrlEncoded @FormUrlEncoded
@POST("/message/remove") @POST("/message/remove")
suspend fun messageRemove(@Field("token")token:String,@Field("id")id:Int): String suspend fun messageRemove(@Field("token") token: String, @Field("id") id: Int): String
} }

View File

@ -13,7 +13,8 @@ class DeviceInfo {
"token" to token, "token" to token,
"name" to name, "name" to name,
"device_id" to device_id, "device_id" to device_id,
"is_clip" to is_clip.toString() "is_clip" to is_clip.toString(),
"type" to "android"
) )
} }

View File

@ -10,6 +10,7 @@ class Message : MessageEntity() {
this.text = this@Message.text this.text = this@Message.text
this.desp = this@Message.desp this.desp = this@Message.desp
this.type = this@Message.type this.type = this@Message.type
this.pushkey_name = this@Message.pushkey_name
this.created_at = this@Message.created_at this.created_at = this@Message.created_at
} }
} }

View File

@ -13,6 +13,7 @@ public class MessageEntity {
public String text; public String text;
public String desp; public String desp;
public String type; public String type;
public String pushkey_name;
public String created_at; public String created_at;
@ -20,10 +21,11 @@ public class MessageEntity {
Message m = new Message(); Message m = new Message();
m.id = id; m.id = id;
m.uid = uid; m.uid = uid;
m.created_at = created_at;
m.desp = desp;
m.text = text; m.text = text;
m.desp = desp;
m.type = type; m.type = type;
m.pushkey_name = pushkey_name;
m.created_at = created_at;
return m; return m;
} }
} }

View File

@ -6,6 +6,11 @@ import android.content.Intent
import android.util.Log import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.fragment.app.FragmentManager
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import coil.ImageLoader import coil.ImageLoader
import com.pushdeer.os.activity.QrScanActivity import com.pushdeer.os.activity.QrScanActivity
@ -26,7 +31,7 @@ interface RequestHolder {
val uiViewModel: UiViewModel val uiViewModel: UiViewModel
val pushDeerViewModel: PushDeerViewModel val pushDeerViewModel: PushDeerViewModel
val logDogViewModel: LogDogViewModel val logDogViewModel: LogDogViewModel
val messageViewModel:MessageViewModel val messageViewModel: MessageViewModel
val settingStore: SettingStore val settingStore: SettingStore
val globalNavController: NavHostController val globalNavController: NavHostController
val coroutineScope: CoroutineScope val coroutineScope: CoroutineScope
@ -35,6 +40,10 @@ interface RequestHolder {
val activityOpener: ActivityResultLauncher<Intent> val activityOpener: ActivityResultLauncher<Intent>
val coilImageLoader: ImageLoader val coilImageLoader: ImageLoader
val fragmentManager:FragmentManager
val alert: AlertRequest
val clipboardManager: ClipboardManager val clipboardManager: ClipboardManager
fun copyPlainString(str: String) { fun copyPlainString(str: String) {
@ -64,7 +73,7 @@ interface RequestHolder {
} }
} }
fun deviceReg(deviceInfo: DeviceInfo){ fun deviceReg(deviceInfo: DeviceInfo) {
coroutineScope.launch { coroutineScope.launch {
pushDeerViewModel.deviceReg(deviceInfo) pushDeerViewModel.deviceReg(deviceInfo)
} }
@ -83,18 +92,20 @@ interface RequestHolder {
} }
} }
fun messagePushTest(desp: String) { fun messagePushTest(text: String) {
if (pushDeerViewModel.keyList.isNotEmpty()) { if (pushDeerViewModel.keyList.isNotEmpty()) {
messagePush("pushtest", desp, "markdown", pushDeerViewModel.keyList[0].key) messagePush(text, "pushtest", "markdown", pushDeerViewModel.keyList[0].key)
coroutineScope.launch { coroutineScope.launch {
delay(1000) delay(1000)
pushDeerViewModel.messageList() pushDeerViewModel.messageList()
} }
} else } else {
alert.alert("Alert", "You Should Add One PushKey", onOk = {})
Log.d("WH_", "messagePushTest: keylist is empty") Log.d("WH_", "messagePushTest: keylist is empty")
}
} }
fun messageRemove(message: Message,onDone:()->Unit={}) { fun messageRemove(message: Message, onDone: () -> Unit = {}) {
coroutineScope.launch { coroutineScope.launch {
// pushDeerViewModel.messageList.remove(message) // pushDeerViewModel.messageList.remove(message)
pushDeerViewModel.messageRemove(message.id) pushDeerViewModel.messageRemove(message.id)
@ -112,4 +123,33 @@ interface RequestHolder {
logDogViewModel.clear() logDogViewModel.clear()
} }
} }
abstract class AlertRequest {
val show: MutableState<Boolean> = mutableStateOf(false)
var title: String = ""
var content: @Composable () -> Unit = {}
var onOKAction: () -> Unit = {}
var onCancelAction: () -> Unit = {}
fun alert(
title: String,
content: @Composable () -> Unit,
onOk: () -> Unit,
onCancel: () -> Unit = {}
) {
this.title = title
this.content = content
this.onOKAction = onOk
this.onCancelAction = onCancel
this.show.value = true
}
fun alert(title: String, content: String, onOk: () -> Unit, onCancel: () -> Unit = {}) {
this.title = title
this.content = { Text(text = content) }
this.onOKAction = onOk
this.onCancelAction = onCancel
this.show.value = true
}
}
} }

View File

@ -4,7 +4,10 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.* 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.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -18,7 +21,6 @@ import androidx.compose.ui.viewinterop.AndroidView
import com.pushdeer.os.R import com.pushdeer.os.R
import com.pushdeer.os.data.database.entity.MessageEntity import com.pushdeer.os.data.database.entity.MessageEntity
import com.pushdeer.os.holder.RequestHolder import com.pushdeer.os.holder.RequestHolder
import com.pushdeer.os.ui.theme.MBlue
import com.pushdeer.os.util.CurrentTimeUtil import com.pushdeer.os.util.CurrentTimeUtil
import com.pushdeer.os.values.ConstValues import com.pushdeer.os.values.ConstValues
@ -129,18 +131,18 @@ fun MarkdownMessageItem(message: MessageEntity, requestHolder: RequestHolder) {
contentDescription = "", contentDescription = "",
modifier = Modifier.size(40.dp) modifier = Modifier.size(40.dp)
) )
Icon( // Icon(
painter = painterResource(id = R.drawable.ic_markdown), // painter = painterResource(id = R.drawable.ic_markdown),
contentDescription = "", // contentDescription = "",
tint = MaterialTheme.colors.MBlue, // tint = MaterialTheme.colors.MBlue,
modifier = Modifier // modifier = Modifier
.size(20.dp) // .size(20.dp)
.align(alignment = Alignment.BottomCenter) // .align(alignment = Alignment.BottomCenter)
) // )
} }
Text( Text(
text = "${message.text}·${ text = "${message.pushkey_name}·${
CurrentTimeUtil.resolveUTCTimeAndNow( CurrentTimeUtil.resolveUTCTimeAndNow(
message.created_at, message.created_at,
System.currentTimeMillis() System.currentTimeMillis()
@ -155,7 +157,7 @@ fun MarkdownMessageItem(message: MessageEntity, requestHolder: RequestHolder) {
android.widget.TextView(ctx).apply { android.widget.TextView(ctx).apply {
this.post { this.post {
// requestHolder.markdown.configuration().theme(). // requestHolder.markdown.configuration().theme().
requestHolder.markdown.setMarkdown(this, message.desp) requestHolder.markdown.setMarkdown(this, message.text)
} }
} }

View File

@ -0,0 +1,36 @@
package com.pushdeer.os.ui.compose.componment
import androidx.compose.material.AlertDialog
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import com.pushdeer.os.holder.RequestHolder
@Composable
fun MyAlertDialog(alertRequest: RequestHolder.AlertRequest) {
if (alertRequest.show.value) {
AlertDialog(
onDismissRequest = { alertRequest.show.value = false },
confirmButton = {
TextButton(onClick = {
alertRequest.onOKAction.invoke()
alertRequest.show.value = false
}) {
Text(text = "Ok")
}
},
dismissButton = {
TextButton(onClick = {
alertRequest.onCancelAction.invoke()
alertRequest.show.value = false
}) {
Text(text = "Cancel")
}
},
title = { Text(text = alertRequest.title) },
text = alertRequest.content
)
}
}

View File

@ -7,7 +7,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Done
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -20,13 +19,13 @@ import com.pushdeer.os.values.ConstValues
@ExperimentalMaterialApi @ExperimentalMaterialApi
@Composable @Composable
fun SwipeToDismissItem( fun SwipeToDismissItem(
onDismiss: () -> Unit, onAction: () -> Unit,
sidePadding: Boolean = false, sidePadding: Boolean = false,
content: @Composable RowScope.() -> Unit content: @Composable RowScope.() -> Unit
) { ) {
val dismissState = rememberDismissState() val dismissState = rememberDismissState()
if (dismissState.isDismissed(DismissDirection.EndToStart)) { if (dismissState.isDismissed(DismissDirection.EndToStart)) {
onDismiss() onAction()
} }
Column(modifier = Modifier Column(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -34,7 +33,7 @@ fun SwipeToDismissItem(
SwipeToDismiss( SwipeToDismiss(
state = dismissState, state = dismissState,
background = { background = {
val direction = dismissState.dismissDirection ?: return@SwipeToDismiss // val direction = dismissState.dismissDirection ?: return@SwipeToDismiss
val color by animateColorAsState( val color by animateColorAsState(
when (dismissState.targetValue) { when (dismissState.targetValue) {
@ -44,15 +43,18 @@ fun SwipeToDismissItem(
} }
) )
val alignment = when (direction) { // val alignment = when (direction) {
DismissDirection.StartToEnd -> Alignment.CenterStart // DismissDirection.StartToEnd -> Alignment.CenterStart
DismissDirection.EndToStart -> Alignment.CenterEnd // DismissDirection.EndToStart -> Alignment.CenterEnd
} // }
//
// val icon = when (direction) {
// DismissDirection.StartToEnd -> Icons.Default.Done
// DismissDirection.EndToStart -> Icons.Default.Delete
// }
val icon = when (direction) { val alignment = Alignment.CenterEnd
DismissDirection.StartToEnd -> Icons.Default.Done val icon = Icons.Default.Delete
DismissDirection.EndToStart -> Icons.Default.Delete
}
Box( Box(
contentAlignment = alignment, contentAlignment = alignment,
@ -69,7 +71,7 @@ fun SwipeToDismissItem(
) )
} }
}, },
directions = setOf(DismissDirection.EndToStart, DismissDirection.EndToStart), directions = setOf(DismissDirection.EndToStart),
dismissThresholds = { direction -> dismissThresholds = { direction ->
FractionalThreshold(if (direction == DismissDirection.EndToStart) 0.45f else 0.57f) FractionalThreshold(if (direction == DismissDirection.EndToStart) 0.45f else 0.57f)
}, },

View File

@ -25,7 +25,7 @@ import com.pushdeer.os.ui.compose.page.main.MainPageFrame
@ExperimentalMaterialApi @ExperimentalMaterialApi
@Composable @Composable
fun LogDaoPage(requestHolder: RequestHolder) { fun LogDogPage(requestHolder: RequestHolder) {
MainPageFrame( MainPageFrame(
titleStringId = R.string.global_logdog, titleStringId = R.string.global_logdog,
sideIcon = Icons.Default.Delete, sideIcon = Icons.Default.Delete,

View File

@ -1,7 +1,9 @@
package com.pushdeer.os.ui.compose.page package com.pushdeer.os.ui.compose.page
import android.util.Log
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@ -16,24 +18,86 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.pushdeer.os.R import com.pushdeer.os.R
import com.pushdeer.os.holder.RequestHolder import com.pushdeer.os.holder.RequestHolder
import com.pushdeer.os.ui.theme.MainBlue import com.pushdeer.os.ui.theme.MainBlue
import com.pushdeer.os.ui.theme.MainGreen import com.pushdeer.os.ui.theme.MainGreen
import com.willowtreeapps.signinwithapplebutton.SignInWithAppleConfiguration
import com.willowtreeapps.signinwithapplebutton.SignInWithAppleResult
import com.willowtreeapps.signinwithapplebutton.view.SignInWithAppleButton
import kotlinx.coroutines.launch
@ExperimentalMaterialApi @ExperimentalMaterialApi
@Composable @Composable
fun LoginPage(requestHolder: RequestHolder) { fun LoginPage(requestHolder: RequestHolder) {
Column( Column(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
val configuration = SignInWithAppleConfiguration.Builder()
.clientId("com.pushdeer.site")
.redirectUri("https://api2.pushdeer.com/callback/apple")
.responseType(SignInWithAppleConfiguration.ResponseType.ALL)
.scope(SignInWithAppleConfiguration.Scope.EMAIL)
.build()
Image( Image(
painter = painterResource(R.drawable.logo_com_x2), painter = painterResource(R.drawable.logo_com_x2),
contentDescription = "big push deer logo" contentDescription = "big push deer logo",
modifier = Modifier.clickable {
requestHolder.globalNavController.navigate("logdog")
}
)
AndroidView(
factory = {
SignInWithAppleButton(it).apply {
setUpSignInWithAppleOnClick(
requestHolder.fragmentManager,
configuration
) { result ->
when (result) {
is SignInWithAppleResult.Success -> {
Log.d("WH_", "apple-id_token:${result.idToken}")
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.login(result.idToken) {
requestHolder.globalNavController.navigate("main") {
requestHolder.globalNavController.popBackStack()
}
}
}
}
is SignInWithAppleResult.Failure -> {
requestHolder.alert.alert("Warning", {
result.error.message
}, onOk = {})
Log.d(
"WH_",
"Received error from Apple Sign In ${result.error.message}"
)
}
is SignInWithAppleResult.Cancel -> {
Log.d("WH_", "User canceled Apple Sign In")
}
}
}
}
},
modifier = Modifier
.padding(bottom = 16.dp)
.border(
width = 1.dp,
color = MainBlue,
shape = RoundedCornerShape(4.dp)
)
) )
Card( Card(
onClick = { /*TODO*/ }, onClick = {
},
shape = RoundedCornerShape(4.dp), shape = RoundedCornerShape(4.dp),
modifier = Modifier modifier = Modifier
.padding(bottom = 16.dp) .padding(bottom = 16.dp)

View File

@ -1,23 +1,18 @@
package com.pushdeer.os.ui.compose.page.main package com.pushdeer.os.ui.compose.page.main
import android.os.Build import androidx.compose.foundation.layout.Arrangement
import android.text.TextUtils
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize 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.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Card
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.pushdeer.os.R import com.pushdeer.os.R
import com.pushdeer.os.data.api.data.request.DeviceInfo 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.holder.RequestHolder
import com.pushdeer.os.ui.compose.componment.CardItemSingleLineWithIcon import com.pushdeer.os.ui.compose.componment.CardItemSingleLineWithIcon
import com.pushdeer.os.ui.compose.componment.ListBottomBlankItem import com.pushdeer.os.ui.compose.componment.ListBottomBlankItem
@ -32,78 +27,54 @@ fun DeviceListPage(requestHolder: RequestHolder) {
MainPageFrame( MainPageFrame(
titleStringId = Page.Devices.labelStringId, titleStringId = Page.Devices.labelStringId,
onSideIconClick = { onSideIconClick = {
requestHolder.deviceReg( if (requestHolder.settingStore.thisDeviceId == "") {
deviceInfo = DeviceInfo().apply { requestHolder.alert.alert(
name = System.currentTimeMillis().toString() title = "Confirm",
device_id = "sdsdf" content = "This Device Registered Failed in PushSDK",
is_clip = 0 onOk = {})
} // device regid got failed
) } else {
} requestHolder.deviceReg(
deviceInfo = DeviceInfo().apply {
name = SystemUtil.getDeviceModel()
device_id = requestHolder.settingStore.thisDeviceId
is_clip = 0
}
)
}
},
showSideIcon = requestHolder.pushDeerViewModel.shouldRegDevice()
) { ) {
val state = rememberLazyListState() if (requestHolder.pushDeerViewModel.deviceList.isEmpty()) {
LazyColumn(state = state) { Column(
items( modifier = Modifier.fillMaxSize(),
items = requestHolder.pushDeerViewModel.deviceList, horizontalAlignment = Alignment.CenterHorizontally,
key = { item: DeviceInfo -> item.id }) { deviceInfo: DeviceInfo -> verticalArrangement = Arrangement.Center
SwipeToDismissItem(onDismiss = { requestHolder.deviceRemove(deviceInfo) }) { ) {
CardItemSingleLineWithIcon( Text(
onClick = {}, text = "It's Empty, Click '+' to Add New Device"
resId = R.drawable.ipad_landscape2x, )
text = if (deviceInfo.device_id == requestHolder.settingStore.thisDeviceId) "${deviceInfo.name} (this device)" else deviceInfo.name }
) } else {
val state = rememberLazyListState()
LazyColumn(state = state) {
items(
items = requestHolder.pushDeerViewModel.deviceList,
key = { item: DeviceInfo -> item.id }) { deviceInfo: DeviceInfo ->
SwipeToDismissItem(
onAction = { 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 {
item { ListBottomBlankItem()
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}")
} }
} }

View File

@ -1,10 +1,15 @@
package com.pushdeer.os.ui.compose.page.main package com.pushdeer.os.ui.compose.page.main
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.pushdeer.os.data.api.data.response.PushKey import com.pushdeer.os.data.api.data.response.PushKey
import com.pushdeer.os.holder.RequestHolder import com.pushdeer.os.holder.RequestHolder
@ -20,17 +25,29 @@ fun KeyListPage(requestHolder: RequestHolder) {
titleStringId = Page.Keys.labelStringId, titleStringId = Page.Keys.labelStringId,
onSideIconClick = { requestHolder.keyGen() } onSideIconClick = { requestHolder.keyGen() }
) { ) {
LazyColumn(modifier = Modifier.fillMaxWidth()) { if(requestHolder.pushDeerViewModel.keyList.isEmpty()){
items( Column(
requestHolder.pushDeerViewModel.keyList, modifier = Modifier.fillMaxSize(),
key = { item: PushKey -> item.id }) { pushKey: PushKey -> horizontalAlignment = Alignment.CenterHorizontally,
SwipeToDismissItem(onDismiss = { requestHolder.keyRemove(pushKey) } verticalArrangement = Arrangement.Center
) { ) {
KeyItem(key = pushKey, requestHolder = requestHolder) Text(
} text = "It's Empty, Click '+' to Add New Key"
)
} }
item { }else{
ListBottomBlankItem() LazyColumn(modifier = Modifier.fillMaxWidth()) {
items(
requestHolder.pushDeerViewModel.keyList,
key = { item: PushKey -> item.id }) { pushKey: PushKey ->
SwipeToDismissItem(onAction = { requestHolder.keyRemove(pushKey) }
) {
KeyItem(key = pushKey, requestHolder = requestHolder)
}
}
item {
ListBottomBlankItem()
}
} }
} }
} }

View File

@ -26,12 +26,22 @@ import com.pushdeer.os.holder.RequestHolder
import com.pushdeer.os.ui.navigation.Page import com.pushdeer.os.ui.navigation.Page
import com.pushdeer.os.ui.navigation.pageList import com.pushdeer.os.ui.navigation.pageList
import com.pushdeer.os.ui.theme.mainBottomBtn import com.pushdeer.os.ui.theme.mainBottomBtn
import kotlinx.coroutines.launch
@ExperimentalAnimationApi @ExperimentalAnimationApi
@ExperimentalMaterialApi @ExperimentalMaterialApi
@Composable @Composable
fun MainPage(requestHolder: RequestHolder) { fun MainPage(requestHolder: RequestHolder) {
SideEffect {
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.userInfo()
requestHolder.pushDeerViewModel.keyList()
requestHolder.pushDeerViewModel.deviceList()
requestHolder.pushDeerViewModel.messageList()
}
}
var titleStringId by remember { var titleStringId by remember {
mutableStateOf(Page.Devices.labelStringId) mutableStateOf(Page.Devices.labelStringId)
} }

View File

@ -87,15 +87,13 @@ fun MessageListPage(requestHolder: RequestHolder) {
items = messageList, items = messageList,
key = { item: MessageEntity -> item.id }) { message: MessageEntity -> key = { item: MessageEntity -> item.id }) { message: MessageEntity ->
SwipeToDismissItem( SwipeToDismissItem(
onDismiss = { onAction = {
requestHolder.messageRemove(message.toMessage(), onDone = { requestHolder.messageRemove(message.toMessage(), onDone = {
requestHolder.messageViewModel.delete(message) requestHolder.messageViewModel.delete(message)
}) })
}, },
// sidePadding = false
sidePadding = message.type != "image" sidePadding = message.type != "image"
) { ) {
// ImageMessageItem(message)
when (message.type) { when (message.type) {
"markdown" -> MarkdownMessageItem(message, requestHolder) "markdown" -> MarkdownMessageItem(message, requestHolder)
"text" -> PlainTextMessageItem(message) "text" -> PlainTextMessageItem(message)

View File

@ -19,7 +19,7 @@ import kotlinx.coroutines.withContext
class PushDeerViewModel( class PushDeerViewModel(
private val settingStore: SettingStore, private val settingStore: SettingStore,
private val logDogRepository: LogDogRepository, private val logDogRepository: LogDogRepository,
private val pushDeerService:PushDeerApi, private val pushDeerService: PushDeerApi,
private val messageRepository: MessageRepository private val messageRepository: MessageRepository
) : ViewModel() { ) : ViewModel() {
private val TAG = "WH_" private val TAG = "WH_"
@ -30,14 +30,18 @@ class PushDeerViewModel(
val keyList = mutableStateListOf<PushKey>() val keyList = mutableStateListOf<PushKey>()
// var messageList = mutableStateListOf<Message>() // var messageList = mutableStateListOf<Message>()
suspend fun login(onReturn: (String) -> Unit = {}) { suspend fun login(idToken: String = "", onReturn: (String) -> Unit = {}) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
if (token == "") { if (token == "" && idToken != "") {
try { try {
pushDeerService.fakeLogin().let { pushDeerService.loginIdToken(idToken).let {
it.content?.let { tokenOnly -> it.content?.let { tokenOnly ->
settingStore.userToken = tokenOnly.token settingStore.userToken = tokenOnly.token
token = tokenOnly.token token = tokenOnly.token
Log.d(TAG, "login: $token")
withContext(Dispatchers.Main) {
onReturn.invoke(token)
}
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -45,18 +49,26 @@ class PushDeerViewModel(
logDogRepository.loge("login", "", e.toString()) logDogRepository.loge("login", "", e.toString())
return@withContext return@withContext
} }
logDogRepository.logi("login","normally","nothing happened") logDogRepository.logi("login", "normally", "nothing happened")
} else if (token == "" && idToken == "") {
return@withContext
} else if (token != "") {
withContext(Dispatchers.Main) {
onReturn.invoke(token)
}
} }
// Log.d(TAG, "login: token $token")
} }
} }
suspend fun userInfo(onReturn: (UserInfo) -> Unit = {}) { suspend fun userInfo(onOk: (UserInfo) -> Unit = {}, onFailed: () -> Unit = {}) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
try { try {
pushDeerService.userInfo(token).let { pushDeerService.userInfo(token).let {
it.content?.let { ita -> it.content?.let { ita ->
userInfo = ita userInfo = ita
withContext(Dispatchers.Main) {
onOk(userInfo)
}
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -107,7 +119,6 @@ class PushDeerViewModel(
} }
fun shouldRegDevice(): Boolean { fun shouldRegDevice(): Boolean {
// Log.d(TAG, "isDeviceReged: current device id ${settingStore.thisDeviceId}")
return deviceList.none { it.device_id == settingStore.thisDeviceId } return deviceList.none { it.device_id == settingStore.thisDeviceId }
} }

View File

@ -6,6 +6,7 @@ buildscript {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
maven { url 'https://jitpack.io' }
} }
dependencies { dependencies {
classpath "com.android.tools.build:gradle:7.0.4" classpath "com.android.tools.build:gradle:7.0.4"

View File

@ -3,6 +3,7 @@ dependencyResolutionManagement {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
maven { url 'https://jitpack.io' }
// maven { url 'https://developer.huawei.com/repo/' } // maven { url 'https://developer.huawei.com/repo/' }
} }
} }