修改icon,完善注销逻辑,增加点击MessageListItem复制文本功能

This commit is contained in:
alone-wolf 2022-01-22 13:16:34 +08:00
parent 8e4ba1f27e
commit 2ed1093b66
67 changed files with 957 additions and 457 deletions

View File

@ -13,6 +13,8 @@
<option value="$PROJECT_DIR$/app" /> <option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/common" /> <option value="$PROJECT_DIR$/common" />
<option value="$PROJECT_DIR$/compose" /> <option value="$PROJECT_DIR$/compose" />
<option value="$PROJECT_DIR$/pushdeerclient" />
<option value="$PROJECT_DIR$/pushdeercommon" />
</set> </set>
</option> </option>
<option name="resolveModulePerSourceSet" value="false" /> <option name="resolveModulePerSourceSet" value="false" />

View File

@ -14,8 +14,13 @@
<entry key="../../../../layout/compose-model-1641659962289.xml" value="2.0" /> <entry key="../../../../layout/compose-model-1641659962289.xml" value="2.0" />
<entry key="../../../../layout/compose-model-1641694023752.xml" value="0.1" /> <entry key="../../../../layout/compose-model-1641694023752.xml" value="0.1" />
<entry key="../../../../layout/compose-model-1642733328920.xml" value="2.0" /> <entry key="../../../../layout/compose-model-1642733328920.xml" value="2.0" />
<entry key="../../../../layout/compose-model-1642826587452.xml" value="2.0" />
<entry key="app/src/main/res/drawable/fragment_qr_scan.xml" value="0.12314814814814815" /> <entry key="app/src/main/res/drawable/fragment_qr_scan.xml" value="0.12314814814814815" />
<entry key="app/src/main/res/drawable/ic_markdown.xml" value="0.12962962962962962" /> <entry key="app/src/main/res/drawable/ic_markdown.xml" value="0.12962962962962962" />
<entry key="app/src/main/res/layout/activity_qr_scan.xml" value="0.1" />
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml" value="0.1" />
<entry key="common/src/main/res/layout/activity_qr_scan.xml" value="0.1" />
<entry key="pushdeerclient/src/main/res/drawable-v24/ic_markdown.xml" value="0.11944444444444445" />
</map> </map>
</option> </option>
</component> </component>

View File

@ -1,32 +1,34 @@
# PushDeer for Android # PushDeer for Android
### 适配进度 ### 适配进度
* MiPush状态已调通、已接入 * MiPush状态已调通、已接入
* miui 12.5.6 正常 * miui √
* 原生、类原生 可成功注册,无法收到推送,可能和地区识别有关,待解决 * Mokee √
* miui下处于"几乎可用"的状态,已接入 appleId * HuaWei √
* 部分原生、类原生 可成功注册,无法收到推送,可能和地区识别有关,待解决
### TODO ### TODO
* ~~调通 MiPush~~ * ~~调通 MiPush~~
* ~~接入 MiPush~~ * ~~接入 MiPush~~
* 完善 log ~~采集~~ 回传机制,便于调试 * 完善 log ~~采集~~ 回传机制,便于调试
* 测试不同厂商设备上的通知推送效果 * 测试不同厂商设备/系统上的通知推送效果 miui√ Mokee√ HuaWei√
* ~~接入 PushDeer~~ * ~~接入 PushDeer~~
* ~~界面设计BottomBar+Navigation(Device Key Message Setting)~~ * ~~界面设计BottomBar+Navigation(Device Key Message Setting)~~
* 调整 KeyList MessageList 等处的自定义绘制 * 调整 KeyList MessageList 等处的自定义绘制
* ~~增加 DeviceList 外的侧滑手势~~ * ~~增加 DeviceList 外的侧滑手势~~
* ~~增加侧滑手势相关动作~~ * ~~增加侧滑手势相关动作~~
* 增加各种操作前的二次确认弹窗,包括自动登陆 * 增加手动修改服务器地址逻辑(并放置到登陆界面)
* 增加非Miui设备的权限获取 * ~~增加退出登陆的逻辑~~
* 增加手动修改服务器地址/退出登陆的逻辑(并放置到登陆界面)
* 增加登陆过程中的图形提示 * 增加登陆过程中的图形提示
* 增加对PushKey重命名的逻辑 * ~~增加对PushKey重命名的逻辑~~
* 增加对设备重命名的逻辑 * ~~增加对设备重命名的逻辑~~
* 增加长按复制消息内容的逻辑 * ~~增加长按复制消息内容的逻辑~~
* 增加点击设置界面用户名修改用户名逻辑
* ~~适配image类型的消息显示~~
* ~~修改app图标~~
### 日志 ### 日志
@ -105,6 +107,12 @@
* 修改登陆界面ui * 修改登陆界面ui
* 增加key/device的重命名逻辑 * 增加key/device的重命名逻辑
* 适配英语和中文 * 适配英语和中文
* 增加点击PushKey显示二维码功能
* 2022-01-22
* 增加点击 Message 列表项目复制文本功能
* 增加适配Image类型Message的显示
* 完善登陆注销逻辑
### 感谢 ### 感谢

View File

