Merge pull request #6 from Freedom0925/master

add android demo
This commit is contained in:
Him188 2019-11-16 22:39:36 +08:00 committed by GitHub
commit 126a4914eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 365 additions and 69 deletions

View File

@ -1,17 +1,17 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.multiplatform'
id 'kotlin-android-extensions'
}
android {
compileSdkVersion 29
defaultConfig {
applicationId "net.mamoe.mirai.demo"
minSdkVersion 23
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
@ -53,10 +53,11 @@ dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
def anko_version = "0.10.8"
implementation "org.jetbrains.anko:anko-commons:$anko_version"
implementation group: 'io.ktor', name: 'ktor-client-android', version: '1.2.5'
implementation("io.ktor:ktor-client-android:1.2.5")
}

View File

@ -2,9 +2,11 @@
package="net.mamoe.mirai.demo">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:name="net.mamoe.mirai.demo.MyApplication"
android:label="@string/app_name"
android:allowBackup="true"
android:supportsRtl="true"
android:theme="@style/AppTheme">
@ -18,5 +20,6 @@
</intent-filter>
</activity>
<service android:name=".MiraiService"/>
</application>
</manifest>

View File

@ -0,0 +1,11 @@
package net.mamoe.mirai.demo
import android.graphics.Bitmap
interface LoginCallback {
suspend fun onCaptcha(bitmap: Bitmap)
suspend fun onSuccess()
suspend fun onFailed()
suspend fun onMessage(message:String)
}

View File

@ -0,0 +1,121 @@
package net.mamoe.mirai.demo
import android.app.ProgressDialog
import android.app.Service
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import io.ktor.util.cio.writeChannel
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.*
import net.mamoe.mirai.Bot
import net.mamoe.mirai.event.subscribeFriendMessages
import net.mamoe.mirai.login
import net.mamoe.mirai.network.protocol.tim.packet.login.requireSuccess
import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.PlatformLogger
import net.mamoe.mirai.utils.SimpleLogger
import java.io.File
class MainActivity : AppCompatActivity(),LoginCallback {
private lateinit var progressDialog : ProgressDialog
override suspend fun onCaptcha(bitmap: Bitmap) {
withContext(Dispatchers.Main){
ll_captcha.visibility = View.VISIBLE
iv_captcha.setImageBitmap(bitmap)
needCaptcha = true
if (progressDialog.isShowing){
progressDialog.dismiss()
}
}
}
override suspend fun onMessage(message:String) {
withContext(Dispatchers.Main){
msg.text = "${msg.text}\n$message"
}
}
override suspend fun onSuccess() {
withContext(Dispatchers.Main){
Toast.makeText(this@MainActivity,"登录成功",Toast.LENGTH_SHORT).show()
if (progressDialog.isShowing){
progressDialog.dismiss()
}
ll_captcha.visibility = View.GONE
et_pwd.visibility = View.GONE
et_qq.visibility = View.GONE
bt_login.visibility = View.GONE
}
}
override suspend fun onFailed() {
withContext(Dispatchers.Main){
Toast.makeText(this@MainActivity,"登录失败",Toast.LENGTH_SHORT).show()
if (progressDialog.isShowing){
progressDialog.dismiss()
}
}
}
var binder: MiraiService.MiraiBinder? = null
var needCaptcha = false
private val conn = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
binder = service as MiraiService.MiraiBinder?
}
override fun onServiceDisconnected(name: ComponentName?) {
binder = null
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val intent = Intent(this, MiraiService::class.java)
startService(intent)
bindService(intent, conn, Service.BIND_AUTO_CREATE)
progressDialog = ProgressDialog(this)
bt_login.setOnClickListener {
if (!progressDialog.isShowing){
progressDialog.show()
}
binder?.setCallback(this)
if (!needCaptcha){
val qq = et_qq.text.toString().toUInt()
val pwd = et_pwd.text.toString()
binder?.startLogin(qq, pwd)
}else{
val captcha = et_captcha.text.toString()
binder?.setCaptcha(captcha)
}
}
}
override fun onDestroy() {
super.onDestroy()
unbindService(conn)
}
}

View File

