主要修改:适配微信登陆

This commit is contained in:
alone-wolf 2022-01-26 20:40:14 +08:00
parent 3faff74557
commit ad55b81b47
41 changed files with 972 additions and 239 deletions

View File

@ -5,14 +5,15 @@ plugins {
}
android {
compileSdk 31
defaultConfig {
applicationId "com.pushdeer.os"
minSdk 22
targetSdk 31
versionCode 5
versionName "1.0-dev-5"
versionCode 8
versionName "1.0-dev-8"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
@ -111,5 +112,7 @@ dependencies {
implementation 'com.github.vishalkumarsinghvi:sign-in-with-apple-button-android:0.6'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
api 'com.tencent.mm.opensdk:wechat-sdk-android:6.8.0'
// debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
}

Binary file not shown.

View File

@ -0,0 +1,20 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.pushdeer.os",
"variantName": "debug",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 8,
"versionName": "1.0-dev-8",
"outputFile": "app-debug.apk"
}
],
"elementType": "File"
}

View File

@ -27,4 +27,18 @@
# MiPush
-keep class com.pushdeer.os.receiver.MessageReceiver {*;}
#可以防止一个误报的 warning 导致无法成功编译,如果编译使用的 Android 版本是 23
-dontwarn com.xiaomi.push.**
-dontwarn com.xiaomi.push.**
# XiaoErMei
-keep class com.tencent.mm.opensdk.** {
*;
}
-keep class com.tencent.wxop.** {
*;
}
-keep class com.tencent.mm.sdk.** {
*;
}

Binary file not shown.

View File

@ -0,0 +1,20 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.pushdeer.os",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 5,
"versionName": "1.0-dev-5",
"outputFile": "app-release.apk"
}
],
"elementType": "File"
}

View File

@ -55,6 +55,15 @@
android:theme="@style/Theme.PushDeer.NoActionBar"
/>
<activity
android:name=".wxapi.WXEntryActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:exported="true"
android:taskAffinity="com.pushdeer.os"
android:launchMode="singleTask">
</activity>
<!-- miPush components start -->
<service

View File

@ -10,6 +10,8 @@ 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.tencent.mm.opensdk.openapi.IWXAPI
import com.tencent.mm.opensdk.openapi.WXAPIFactory
import com.xiaomi.channel.commonutils.logger.LoggerInterface
import com.xiaomi.mipush.sdk.Logger
import com.xiaomi.mipush.sdk.MiPushClient
@ -21,7 +23,7 @@ class App : Application() {
val storeKeeper by lazy { StoreKeeper(this) }
val database by lazy { AppDatabase.getDatabase(this) }
val repositoryKeeper by lazy { RepositoryKeeper(database) }
val repositoryKeeper by lazy { RepositoryKeeper(database,storeKeeper.settingStore) }
private val pushDeerService: PushDeerApi by lazy {
Retrofit.Builder()
.baseUrl(PushDeerApi.baseUrl)
@ -38,6 +40,8 @@ class App : Application() {
)
}
val iwxapi:IWXAPI by lazy { WXAPIFactory.createWXAPI(this, AppKeys.WX_Id, true) }
override fun onCreate() {
super.onCreate()
//初始化push推送服务
@ -52,10 +56,27 @@ class App : Application() {
override fun log(content: String, t: Throwable) {
Log.d(TAG, content, t)
Thread{
repositoryKeeper.logDogRepository.log(
entity = "mipush",
level = "e",
event = t.message.toString(),
log = content
)
}.start()
}
override fun log(content: String) {
Log.d(TAG, content)
// Thread{
// repositoryKeeper.logDogRepository.log(
// entity = "mipush",
// level = "d",
// event = "",
// log = content
// )
// }.start()
}
})
}

View File