@ -91,11 +91,12 @@ dependencies {
implementation "androidx.room:room-runtime:$room_version" implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version" kapt "androidx.room:room-compiler:$room_version"
// qr
implementation 'com.google.zxing:core:3.2.1' implementation 'com.google.zxing:core:3.2.1'
implementation 'cn.bingoogolapple:bga-qrcodecore:1.1.7@aar' implementation 'cn.bingoogolapple:bga-qrcodecore:1.1.7@aar'
implementation 'cn.bingoogolapple:bga-zxing:1.1.7@aar' implementation 'cn.bingoogolapple:bga-zxing:1.1.7@aar'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2' implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
final def markwon_version = '4.6.2' final def markwon_version = '4.6.2'
implementation "io.noties.markwon:core:$markwon_version" implementation "io.noties.markwon:core:$markwon_version"

View File

@ -4,12 +4,13 @@
package="com.pushdeer.os"> package="com.pushdeer.os">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- <uses-permission android:name="android.permission.READ_PHONE_STATE" />-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" <!-- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"-->
tools:ignore="ScopedStorage" /> <!-- tools:ignore="ScopedStorage" />-->
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
@ -18,8 +19,9 @@
android:protectionLevel="signature" /> android:protectionLevel="signature" />
<uses-permission android:name="com.pushdeer.os.permission.MIPUSH_RECEIVE" /> <uses-permission android:name="com.pushdeer.os.permission.MIPUSH_RECEIVE" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- for QR -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.autofocus" /> <uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera" />
@ -49,11 +51,11 @@
<activity <activity
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:name=".activity.QrScanActivity" android:name="com.wh.common.activity.QrScanActivity"
android:theme="@style/Theme.PushDeer.NoActionBar" android:theme="@style/Theme.PushDeer.NoActionBar"
/> />
<!-- start --> <!-- miPush components start -->
<service <service
android:name="com.xiaomi.push.service.XMPushService" android:name="com.xiaomi.push.service.XMPushService"
@ -86,7 +88,7 @@
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<!-- end --> <!-- miPush components end -->
<receiver <receiver
android:name="com.xiaomi.push.service.receivers.PingReceiver" android:name="com.xiaomi.push.service.receivers.PingReceiver"
@ -101,7 +103,6 @@
<receiver <receiver
android:name="com.pushdeer.os.receiver.MessageReceiver" android:name="com.pushdeer.os.receiver.MessageReceiver"
android:exported="true"> android:exported="true">
<!--这里com.xiaomi.mipushdemo.DemoMessageRreceiver改成app中定义的完整类名-->
<intent-filter> <intent-filter>
<action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE" /> <action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE" />
</intent-filter> </intent-filter>

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -5,11 +5,8 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.text.util.Linkify import android.text.util.Linkify
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
@ -29,7 +26,6 @@ import coil.ImageLoader
import com.google.accompanist.insets.ProvideWindowInsets import com.google.accompanist.insets.ProvideWindowInsets
import com.google.accompanist.insets.statusBarsPadding import com.google.accompanist.insets.statusBarsPadding
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.pushdeer.os.activity.QrScanActivity
import com.pushdeer.os.holder.RequestHolder import com.pushdeer.os.holder.RequestHolder
import com.pushdeer.os.store.SettingStore import com.pushdeer.os.store.SettingStore
import com.pushdeer.os.ui.compose.componment.MyAlertDialog import com.pushdeer.os.ui.compose.componment.MyAlertDialog
@ -37,12 +33,12 @@ import com.pushdeer.os.ui.compose.page.LogDogPage
import com.pushdeer.os.ui.compose.page.LoginPage import com.pushdeer.os.ui.compose.page.LoginPage
import com.pushdeer.os.ui.compose.page.main.MainPage import com.pushdeer.os.ui.compose.page.main.MainPage
import com.pushdeer.os.ui.theme.PushDeerTheme import com.pushdeer.os.ui.theme.PushDeerTheme
import com.pushdeer.os.util.ActivityOpener
import com.pushdeer.os.util.SystemUtil import com.pushdeer.os.util.SystemUtil
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.wh.common.util.UiUtils
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
@ -62,18 +58,36 @@ class MainActivity : AppCompatActivity(), RequestHolder {
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 fragmentManager: FragmentManager by lazy { this.supportFragmentManager }
// override val resource: Resources by lazy { this.resource }
override val coilImageLoader: ImageLoader by lazy { override val coilImageLoader: ImageLoader by lazy {
ImageLoader.Builder(this) ImageLoader.Builder(this)
.apply { .apply {
availableMemoryPercentage(0.5) availableMemoryPercentage(0.5)
bitmapPoolPercentage(0.5) bitmapPoolPercentage(0.5)
crossfade(true) crossfade(750)
allowHardware(true)
} }
.build() .build()
} }
override val alert: RequestHolder.AlertRequest by lazy { object : RequestHolder.AlertRequest(resources) {} } override val alert: RequestHolder.AlertRequest by lazy {
object : RequestHolder.AlertRequest(resources) {}
}
override val key: RequestHolder.KeyRequest by lazy {
object : RequestHolder.KeyRequest(this) {}
}
override val device: RequestHolder.DeviceRequest by lazy {
object : RequestHolder.DeviceRequest(this) {}
}
override val message: RequestHolder.MessageRequest by lazy {
object : RequestHolder.MessageRequest(this) {}
}
override val clip: RequestHolder.ClipRequest by lazy {
object : RequestHolder.ClipRequest(
getSystemService(
Context.CLIPBOARD_SERVICE
) as ClipboardManager
) {}
}
override val markdown: Markwon by lazy { override val markdown: Markwon by lazy {
Markwon.builder(this) Markwon.builder(this)
@ -84,10 +98,9 @@ class MainActivity : AppCompatActivity(), RequestHolder {
override lateinit var globalNavController: NavHostController override lateinit var globalNavController: NavHostController
override lateinit var coroutineScope: CoroutineScope override lateinit var coroutineScope: CoroutineScope
override lateinit var myActivity: ComponentActivity override lateinit var myActivity: AppCompatActivity
override val clipboardManager by lazy { getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager } override lateinit var qrScanActivityOpener: ActivityResultLauncher<Intent>
override lateinit var requestPermissionOpener: ActivityResultLauncher<Array<String>>
override lateinit var activityOpener: ActivityResultLauncher<Intent>
@ExperimentalAnimationApi @ExperimentalAnimationApi
@ExperimentalMaterialApi @ExperimentalMaterialApi
@ -95,28 +108,23 @@ class MainActivity : AppCompatActivity(), RequestHolder {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
myActivity = this myActivity = this
activityOpener = qrScanActivityOpener = ActivityOpener.forResult(this)
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> requestPermissionOpener = ActivityOpener.forPermission(this)
Toast.makeText(
this,
"${result.data?.getStringExtra(QrScanActivity.DataKey)}",
Toast.LENGTH_SHORT
).show()
}
UiUtils.keepScreenOn(window)
setContent { setContent {
globalNavController = rememberNavController() globalNavController = rememberNavController()
coroutineScope = rememberCoroutineScope() coroutineScope = rememberCoroutineScope()
val useDarkIcons = MaterialTheme.colors.isLight val useDarkIcons = MaterialTheme.colors.isLight
val systemUiController = rememberSystemUiController() val systemUiController = rememberSystemUiController()
when { when {
SystemUtil.isMiui() -> systemUiController.setStatusBarColor(Color.Transparent, useDarkIcons) SystemUtil.isMiui() -> systemUiController.setStatusBarColor(
Color.Transparent,
useDarkIcons
)
else -> systemUiController.setSystemBarsColor(Color.Transparent, useDarkIcons) else -> systemUiController.setSystemBarsColor(Color.Transparent, useDarkIcons)
} }
WindowCompat.setDecorFitsSystemWindows(window, true) WindowCompat.setDecorFitsSystemWindows(window, true)
miPushRepository.regId.observe(this) { miPushRepository.regId.observe(this) {
// 这个操作放到注册成功后进行
settingStore.thisDeviceId = it settingStore.thisDeviceId = it
} }

View File

@ -1,66 +1,66 @@
package com.pushdeer.os.activity //package com.pushdeer.os.activity
//
//
import android.content.Context //import android.content.Context
import android.content.Intent //import android.content.Intent
import android.os.Bundle //import android.os.Bundle
import android.util.Log //import android.util.Log
import androidx.appcompat.app.AppCompatActivity //import androidx.appcompat.app.AppCompatActivity
import cn.bingoogolapple.qrcode.core.QRCodeView //import cn.bingoogolapple.qrcode.core.QRCodeView
import com.pushdeer.os.R //import com.pushdeer.os.R
//
//
class QrScanActivity : AppCompatActivity(), QRCodeView.Delegate { //class QrScanActivity : AppCompatActivity(), QRCodeView.Delegate {
//
private val TAG = "WH_" + javaClass.simpleName // private val TAG = "WH_" + javaClass.simpleName
private lateinit var qrCode: QRCodeView // private lateinit var qrCode: QRCodeView
//
companion object { // companion object {
val RequestCode_get_scan_result = 436 // val RequestCode_get_scan_result = 436
val DataKey = "qr_scan_result" // val DataKey = "qr_scan_result"
//
fun forScanResultIntent(context: Context): Intent { // fun forScanResultIntent(context: Context): Intent {
return Intent(context, QrScanActivity::class.java).apply { // return Intent(context, QrScanActivity::class.java).apply {
putExtra(DataKey, 1) // putExtra(DataKey, 1)
} // }
} // }
} // }
//
override fun onCreate(savedInstanceState: Bundle?) { // override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) // super.onCreate(savedInstanceState)
setContentView(R.layout.activity_qr_scan) // setContentView(R.layout.activity_qr_scan)
qrCode = findViewById(R.id.qrcode1) // qrCode = findViewById(R.id.qrcode1)
qrCode.setDelegate(this) // qrCode.setDelegate(this)
} // }
//
override fun onStart() { // override fun onStart() {
super.onStart() // super.onStart()
Log.d(TAG, "onStart") // Log.d(TAG, "onStart")
qrCode.startSpotAndShowRect() // qrCode.startSpotAndShowRect()
} // }
//
override fun onStop() { // override fun onStop() {
Log.d(TAG, "onStop") // Log.d(TAG, "onStop")
qrCode.stopCamera() // qrCode.stopCamera()
super.onStop() // super.onStop()
} // }
//
override fun onDestroy() { // override fun onDestroy() {
qrCode.onDestroy() // qrCode.onDestroy()
super.onDestroy() // super.onDestroy()
} // }
//
override fun onScanQRCodeSuccess(result: String?) { // override fun onScanQRCodeSuccess(result: String?) {
Log.d(TAG, "onScanQRCodeSuccess: $result") // Log.d(TAG, "onScanQRCodeSuccess: $result")
qrCode.stopCamera() // qrCode.stopCamera()
val intent = Intent() // val intent = Intent()
intent.putExtra(DataKey, result) // intent.putExtra(DataKey, result)
setResult(RequestCode_get_scan_result, intent) // setResult(RequestCode_get_scan_result, intent)
finish() // finish()
} // }
//
override fun onScanQRCodeOpenCameraError() { // override fun onScanQRCodeOpenCameraError() {
Log.e(TAG, "onScanQRCodeOpenCameraError") // Log.e(TAG, "onScanQRCodeOpenCameraError")
qrCode.startSpotAndShowRect() // qrCode.startSpotAndShowRect()
} // }
} //}

View File

@ -4,9 +4,9 @@ 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 androidx.activity.ComponentActivity
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.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
@ -15,7 +15,6 @@ import androidx.fragment.app.FragmentManager
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import coil.ImageLoader import coil.ImageLoader
import com.pushdeer.os.R import com.pushdeer.os.R
import com.pushdeer.os.activity.QrScanActivity
import com.pushdeer.os.data.api.data.request.DeviceInfo import com.pushdeer.os.data.api.data.request.DeviceInfo
import com.pushdeer.os.data.api.data.response.Message import com.pushdeer.os.data.api.data.response.Message
import com.pushdeer.os.data.api.data.response.PushKey import com.pushdeer.os.data.api.data.response.PushKey
@ -24,6 +23,7 @@ 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.wh.common.activity.QrScanActivity
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
@ -37,106 +37,22 @@ interface RequestHolder {
val settingStore: SettingStore val settingStore: SettingStore
val globalNavController: NavHostController val globalNavController: NavHostController
val coroutineScope: CoroutineScope val coroutineScope: CoroutineScope
val myActivity: ComponentActivity val myActivity: AppCompatActivity
val markdown: Markwon val markdown: Markwon
val activityOpener: ActivityResultLauncher<Intent> val qrScanActivityOpener: ActivityResultLauncher<Intent>
val requestPermissionOpener:ActivityResultLauncher<Array<String>>
val coilImageLoader: ImageLoader val coilImageLoader: ImageLoader
// val resource:Resources
val fragmentManager: FragmentManager val fragmentManager: FragmentManager
val alert: AlertRequest val alert: AlertRequest
val key:KeyRequest
val clipboardManager: ClipboardManager val device:DeviceRequest
val message:MessageRequest
fun copyPlainString(str: String) { val clip:ClipRequest
clipboardManager.setPrimaryClip(ClipData.newPlainText("pushdeer-pushkey", str))
}
fun startQrScanActivity() { fun startQrScanActivity() {
activityOpener.launch(QrScanActivity.forScanResultIntent(myActivity)) qrScanActivityOpener.launch(QrScanActivity.forScanResultIntent(myActivity))
}
fun keyGen() {
coroutineScope.launch {
pushDeerViewModel.keyGen()
}
}
fun keyRegen(keyId: String) {
coroutineScope.launch {
pushDeerViewModel.keyRegen(keyId)
}
}
fun keyRemove(pushKey: PushKey) {
coroutineScope.launch {
pushDeerViewModel.keyList.remove(pushKey)
pushDeerViewModel.keyRemove(pushKey.id)
}
}
fun keyRename(pushKey: PushKey){
coroutineScope.launch {
pushDeerViewModel.keyRename(pushKey){
coroutineScope.launch {
pushDeerViewModel.keyList()
}
}
}
}
fun deviceReg(deviceInfo: DeviceInfo) {
coroutineScope.launch {
pushDeerViewModel.deviceReg(deviceInfo)
}
}
fun deviceRemove(deviceInfo: DeviceInfo) {
coroutineScope.launch {
pushDeerViewModel.deviceList.remove(deviceInfo)
pushDeerViewModel.deviceRemove(deviceInfo.id)
}
}
fun deviceRename(deviceInfo: DeviceInfo) {
coroutineScope.launch {
pushDeerViewModel.deviceRename(deviceInfo) {
coroutineScope.launch {
pushDeerViewModel.deviceList()
}
}
}
}
fun messagePush(text: String, desp: String, type: String, pushkey: String) {
coroutineScope.launch {
pushDeerViewModel.messagePush(text, desp, type, pushkey)
}
}
fun messagePushTest(text: String) {
if (pushDeerViewModel.keyList.isNotEmpty()) {
messagePush(text, "pushtest", "markdown", pushDeerViewModel.keyList[0].key)
coroutineScope.launch {
delay(1000)
pushDeerViewModel.messageList()
}
} else {
alert.alert(
R.string.global_alert_title_alert,
R.string.main_message_send_alert,
onOk = {})
}
}
fun messageRemove(message: Message, onDone: () -> Unit = {}) {
coroutineScope.launch {
// pushDeerViewModel.messageList.remove(message)
pushDeerViewModel.messageRemove(message.id)
onDone()
}
} }
fun toggleMessageSender() { fun toggleMessageSender() {
@ -145,8 +61,20 @@ interface RequestHolder {
} }
fun clearLogDog() { fun clearLogDog() {
coroutineScope.launch { alert.alert(R.string.global_alert_title_confirm,"Clear?",onOk = {
logDogViewModel.clear() coroutineScope.launch {
logDogViewModel.clear()
}
})
}
abstract class ClipRequest(private val clipboardManager: ClipboardManager) {
fun copyMessagePlainText(str: String) {
clipboardManager.setPrimaryClip(ClipData.newPlainText("pushdeer-copy-plain-text", str))
}
fun copyPushKey(str: String){
clipboardManager.setPrimaryClip(ClipData.newPlainText("pushdeer-copy-pushkey", str))
} }
} }
@ -188,9 +116,105 @@ interface RequestHolder {
@StringRes title: Int, @StringRes title: Int,
content: @Composable () -> Unit, content: @Composable () -> Unit,
onOk: () -> Unit, onOk: () -> Unit,
onCancel: () -> Unit={} onCancel: () -> Unit = {}
) {
alert(resources.getString(title), content, onOk, onCancel)
}
fun alert(
@StringRes title: Int,
content: String,
onOk: () -> Unit,
onCancel: () -> Unit = {}
) { ) {
alert(resources.getString(title), content, onOk, onCancel) alert(resources.getString(title), content, onOk, onCancel)
} }
} }
abstract class KeyRequest(private val requestHolder: RequestHolder){
fun gen() {
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.keyGen()
}
}
fun regen(keyId: String) {
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.keyRegen(keyId)
}
}
fun remove(pushKey: PushKey) {
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.keyList.remove(pushKey)
requestHolder.pushDeerViewModel.keyRemove(pushKey.id)
}
}
fun rename(pushKey: PushKey) {
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.keyRename(pushKey) {
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.keyList()
}
}
}
}
}
abstract class DeviceRequest(private val requestHolder: RequestHolder){
fun deviceReg(deviceInfo: DeviceInfo) {
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.deviceReg(deviceInfo)
}
}
fun deviceRemove(deviceInfo: DeviceInfo) {
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.deviceList.remove(deviceInfo)
requestHolder.pushDeerViewModel.deviceRemove(deviceInfo.id)
}
}
fun deviceRename(deviceInfo: DeviceInfo) {
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.deviceRename(deviceInfo) {
requestHolder.coroutineScope.launch {
requestHolder.pushDeerViewModel.deviceList()
}
}
}
}
}
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)
}
}
fun messagePushTest(text: String) {
if (requestHolder.pushDeerViewModel.keyList.isNotEmpty()) {
messagePush(text, "pushtest", "markdown", requestHolder.pushDeerViewModel.keyList[0].key)
requestHolder.coroutineScope.launch {
delay(1000)
requestHolder.pushDeerViewModel.messageList()
}
} else {
requestHolder.alert.alert(
R.string.global_alert_title_alert,
R.string.main_message_send_alert,
onOk = {})
}
}
fun messageRemove(message: Message, onDone: () -> Unit = {}) {
requestHolder.coroutineScope.launch {
// pushDeerViewModel.messageList.remove(message)
requestHolder.pushDeerViewModel.messageRemove(message.id)
onDone()
}
}
}
} }

