Merge pull request #31 from alone-wolf/main

主要修改:适配微信登陆
This commit is contained in:
Easy 2022-01-26 20:41:52 +08:00 committed by GitHub
commit 5b4371ac8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 972 additions and 239 deletions

View File

@ -5,14 +5,15 @@ plugins {
} }
android { android {
compileSdk 31 compileSdk 31
defaultConfig { defaultConfig {
applicationId "com.pushdeer.os" applicationId "com.pushdeer.os"
minSdk 22 minSdk 22
targetSdk 31 targetSdk 31
versionCode 5 versionCode 8
versionName "1.0-dev-5" versionName "1.0-dev-8"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {
@ -111,5 +112,7 @@ dependencies {
implementation 'com.github.vishalkumarsinghvi:sign-in-with-apple-button-android:0.6' 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 # MiPush
-keep class com.pushdeer.os.receiver.MessageReceiver {*;} -keep class com.pushdeer.os.receiver.MessageReceiver {*;}
#可以防止一个误报的 warning 导致无法成功编译,如果编译使用的 Android 版本是 23 #可以防止一个误报的 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" 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 --> <!-- miPush components start -->
<service <service

View File

@ -10,6 +10,8 @@ import com.pushdeer.os.factory.ViewModelFactory
import com.pushdeer.os.keeper.RepositoryKeeper import com.pushdeer.os.keeper.RepositoryKeeper
import com.pushdeer.os.keeper.StoreKeeper import com.pushdeer.os.keeper.StoreKeeper
import com.pushdeer.os.values.AppKeys 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.channel.commonutils.logger.LoggerInterface
import com.xiaomi.mipush.sdk.Logger import com.xiaomi.mipush.sdk.Logger
import com.xiaomi.mipush.sdk.MiPushClient import com.xiaomi.mipush.sdk.MiPushClient
@ -21,7 +23,7 @@ class App : Application() {
val storeKeeper by lazy { StoreKeeper(this) } val storeKeeper by lazy { StoreKeeper(this) }
val database by lazy { AppDatabase.getDatabase(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 { private val pushDeerService: PushDeerApi by lazy {
Retrofit.Builder() Retrofit.Builder()
.baseUrl(PushDeerApi.baseUrl) .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() { override fun onCreate() {
super.onCreate() super.onCreate()
//初始化push推送服务 //初始化push推送服务
@ -52,10 +56,27 @@ class App : Application() {
override fun log(content: String, t: Throwable) { override fun log(content: String, t: Throwable) {
Log.d(TAG, content, t) 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) { override fun log(content: String) {
Log.d(TAG, content) 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 package com.pushdeer.os
import android.content.ClipboardManager import android.content.*
import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.text.util.Linkify import android.text.util.Linkify
import android.util.Log
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.viewModels import androidx.activity.viewModels
@ -17,7 +16,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.lifecycle.lifecycleScope
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
@ -36,10 +35,13 @@ import com.pushdeer.os.ui.theme.PushDeerTheme
import com.pushdeer.os.util.ActivityOpener import com.pushdeer.os.util.ActivityOpener
import com.pushdeer.os.util.NotificationUtil import com.pushdeer.os.util.NotificationUtil
import com.pushdeer.os.util.SystemUtil import com.pushdeer.os.util.SystemUtil
import com.pushdeer.os.values.AppKeys
import com.pushdeer.os.viewmodel.LogDogViewModel import com.pushdeer.os.viewmodel.LogDogViewModel
import com.pushdeer.os.viewmodel.MessageViewModel import com.pushdeer.os.viewmodel.MessageViewModel
import com.pushdeer.os.viewmodel.PushDeerViewModel import com.pushdeer.os.viewmodel.PushDeerViewModel
import com.pushdeer.os.viewmodel.UiViewModel 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.Markwon
import io.noties.markwon.image.coil.CoilImagesPlugin import io.noties.markwon.image.coil.CoilImagesPlugin
import io.noties.markwon.linkify.LinkifyPlugin import io.noties.markwon.linkify.LinkifyPlugin
@ -58,7 +60,6 @@ class MainActivity : AppCompatActivity(), 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)
@ -89,6 +90,13 @@ class MainActivity : AppCompatActivity(), RequestHolder {
) as ClipboardManager ) 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 { override val markdown: Markwon by lazy {
Markwon.builder(this) Markwon.builder(this)
@ -103,11 +111,64 @@ class MainActivity : AppCompatActivity(), RequestHolder {
override lateinit var qrScanActivityOpener: ActivityResultLauncher<Intent> override lateinit var qrScanActivityOpener: ActivityResultLauncher<Intent>
override lateinit var requestPermissionOpener: ActivityResultLauncher<Array<String>> 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 @ExperimentalAnimationApi
@ExperimentalMaterialApi @ExperimentalMaterialApi
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
registerReceiver(wxRegReceiver,
IntentFilter().apply {
addAction(ConstantsAPI.ACTION_REFRESH_WXAPP)
addAction(WXEntryActivity.ACTION_RETURN_CODE)
})
NotificationUtil.setupChannel(this) NotificationUtil.setupChannel(this)
myActivity = this myActivity = this
@ -124,7 +185,7 @@ class MainActivity : AppCompatActivity(), RequestHolder {
Color.Transparent, Color.Transparent,
useDarkIcons 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) {
@ -133,7 +194,7 @@ class MainActivity : AppCompatActivity(), RequestHolder {
SideEffect { SideEffect {
coroutineScope.launch { coroutineScope.launch {
pushDeerViewModel.login(onReturn = { pushDeerViewModel.loginWithApple(onReturn = {
globalNavController.navigate("main") { globalNavController.navigate("main") {
globalNavController.popBackStack() 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 @FormUrlEncoded
@POST("/login/idtoken") @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") // @GET("/login/fake")
// suspend fun fakeLogin(): ReturnData<TokenOnly> // suspend fun fakeLogin(): ReturnData<TokenOnly>

View File

@ -2,22 +2,44 @@ package com.pushdeer.os.data.api.data.response
class UserInfo { class UserInfo {
var id: String = "" var id: String = ""
// var uid: String = ""
var name: String = "" var name: String = ""
var email: String = "" var email: String = ""
var app_id: String = "" var apple_id: String? = ""
var wechat_id: String = "" var wechat_id: String? = ""
var level: Int = 1 var level: Int = 1
var created_at: String = "" var created_at: String = ""
var updated_at: String = "" var updated_at: String = ""
override fun toString(): String { override fun toString(): String {
return "id:$id\n" + return "id:$id\n" +
// "uid:$uid\n" +
"name:$name\n" + "name:$name\n" +
"email:$email\n" + "email:$email\n" +
"app_id:$app_id\n" + "apple_id:$apple_id\n" +
"wechat_id:$wechat_id\n" + "wechat_id:$wechat_id\n" +
"level:$level\n" + "level:$level\n" +
"created:$created_at\n" + "created:$created_at\n" +
"updated:$updated_at" "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 @Insert
suspend fun insert(vararg logDog: LogDog) suspend fun insert(vararg logDog: LogDog)
@Insert
fun insert1(vararg logDog: LogDog)
@Query("delete from LogDog") @Query("delete from LogDog")
suspend fun clear() suspend fun clear()
} }

View File

@ -18,6 +18,7 @@ class LogDog {
return "id:$id\n" + return "id:$id\n" +
"level:$level\n" + "level:$level\n" +
"entity:$entity\n" + "entity:$entity\n" +
"event:$event\n" +
"log:$log\n" + "log:$log\n" +
"time:${timestamp.toTimestamp()}" "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.dao.LogDogDao
import com.pushdeer.os.data.database.entity.LogDog import com.pushdeer.os.data.database.entity.LogDog
import com.pushdeer.os.store.SettingStore
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class LogDogRepository(private val logDogDao: LogDogDao) { class LogDogRepository(private val logDogDao: LogDogDao,private val settingStore: SettingStore) {
val all = logDogDao.all val all = logDogDao.all
suspend fun clear(){ suspend fun clear() {
logDogDao.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) { suspend fun logi(entity: String, event: String, log: String) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
insert(LogDog.logi(entity, event, log)) insert(LogDog.logi(entity, event, log))

View File

@ -4,6 +4,7 @@ import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Intent import android.content.Intent
import android.content.res.Resources import android.content.res.Resources
import android.util.Log
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity 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.MessageViewModel
import com.pushdeer.os.viewmodel.PushDeerViewModel import com.pushdeer.os.viewmodel.PushDeerViewModel
import com.pushdeer.os.viewmodel.UiViewModel 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.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 io.noties.markwon.Markwon
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -40,16 +46,20 @@ interface RequestHolder {
val myActivity: AppCompatActivity val myActivity: AppCompatActivity
val markdown: Markwon val markdown: Markwon
val qrScanActivityOpener: ActivityResultLauncher<Intent> val qrScanActivityOpener: ActivityResultLauncher<Intent>
val requestPermissionOpener:ActivityResultLauncher<Array<String>> val requestPermissionOpener: ActivityResultLauncher<Array<String>>
val coilImageLoader: ImageLoader val coilImageLoader: ImageLoader
val fragmentManager: FragmentManager
// requests
val alert: AlertRequest val alert: AlertRequest
val key:KeyRequest val key: KeyRequest
val device:DeviceRequest val device: DeviceRequest
val message:MessageRequest val message: MessageRequest
val clip:ClipRequest val clip: ClipRequest
val weChatLogin: WeChatLoginRequest
val appleLogin: AppleLoginRequest
// val iwxapi: IWXAPI
fun startQrScanActivity() { fun startQrScanActivity() {
qrScanActivityOpener.launch(QrScanActivity.forScanResultIntent(myActivity)) qrScanActivityOpener.launch(QrScanActivity.forScanResultIntent(myActivity))
@ -61,29 +71,98 @@ interface RequestHolder {
} }
fun clearLogDog() { fun clearLogDog() {
alert.alert(R.string.global_alert_title_confirm,"Clear?",onOk = { alert.alert(R.string.global_alert_title_confirm, "Clear?", onOk = {
logDogViewModel.clear() 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) { abstract class ClipRequest(private val clipboardManager: ClipboardManager) {
fun copyMessagePlainText(str: String) { fun copyMessagePlainText(str: String) {
clipboardManager.setPrimaryClip(ClipData.newPlainText("pushdeer-copy-plain-text", str)) 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)) clipboardManager.setPrimaryClip(ClipData.newPlainText("pushdeer-copy-pushkey", str))
} }
} }
abstract class AlertRequest(private val resources: Resources) { 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 title: String = ""
var content: @Composable () -> Unit = {} var content: @Composable () -> Unit = {}
var onOKAction: () -> Unit = {} var onOKAction: () -> Unit = {}
@ -94,19 +173,34 @@ interface RequestHolder {
title: String, title: String,
content: @Composable () -> Unit, content: @Composable () -> Unit,
onOk: () -> 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.title = title
this.content = content this.content = content
this.onOKAction = onOk this.onOKAction = onOk
this.onCancelAction = onCancel this.onCancelAction = onCancel
this.show.value = true this.show2BtnDialog.value = true
} }
fun alert(title: String, content: String, onOk: () -> Unit, onCancel: () -> Unit = {}) { fun alert(title: String, content: String, onOk: () -> Unit, onCancel: () -> Unit = {}) {
alert(title, { Text(text = content) }, onOk, onCancel) alert(title, { Text(text = content) }, onOk, onCancel)
} }
fun alert(title: String, content: String, onOk: () -> Unit) {
alert(title, { Text(text = content) }, onOk)
}
fun alert( fun alert(
@StringRes title: Int, @StringRes title: Int,
@StringRes content: Int, @StringRes content: Int,
@ -116,6 +210,14 @@ interface RequestHolder {
alert(resources.getString(title), resources.getString(content), onOk, onCancel) 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( fun alert(
@StringRes title: Int, @StringRes title: Int,
content: @Composable () -> Unit, content: @Composable () -> Unit,
@ -125,6 +227,14 @@ interface RequestHolder {
alert(resources.getString(title), content, onOk, onCancel) 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( fun alert(
@StringRes title: Int, @StringRes title: Int,
content: String, content: String,
@ -133,9 +243,17 @@ interface RequestHolder {
) { ) {
alert(resources.getString(title), content, onOk, onCancel) 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() { fun gen() {
requestHolder.coroutineScope.launch { requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.keyGen() 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) { fun deviceReg(deviceInfo: DeviceInfo) {
requestHolder.coroutineScope.launch { requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.deviceReg(deviceInfo) 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) { fun messagePush(text: String, desp: String, type: String, pushkey: String) {
requestHolder.coroutineScope.launch { requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.messagePush(text, desp, type, pushkey) requestHolder.pushDeerViewModel.messagePush(text, desp, type, pushkey)
@ -200,16 +318,21 @@ interface RequestHolder {
fun messagePushTest(text: String) { fun messagePushTest(text: String) {
if (requestHolder.pushDeerViewModel.keyList.isNotEmpty()) { 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 { requestHolder.coroutineScope.launch {
delay(1000) delay(900)
requestHolder.pushDeerViewModel.messageList() requestHolder.pushDeerViewModel.messageList()
} }
} else { } else {
requestHolder.alert.alert( requestHolder.alert.alert(
R.string.global_alert_title_alert, R.string.global_alert_title_alert,
R.string.main_message_send_alert, R.string.main_message_send_alert
onOk = {}) )
} }
} }

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.LogDogRepository
import com.pushdeer.os.data.repository.MessageRepository import com.pushdeer.os.data.repository.MessageRepository
import com.pushdeer.os.data.repository.MiPushRepository 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 miPushRepository = MiPushRepository()
val logDogRepository = LogDogRepository(database.logDogDao()) val logDogRepository = LogDogRepository(database.logDogDao(),settingStore)
val messageRepository = MessageRepository(database.messageDao()) 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 showMessageSender by store.boolean("show-message-sender",true)
var thisPushSdk by store.string("this-push-sdk","mi-push") var thisPushSdk by store.string("this-push-sdk","mi-push")
var thisDeviceId by store.string("this-device-id","") 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( Card(
onClick = onClick, onClick = onClick,
shape = RoundedCornerShape(4.dp), shape = RoundedCornerShape(8.dp),
modifier = Modifier modifier = Modifier
.border( .border(
width = 1.dp, width = 1.dp,
color = MainBlue, color = MainBlue,
shape = RoundedCornerShape(4.dp) shape = RoundedCornerShape(8.dp)
), ),
elevation = 5.dp elevation = 5.dp
@ -72,13 +72,13 @@ fun CardItemMultiLine(
) { ) {
Card( Card(
onClick = onClick, onClick = onClick,
shape = RoundedCornerShape(4.dp), shape = RoundedCornerShape(8.dp),
modifier = Modifier modifier = Modifier
// .padding(bottom = 16.dp) // .padding(bottom = 16.dp)
.border( .border(
width = 1.dp, width = 1.dp,
color = MainBlue, color = MainBlue,
shape = RoundedCornerShape(4.dp) shape = RoundedCornerShape(8.dp)
), ),
elevation = 5.dp elevation = 5.dp
@ -111,12 +111,12 @@ fun CardItemMultiLine(
fun CardItemWithContent(onClick: () -> Unit, content: @Composable () -> Unit = {}) { fun CardItemWithContent(onClick: () -> Unit, content: @Composable () -> Unit = {}) {
Card( Card(
onClick = onClick, onClick = onClick,
shape = RoundedCornerShape(4.dp), shape = RoundedCornerShape(8.dp),
modifier = Modifier modifier = Modifier
.border( .border(
width = 1.dp, width = 1.dp,
color = MainBlue, color = MainBlue,
shape = RoundedCornerShape(4.dp) shape = RoundedCornerShape(8.dp)
), ),
content = content, content = content,
elevation = 5.dp elevation = 5.dp
@ -127,12 +127,12 @@ fun CardItemWithContent(onClick: () -> Unit, content: @Composable () -> Unit = {
@Composable @Composable
fun CardItemWithContent(content: @Composable () -> Unit = {}) { fun CardItemWithContent(content: @Composable () -> Unit = {}) {
Card( Card(
shape = RoundedCornerShape(4.dp), shape = RoundedCornerShape(8.dp),
modifier = Modifier modifier = Modifier
.border( .border(
width = 1.dp, width = 1.dp,
color = MainBlue, color = MainBlue,
shape = RoundedCornerShape(4.dp) shape = RoundedCornerShape(8.dp)
), ),
content = content, content = content,
elevation = 5.dp elevation = 5.dp

View File

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

View File

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

View File

@ -10,13 +10,13 @@ import com.pushdeer.os.holder.RequestHolder
@Composable @Composable
fun MyAlertDialog(alertRequest: RequestHolder.AlertRequest) { fun MyAlertDialog(alertRequest: RequestHolder.AlertRequest) {
if (alertRequest.show.value) { if (alertRequest.show2BtnDialog.value) {
AlertDialog( AlertDialog(
onDismissRequest = { alertRequest.show.value = false }, onDismissRequest = { alertRequest.show2BtnDialog.value = false },
confirmButton = { confirmButton = {
TextButton(onClick = { TextButton(onClick = {
alertRequest.onOKAction.invoke() alertRequest.onOKAction.invoke()
alertRequest.show.value = false alertRequest.show2BtnDialog.value = false
}) { }) {
Text(text = stringResource(id = R.string.global_alert_ok)) Text(text = stringResource(id = R.string.global_alert_ok))
} }
@ -25,7 +25,7 @@ fun MyAlertDialog(alertRequest: RequestHolder.AlertRequest) {
dismissButton = { dismissButton = {
TextButton(onClick = { TextButton(onClick = {
alertRequest.onCancelAction.invoke() alertRequest.onCancelAction.invoke()
alertRequest.show.value = false alertRequest.show2BtnDialog.value = false
}) { }) {
Text(text = stringResource(id = R.string.global_alert_cancel)) Text(text = stringResource(id = R.string.global_alert_cancel))
} }
@ -35,4 +35,20 @@ fun MyAlertDialog(alertRequest: RequestHolder.AlertRequest) {
text = alertRequest.content 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 @ExperimentalMaterialApi
@Composable @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( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(bottom = 16.dp) .padding(paddingValues)
) { ) {
CardItemWithContent(onClick = onItemClick) { CardItemWithContent(onClick = onItemClick) {
Row( 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 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.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.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp 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 com.pushdeer.os.values.ConstValues
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ExperimentalMaterialApi @ExperimentalMaterialApi
@Composable @Composable
fun SwipeToDismissItem( fun SwipeToDismissItem(
onAction: () -> Unit, onAction: () -> Unit,
sidePadding: Boolean = false, sidePadding: Boolean = false,
requestHolder: RequestHolder,
content: @Composable RowScope.() -> Unit content: @Composable RowScope.() -> Unit
) { ) {
val dismissState = rememberDismissState() var visible by remember {
if (dismissState.isDismissed(DismissDirection.EndToStart)) { mutableStateOf(true)
onAction()
} }
Column(modifier = Modifier val dismissv = DismissValue.Default
.fillMaxWidth() val dismissState = rememberDismissState(initialValue = dismissv)
.padding(bottom = 16.dp)) { LaunchedEffect(Unit){
SwipeToDismiss( if (dismissState.isDismissed(DismissDirection.EndToStart)){
state = dismissState, requestHolder.coroutineScope.launch {
background = { dismissState.reset()
// val direction = dismissState.dismissDirection ?: return@SwipeToDismiss }
}
}
AnimatedVisibility(
visible = visible,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically()
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp)
) {
val color by animateColorAsState( SwipeToDismiss(
when (dismissState.targetValue) { state = dismissState,
DismissValue.DismissedToEnd -> Color.Green background = {
DismissValue.DismissedToStart -> Color.Red
else -> Color.Gray
}
)
// val alignment = when (direction) { val color by animateColorAsState(
// DismissDirection.StartToEnd -> Alignment.CenterStart when (dismissState.targetValue) {
// DismissDirection.EndToStart -> Alignment.CenterEnd DismissValue.DismissedToEnd -> SwipeToDismissGray
// } DismissValue.DismissedToStart -> SwipeToDismissRed
// else -> SwipeToDismissGray
// 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
) )
}
}, Row(
directions = setOf(DismissDirection.EndToStart), modifier = Modifier
dismissThresholds = { direction -> .fillMaxSize()
FractionalThreshold(if (direction == DismissDirection.EndToStart) 0.45f else 0.57f) .clip(RoundedCornerShape(8.dp))
}, .background(color)
dismissContent = content, .padding(end = 32.dp),
modifier = Modifier.padding(horizontal = if (sidePadding) ConstValues.MainPageSidePadding else 0.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 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.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
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.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
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.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
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.willowtreeapps.signinwithapplebutton.SignInWithAppleConfiguration import com.pushdeer.os.ui.theme.MainGreen
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) {
Box(modifier = Modifier.fillMaxSize()) { 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( 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",
@ -40,43 +34,50 @@ fun LoginPage(requestHolder: RequestHolder) {
.align(Alignment.TopCenter) .align(Alignment.TopCenter)
.padding(top = 50.dp) .padding(top = 50.dp)
) )
AndroidView( Card(
factory = { onClick = requestHolder.weChatLogin.login,
SignInWithAppleButton(it).apply { shape = RoundedCornerShape(4.dp),
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")
}
}
}
}
},
modifier = Modifier modifier = Modifier
.padding(bottom = 220.dp)
.border(
width = 1.dp,
color = MainGreen,
shape = RoundedCornerShape(4.dp)
)
.align(alignment = Alignment.BottomCenter) .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() val state = rememberLazyListState()
LazyColumn(state = state) { LazyColumn(state = state) {
items( items(
items = requestHolder.pushDeerViewModel.deviceList, items = requestHolder.pushDeerViewModel.deviceList.sortedByDescending { it.id },
key = { item: DeviceInfo -> item.id }) { deviceInfo: DeviceInfo -> key = { item: DeviceInfo -> item.id }) { deviceInfo: DeviceInfo ->
var name by remember { var name by remember {
mutableStateOf(deviceInfo.name) mutableStateOf(deviceInfo.name)
} }
SwipeToDismissItem( SwipeToDismissItem(
requestHolder = requestHolder,
onAction = { requestHolder.device.deviceRemove(deviceInfo) } onAction = { requestHolder.device.deviceRemove(deviceInfo) }
) { ) {
CardItemSingleLineWithIcon( CardItemSingleLineWithIcon(

View File

@ -49,9 +49,11 @@ fun KeyListPage(requestHolder: RequestHolder) {
}else{ }else{
LazyColumn(modifier = Modifier.fillMaxWidth()) { LazyColumn(modifier = Modifier.fillMaxWidth()) {
items( items(
requestHolder.pushDeerViewModel.keyList, requestHolder.pushDeerViewModel.keyList.sortedBy { it.id },
key = { item: PushKey -> item.id }) { pushKey: PushKey -> 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) KeyItem(key = pushKey, requestHolder = requestHolder)
} }

View File

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

View File

@ -1,6 +1,7 @@
package com.pushdeer.os.ui.compose.page.main package com.pushdeer.os.ui.compose.page.main
import androidx.compose.animation.* import androidx.compose.animation.*
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -10,9 +11,9 @@ 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.KeyboardArrowDown import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp 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( MainPageFrame(
titleStringId = Page.Messages.labelStringId, 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() }, onSideIconClick = { requestHolder.toggleMessageSender() },
sidePadding = false sidePadding = false
) { ) {
@ -58,7 +64,7 @@ fun MessageListPage(requestHolder: RequestHolder) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(bottom = 16.dp) .padding(bottom = 20.dp)
.padding(horizontal = ConstValues.MainPageSidePadding) .padding(horizontal = ConstValues.MainPageSidePadding)
) { ) {
@ -97,6 +103,7 @@ fun MessageListPage(requestHolder: RequestHolder) {
items = messageList, items = messageList,
key = { item: MessageEntity -> item.id }) { message: MessageEntity -> key = { item: MessageEntity -> item.id }) { message: MessageEntity ->
SwipeToDismissItem( SwipeToDismissItem(
requestHolder = requestHolder,
onAction = { onAction = {
requestHolder.message.messageRemove(message.toMessage(), onDone = { requestHolder.message.messageRemove(message.toMessage(), onDone = {
requestHolder.messageViewModel.delete(message) requestHolder.messageViewModel.delete(message)

View File

@ -3,16 +3,32 @@ package com.pushdeer.os.ui.compose.page.main
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.util.Log 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.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.ExperimentalMaterialApi 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.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.pushdeer.os.R import com.pushdeer.os.R
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.SettingItem import com.pushdeer.os.ui.compose.componment.SettingItem
import com.pushdeer.os.ui.navigation.Page import com.pushdeer.os.ui.navigation.Page
import com.pushdeer.os.ui.theme.MainBlue
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ExperimentalMaterialApi @ExperimentalMaterialApi
@Composable @Composable
@ -21,51 +37,123 @@ fun SettingPage(requestHolder: RequestHolder) {
titleStringId = Page.Settings.labelStringId, titleStringId = Page.Settings.labelStringId,
showSideIcon = false showSideIcon = false
) { ) {
var showLoginMethod by remember {
mutableStateOf(false)
}
LaunchedEffect(Unit) {
requestHolder.coroutineScope.launch {
delay(300)
showLoginMethod = true
}
}
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) { ) {
item { item {
// var newName by remember { Card(
// mutableStateOf(requestHolder.pushDeerViewModel.userInfo.name) elevation = 5.dp,
// } modifier = Modifier.padding(bottom = 16.dp),
SettingItem( shape = RoundedCornerShape(8.dp),
text = "${stringResource(id = R.string.main_setting_user_hi)} ${requestHolder.pushDeerViewModel.userInfo.name} !", border = BorderStroke(1.dp, MainBlue)
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 = {
//
// }
// )
}
) { ) {
requestHolder.pushDeerViewModel.deviceList.filter { it.device_id == requestHolder.settingStore.thisDeviceId } Column(
.forEach { modifier = Modifier
requestHolder.device.deviceRemove(it) .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 { // item {
@ -81,7 +169,7 @@ fun SettingPage(requestHolder: RequestHolder) {
text = stringResource(id = R.string.main_setting_douyoulike), text = stringResource(id = R.string.main_setting_douyoulike),
buttonString = stringResource(id = R.string.main_setting_btn_like) 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 { Intent(Intent.ACTION_VIEW, uri).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}.let { }.let {
@ -107,12 +195,43 @@ fun SettingPage(requestHolder: RequestHolder) {
} }
} }
//@Composable @Composable
//fun TogglePreferenceItem(label: String) { fun IconYes() {
// Row( Icon(
// verticalAlignment = Alignment.CenterVertically, painter = painterResource(id = R.drawable.ic_okok2),
// modifier = Modifier.fillMaxSize() contentDescription = "",
// ) { tint = MainBlue,
// Text(text = label) 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 MainBlue = Color(0xFF3B4789)
val MainGreen = Color(0xFF296C05) 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>() val keyList = mutableStateListOf<PushKey>()
// var messageList = mutableStateListOf<Message>() // var messageList = mutableStateListOf<Message>()
suspend fun login(idToken: String = "", onReturn: (String) -> Unit = {}) { suspend fun loginWithApple(idToken: String = "", onReturn: (String) -> Unit = {}) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
if (token == "" && idToken != "") { if (token == "" && idToken != "") {
try { try {
pushDeerService.loginIdToken(idToken).let { pushDeerService.loginWithAppleIdToken(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") Log.d(TAG, "loginWithApple: $token")
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
onReturn.invoke(token) onReturn.invoke(token)
} }
} }
} }
logDogRepository.logi("login", "normally", "nothing happened") logDogRepository.logi("loginWithApple", "withAppleId", "nothing happened")
} catch (e: Exception) { } catch (e: Exception) {
Log.d(TAG, "login: ${e.localizedMessage}") Log.d(TAG, "loginWithApple: ${e.localizedMessage}")
logDogRepository.loge("login", "", e.toString()) logDogRepository.loge("loginWithApple", "", e.toString())
return@withContext
} }
} else if (token == "" && idToken == "") { } else if (token == "" && idToken == "") {
return@withContext 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 = {}) { suspend fun userInfo(onOk: (UserInfo) -> Unit = {}, onFailed: () -> Unit = {}) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
try { try {
pushDeerService.userInfo(token).let { pushDeerService.userInfo(token).let {
Log.d(TAG, "userInfo: ${it.content}")
it.content?.let { ita -> it.content?.let { ita ->
userInfo = ita userInfo = ita
withContext(Dispatchers.Main) { 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_douyoulike">喜欢 PushDeer 吗?</string>
<string name="main_setting_btn_like">喜欢</string> <string name="main_setting_btn_like">喜欢</string>
<string name="main_setting_alert_logout">由于厂商推送设备服务限制,暂时不支持更换为自建 PushDeer 服务器,但仅更换登陆账号并不会影响您的使用。</string> <string name="main_setting_alert_logout">由于厂商推送设备服务限制,暂时不支持更换为自建 PushDeer 服务器,但仅更换登陆账号并不会影响您的使用。</string>
<string name="global_swipetodismiss_alert_delete">确定删除?</string>
</resources> </resources>

View File

@ -28,4 +28,5 @@
<string name="main_setting_douyoulike">Do you line PushDeer?</string> <string name="main_setting_douyoulike">Do you line PushDeer?</string>
<string name="main_setting_btn_like">Like</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="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> </resources>