@ -0,0 +1,159 @@
package net.mamoe.mirai.demo
import android.app.Service
import android.content.Intent
import android.graphics.BitmapFactory
import android.os.Binder
import android.os.IBinder
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.event.subscribeMessages
import net.mamoe.mirai.login
import net.mamoe.mirai.message.Image
import net.mamoe.mirai.network.protocol.tim.packet.event.GroupMessage
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import java.lang.ref.WeakReference
class MiraiService : Service() {
private var mCaptchaDeferred: CompletableDeferred<String>? = null
private var mBot: Bot? = null
private var mCaptcha = ""
set(value) {
field = value
mCaptchaDeferred?.complete(value)
}
private var mBinder: MiraiBinder? = null
private var mCallback: WeakReference<LoginCallback>? = null
override fun onCreate() {
super.onCreate()
mBinder = MiraiBinder()
}
private fun login(qq: UInt, pwd: String) {
GlobalScope.launch {
mBot = Bot(qq, pwd).apply {
val loginResult = login {
captchaSolver = {
val byteArray = byteArrayOf()
it.readFully(byteArray, 0, it.writeRemaining)
val bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
mCallback?.get()?.onCaptcha(bitmap)
mCaptchaDeferred?.await()
}
}
if (loginResult == LoginResult.SUCCESS) {
mCallback?.get()?.onSuccess()
} else {
mCallback?.get()?.onFailed()
}
}
mBot!!.subscribeMessages {
content({ true }) {
mCallback?.get()?.onMessage("收到来自${sender.id}的消息")
}
// 当接收到消息 == "你好" 时就回复 "你好!"
"你好" reply "你好!"
// 当消息 == "查看 subject" 时, 执行 lambda
case("查看 subject") {
if (subject is QQ) {
reply("消息主体为 QQ, 你在跟发私聊消息")
} else {
reply("消息主体为 Group, 你在群里发消息")
}
// 在回复的时候, 一般使用 subject 来作为回复对象.
// 因为当群消息时, subject 为这个群.
// 当好友消息时, subject 为这个好友.
// 所有在 MessagePacket(也就是此时的 this 指代的对象) 中实现的扩展方法, 如刚刚的 "reply", 都是以 subject 作为目标
}
// 当消息里面包含这个类型的消息时
has<Image> {
// this: MessagePacket
// message: MessageChain
// sender: QQ
// it: String (MessageChain.toString)
if (this is GroupMessage) {
//如果是群消息
// group: Group
this.group.sendMessage("你在一个群里")
// 等同于 reply("你在一个群里")
}
reply("图片, ID= ${message[Image].id}")//获取第一个 Image 类型的消息
reply(message)
}
"123" containsReply "你的消息里面包含 123"
// 当收到 "我的qq" 就执行 lambda 并回复 lambda 的返回值 String
"我的qq" reply { sender.id.toString() }
// 当消息前缀为 "我是" 时
startsWith("我是", removePrefix = true) {
// it: 删除了消息前缀 "我是" 后的消息
// 如一条消息为 "我是张三", 则此时的 it 为 "张三".
reply("你是$it")
}
// 当消息中包含 "复读" 时
contains("复读") {
reply(message)
}
// 自定义的 filter, filter 中 it 为转为 String 的消息.
// 也可以用任何能在处理时使用的变量, 如 subject, sender, message
content({ it.length == 3 }) {
reply("你发送了长度为 3 的消息")
}
}
}
}
override fun onBind(intent: Intent?): IBinder? {
return mBinder
}
inner class MiraiBinder : Binder() {
fun startLogin(qq: UInt, pwd: String) {
login(qq, pwd)
}
fun setCaptcha(captcha: String) {
mCaptcha = captcha
}
fun setCallback(callback: LoginCallback) {
mCallback = WeakReference(callback)
}
}
}

View File

@ -1,57 +0,0 @@
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.demo
import android.app.Application
import android.os.Bundle
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity
import net.mamoe.mirai.Bot
import net.mamoe.mirai.event.subscribeFriendMessages
import net.mamoe.mirai.login
import net.mamoe.mirai.network.protocol.tim.packet.login.requireSuccess
import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.PlatformLogger
import net.mamoe.mirai.utils.SimpleLogger
import java.io.ByteArrayOutputStream
import java.io.PrintStream
import kotlin.properties.Delegates
@Suppress("unused")
private val Throwable.stacktraceString: String
get() = ByteArrayOutputStream().also { printStackTrace(PrintStream(it)) }.toString()
class MyApplication : Application()
class MainActivity : AppCompatActivity() {
private var rootLayout: LinearLayout by Delegates.notNull()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
rootLayout = findViewById(R.id.main_view)
}
private suspend fun initializeBot(qq: UInt, password: String) {
DefaultLogger = {
PlatformLogger(it) + SimpleLogger { message, e ->
// TODO: 2019/11/6
}
}
val bot = Bot(qq, password).apply {
login {
captchaSolver = {
"ABCD"
}
}.requireSuccess()
}
bot.subscribeFriendMessages {
"Hello" reply "Hello Mirai!"
}
}
}

View File

@ -1,8 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"/>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="46dp"
android:inputType="number"
android:layout_marginTop="10dp"
android:ems="15"
android:hint="请输入QQ号"
android:id="@+id/et_qq" />
<EditText
android:layout_width="match_parent"
android:layout_height="46dp"
android:inputType="textPassword"
android:hint="请输入密码"
android:layout_marginTop="10dp"
android:id="@+id/et_pwd" />
<LinearLayout
android:id="@+id/ll_captcha"
android:layout_marginTop="5dp"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/et_captcha"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="46dp"
android:hint="输入验证码"/>
<ImageView
android:id="@+id/iv_captcha"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="5dp"
android:scaleType="fitXY"/>
</LinearLayout>
<Button
android:text="登录"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:id="@+id/bt_login"
android:background="#3F51B5"
android:textColor="#FFFFFF"/>
<TextView
android:id="@+id/msg"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Mirai</string>
</resources>