View File

@ -108,7 +108,7 @@ fun CardItemMultiLine(
@ExperimentalMaterialApi @ExperimentalMaterialApi
@Composable @Composable
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(4.dp),
@ -123,6 +123,22 @@ fun CardItemWithContent(onClick: () -> Unit = {}, content: @Composable () -> Uni
) )
} }
@ExperimentalMaterialApi
@Composable
fun CardItemWithContent(content: @Composable () -> Unit = {}) {
Card(
shape = RoundedCornerShape(4.dp),
modifier = Modifier
.border(
width = 1.dp,
color = MainBlue,
shape = RoundedCornerShape(4.dp)
),
content = content,
elevation = 5.dp
)
}
@Composable @Composable
fun ListBottomBlankItem() { fun ListBottomBlankItem() {
Row( Row(

View File

@ -1,8 +1,10 @@
package com.pushdeer.os.ui.compose.componment package com.pushdeer.os.ui.compose.componment
import android.widget.ImageView
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* 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.*
@ -17,15 +19,17 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import com.pushdeer.os.R import com.pushdeer.os.R
import com.pushdeer.os.data.api.data.response.PushKey import com.pushdeer.os.data.api.data.response.PushKey
import com.pushdeer.os.holder.RequestHolder import com.pushdeer.os.holder.RequestHolder
import com.pushdeer.os.ui.theme.MBlue import com.pushdeer.os.ui.theme.MBlue
import com.wh.common.util.QRCodeGenerator
import com.wh.common.util.TimeUtils import com.wh.common.util.TimeUtils
@ExperimentalMaterialApi @ExperimentalMaterialApi
@Composable @Composable
fun KeyItem(key: PushKey,requestHolder: RequestHolder) { fun KeyItem(key: PushKey, requestHolder: RequestHolder) {
var name by remember { var name by remember {
mutableStateOf(key.name) mutableStateOf(key.name)
} }
@ -52,7 +56,7 @@ fun KeyItem(key: PushKey,requestHolder: RequestHolder) {
}, },
onOk = { onOk = {
key.name = name key.name = name
requestHolder.keyRename(key) requestHolder.key.rename(key)
} }
) )
}) { }) {
@ -118,24 +122,36 @@ fun KeyItem(key: PushKey,requestHolder: RequestHolder) {
color = Color.Gray, color = Color.Gray,
shape = RoundedCornerShape(4.dp) shape = RoundedCornerShape(4.dp)
) )
.clickable {
requestHolder.alert.alert("QrCode For ${key.name}", {
Box(
modifier = Modifier.width(400.dp)
) {
AndroidView(
factory = {
ImageView(it).apply {
this.setImageBitmap(
QRCodeGenerator(
key.key,
400.dp.value.toInt(),
400.dp.value.toInt()
).qrCode
)
}
},
modifier = Modifier.align(alignment = Alignment.Center)
)
}
}, onOk = {})
}
.padding(horizontal = 14.dp, vertical = 8.dp) .padding(horizontal = 14.dp, vertical = 8.dp)
) )
// Canvas(modifier = Modifier
// .fillMaxWidth()
// .height(16.dp), onDraw = {
// val linePath = Path()
// val linePaint = Paint()
// linePaint.pathEffect = PathEffect.dashPathEffect(FloatArray(10),10F)
// drawIntoCanvas {
// it.drawPath(linePath, linePaint)
// }
// })
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween horizontalArrangement = Arrangement.SpaceBetween
) { ) {
OutlinedButton( OutlinedButton(
onClick = { requestHolder.keyRegen(key.id) }, onClick = { requestHolder.key.regen(key.id) },
colors = ButtonDefaults.outlinedButtonColors( colors = ButtonDefaults.outlinedButtonColors(
backgroundColor = Color.Transparent, backgroundColor = Color.Transparent,
contentColor = MaterialTheme.colors.MBlue contentColor = MaterialTheme.colors.MBlue
@ -147,7 +163,7 @@ fun KeyItem(key: PushKey,requestHolder: RequestHolder) {
} }
Button( Button(
onClick = { onClick = {
requestHolder.copyPlainString(key.key) requestHolder.clip.copyPushKey(key.key)
}, },
colors = ButtonDefaults.buttonColors( colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.MBlue, backgroundColor = MaterialTheme.colors.MBlue,

View File

@ -1,7 +1,9 @@
package com.pushdeer.os.ui.compose.componment package com.pushdeer.os.ui.compose.componment
import android.widget.ImageView
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
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.Card import androidx.compose.material.Card
@ -12,12 +14,12 @@ 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.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import coil.load
import com.pushdeer.os.R import com.pushdeer.os.R
import com.pushdeer.os.data.database.entity.MessageEntity import com.pushdeer.os.data.database.entity.MessageEntity
import com.pushdeer.os.holder.RequestHolder import com.pushdeer.os.holder.RequestHolder
@ -27,11 +29,14 @@ import com.pushdeer.os.values.ConstValues
@ExperimentalMaterialApi @ExperimentalMaterialApi
@Composable @Composable
fun PlainTextMessageItem(message: MessageEntity) { fun PlainTextMessageItem(message: MessageEntity,requestHolder: RequestHolder) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clip(RoundedCornerShape(4.dp)) .clip(RoundedCornerShape(4.dp))
.clickable {
requestHolder.clip.copyMessagePlainText(message.text)
}
.background(color = MaterialTheme.colors.surface) .background(color = MaterialTheme.colors.surface)
) { ) {
@ -47,7 +52,7 @@ fun PlainTextMessageItem(message: MessageEntity) {
modifier = Modifier.size(40.dp) modifier = Modifier.size(40.dp)
) )
Text( Text(
text = "${message.text}·${ text = "${message.pushkey_name}·${
CurrentTimeUtil.resolveUTCTimeAndNow( CurrentTimeUtil.resolveUTCTimeAndNow(
message.created_at, message.created_at,
System.currentTimeMillis() System.currentTimeMillis()
@ -56,9 +61,9 @@ fun PlainTextMessageItem(message: MessageEntity) {
) )
} }
CardItemWithContent() { CardItemWithContent {
Text( Text(
text = message.desp, text = message.text,
overflow = TextOverflow.Visible, overflow = TextOverflow.Visible,
lineHeight = 24.sp, lineHeight = 24.sp,
modifier = Modifier modifier = Modifier
@ -71,12 +76,15 @@ fun PlainTextMessageItem(message: MessageEntity) {
@ExperimentalMaterialApi @ExperimentalMaterialApi
@Composable @Composable
fun ImageMessageItem(message: MessageEntity) { fun ImageMessageItem(message: MessageEntity, requestHolder: RequestHolder) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clip(RoundedCornerShape(4.dp)) .clip(RoundedCornerShape(4.dp))
.background (color = MaterialTheme.colors.surface) .clickable {
requestHolder.clip.copyMessagePlainText(message.text)
}
.background(color = MaterialTheme.colors.surface)
) { ) {
Row( Row(
@ -92,7 +100,7 @@ fun ImageMessageItem(message: MessageEntity) {
modifier = Modifier.size(40.dp) modifier = Modifier.size(40.dp)
) )
Text( Text(
text = "${message.text}·${ text = "${message.pushkey_name}·${
CurrentTimeUtil.resolveUTCTimeAndNow( CurrentTimeUtil.resolveUTCTimeAndNow(
message.created_at, message.created_at,
System.currentTimeMillis() System.currentTimeMillis()
@ -100,12 +108,13 @@ fun ImageMessageItem(message: MessageEntity) {
}" }"
) )
} }
Card(modifier = Modifier.fillMaxWidth(), onClick = {}) { Card(modifier = Modifier.fillMaxWidth()) {
Image( AndroidView(factory = {
painter = painterResource(id = R.drawable.logo_com_x2), ImageView(it).apply {
contentDescription = "", scaleType = ImageView.ScaleType.FIT_CENTER
contentScale = ContentScale.FillWidth load(message.text, requestHolder.coilImageLoader)
) }
}, modifier = Modifier.fillMaxWidth())
} }
} }
} }
@ -117,6 +126,9 @@ fun MarkdownMessageItem(message: MessageEntity, requestHolder: RequestHolder) {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clip(RoundedCornerShape(4.dp)) .clip(RoundedCornerShape(4.dp))
.clickable {
requestHolder.clip.copyMessagePlainText(message.text)
}
.background(color = MaterialTheme.colors.surface) .background(color = MaterialTheme.colors.surface)
) { ) {
@ -163,6 +175,13 @@ fun MarkdownMessageItem(message: MessageEntity, requestHolder: RequestHolder) {
}, modifier = Modifier }, modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
// .pointerInput(Unit) {
// this.detectTapGestures(
// onLongPress = {
// Log.d("WH_", "MarkdownMessageItem: ")
// }
// )
// }
.padding(16.dp) .padding(16.dp)
) )
} }

View File

@ -59,7 +59,7 @@ fun LoginPage(requestHolder: RequestHolder) {
} }
} }
is SignInWithAppleResult.Failure -> { is SignInWithAppleResult.Failure -> {
requestHolder.alert.alert("Warning", { requestHolder.alert.alert("Warning Apple Id Login Failed", {
result.error.message result.error.message
}, onOk = {}) }, onOk = {})
Log.d( Log.d(

View File

@ -1,6 +1,5 @@
package com.pushdeer.os.ui.compose.page.main package com.pushdeer.os.ui.compose.page.main
import android.util.Log
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@ -41,7 +40,7 @@ fun DeviceListPage(requestHolder: RequestHolder) {
onOk = {}) onOk = {})
// device regid got failed // device regid got failed
} else { } else {
requestHolder.deviceReg( requestHolder.device.deviceReg(
deviceInfo = DeviceInfo().apply { deviceInfo = DeviceInfo().apply {
name = SystemUtil.getDeviceModel() name = SystemUtil.getDeviceModel()
device_id = requestHolder.settingStore.thisDeviceId device_id = requestHolder.settingStore.thisDeviceId
@ -72,7 +71,7 @@ fun DeviceListPage(requestHolder: RequestHolder) {
mutableStateOf(deviceInfo.name) mutableStateOf(deviceInfo.name)
} }
SwipeToDismissItem( SwipeToDismissItem(
onAction = { requestHolder.deviceRemove(deviceInfo) } onAction = { requestHolder.device.deviceRemove(deviceInfo) }
) { ) {
CardItemSingleLineWithIcon( CardItemSingleLineWithIcon(
onClick = { onClick = {
@ -99,7 +98,7 @@ fun DeviceListPage(requestHolder: RequestHolder) {
}, },
onOk = { onOk = {
deviceInfo.name = name deviceInfo.name = name
requestHolder.deviceRename(deviceInfo) requestHolder.device.deviceRename(deviceInfo)
} }
) )
}, },
@ -110,7 +109,7 @@ fun DeviceListPage(requestHolder: RequestHolder) {
) )
}) " else deviceInfo.name }) " else deviceInfo.name
) )
Log.d("WH_", "DeviceListPage: $deviceInfo") // Log.d("WH_", "DeviceListPage: $deviceInfo")
} }
} }
item { item {

View File

@ -25,7 +25,7 @@ import com.pushdeer.os.ui.navigation.Page
fun KeyListPage(requestHolder: RequestHolder) { fun KeyListPage(requestHolder: RequestHolder) {
MainPageFrame( MainPageFrame(
titleStringId = Page.Keys.labelStringId, titleStringId = Page.Keys.labelStringId,
onSideIconClick = { requestHolder.keyGen() } onSideIconClick = { requestHolder.key.gen() }
) { ) {
if(requestHolder.pushDeerViewModel.keyList.isEmpty()){ if(requestHolder.pushDeerViewModel.keyList.isEmpty()){
Column( Column(
@ -42,7 +42,7 @@ fun KeyListPage(requestHolder: RequestHolder) {
items( items(
requestHolder.pushDeerViewModel.keyList, requestHolder.pushDeerViewModel.keyList,
key = { item: PushKey -> item.id }) { pushKey: PushKey -> key = { item: PushKey -> item.id }) { pushKey: PushKey ->
SwipeToDismissItem(onAction = { requestHolder.keyRemove(pushKey) } SwipeToDismissItem(onAction = { requestHolder.key.remove(pushKey) }
) { ) {
KeyItem(key = pushKey, requestHolder = requestHolder) KeyItem(key = pushKey, requestHolder = requestHolder)
} }

View File

@ -43,7 +43,7 @@ fun MainPage(requestHolder: RequestHolder) {
} }
var titleStringId by remember { var titleStringId by remember {
mutableStateOf(Page.Devices.labelStringId) mutableStateOf(Page.Messages.labelStringId)
} }
val navController = rememberNavController() val navController = rememberNavController()
Scaffold( Scaffold(
@ -98,7 +98,7 @@ fun MainPage(requestHolder: RequestHolder) {
) )
NavHost( NavHost(
navController = navController, navController = navController,
startDestination = Page.Devices.route, startDestination = Page.Messages.route,
) { ) {
composable(Page.Devices.route) { composable(Page.Devices.route) {
DeviceListPage(requestHolder = requestHolder) DeviceListPage(requestHolder = requestHolder)

View File

@ -73,7 +73,7 @@ fun MessageListPage(requestHolder: RequestHolder) {
) )
Button( Button(
onClick = { onClick = {
requestHolder.messagePushTest(s) requestHolder.message.messagePushTest(s)
}, },
colors = ButtonDefaults.buttonColors( colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.MBlue, backgroundColor = MaterialTheme.colors.MBlue,
@ -90,7 +90,7 @@ fun MessageListPage(requestHolder: RequestHolder) {
key = { item: MessageEntity -> item.id }) { message: MessageEntity -> key = { item: MessageEntity -> item.id }) { message: MessageEntity ->
SwipeToDismissItem( SwipeToDismissItem(
onAction = { onAction = {
requestHolder.messageRemove(message.toMessage(), onDone = { requestHolder.message.messageRemove(message.toMessage(), onDone = {
requestHolder.messageViewModel.delete(message) requestHolder.messageViewModel.delete(message)
}) })
}, },
@ -98,8 +98,8 @@ fun MessageListPage(requestHolder: RequestHolder) {
) { ) {
when (message.type) { when (message.type) {
"markdown" -> MarkdownMessageItem(message, requestHolder) "markdown" -> MarkdownMessageItem(message, requestHolder)
"text" -> PlainTextMessageItem(message) "text" -> PlainTextMessageItem(message, requestHolder)
"image" -> ImageMessageItem(message) "image" -> ImageMessageItem(message, requestHolder)
} }
} }
} }

View File

@ -26,10 +26,18 @@ fun SettingPage(requestHolder: RequestHolder) {
text = "${stringResource(id = R.string.main_setting_user_hi)} ${requestHolder.pushDeerViewModel.userInfo.name} !", text = "${stringResource(id = R.string.main_setting_user_hi)} ${requestHolder.pushDeerViewModel.userInfo.name} !",
buttonString = stringResource(id = R.string.main_setting_user_logout) buttonString = stringResource(id = R.string.main_setting_user_logout)
) { ) {
requestHolder.pushDeerViewModel.deviceList.filter { it.device_id == requestHolder.settingStore.thisDeviceId }.forEach {
requestHolder.device.deviceRemove(it)
}
requestHolder.settingStore.userToken = "" requestHolder.settingStore.userToken = ""
// logout 操作: requestHolder.globalNavController.navigate("login") {
// 从服务器删除本设备 requestHolder.globalNavController.popBackStack()
// 删除保存的 token }
requestHolder.alert.alert(
"提示",
"由于厂商推送设备服务限制,暂时不支持更换为自建 PushDeer 服务器,但仅更换登陆账号并不会影响您的使用",
{}
)
} }
} }
// item { // item {

View File

@ -0,0 +1,30 @@
package com.pushdeer.os.util
import android.content.Intent
import android.util.Log
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import com.wh.common.activity.QrScanActivity
object ActivityOpener {
fun forResult(activity: AppCompatActivity): ActivityResultLauncher<Intent> {
return activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
Toast.makeText(
activity,
"${result.data?.getStringExtra(QrScanActivity.DataKey)}",
Toast.LENGTH_SHORT
).show()
}
}
fun forPermission(activity: AppCompatActivity): ActivityResultLauncher<Array<String>> {
return activity.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
it.entries.forEach {
Log.d("WH_", "forPermission:${it.key} ${it.value} ")
}
}
}
}

View File

@ -1,30 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@ -1,170 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@ -0,0 +1,29 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group android:scaleX="0.1981214"
android:scaleY="0.1981214"
android:translateX="33.056"
android:translateY="30.277714">
<group>
<clip-path
android:pathData="M0,0h233v233h-233z"/>
<path
android:pathData="M-282.333,375.018h26.8l-0.7,-14.526c1.7,-5.491 13.527,-40.982 44.275,-61.545q9.332,-6.241 17.071,-11.58h0a69.072,69.072 0,0 0,-5.244 7.935c-1.149,2.3 -3.8,7.636 26.856,67.089l-1.1,12.629h29.6l0.6,-4.293c1.048,-7.437 -4.992,-19.965 -14.474,-38.586 -2.744,-5.39 -5.292,-10.431 -7.186,-14.624h0a13.171,13.171 0,0 1,-1.546 -9.586,3.626 3.626,0 0,1 1.845,-1.894l1.2,-0.349 43.627,-41.73 3.694,1.348c19.366,7.635 30.947,9.982 74.574,9.186 2.246,9.982 8.234,34.491 14.976,44.922 -2.844,15.473 -5.741,48.67 -5.839,49.916l-0.55,5.689h27.5v-12.03a383.644,383.644 0,0 0,5.439 -49.017c-0.349,-8.635 1.2,-28.5 2.145,-39.581l34.042,19.965h0a24.254,24.254 0,0 1,9.234 12.228c2.747,9.533 16.723,64.143 16.873,64.442l0.947,3.743h23.61l1.4,-1.2c4.342,-3.743 3.095,-13.127 -3.695,-27.9h0a432.137,432.137 0,0 1,-17.67 -56.851c0,-2.893 -2.245,-11.18 -15.922,-19.965l0,0a76.686,76.686 0,0 0,-8.934 -4.989l-2.545,-1.247v0a57.934,57.934 0,0 0,25.257 -16.97A112.324,112.324 0,0 1,75.363 235.307a56.1,56.1 0,0 0,20.564 -30.8l9.982,-42.329c4.342,-0.25 8.736,-0.647 13.127,-1.149 5.891,-0.647 11.929,-1.3 17.52,-1.4 17.869,0 23.011,-2.594 27.851,-7.786s4.992,-21.761 2.4,-27.5l0,0a6.684,6.684 0,0 0,-4.99 -4.289c-13.377,-4.143 -32.593,-12.827 -34.94,-16.622 -3.993,-6.239 -20.466,-7.138 -35.79,-6.539l-9.036,-11.629 7.337,-3.746L134.31,88.007v-0.449h54.411L203.697,68.287l-7.938,-6.288 -11.88,15.372L139.454,77.371l5.94,-11.73L170.351,37.887l-7.437,-6.641 -20.766,23.21 -8.784,-14.074 -8.535,4.993 10.731,17.569 -7.089,13.725L94.53,71.677l6.389,-27.848 26.653,-25.707 -6.689,-7.19L98.77,32.246l-5.539,-15.623 -9.436,3.294 7.44,21.664L84.147,72.827l-9.982,4.993L51.702,64.993l11.131,-16.574 -8.287,-5.54L44.165,58.353l-14.976,-18.07L50.202,22.207l-6.539,-7.587L24.147,31.599l-11.33,-24.46 -9.036,4.5 12.28,26.856 -0.849,0.749 25.957,31.448 29.8,16.768 0.449,0.55c-8.287,-4.641 -16.671,-8.287 -21.761,-7.486v0a8.588,8.588 0,0 0,-7.388 9.036,33.934 33.934,0 0,0 8.736,19.317l-32.4,37.537a513.11,513.11 0,0 0,-82.812 8.234c-17.221,3.346 -38.137,1.348 -58.4,-0.6 -28.75,-2.642 -56.005,-5.288 -71.73,7.34a59.131,59.131 0,0 0,-12.629 13.778c-7.838,1.8 -29.951,11.233 -29.951,59.2v4.992h4.992a19.5,19.5 0,0 0,13.377 -7.089c0,4.443 0,8.983 0.3,13.628v3.942a33.606,33.606 0,0 1,-4.042 9.185,17.928 17.928,0 0,0 -3.294,9.982 163.081,163.081 0,0 0,-21.263 13.078l-1.1,0.749L-282.336,358.05ZM-169.72,296.948a13.677,13.677 0,0 0,-6.689 6.64,21.454 21.454,0 0,0 1.448,17.97c1.995,4.394 4.641,9.586 7.388,14.976h0A210.546,210.546 0,0 1,-154.698 364.934h-8.934l0.4,-4.693 -0.4,-1.2c-12.629,-24.408 -26.2,-53.659 -27.054,-59.9 1.1,-1.845 3.444,-4.993 5.742,-8.234a83.879,83.879 0,0 0,11.33 -18.718c7.586,-5.492 12.43,-9.185 14.377,-10.682a82.932,82.932 0,0 1,26.9 -0.3ZM-16.176,361.839v3.245h-6.689c1,-10.881 3.4,-35.24 5.64,-45.724l0.5,-2.347 -1.547,-1.845c-4.693,-5.689 -10.532,-26.054 -13.976,-40.832l23.61,-0.551c-0.947,11.33 -2.5,31.4 -2.145,40.633h0a397.657,397.657 0,0 1,-5.39 47.322ZM-226.624,226.274c1.149,-20.366 7.037,-30.748 12.58,-36.041a99.151,99.151 0,0 0,-4.095 24.31,50.532 50.532,0 0,1 -8.485,11.73ZM-272.348,360.199 L-239.698,289.421a109.45,109.45 0,0 1,21.162 -12.382l4.592,-1.246 -1.1,-4.593c-0.7,-3.095 0,-3.944 1.6,-6.988a43.328,43.328 0,0 0,5.288 -12.479v-6.037c-1,-34.293 -1.7,-59.049 19.965,-76.419 12.629,-9.982 37.837,-7.688 64.539,-4.992 20.965,1.995 42.628,4.094 61.246,0.448l0,0a502.892,502.892 0,0 1,83.46 -8.336h2.3l34.94,-40.633 0,0a28.239,28.239 0,0 0,11.58 5.39l1.846,-9.982a27.909,27.909 0,0 1,-15.971 -12.879,21.824 21.824,0 0,1 -3.5,-8.085c5.891,0.749 22.712,10.784 35.988,20.616l3.043,-4.143c13.078,-0.449 24.958,0.449 27.005,1.947 4.592,7.239 24.16,14.976 34.443,18.718h0a6.434,6.434 0,0 0,5.491 7.935,29.034 29.034,0 0,1 -1.3,9.635c-2.2,2.347 -4.042,4.394 -20.665,4.592a178.548,178.548 0,0 0,-18.52 1.448c-12.13,1.348 -23.61,2.594 -31.246,-1 -9.982,-4.992 -11.281,-10.382 -11.33,-10.431l-9.982,1.2c0,1.1 1.748,11.131 16.97,18.269h0a37,37 0,0 0,13.179 3.1l-9.185,39.933v0a46.837,46.837 0,0 1,-17.02 24.958l-0.9,0.7A229.149,229.149 0,0 1,83.202 194.932l4.641,-8.635 -14.976,2.047 9.982,-20.766 -8.934,-4.393 -18.269,38.137 14.077,-1.947C63.036,212.707 55.802,229.326 56.396,238.907c-7.785,7.635 -14.227,13.827 -25.606,14.976v0a50.516,50.516 0,0 1,-0.7 -14.373l-9.982,-0.55c-1.246,24.011 5.39,28.5 16.424,33.791h0a69.288,69.288 0,0 1,7.838 4.245c10.634,7.036 11.629,12.479 11.629,12.479v1.3c0.4,1.646 9.982,40.281 18.618,59.3l0,0a57.23,57.23 0,0 1,4.99 14.976L70.122,365.051c-3.245,-12.629 -13.575,-52.712 -15.922,-60.946v0a34.29,34.29 0,0 0,-13.624 -17.918l-57.7,-34.341 -4.993,8.586 5.892,3.5c-67.088,1.647 -76.719,0 -97.086,-7.987l0,0a89.857,89.857 0,0 0,-49.017 -3.942l-1.047,0.25 -0.849,0.647S-182.845,267.407 -217.389,290.519c-36.389,24.359 -48.168,66.238 -48.666,68.035v0.749l0.3,5.69L-272.347,364.993Z"
android:fillColor="#3b4789"/>
<path
android:pathData="M110.502,119.904a5.39,5.39 0,1 1,-5.39 -5.394,5.39 5.39,0 0,1 5.39,5.394"
android:fillColor="#3b4789"/>
<path
android:pathData="M-152.554,175.257l1,-9.986v0a50.958,50.958 0,0 0,-50.911 33.994l9.335,3.545h0a40.393,40.393 0,0 1,40.584 -27.555Z"
android:fillColor="#3b4789"/>
<path
android:pathData="M153.592,151.55A2.54,2.54 83.016,0 0,150.072 151.982L124.335,184.905a2.54,2.54 83.016,0 0,0.431 3.52l53.994,42.208a2.54,2.54 83.016,0 0,3.52 -0.431L208.016,197.278A2.54,2.54 83.016,0 0,207.585 193.759ZM153.734,155.904L203.325,194.671L162.38,195.947ZM150.519,156.839 L155.87,181.674 128.916,184.473ZM203.196,198.018L181.593,225.652l-3.791,-26.831ZM156.586,184.954 L159.399,198.027a1.671,1.671 83.016,0 0,1.688 1.319L174.447,198.916 178.286,226.02L129.355,187.769Z"
android:strokeWidth="2.0003998"
android:fillColor="#3b4789"
android:strokeColor="#3b4789"/>
</group>
</group>
</vector>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" /> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" /> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>

View File

@ -48,4 +48,9 @@ dependencies {
implementation("com.squareup.okhttp3:okhttp:4.9.2") implementation("com.squareup.okhttp3:okhttp:4.9.2")
implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.code.gson:gson:2.8.9'
// qr
implementation 'com.google.zxing:core:3.2.1'
implementation 'cn.bingoogolapple:bga-qrcodecore:1.1.7@aar'
implementation 'cn.bingoogolapple:bga-zxing:1.1.7@aar'
} }

View File

@ -5,4 +5,6 @@
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
</manifest> </manifest>

View File

@ -0,0 +1,65 @@
package com.wh.common.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import cn.bingoogolapple.qrcode.core.QRCodeView
import com.wh.common.R
class QrScanActivity : AppCompatActivity(), QRCodeView.Delegate {
private val TAG = "WH_" + javaClass.simpleName
private lateinit var qrCode: QRCodeView
companion object {
val RequestCode_get_scan_result = 436
val DataKey = "qr_scan_result"
fun forScanResultIntent(context: Context): Intent {
return Intent(context, QrScanActivity::class.java).apply {
putExtra(DataKey, 1)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_qr_scan)
qrCode = findViewById(R.id.qrcode1)
qrCode.setDelegate(this)
}
override fun onStart() {
super.onStart()
Log.d(TAG, "onStart")
qrCode.startSpotAndShowRect()
}
override fun onStop() {
Log.d(TAG, "onStop")
qrCode.stopCamera()
super.onStop()
}
override fun onDestroy() {
qrCode.onDestroy()
super.onDestroy()
}
override fun onScanQRCodeSuccess(result: String?) {
Log.d(TAG, "onScanQRCodeSuccess: $result")
qrCode.stopCamera()
val intent = Intent()
intent.putExtra(DataKey, result)
setResult(RequestCode_get_scan_result, intent)
finish()
}
override fun onScanQRCodeOpenCameraError() {
Log.e(TAG, "onScanQRCodeOpenCameraError")
qrCode.startSpotAndShowRect()
}
}

View File

@ -0,0 +1,38 @@
package com.wh.common.util
import android.content.Intent
import android.util.Log
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import com.wh.common.activity.QrScanActivity
object ActivityOpener {
fun forQrScanResult(
activity: AppCompatActivity,
onReturn: (String) -> Unit = {}
): ActivityResultLauncher<Intent> {
return activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
result.data?.getStringExtra(QrScanActivity.DataKey)?.let {
onReturn(it)
Toast.makeText(activity, it, Toast.LENGTH_SHORT).show()
}
}
}
fun forPermission(
activity: AppCompatActivity,
onReturn: () -> Unit = {}
): ActivityResultLauncher<Array<String>> {
return activity.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
it.entries.forEach {
Log.d("WH_", "forPermission:${it.key} ${it.value} ")
}
if (it.entries.all { it.value == true }){
onReturn()
}
}
}
}

View File

@ -0,0 +1,54 @@
package com.wh.common.util;
import static android.graphics.Color.BLACK;
import static android.graphics.Color.WHITE;
import android.graphics.Bitmap;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import java.util.HashMap;
import java.util.Map;
public class QRCodeGenerator {
private final Map<EncodeHintType, String> hints;
private Bitmap bitmap = null;
private final String str;
private final int WIDTH;
private final int HEIGHT;
public QRCodeGenerator(String str, int WIDTH, int HEIGHT){
this.str = (str.length()>300)?"str is too long":str;
this.WIDTH=WIDTH;
this.HEIGHT=HEIGHT;
this.hints = new HashMap<>();
this.hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
}
public Bitmap getQRCode() {
try {
BitMatrix Result = new MultiFormatWriter().encode(str, BarcodeFormat.QR_CODE, WIDTH, HEIGHT, hints);//通过字符串创建二维矩阵
int width = Result.getWidth();
int height = Result.getHeight();
int[] pixels = new int[width * height];
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
pixels[offset + x] = Result.get(x, y) ? BLACK : WHITE;//根据二维矩阵数据创建数组
}
}
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//创建位图
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);//将数组加载到位图中
return bitmap;
} catch (WriterException e) {
e.printStackTrace();
}
return bitmap;
}
}

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
tools:ignore="NewApi">
<cn.bingoogolapple.qrcode.zxing.ZXingView
android:id="@+id/qrcode1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:qrcv_animTime="300"
app:qrcv_barCodeTipText="将条码放入框内,即可自动扫描"
app:qrcv_barcodeRectHeight="40dp"
app:qrcv_borderColor="@android:color/black"
app:qrcv_borderSize="0.1dp"
app:qrcv_cornerColor="@android:color/black"
app:qrcv_cornerLength="20dp"
app:qrcv_cornerSize="3dp"
app:qrcv_isBarcode="false"
app:qrcv_isCenterVertical="false"
app:qrcv_isOnlyDecodeScanBoxArea="true"
app:qrcv_isScanLineReverse="true"
app:qrcv_isShowDefaultGridScanLineDrawable="false"
app:qrcv_isShowDefaultScanLineDrawable="true"
app:qrcv_isShowTipBackground="false"
app:qrcv_isShowTipTextAsSingleLine="false"
app:qrcv_isTipTextBelowRect="true"
app:qrcv_maskColor="#DEBCBCBC"
app:qrcv_qrCodeTipText="请将QRCode放到扫描框中"
app:qrcv_rectWidth="240dp"
app:qrcv_scanLineColor="@android:color/black"
app:qrcv_scanLineMargin="0dp"
app:qrcv_scanLineSize="0.5dp"
app:qrcv_tipTextColor="#80ffffff"
app:qrcv_tipTextSize="14sp"
app:qrcv_toolbarHeight="40dp"
app:qrcv_topOffset="80dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -2,4 +2,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.wh.sbw.compose"> package="com.wh.sbw.compose">
</manifest> </manifest>

1
android/pushdeercommon/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,47 @@
plugins {
id 'com.android.library'
id 'kotlin-android'
}
android {
compileSdk 32
defaultConfig {
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-scalars:2.0.0'
implementation 'com.google.code.gson:gson:2.8.9'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
}

View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,24 @@
package com.pushdeer.common
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.pushdeer.common.test", appContext.packageName)
}
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.pushdeer.common">
</manifest>

View File

@ -0,0 +1,93 @@
package com.pushdeer.common.api
import com.pushdeer.common.api.data.response.*
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.converter.scalars.ScalarsConverterFactory
import retrofit2.http.Field
import retrofit2.http.FieldMap
import retrofit2.http.FormUrlEncoded
import retrofit2.http.POST
interface PushDeerApi {
companion object {
private const val baseUrl = "https://api2.pushdeer.com"
fun create(): PushDeerApi {
return Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(PushDeerApi::class.java)
}
}
@FormUrlEncoded
@POST("/login/idtoken")
suspend fun loginIdToken(@Field("idToken") idToken: String): ReturnData<TokenOnly>
// @GET("/login/fake")
// suspend fun fakeLogin(): ReturnData<TokenOnly>
@FormUrlEncoded
@POST("/user/info")
suspend fun userInfo(@Field("token") token: String): ReturnData<UserInfo>
@FormUrlEncoded
@POST("/device/reg")
suspend fun deviceReg(@FieldMap data: Map<String, String>): ReturnData<DeviceInfoList>
@FormUrlEncoded
@POST("/device/list")
suspend fun deviceList(@Field("token") token: String): ReturnData<DeviceInfoList>
@FormUrlEncoded
@POST("/device/remove")
suspend fun deviceRemove(@Field("token") token: String, @Field("id") id: Int): String
@FormUrlEncoded
@POST("/device/rename")
suspend fun deviceRename(
@Field("token") token: String,
@Field("id") id: Int,
@Field("name") newName: String
): String
@FormUrlEncoded
@POST("/key/gen")
suspend fun keyGen(@Field("token") token: String): ReturnData<PushKeyList>
@FormUrlEncoded
@POST("/key/regen")
suspend fun keyRegen(@FieldMap data: Map<String, String>): String
@FormUrlEncoded
@POST("/key/list")
suspend fun keyList(@Field("token") token: String): ReturnData<PushKeyList>
@FormUrlEncoded
@POST("/key/remove")
suspend fun keyRemove(@FieldMap data: Map<String, String>): String
@FormUrlEncoded
@POST("/key/rename")
suspend fun keyRename(
@Field("token") token: String,
@Field("id") id: String,
@Field("name") newName: String
): String
// pushkey text desp type:text/image/markdown
@FormUrlEncoded
@POST("/message/push")
suspend fun messagePush(@FieldMap data: Map<String, String>): String
@FormUrlEncoded
@POST("/message/list")
suspend fun messageList(@Field("token") token: String): ReturnData<MessageList>
@FormUrlEncoded
@POST("/message/remove")
suspend fun messageRemove(@Field("token") token: String, @Field("id") id: Int): String
}

View File

@ -0,0 +1,24 @@
package com.pushdeer.common.api.data.request
class DeviceInfo {
var id:Int = 0
var uid:String = ""
var name:String = ""
var type:String = ""
var device_id: String = ""
var is_clip: Int = 0
fun toRequestMap(token:String): Map<String, String> {
return mapOf(
"token" to token,
"name" to name,
"device_id" to device_id,
"is_clip" to is_clip.toString(),
"type" to "android"
)
}
override fun toString(): String {
return "id:$id uid:$uid name:$name type:$type device_id:$device_id is_clip:$is_clip"
}
}

View File

@ -0,0 +1,12 @@
package com.pushdeer.common.api.data.response
import com.pushdeer.common.api.data.request.DeviceInfo
class DeviceInfoList{
var devices:List<DeviceInfo> = emptyList()
override fun toString(): String {
return "devices:$devices"
}
}

View File

@ -0,0 +1,16 @@
package com.pushdeer.common.api.data.response
class Message {
var id = 0
var uid: String? = null
var text: String? = null
var desp: String? = null
var type: String? = null
var pushkey_name: String? = null
var created_at: String? = null
}
class MessageList {
var messages = emptyList<Message>()
}

View File

@ -0,0 +1,20 @@
package com.pushdeer.common.api.data.response
class PushKey {
var id:String = ""
var key: String = ""
var name: String = ""
var created_at = ""
override fun toString(): String {
return "id:$id key:$key name:$name created_at:$created_at"
}
}
class PushKeyList {
var keys: List<PushKey> = emptyList()
override fun toString(): String {
return "keys:$keys"
}
}

View File

@ -0,0 +1,15 @@
package com.pushdeer.common.api.data.response
class ReturnData<T> {
var code: Int = 0
var content: T?=null
var error:String = ""
override fun toString(): String {
return "code:${code} error:${error} content:${content.toString()}"
}
}
class TokenOnly{
var token:String = ""
}

View File

@ -0,0 +1,23 @@
package com.pushdeer.common.api.data.response
class UserInfo {
var id: String = ""
var name: String = ""
var email: String = ""
var app_id: String = ""
var wechat_id: String = ""
var level: Int = 1
var created_at: String = ""
var updated_at: String = ""
override fun toString(): String {
return "id:$id\n" +
"name:$name\n" +
"email:$email\n" +
"app_id:$app_id\n" +
"wechat_id:$wechat_id\n" +
"level:$level\n" +
"created:$created_at\n" +
"updated:$updated_at"
}
}

View File

@ -0,0 +1,17 @@
package com.pushdeer.common
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

View File

@ -11,3 +11,4 @@ rootProject.name = "PushDeer"
include ':app' include ':app'
include ':common' include ':common'
include ':compose' include ':compose'
include ':pushdeercommon'