@ -1,10 +1,9 @@
package com.pushdeer.os
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.*
import android.os.Bundle
import android.text.util.Linkify
import android.util.Log
import androidx.activity.compose.setContent
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.viewModels
@ -17,7 +16,7 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.core.view.WindowCompat
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
@ -36,10 +35,13 @@ import com.pushdeer.os.ui.theme.PushDeerTheme
import com.pushdeer.os.util.ActivityOpener
import com.pushdeer.os.util.NotificationUtil
import com.pushdeer.os.util.SystemUtil
import com.pushdeer.os.values.AppKeys
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.pushdeer.os.wxapi.WXEntryActivity
import com.tencent.mm.opensdk.constants.ConstantsAPI
import io.noties.markwon.Markwon
import io.noties.markwon.image.coil.CoilImagesPlugin
import io.noties.markwon.linkify.LinkifyPlugin
@ -58,7 +60,6 @@ class MainActivity : AppCompatActivity(), RequestHolder {
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 fragmentManager: FragmentManager by lazy { this.supportFragmentManager }
override val coilImageLoader: ImageLoader by lazy {
ImageLoader.Builder(this)
@ -89,6 +90,13 @@ class MainActivity : AppCompatActivity(), RequestHolder {
) as ClipboardManager
) {}
}
override val weChatLogin: RequestHolder.WeChatLoginRequest by lazy {
object : RequestHolder.WeChatLoginRequest((application as App).iwxapi) {}
}
override val appleLogin: RequestHolder.AppleLoginRequest by lazy {
object : RequestHolder.AppleLoginRequest(supportFragmentManager, this) {}
}
override val markdown: Markwon by lazy {
Markwon.builder(this)
@ -103,11 +111,64 @@ class MainActivity : AppCompatActivity(), RequestHolder {
override lateinit var qrScanActivityOpener: ActivityResultLauncher<Intent>
override lateinit var requestPermissionOpener: ActivityResultLauncher<Array<String>>
val wxRegReceiver: BroadcastReceiver by lazy {
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
intent?.let {
when (it.action) {
ConstantsAPI.ACTION_REFRESH_WXAPP -> {
weChatLogin.iwxapi.registerApp(AppKeys.WX_Id)
}
WXEntryActivity.ACTION_RETURN_CODE -> {
val code = intent.getStringExtra(WXEntryActivity.CODE_KEY)!!
lifecycleScope.launch {
if (pushDeerViewModel.userInfo.isAppleLogin) {
Log.d("WH_", "onReceive: isAppleLogin")
// if login, perform merge
coroutineScope.launch {
pushDeerViewModel.userMerge(
"wechat",
code
) {
coroutineScope.launch {
pushDeerViewModel.userInfo()
}
}
}
} else {
Log.d("WH_", "onReceive: plainLogin")
// if not, plain login
coroutineScope.launch {
pushDeerViewModel.loginWithWeiXin(code) {
globalNavController.navigate("main") {
globalNavController.popBackStack()
}
}
}
}
}
}
else -> {
}
}
}
}
}
}
@ExperimentalAnimationApi
@ExperimentalMaterialApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
registerReceiver(wxRegReceiver,
IntentFilter().apply {
addAction(ConstantsAPI.ACTION_REFRESH_WXAPP)
addAction(WXEntryActivity.ACTION_RETURN_CODE)
})
NotificationUtil.setupChannel(this)
myActivity = this
@ -124,7 +185,7 @@ class MainActivity : AppCompatActivity(), RequestHolder {
Color.Transparent,
useDarkIcons
)
else -> systemUiController.setSystemBarsColor(Color.Transparent, useDarkIcons)
else -> systemUiController.setSystemBarsColor(Color.Transparent, !useDarkIcons)
}
WindowCompat.setDecorFitsSystemWindows(window, true)
miPushRepository.regId.observe(this) {
@ -133,7 +194,7 @@ class MainActivity : AppCompatActivity(), RequestHolder {
SideEffect {
coroutineScope.launch {
pushDeerViewModel.login(onReturn = {
pushDeerViewModel.loginWithApple(onReturn = {
globalNavController.navigate("main") {
globalNavController.popBackStack()
}
@ -165,4 +226,9 @@ class MainActivity : AppCompatActivity(), RequestHolder {
}
}
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(wxRegReceiver)
}
}

View File

@ -13,7 +13,19 @@ interface PushDeerApi {
@FormUrlEncoded
@POST("/login/idtoken")
suspend fun loginIdToken(@Field("idToken") idToken: String): ReturnData<TokenOnly>
suspend fun loginWithAppleIdToken(@Field("idToken") idToken: String): ReturnData<TokenOnly>
@FormUrlEncoded
@POST("/login/wecode")
suspend fun loginWithWeXin(@Field("code") code: String): ReturnData<TokenOnly>
@FormUrlEncoded
@POST("/user/merge")
suspend fun userMerge(
@Field("token") token: String,
@Field("type") type: String, // apple wechat
@Field("tokenorcode") tokenorcode: String // input idToken / code
): String
// @GET("/login/fake")
// suspend fun fakeLogin(): ReturnData<TokenOnly>

View File

@ -2,22 +2,44 @@ package com.pushdeer.os.data.api.data.response
class UserInfo {
var id: String = ""
// var uid: String = ""
var name: String = ""
var email: String = ""
var app_id: String = ""
var wechat_id: String = ""
var apple_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" +
// "uid:$uid\n" +
"name:$name\n" +
"email:$email\n" +
"app_id:$app_id\n" +
"apple_id:$apple_id\n" +
"wechat_id:$wechat_id\n" +
"level:$level\n" +
"created:$created_at\n" +
"updated:$updated_at"
}
val isWeChatLogin: Boolean
get() {
return if (wechat_id == null) {
false
} else {
wechat_id!!.length > 4
}
}
val isAppleLogin: Boolean
get() {
return if (apple_id == null) {
false
} else {
apple_id!!.length > 4
}
}
val isLogin: Boolean
get() = isWeChatLogin or isAppleLogin
}

View File

@ -15,6 +15,9 @@ interface LogDogDao {
@Insert
suspend fun insert(vararg logDog: LogDog)
@Insert
fun insert1(vararg logDog: LogDog)
@Query("delete from LogDog")
suspend fun clear()
}

View File

@ -18,6 +18,7 @@ class LogDog {
return "id:$id\n" +
"level:$level\n" +
"entity:$entity\n" +
"event:$event\n" +
"log:$log\n" +
"time:${timestamp.toTimestamp()}"
}

View File

@ -2,13 +2,14 @@ package com.pushdeer.os.data.repository
import com.pushdeer.os.data.database.dao.LogDogDao
import com.pushdeer.os.data.database.entity.LogDog
import com.pushdeer.os.store.SettingStore
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class LogDogRepository(private val logDogDao: LogDogDao) {
class LogDogRepository(private val logDogDao: LogDogDao,private val settingStore: SettingStore) {
val all = logDogDao.all
suspend fun clear(){
suspend fun clear() {
logDogDao.clear()
}
@ -28,6 +29,20 @@ class LogDogRepository(private val logDogDao: LogDogDao) {
}
}
fun log(
entity: String,
level: String,
event: String,
log: String
) {
logDogDao.insert1(LogDog().apply {
this.entity = entity
this.level = level
this.event = event
this.log = log
})
}
suspend fun logi(entity: String, event: String, log: String) {
withContext(Dispatchers.IO) {
insert(LogDog.logi(entity, event, log))

View File

@ -4,6 +4,7 @@ import android.content.ClipData
import android.content.ClipboardManager
import android.content.Intent
import android.content.res.Resources
import android.util.Log
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
@ -23,7 +24,12 @@ 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.tencent.mm.opensdk.modelmsg.SendAuth
import com.tencent.mm.opensdk.openapi.IWXAPI
import com.wh.common.activity.QrScanActivity
import com.willowtreeapps.signinwithapplebutton.SignInWithAppleConfiguration
import com.willowtreeapps.signinwithapplebutton.SignInWithAppleResult
import com.willowtreeapps.signinwithapplebutton.SignInWithAppleService
import io.noties.markwon.Markwon
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
@ -40,16 +46,20 @@ interface RequestHolder {
val myActivity: AppCompatActivity
val markdown: Markwon
val qrScanActivityOpener: ActivityResultLauncher<Intent>
val requestPermissionOpener:ActivityResultLauncher<Array<String>>
val requestPermissionOpener: ActivityResultLauncher<Array<String>>
val coilImageLoader: ImageLoader
val fragmentManager: FragmentManager
// requests
val alert: AlertRequest
val key:KeyRequest
val device:DeviceRequest
val message:MessageRequest
val clip:ClipRequest
val key: KeyRequest
val device: DeviceRequest
val message: MessageRequest
val clip: ClipRequest
val weChatLogin: WeChatLoginRequest
val appleLogin: AppleLoginRequest
// val iwxapi: IWXAPI
fun startQrScanActivity() {
qrScanActivityOpener.launch(QrScanActivity.forScanResultIntent(myActivity))
@ -61,29 +71,98 @@ interface RequestHolder {
}
fun clearLogDog() {
alert.alert(R.string.global_alert_title_confirm,"Clear?",onOk = {
logDogViewModel.clear()
alert.alert(R.string.global_alert_title_confirm, "Clear?", onOk = {
logDogViewModel.clear()
})
}
fun userRename(newName:String){
abstract class AppleLoginRequest(
private val fragmentManager: FragmentManager,
private val requestHolder: RequestHolder
) {
private val appleLoginCallBack: (SignInWithAppleResult) -> Unit =
{ result: SignInWithAppleResult ->
when (result) {
is SignInWithAppleResult.Success -> {
if (requestHolder.pushDeerViewModel.userInfo.isWeChatLogin) {
// if login with wechat, perform merge
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.userMerge(
type = "apple",
tokenorcode = result.idToken,
onReturn = {
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.userInfo()
}
}
)
}
} else {
// else ( is not login ), plain login with apple
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.loginWithApple(result.idToken) {
requestHolder.globalNavController.navigate("main") {
requestHolder.globalNavController.popBackStack()
}
}
}
}
}
is SignInWithAppleResult.Failure -> {
requestHolder.alert.alert("Warning Apple Id Login Failed", {
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")
}
}
}
private val appleLoginConfiguration = SignInWithAppleConfiguration.Builder()
.clientId("com.pushdeer.site")
.redirectUri("https://api2.pushdeer.com/callback/apple")
.responseType(SignInWithAppleConfiguration.ResponseType.ALL)
.scope(SignInWithAppleConfiguration.Scope.EMAIL)
.build()
val login = {
val service = SignInWithAppleService(
fragmentManager = fragmentManager,
fragmentTag = "SignInWithAppleButton-1-SignInWebViewDialogFragment",
configuration = appleLoginConfiguration,
callback = appleLoginCallBack
)
service.show()
}
}
// abstract class LogDogRequest(private val )
abstract class WeChatLoginRequest(val iwxapi: IWXAPI) {
val login: () -> Unit = {
val req = SendAuth.Req()
req.scope = "snsapi_userinfo"
req.state = System.currentTimeMillis().toString()
iwxapi.sendReq(req)
}
}
abstract class ClipRequest(private val clipboardManager: ClipboardManager) {
fun copyMessagePlainText(str: String) {
clipboardManager.setPrimaryClip(ClipData.newPlainText("pushdeer-copy-plain-text", str))
}
fun copyPushKey(str: String){
fun copyPushKey(str: String) {
clipboardManager.setPrimaryClip(ClipData.newPlainText("pushdeer-copy-pushkey", str))
}
}
abstract class AlertRequest(private val resources: Resources) {
val show: MutableState<Boolean> = mutableStateOf(false)
val show2BtnDialog: MutableState<Boolean> = mutableStateOf(false)
val show1BtnDialog: MutableState<Boolean> = mutableStateOf(false)
var title: String = ""
var content: @Composable () -> Unit = {}
var onOKAction: () -> Unit = {}
@ -94,19 +173,34 @@ interface RequestHolder {
title: String,
content: @Composable () -> Unit,
onOk: () -> Unit,
onCancel: () -> Unit = {}
) {
this.title = title
this.content = content
this.onOKAction = onOk
this.show1BtnDialog.value = true
}
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
this.show2BtnDialog.value = true
}
fun alert(title: String, content: String, onOk: () -> Unit, onCancel: () -> Unit = {}) {
alert(title, { Text(text = content) }, onOk, onCancel)
}
fun alert(title: String, content: String, onOk: () -> Unit) {
alert(title, { Text(text = content) }, onOk)
}
fun alert(
@StringRes title: Int,
@StringRes content: Int,
@ -116,6 +210,14 @@ interface RequestHolder {
alert(resources.getString(title), resources.getString(content), onOk, onCancel)
}
fun alert(
@StringRes title: Int,
@StringRes content: Int,
onOk: () -> Unit = {},
) {
alert(resources.getString(title), resources.getString(content), onOk)
}
fun alert(
@StringRes title: Int,
content: @Composable () -> Unit,
@ -125,6 +227,14 @@ interface RequestHolder {
alert(resources.getString(title), content, onOk, onCancel)
}
fun alert(
@StringRes title: Int,
content: @Composable () -> Unit,
onOk: () -> Unit,
) {
alert(resources.getString(title), content, onOk)
}
fun alert(
@StringRes title: Int,
content: String,
@ -133,9 +243,17 @@ interface RequestHolder {
) {
alert(resources.getString(title), content, onOk, onCancel)
}
fun alert(
@StringRes title: Int,
content: String,
onOk: () -> Unit,
) {
alert(resources.getString(title), content, onOk)
}
}
abstract class KeyRequest(private val requestHolder: RequestHolder){
abstract class KeyRequest(private val requestHolder: RequestHolder) {
fun gen() {
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.keyGen()
@ -166,7 +284,7 @@ interface RequestHolder {
}
}
abstract class DeviceRequest(private val requestHolder: RequestHolder){
abstract class DeviceRequest(private val requestHolder: RequestHolder) {
fun deviceReg(deviceInfo: DeviceInfo) {
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.deviceReg(deviceInfo)
@ -191,7 +309,7 @@ interface RequestHolder {
}
}
abstract class MessageRequest(private val requestHolder: RequestHolder){
abstract class MessageRequest(private val requestHolder: RequestHolder) {
fun messagePush(text: String, desp: String, type: String, pushkey: String) {
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.messagePush(text, desp, type, pushkey)
@ -200,16 +318,21 @@ interface RequestHolder {
fun messagePushTest(text: String) {
if (requestHolder.pushDeerViewModel.keyList.isNotEmpty()) {
messagePush(text, "pushtest", "markdown", requestHolder.pushDeerViewModel.keyList[0].key)
messagePush(
text,
"pushtest",
"markdown",
requestHolder.pushDeerViewModel.keyList[0].key
)
requestHolder.coroutineScope.launch {
delay(1000)
delay(900)
requestHolder.pushDeerViewModel.messageList()
}
} else {
requestHolder.alert.alert(
R.string.global_alert_title_alert,
R.string.main_message_send_alert,
onOk = {})
R.string.main_message_send_alert
)
}
}

View File

@ -4,9 +4,10 @@ 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
import com.pushdeer.os.store.SettingStore
class RepositoryKeeper(database: AppDatabase) {
class RepositoryKeeper(database: AppDatabase,settingStore: SettingStore) {
val miPushRepository = MiPushRepository()
val logDogRepository = LogDogRepository(database.logDogDao())
val logDogRepository = LogDogRepository(database.logDogDao(),settingStore)
val messageRepository = MessageRepository(database.messageDao())
}

View File

@ -0,0 +1,23 @@
package com.pushdeer.os.okhttp
import android.util.Log
import com.pushdeer.os.data.repository.LogDogRepository
import okhttp3.Interceptor
import okhttp3.Response
class LogInterceptor(private val logDogRepository: LogDogRepository): Interceptor{
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val url = request.url
val methods = request.method
val isHttps = request.isHttps
val contentType = request.body?.contentType()
val response = chain.proceed(request)
Log.d("WH_", "intercept: $response.")
return response
}
}

View File

@ -18,4 +18,6 @@ class SettingStore(context:Context) {
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","")
var logLevel by store.string("log-level","i") // i w e - d
}

View File

@ -30,12 +30,12 @@ fun CardItemSingleLineWithIcon(
) {
Card(
onClick = onClick,
shape = RoundedCornerShape(4.dp),
shape = RoundedCornerShape(8.dp),
modifier = Modifier
.border(
width = 1.dp,
color = MainBlue,
shape = RoundedCornerShape(4.dp)
shape = RoundedCornerShape(8.dp)
),
elevation = 5.dp
@ -72,13 +72,13 @@ fun CardItemMultiLine(
) {
Card(
onClick = onClick,
shape = RoundedCornerShape(4.dp),
shape = RoundedCornerShape(8.dp),
modifier = Modifier
// .padding(bottom = 16.dp)
.border(
width = 1.dp,
color = MainBlue,
shape = RoundedCornerShape(4.dp)
shape = RoundedCornerShape(8.dp)
),
elevation = 5.dp
@ -111,12 +111,12 @@ fun CardItemMultiLine(
fun CardItemWithContent(onClick: () -> Unit, content: @Composable () -> Unit = {}) {
Card(
onClick = onClick,
shape = RoundedCornerShape(4.dp),
shape = RoundedCornerShape(8.dp),
modifier = Modifier
.border(
width = 1.dp,
color = MainBlue,
shape = RoundedCornerShape(4.dp)
shape = RoundedCornerShape(8.dp)
),
content = content,
elevation = 5.dp
@ -127,12 +127,12 @@ fun CardItemWithContent(onClick: () -> Unit, content: @Composable () -> Unit = {
@Composable
fun CardItemWithContent(content: @Composable () -> Unit = {}) {
Card(
shape = RoundedCornerShape(4.dp),
shape = RoundedCornerShape(8.dp),
modifier = Modifier
.border(
width = 1.dp,
color = MainBlue,
shape = RoundedCornerShape(4.dp)
shape = RoundedCornerShape(8.dp)
),
content = content,
elevation = 5.dp

View File

@ -176,7 +176,7 @@ fun KeyItem(key: PushKey, requestHolder: RequestHolder) {
backgroundColor = MaterialTheme.colors.MBlue,
contentColor = Color.White
),
shape = RoundedCornerShape(6.dp)
shape = RoundedCornerShape(8.dp)
) {
Text(text = stringResource(id = R.string.main_key_copy))
}

View File

@ -33,7 +33,7 @@ fun PlainTextMessageItem(message: MessageEntity,requestHolder: RequestHolder) {
Column(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(4.dp))
.clip(RoundedCornerShape(8.dp))
.clickable {
requestHolder.clip.copyMessagePlainText(message.text)
}
@ -80,7 +80,7 @@ fun ImageMessageItem(message: MessageEntity, requestHolder: RequestHolder) {
Column(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(4.dp))
.clip(RoundedCornerShape(8.dp))
.clickable {
requestHolder.clip.copyMessagePlainText(message.text)
}
@ -125,7 +125,7 @@ fun MarkdownMessageItem(message: MessageEntity, requestHolder: RequestHolder) {
Column(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(4.dp))
.clip(RoundedCornerShape(8.dp))
.clickable {
requestHolder.clip.copyMessagePlainText(message.text)
}

View File

@ -10,13 +10,13 @@ import com.pushdeer.os.holder.RequestHolder
@Composable
fun MyAlertDialog(alertRequest: RequestHolder.AlertRequest) {
if (alertRequest.show.value) {
if (alertRequest.show2BtnDialog.value) {
AlertDialog(
onDismissRequest = { alertRequest.show.value = false },
onDismissRequest = { alertRequest.show2BtnDialog.value = false },
confirmButton = {
TextButton(onClick = {
alertRequest.onOKAction.invoke()
alertRequest.show.value = false
alertRequest.show2BtnDialog.value = false
}) {
Text(text = stringResource(id = R.string.global_alert_ok))
}
@ -25,7 +25,7 @@ fun MyAlertDialog(alertRequest: RequestHolder.AlertRequest) {
dismissButton = {
TextButton(onClick = {
alertRequest.onCancelAction.invoke()
alertRequest.show.value = false
alertRequest.show2BtnDialog.value = false
}) {
Text(text = stringResource(id = R.string.global_alert_cancel))
}
@ -35,4 +35,20 @@ fun MyAlertDialog(alertRequest: RequestHolder.AlertRequest) {
text = alertRequest.content
)
}
if (alertRequest.show1BtnDialog.value){
AlertDialog(
onDismissRequest = { alertRequest.show1BtnDialog.value = false },
confirmButton = {
TextButton(onClick = {
alertRequest.onOKAction.invoke()
alertRequest.show1BtnDialog.value = false
}) {
Text(text = stringResource(id = R.string.global_alert_ok))
}
},
title = { Text(text = alertRequest.title) },
text = alertRequest.content
)
}
}

View File

@ -14,11 +14,17 @@ import com.pushdeer.os.ui.theme.MBlue
@ExperimentalMaterialApi
@Composable
fun SettingItem(text: String, buttonString: String, onItemClick:()->Unit={},onButtonClick: () -> Unit) {
fun SettingItem(
text: String,
buttonString: String,
onItemClick: () -> Unit = {},
paddingValues: PaddingValues = PaddingValues(bottom = 16.dp),
onButtonClick: () -> Unit
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp)
.padding(paddingValues)
) {
CardItemWithContent(onClick = onItemClick) {
Row(
@ -51,4 +57,21 @@ fun SettingItem(text: String, buttonString: String, onItemClick:()->Unit={},onBu
}
}
}
}
}
//@ExperimentalMaterialApi
//@Composable
//fun SettingItem(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
// )
//}

View File

@ -1,82 +1,110 @@
package com.pushdeer.os.ui.compose.componment
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.*
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.ArrowBack
import androidx.compose.material.icons.filled.Delete
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.*
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.holder.RequestHolder
import com.pushdeer.os.ui.theme.SwipeToDismissGray
import com.pushdeer.os.ui.theme.SwipeToDismissRed
import com.pushdeer.os.values.ConstValues
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ExperimentalMaterialApi
@Composable
fun SwipeToDismissItem(
onAction: () -> Unit,
sidePadding: Boolean = false,
requestHolder: RequestHolder,
content: @Composable RowScope.() -> Unit
) {
val dismissState = rememberDismissState()
if (dismissState.isDismissed(DismissDirection.EndToStart)) {
onAction()
var visible by remember {
mutableStateOf(true)
}
Column(modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp)) {
SwipeToDismiss(
state = dismissState,
background = {
// val direction = dismissState.dismissDirection ?: return@SwipeToDismiss
val dismissv = DismissValue.Default
val dismissState = rememberDismissState(initialValue = dismissv)
LaunchedEffect(Unit){
if (dismissState.isDismissed(DismissDirection.EndToStart)){
requestHolder.coroutineScope.launch {
dismissState.reset()
}
}
}
AnimatedVisibility(
visible = visible,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically()
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp)
) {
val color by animateColorAsState(
when (dismissState.targetValue) {
DismissValue.DismissedToEnd -> Color.Green
DismissValue.DismissedToStart -> Color.Red
else -> Color.Gray
}
)
SwipeToDismiss(
state = dismissState,
background = {
// 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
// }
val alignment = Alignment.CenterEnd
val icon = 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
val color by animateColorAsState(
when (dismissState.targetValue) {
DismissValue.DismissedToEnd -> SwipeToDismissGray
DismissValue.DismissedToStart -> SwipeToDismissRed
else -> SwipeToDismissGray
}
)
}
},
directions = setOf(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)
)
Row(
modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(8.dp))
.background(color)
.padding(end = 32.dp),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically
) {
IconButton(onClick = {
requestHolder.coroutineScope.launch {
dismissState.reset()
}
}) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = "",
)
}
IconButton(onClick = {
visible = false
requestHolder.coroutineScope.launch {
delay(300)
onAction()
}
}) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "",
)
}
}
},
directions = setOf(DismissDirection.EndToStart),
dismissThresholds = {
FractionalThreshold(0.45f)
},
dismissContent = content,
modifier = Modifier.padding(horizontal = if (sidePadding) ConstValues.MainPageSidePadding else 0.dp)
)
}
}
}

View File

@ -0,0 +1,43 @@
package com.pushdeer.os.ui.compose.componment
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.willowtreeapps.signinwithapplebutton.view.SignInWithAppleButton
@Composable
fun WeChatLoginButton() {
Row(
modifier = Modifier
.clip(RoundedCornerShape(4.dp))
.border(BorderStroke(1.dp, color = Color.Green), shape = RoundedCornerShape(4.dp))
.background(color = Color.Green)
.width((150).dp)
.height(38.dp)
) {
Text(text = "aaa")
}
}
@Preview(showBackground = true)
@Composable
fun W() {
Row {
WeChatLoginButton()
AndroidView(factory = {
SignInWithAppleButton(it)
}, modifier = Modifier.border(1.dp, color = Color.Black, shape = RoundedCornerShape(4.dp)))
}
}

View File

@ -1,37 +1,31 @@
package com.pushdeer.os.ui.compose.page
import android.util.Log
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
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.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.pushdeer.os.R
import com.pushdeer.os.holder.RequestHolder
import com.willowtreeapps.signinwithapplebutton.SignInWithAppleConfiguration
import com.willowtreeapps.signinwithapplebutton.SignInWithAppleResult
import com.willowtreeapps.signinwithapplebutton.view.SignInWithAppleButton
import kotlinx.coroutines.launch
import com.pushdeer.os.ui.theme.MainGreen
@ExperimentalMaterialApi
@Composable
fun LoginPage(requestHolder: RequestHolder) {
Box(modifier = Modifier.fillMaxSize()) {
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(
painter = painterResource(R.drawable.logo_com_x2),
contentDescription = "big push deer logo",
@ -40,43 +34,50 @@ fun LoginPage(requestHolder: RequestHolder) {
.align(Alignment.TopCenter)
.padding(top = 50.dp)
)
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 Apple Id Login Failed", {
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")
}
}
}
}
},
Card(
onClick = requestHolder.weChatLogin.login,
shape = RoundedCornerShape(4.dp),
modifier = Modifier
.padding(bottom = 220.dp)
.border(
width = 1.dp,
color = MainGreen,
shape = RoundedCornerShape(4.dp)
)
.align(alignment = Alignment.BottomCenter)
.padding(bottom = 100.dp)
)
) {
Text(
text = "Sign in with WeChat",
color = MainGreen,
textAlign = TextAlign.Center,
modifier = Modifier
.padding(vertical = 16.dp)
.fillMaxWidth(0.6F)
)
}
Card(
onClick = requestHolder.appleLogin.login,
shape = RoundedCornerShape(4.dp),
modifier = Modifier
.padding(bottom = 140.dp)
.border(
width = 1.dp,
color = Color.Black,
shape = RoundedCornerShape(4.dp)
)
.align(alignment = Alignment.BottomCenter)
) {
Text(
text = "Sign in with Apple",
color = Color.Black,
textAlign = TextAlign.Center,
modifier = Modifier
.padding(vertical = 16.dp)
.fillMaxWidth(0.6F)
)
}
}
}

View File

@ -72,12 +72,13 @@ fun DeviceListPage(requestHolder: RequestHolder) {
val state = rememberLazyListState()
LazyColumn(state = state) {
items(
items = requestHolder.pushDeerViewModel.deviceList,
items = requestHolder.pushDeerViewModel.deviceList.sortedByDescending { it.id },
key = { item: DeviceInfo -> item.id }) { deviceInfo: DeviceInfo ->
var name by remember {
mutableStateOf(deviceInfo.name)
}
SwipeToDismissItem(
requestHolder = requestHolder,
onAction = { requestHolder.device.deviceRemove(deviceInfo) }
) {
CardItemSingleLineWithIcon(

View File

@ -49,9 +49,11 @@ fun KeyListPage(requestHolder: RequestHolder) {
}else{
LazyColumn(modifier = Modifier.fillMaxWidth()) {
items(
requestHolder.pushDeerViewModel.keyList,
requestHolder.pushDeerViewModel.keyList.sortedBy { it.id },
key = { item: PushKey -> item.id }) { pushKey: PushKey ->
SwipeToDismissItem(onAction = { requestHolder.key.remove(pushKey) }
SwipeToDismissItem(
requestHolder = requestHolder,
onAction = { requestHolder.key.remove(pushKey) }
) {
KeyItem(key = pushKey, requestHolder = requestHolder)
}

View File

@ -24,6 +24,7 @@ fun MainPageFrame(
titleStringId: Int,
showSideIcon: Boolean = true,
sideIcon: ImageVector = Icons.Default.Add,
iconModifier: Modifier = Modifier,
onSideIconClick: () -> Unit = {},
sidePadding: Boolean = true,
content: @Composable BoxScope.() -> Unit
@ -53,7 +54,8 @@ fun MainPageFrame(
Icon(
imageVector = sideIcon,
contentDescription = "",
tint = Color.LightGray
tint = Color.LightGray,
modifier = iconModifier
)
}
// }

View File

@ -1,6 +1,7 @@
package com.pushdeer.os.ui.compose.page.main
import androidx.compose.animation.*
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@ -10,9 +11,9 @@ 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.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@ -37,9 +38,14 @@ fun MessageListPage(requestHolder: RequestHolder) {
}
}
// val a = rotat
val a by animateFloatAsState(targetValue = if (requestHolder.uiViewModel.showMessageSender) 0F else 180F)
MainPageFrame(
titleStringId = Page.Messages.labelStringId,
sideIcon = if (requestHolder.uiViewModel.showMessageSender) Icons.Default.KeyboardArrowUp else Icons.Default.KeyboardArrowDown,
sideIcon = Icons.Default.KeyboardArrowDown,
iconModifier = Modifier.rotate(a),
onSideIconClick = { requestHolder.toggleMessageSender() },
sidePadding = false
) {
@ -58,7 +64,7 @@ fun MessageListPage(requestHolder: RequestHolder) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp)
.padding(bottom = 20.dp)
.padding(horizontal = ConstValues.MainPageSidePadding)
) {
@ -97,6 +103,7 @@ fun MessageListPage(requestHolder: RequestHolder) {
items = messageList,
key = { item: MessageEntity -> item.id }) { message: MessageEntity ->
SwipeToDismissItem(
requestHolder = requestHolder,
onAction = {
requestHolder.message.messageRemove(message.toMessage(), onDone = {
requestHolder.messageViewModel.delete(message)

View File

@ -3,16 +3,32 @@ package com.pushdeer.os.ui.compose.page.main
import android.content.Intent
import android.net.Uri
import android.util.Log
import androidx.compose.foundation.layout.fillMaxSize
import androidx.annotation.DrawableRes
import androidx.compose.animation.*
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.Composable
import androidx.compose.material.Icon
import androidx.compose.runtime.*
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.res.stringResource
import androidx.compose.ui.unit.dp
import com.pushdeer.os.R
import com.pushdeer.os.data.api.data.response.UserInfo
import com.pushdeer.os.holder.RequestHolder
import com.pushdeer.os.ui.compose.componment.SettingItem
import com.pushdeer.os.ui.navigation.Page
import com.pushdeer.os.ui.theme.MainBlue
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ExperimentalMaterialApi
@Composable
@ -21,51 +37,123 @@ fun SettingPage(requestHolder: RequestHolder) {
titleStringId = Page.Settings.labelStringId,
showSideIcon = false
) {
var showLoginMethod by remember {
mutableStateOf(false)
}
LaunchedEffect(Unit) {
requestHolder.coroutineScope.launch {
delay(300)
showLoginMethod = true
}
}
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item {
// var newName by remember {
// mutableStateOf(requestHolder.pushDeerViewModel.userInfo.name)
// }
SettingItem(
text = "${stringResource(id = R.string.main_setting_user_hi)} ${requestHolder.pushDeerViewModel.userInfo.name} !",
buttonString = stringResource(id = R.string.main_setting_user_logout),
onItemClick = {
// requestHolder.alert.alert(
// title = "修改用户名",
// content = {
// TextField(
// value = newName,
// onValueChange = { newName = it },
// colors = TextFieldDefaults.textFieldColors(
// focusedIndicatorColor = Color.Transparent,
// unfocusedIndicatorColor = Color.Transparent,
// disabledIndicatorColor = Color.Transparent,
// errorIndicatorColor = Color.Transparent,
// )
// )
// },
// onOk = {
//
// }
// )
}
Card(
elevation = 5.dp,
modifier = Modifier.padding(bottom = 16.dp),
shape = RoundedCornerShape(8.dp),
border = BorderStroke(1.dp, MainBlue)
) {
requestHolder.pushDeerViewModel.deviceList.filter { it.device_id == requestHolder.settingStore.thisDeviceId }
.forEach {
requestHolder.device.deviceRemove(it)
Column(
modifier = Modifier
.fillMaxWidth()
) {
SettingItem(
text = "${stringResource(id = R.string.main_setting_user_hi)} ${requestHolder.pushDeerViewModel.userInfo.name} !",
buttonString = stringResource(id = R.string.main_setting_user_logout),
paddingValues = PaddingValues(bottom = 0.dp),
onItemClick = {}
) {
requestHolder.pushDeerViewModel.deviceList
.filter { it.device_id == requestHolder.settingStore.thisDeviceId }
.forEach { requestHolder.device.deviceRemove(it) }
requestHolder.settingStore.userToken = ""
requestHolder.globalNavController.navigate("login") {
requestHolder.globalNavController.popBackStack()
requestHolder.pushDeerViewModel.userInfo = UserInfo()
}
requestHolder.alert.alert(
title = R.string.global_alert_title_alert,
content = R.string.main_setting_alert_logout,
onOk = {}
)
}
AnimatedVisibility(
visible = showLoginMethod,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically()
) {
// LoginMethod Row
Row(
Modifier
.fillMaxWidth()
.padding(bottom = 6.dp, start = 6.dp, end = 6.dp, top = 8.dp),
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically
) {
// wx 登陆按钮
Row(
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.weight(0.5F)
.clickable {
if (requestHolder.pushDeerViewModel.userInfo.isWeChatLogin) {
requestHolder.alert.alert(
title = "Hey",
content = "你已经通过 微信账号 成功登陆咯!",
onOk = {})
} else {
requestHolder.alert.alert(
title = "绑定或迁移由 微信账号 创建的账号",
content = "请注意,如果你将要登陆的 微信账号 已经在此登陆过,其设备列表将会与当前账号合并, PushKey 将会在合并时丢失。",
onOk = requestHolder.weChatLogin.login
)
}
}
) {
LoginMethod(
isLogin = requestHolder.pushDeerViewModel.userInfo.isWeChatLogin,
id = R.drawable.ic_wechat_colored
)
}
// apple 登陆按钮
Row(
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.weight(0.5F)
.clickable {
if (requestHolder.pushDeerViewModel.userInfo.isAppleLogin) {
requestHolder.alert.alert(
title = "Hey",
content = "你已经通过 Apple Id 成功登陆咯!",
onOk = {})
} else {
requestHolder.alert.alert(
title = "绑定或迁移由 Apple Id 创建的账号",
content = "请注意,如果你将要用来登陆 PushDeer 的 Apple Id 已经在此登陆过,其设备列表将会与当前账号合并, PushKey 将会在合并时丢失。",
onOk = requestHolder.appleLogin.login
)
}
}
) {
LoginMethod(
isLogin = requestHolder.pushDeerViewModel.userInfo.isAppleLogin,
id = R.drawable.ic_apple_colored
)
}
}
}
requestHolder.settingStore.userToken = ""
requestHolder.globalNavController.navigate("login") {
requestHolder.globalNavController.popBackStack()
}
requestHolder.alert.alert(
R.string.global_alert_title_alert,
R.string.main_setting_alert_logout,
{}
)
}
}
// item {
@ -81,7 +169,7 @@ fun SettingPage(requestHolder: RequestHolder) {
text = stringResource(id = R.string.main_setting_douyoulike),
buttonString = stringResource(id = R.string.main_setting_btn_like)
) {
val uri = Uri.parse("market://details?id=" + "com.pushdeer.os")
val uri = Uri.parse("market://details?id=com.pushdeer.os")
Intent(Intent.ACTION_VIEW, uri).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}.let {
@ -107,12 +195,43 @@ fun SettingPage(requestHolder: RequestHolder) {
}
}
//@Composable
//fun TogglePreferenceItem(label: String) {
// Row(
// verticalAlignment = Alignment.CenterVertically,
// modifier = Modifier.fillMaxSize()
// ) {
// Text(text = label)
// }
//}
@Composable
fun IconYes() {
Icon(
painter = painterResource(id = R.drawable.ic_okok2),
contentDescription = "",
tint = MainBlue,
modifier = Modifier.size(20.dp)
)
}
@Composable
fun IconNo() {
Icon(
painter = painterResource(id = R.drawable.ic_okok2),
contentDescription = "",
tint = Color.Gray,
modifier = Modifier.size(20.dp)
)
}
@Composable
fun LoginMethod(isLogin: Boolean, @DrawableRes id: Int) {
Row(
modifier = Modifier.width(70.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
if (isLogin)
IconYes()
else
IconNo()
Image(
painter = painterResource(id = id),
contentDescription = "",
modifier = Modifier
.padding(vertical = 12.dp)
.size(35.dp),
)
}
}

View File

@ -9,4 +9,6 @@ val Teal200 = Color(0xFF03DAC5)
val MainBlue = Color(0xFF3B4789)
val MainGreen = Color(0xFF296C05)
val MainBottomBtn = Color(0xFF8E8E8E)
val MainBottomBtn = Color(0xFF8E8E8E)
val SwipeToDismissRed = Color(0x92FFF2F2)
val SwipeToDismissGray = Color(0x92B6B6B6)

View File

@ -30,25 +30,24 @@ class PushDeerViewModel(
val keyList = mutableStateListOf<PushKey>()
// var messageList = mutableStateListOf<Message>()
suspend fun login(idToken: String = "", onReturn: (String) -> Unit = {}) {
suspend fun loginWithApple(idToken: String = "", onReturn: (String) -> Unit = {}) {
withContext(Dispatchers.IO) {
if (token == "" && idToken != "") {
try {
pushDeerService.loginIdToken(idToken).let {
pushDeerService.loginWithAppleIdToken(idToken).let {
it.content?.let { tokenOnly ->
settingStore.userToken = tokenOnly.token
token = tokenOnly.token
Log.d(TAG, "login: $token")
Log.d(TAG, "loginWithApple: $token")
withContext(Dispatchers.Main) {
onReturn.invoke(token)
}
}
}
logDogRepository.logi("login", "normally", "nothing happened")
logDogRepository.logi("loginWithApple", "withAppleId", "nothing happened")
} catch (e: Exception) {
Log.d(TAG, "login: ${e.localizedMessage}")
logDogRepository.loge("login", "", e.toString())
return@withContext
Log.d(TAG, "loginWithApple: ${e.localizedMessage}")
logDogRepository.loge("loginWithApple", "", e.toString())
}
} else if (token == "" && idToken == "") {
return@withContext
@ -60,10 +59,50 @@ class PushDeerViewModel(
}
}
suspend fun loginWithWeiXin(code:String,onReturn: (String) -> Unit){
withContext(Dispatchers.IO){
try {
pushDeerService.loginWithWeXin(code).let {
it.content?.let { tokenOnly ->
settingStore.userToken = tokenOnly.token
token = tokenOnly.token
Log.d(TAG, "loginWithWeiXin: $token")
withContext(Dispatchers.Main) {
onReturn.invoke(token)
}
}
}
logDogRepository.logi("loginWithWeiXin", "withWeiXin", "nothing happened")
}catch (e: Exception){
Log.e(TAG, "loginWithWeiXin: ${e.localizedMessage}")
logDogRepository.loge("loginWithWeiXin", "", e.toString())
}
}
}
suspend fun userMerge(type:String,tokenorcode:String,onReturn: (String) -> Unit={}){
Log.d("WH_", ": token:${token} type:${type} tokenorcode:${tokenorcode}")
withContext(Dispatchers.IO){
try {
pushDeerService.userMerge(token,type,tokenorcode).let {
Log.d(TAG, "userMerge: $it")
withContext(Dispatchers.Main){
onReturn("")
}
}
}catch (e: Exception) {
Log.e(TAG, "userMerge:e ${e.localizedMessage}")
logDogRepository.loge("userMerge", "", e.toString())
}
}
}
suspend fun userInfo(onOk: (UserInfo) -> Unit = {}, onFailed: () -> Unit = {}) {
withContext(Dispatchers.IO) {
try {
pushDeerService.userInfo(token).let {
Log.d(TAG, "userInfo: ${it.content}")
it.content?.let { ita ->
userInfo = ita
withContext(Dispatchers.Main) {

View File

@ -0,0 +1,69 @@
package com.pushdeer.os.wxapi
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import com.pushdeer.os.App
import com.tencent.mm.opensdk.modelbase.BaseReq
import com.tencent.mm.opensdk.modelbase.BaseResp
import com.tencent.mm.opensdk.modelmsg.SendAuth
import com.tencent.mm.opensdk.openapi.IWXAPI
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler
class WXEntryActivity : Activity(), IWXAPIEventHandler {
companion object {
const val CODE_KEY = "code"
const val ACTION_RETURN_CODE = "return-code"
}
private val iwxapi: IWXAPI by lazy { (application as App).iwxapi }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
iwxapi.handleIntent(intent, this);
}
// 微信发送的请求
override fun onReq(p0: BaseReq?) {
}
// 发送到微信请求的响应结果
override fun onResp(p0: BaseResp?) {
when (p0?.errCode) {
BaseResp.ErrCode.ERR_AUTH_DENIED, BaseResp.ErrCode.ERR_USER_CANCEL ->
// if (RETURN_MSG_TYPE_SHARE === resp.getType()) {
// Toast.makeText(this, "分享失败", Toast.LENGTH_SHORT).show()
// } else {
Toast.makeText(this, "登录失败", Toast.LENGTH_SHORT).show()
// }
BaseResp.ErrCode.ERR_OK -> when (p0.type) {
1 -> {
// login
val code: String = (p0 as SendAuth.Resp).code
code.let {
sendBroadcast(Intent().apply {
putExtra(CODE_KEY, code)
action = ACTION_RETURN_CODE
})
Log.d("WH_", "onResp: $code")
finish()
}
}
// RETURN_MSG_TYPE_LOGIN -> //拿到了微信返回的code,立马再去请求access_token
// var code
// : String
// ?
// = (resp as SendAuth.Resp).code
// RETURN_MSG_TYPE_SHARE -> {
// Toast.makeText(this, "微信分享成功", Toast.LENGTH_SHORT).show()
// finish()
// }
}
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="33.9dp"
android:height="36.991dp"
android:viewportWidth="33.9"
android:viewportHeight="36.991">
<path
android:pathData="M10.934,36.986c-2.372,0 -4.859,-1.735 -7.289,-5.149 -2.43,-3.529 -3.645,-6.942 -3.645,-10.182a10.774,10.774 0,0 1,2.835 -7.694,8.994 8.994,0 0,1 6.826,-2.835 19.017,19.017 0,0 1,4.223 0.578,13.923 13.923,0 0,0 3.355,0.578 13.4,13.4 0,0 0,3.413 -0.694,15.208 15.208,0 0,1 4.165,-0.694 9.788,9.788 0,0 1,5.091 1.273,10.853 10.853,0 0,1 1.62,1.157L32.916,14.486 31.47,15.586a12.067,12.067 0,0 0,-1.735 1.62,5.859 5.859,0 0,0 -1.446,3.7A6.523,6.523 0,0 0,29.85 25.016a7.144,7.144 0,0 0,2.719 2.025l1.331,0.578 -0.578,1.331a18.746,18.746 0,0 1,-2.6 4.107c-2.083,2.6 -4.165,3.934 -6.19,3.934a13.777,13.777 0,0 1,-3.3 -0.636,13.421 13.421,0 0,0 -3.645,-0.636 11.316,11.316 0,0 0,-3.413 0.636A11.678,11.678 0,0 1,10.934 36.986ZM9.661,14.025a6.332,6.332 0,0 0,-4.744 1.967,7.9 7.9,0 0,0 -2.025,5.669 15.293,15.293 0,0 0,3.124 8.5c1.735,2.488 3.529,3.934 4.917,3.934a6.836,6.836 0,0 0,2.2 -0.521,14.218 14.218,0 0,1 4.4,-0.81 15,15 0,0 1,4.512 0.752,12.512 12.512,0 0,0 2.488,0.521c1.1,0 2.488,-0.983 3.934,-2.835a13.211,13.211 0,0 0,1.562 -2.256,11.955 11.955,0 0,1 -2.43,-2.14 9.342,9.342 0,0 1,-2.2 -5.9,8.511 8.511,0 0,1 2.083,-5.554 9.219,9.219 0,0 1,0.752 -0.752,7.076 7.076,0 0,0 -3.413,-0.752 11.639,11.639 0,0 0,-3.355 0.578,17.3 17.3,0 0,1 -4.223,0.81 16.292,16.292 0,0 1,-3.992 -0.636,12.183 12.183,0 0,0 -3.587,-0.578ZM17.355,9.802a3.161,3.161 0,0 1,-2.2 -0.926,2.417 2.417,0 0,1 -0.578,-2.2c0.578,-2.372 1.331,-3.876 2.372,-4.686l0.058,-0.058A12.262,12.262 0,0 1,22.677 0.025a2.852,2.852 0,0 1,2.488 0.868,2.385 2.385,0 0,1 0.694,2.083 7.988,7.988 0,0 1,-2.545 4.975l-0.058,0.058a12.444,12.444 0,0 1,-5.322 1.793ZM17.413,7.372ZM18.743,4.186a5.55,5.55 0,0 0,-1.215 2.661,11.137 11.137,0 0,0 3.934,-1.273 4.928,4.928 0,0 0,1.388 -2.719A10.286,10.286 0,0 0,18.743 4.186Z"
android:fillColor="#3b4789"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="76dp"
android:height="76dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FF000000"
android:pathData="M950.14,827.09L73.86,827.09a73.81,73.81 0,0 1,-73.81 -73.81L0.04,270.72a73.81,73.81 0,0 1,73.81 -73.81h876.29a73.81,73.81 0,0 1,73.81 73.81v482.47a73.81,73.81 0,0 1,-73.81 73.86zM246.14,679.38v-192l98.47,123.09 98.43,-123.09v192h98.47L541.53,344.66h-98.47l-98.43,123.09 -98.47,-123.09L147.67,344.66v334.81zM905.86,512h-98.47L807.38,344.62h-98.43L708.95,512h-98.47l147.67,172.33z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="76dp"
android:height="76dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FF000000"
android:pathData="M512,321.5c105.22,0 190.5,85.29 190.5,190.5 0,105.22 -85.28,190.5 -190.5,190.5S321.5,617.22 321.5,512C321.5,406.79 406.78,321.5 512,321.5z"/>
</vector>

View File

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="44.557dp"
android:height="36.991dp"
android:viewportWidth="44.557"
android:viewportHeight="36.991">
<path
android:pathData="M27.149,20.122a1.348,1.348 0,1 1,-1.346 -1.282,1.348 1.348,0 0,1 1.346,1.282m9.342,0A1.348,1.348 0,1 1,35.149 18.777a1.348,1.348 0,0 1,1.348 1.348"
android:fillColor="#3b4789"/>
<path
android:pathData="M33.763,35.115l-0.253,0.055q-0.352,0.075 -0.706,0.139a10.942,10.942 0,0 1,-2.43 0.233c-6.374,-0.211 -11.341,-3.337 -13.33,-8.3q-0.138,-0.36 -0.259,-0.726l-0.075,-0.23q-0.082,-0.257 -0.146,-0.518a12.867,12.867 0,0 1,-0.35 -2.874c0,-6.493 5.962,-11.616 13.453,-11.95l0.337,-0.026c0.3,-0.023 0.493,-0.032 0.713,-0.032h0.561l0.353,0.006c7.145,0.557 12.925,5.949 12.925,12.3 0,2.975 -1.474,5.913 -4.164,8.441q-0.133,0.124 -0.269,0.243l0.379,2.7a1.866,1.866 0,0 1,-2.625 2.22L34.129,35.024q-0.179,0.045 -0.36,0.087ZM42.123,23.191c0,-5 -4.731,-9.416 -10.6,-9.876h-0.807c-0.143,0 -0.292,0 -0.531,0.023l-0.405,0.029c-6.3,0.282 -11.137,4.446 -11.137,9.526a10.377,10.377 0,0 0,0.285 2.31c0.023,0.094 0.055,0.214 0.1,0.35l0.065,0.188c0.081,0.25 0.162,0.48 0.214,0.606 1.607,4.015 5.693,6.584 11.153,6.766a8.767,8.767 0,0 0,1.912 -0.194q0.317,-0.057 0.632,-0.123l0.214,-0.049c0.178,-0.042 0.321,-0.075 0.736,-0.181a1.215,1.215 0,0 1,0.813 0.081l3.237,1.523 -0.379,-2.631a1.215,1.215 0,0 1,0.528 -1.186c0.045,-0.032 0.327,-0.262 0.577,-0.5 2.229,-2.1 3.4,-4.42 3.4,-6.668ZM0,14.157c0,-7.848 7.355,-14.157 16.318,-14.157 7.913,0 14.97,4.948 16.4,11.623a1.79,1.79 0,0 1,0.019 0.642,1.215 1.215,0 0,1 -2.417,-0.227c-1.225,-5.437 -7.213,-9.607 -14,-9.607 -7.7,0 -13.888,5.308 -13.888,11.73 0,3.415 1.753,6.513 5.165,9.021A1.215,1.215 0,0 1,8.029 24.542l-0.885,2.657L11.129,25.342a1.215,1.215 0,0 1,0.852 -0.065l0.207,0.065a18.4,18.4 0,0 0,4.128 0.551,7.624 7.624,0 0,0 1.225,-0.152 0.289,0.289 0,0 0,-0.1 0.058,1.257 1.257,0 0,1 1.491,0.042 1.215,1.215 0,0 1,0.2 1.708c-0.447,0.564 -1.8,0.771 -2.819,0.771a19.833,19.833 0,0 1,-4.553 -0.59l-4.715,2.2A1.879,1.879 0,0 1,4.429 27.662l1.021,-3.062c-3.551,-2.868 -5.45,-6.464 -5.45,-10.443Z"
android:fillColor="#3b4789"/>
<path
android:pathData="M12.592,9.455a1.685,1.685 0,1 1,-1.685 -1.682,1.685 1.685,0 0,1 1.685,1.682m11.088,0a1.685,1.685 0,1 1,-1.685 -1.682,1.685 1.685,0 0,1 1.685,1.682"
android:fillColor="#3b4789"/>
</vector>

View File

@ -29,4 +29,5 @@
<string name="main_setting_douyoulike">喜欢 PushDeer 吗?</string>
<string name="main_setting_btn_like">喜欢</string>
<string name="main_setting_alert_logout">由于厂商推送设备服务限制,暂时不支持更换为自建 PushDeer 服务器,但仅更换登陆账号并不会影响您的使用。</string>
<string name="global_swipetodismiss_alert_delete">确定删除?</string>
</resources>

View File

@ -28,4 +28,5 @@
<string name="main_setting_douyoulike">Do you line PushDeer?</string>
<string name="main_setting_btn_like">Like</string>
<string name="main_setting_alert_logout">Due to the limitations of the vendor\'s push device service, it is not supported to change to the self-built PushDeer server for the time being, but only changing the login account will not affect your use.</string>
<string name="global_swipetodismiss_alert_delete">Delete?</string>
</resources>