Page 7 Stage 2

This commit is contained in:
icewithcola 2024-07-25 14:25:13 +08:00
parent da8d47f2b2
commit 4d35371e4a
10 changed files with 231 additions and 28 deletions

View file

@ -10,10 +10,10 @@ android {
defaultConfig {
applicationId = "uk.kagurach.android101"
minSdk = 31
minSdk = 28
targetSdk = 34
versionCode = 160
versionName = "1.6"
versionCode = 172
versionName = "1.7.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@ -89,4 +89,6 @@ dependencies {
implementation(libs.moshi)
implementation(libs.okhttp3)
implementation(libs.ffmpeg.min)
}

View file

@ -5,7 +5,10 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"

View file

@ -60,6 +60,16 @@ public class MainActivity extends KaBaseActivity {
new String[]{Manifest.permission.POST_NOTIFICATIONS},
101);
}
if (ContextCompat
.checkSelfPermission
(MainActivity.this, Manifest.permission.READ_MEDIA_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
textViewAppendString(tv, "Acquiring READ_MEDIA_AUDIO");
ActivityCompat.requestPermissions
(MainActivity.this,
new String[]{Manifest.permission.READ_MEDIA_AUDIO},
102);
}
textViewAppendString(tv, "Check Finished");
textViewAppendString(tv, "提示:常按有惊喜");
}

View file

@ -2,7 +2,12 @@ package uk.kagurach.android101
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.media.AudioAttributes
import android.media.MediaPlayer
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.view.View
import android.view.View.GONE
@ -19,18 +24,27 @@ import androidx.core.view.WindowInsetsCompat
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.arthenica.ffmpegkit.FFmpegKit
import com.arthenica.ffmpegkit.ReturnCode
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import okhttp3.Call
import okhttp3.Callback
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import uk.kagurach.android101.helper.ToastHelper
import java.io.File
import java.io.IOException
import java.io.InputStream
val Context.tgDatastore by preferencesDataStore(name = "tg_settings")
val DefaultAPI = stringPreferencesKey("default_api")
@ -59,6 +73,10 @@ class Page7 : KaBaseActivity() {
findViewById<Button>(R.id.P7SetAPI).setOnClickListener(FrameButtonHandler())
findViewById<ImageButton>(R.id.P7CloseResultButton).setOnClickListener(FrameButtonHandler())
findViewById<ImageButton>(R.id.P7CloseAPIButton).setOnClickListener(FrameButtonHandler())
findViewById<Button>(R.id.P7ListenAudio).setOnClickListener(RecordButtonHandler())
findViewById<Button>(R.id.P7RecordAudio).setOnClickListener(RecordButtonHandler())
findViewById<Button>(R.id.P7SendAudio).setOnClickListener(ButtonHandler(this))
}
inner class ButtonHandler(val activity: Activity) : OnClickListener {
@ -77,12 +95,11 @@ class Page7 : KaBaseActivity() {
when (v.id) {
R.id.P7SaveAndSetAPI -> { // 测试并保存API
tgHandler.testSelf { result ->
if (result.contains("""{"ok":true}""")) {
if (result.contains("\"ok\":true")) {
runBlocking { // 成功
storage.setDefaultAPI(botAPI)
storage.setDefaultID(chatID)
}
ToastHelper.ShowToast("OK", baseContext)
}
runOnUiThread {
resultTextField.text = result
@ -101,6 +118,17 @@ class Page7 : KaBaseActivity() {
}
}
R.id.P7SendAudio ->{ // 发送音频
tgHandler.postAudio(
audioUri,
baseContext,
){result ->
runOnUiThread {
resultTextField.text = result
}
}
}
else -> resultTextField.text = "?"
}
}
@ -121,6 +149,54 @@ class Page7 : KaBaseActivity() {
}
}
}
inner class RecordButtonHandler : OnClickListener {
override fun onClick(v: View) {
when (v.id) {
R.id.P7RecordAudio -> {
val recordIntent = Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
try {
startActivityForResult(recordIntent, 0)
} catch (e : android.content.ActivityNotFoundException){
Log.e("Recorder@Android101",e.printStackTrace().toString())
}
}
R.id.P7ListenAudio -> {
if (audioUri!=Uri.EMPTY){
val mMediaPlayer = MediaPlayer()
mMediaPlayer.reset()
mMediaPlayer.setAudioAttributes(
AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build()
)
mMediaPlayer.setDataSource(baseContext,audioUri)
mMediaPlayer.prepare()
mMediaPlayer.start()
}
}
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
0 -> { // 录音
if (resultCode == RESULT_OK && data != null){
audioUri = data.data
ToastHelper.ShowToast("Saved at ${audioUri}",this)
findViewById<Button>(R.id.P7ListenAudio).isEnabled = true
}
}
}
}
companion object {
var audioUri = Uri.EMPTY
}
}
class telegramHandler(val BOT_KEY: String, val CHATID: String) {
@ -130,20 +206,6 @@ class telegramHandler(val BOT_KEY: String, val CHATID: String) {
fun testSelf(callBack: (String) -> Unit) {
val api = "getMe"
val finalUri = "${basicUri}/${api}"
postData(finalUri, callBack)
}
fun postText(text: String, callBack: (String) -> Unit) {
if (text.isEmpty()) {
callBack("Input Something!!")
}
val api = "sendMessage"
val finalUri = "${basicUri}/${api}?chat_id=${CHATID}&text=${text}"
postData(finalUri, callBack)
}
private fun postData(finalUri: String, callBack: (String) -> Unit) {
val request = Request.Builder().url(finalUri).post("".toRequestBody()).build()
val call = client.newCall(request)
call.enqueue(object : Callback {
@ -159,6 +221,78 @@ class telegramHandler(val BOT_KEY: String, val CHATID: String) {
}
})
}
fun postText(text: String, callBack: (String) -> Unit) {
if (text.isEmpty()) {
callBack("Input Something!!")
}
val api = "sendMessage"
val finalUri = "${basicUri}/${api}?chat_id=${CHATID}&text=${text}"
val request = Request.Builder().url(finalUri).post("".toRequestBody()).build()
val call = client.newCall(request)
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.e("TgHelper", e.printStackTrace().toString())
callBack("${e.printStackTrace()}")
}
override fun onResponse(call: Call, response: Response) {
val responseCode = response.code
val responseBody = response.body?.string()
callBack("${responseCode},${responseBody}")
}
})
}
fun postAudio(audioUri: Uri,context: Context,callBack: (String) -> Unit) {
if (audioUri == Uri.EMPTY){
callBack("Sorry Please Record First")
}
// Copy to local
val inputStream: InputStream = context.contentResolver.openInputStream(audioUri)?:return
val bytes = inputStream.readBytes()
val file = File(context.cacheDir.path + "/source.m4a")
file.writeBytes(bytes)
// Convert to ogg
val session = FFmpegKit.execute("-i ${context.cacheDir.path}/source.m4a -acodec libvorbis -aq 4 -vn -ac 2 -map_metadata 0 -y -f ogg ${context.cacheDir.path}/decoded.ogg")
if (!ReturnCode.isSuccess(session.getReturnCode())) {
// FAILURE
Log.d("FFmpeg@Android101", String.format("Command failed with state %s and rc %s.%s",
session.getState(), session.getReturnCode(), session.getFailStackTrace()))
return
}
val api = "sendVoice"
val finalUri = "${basicUri}/${api}?chat_id=${CHATID}&voice=attach%3A%2F%2Fvoice"
val requestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("voice","decoded.ogg",
RequestBody.create("audio/ogg".toMediaTypeOrNull(),File("${context.cacheDir.path}/decoded.ogg"))
)
.build()
val request = Request.Builder()
.url(finalUri)
.header("Content-Type","multipart/form-data")
.post(requestBody)
.build()
val call = client.newCall(request)
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.e("TgHelper", e.printStackTrace().toString())
callBack("${e.printStackTrace()}")
}
override fun onResponse(call: Call, response: Response) {
val responseCode = response.code
val responseBody = response.body?.string()
callBack("${responseCode},${responseBody}")
}
})
}
}
@ -193,4 +327,4 @@ class TGStorage(val context: Context) {
private val getDefaultIDFlow: Flow<String> = context.tgDatastore.data.map { preferences ->
preferences[DefaultID] ?: ""
}
}
}

View file

@ -17,8 +17,8 @@
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="500dp"
android:layout_height="0dp"
android:layout_weight="6"
>
<TextView
android:id="@+id/tv_hello"
@ -28,6 +28,7 @@
android:textSize="15sp"
android:visibility="visible" />
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="80dp"
@ -38,6 +39,7 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginHorizontal="15dp"
android:text="@string/settings"
/>
@ -49,7 +51,7 @@
android:layout_height="0dp"
android:layout_marginVertical="10dp"
android:layout_marginHorizontal="15dp"
android:layout_weight="1"
android:layout_weight="1.5"
android:text="@string/next_page"
android:enabled="false"
android:backgroundTint="?colorSecondary" />

View file

@ -32,7 +32,9 @@
<View
android:layout_width="match_parent"
android:layout_height="50dp" />
android:layout_height="0dp"
android:layout_weight="1"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -41,7 +43,8 @@
<ScrollView
android:layout_width="match_parent"
android:layout_height="350dp"
android:layout_height="0dp"
android:layout_weight="8"
android:layout_marginStart="10dp">
<TextView
android:layout_width="match_parent"
@ -59,7 +62,8 @@
android:text="@string/ai_ready"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="130dp"
android:layout_height="0dp"
android:layout_weight="3"
android:layout_marginHorizontal="5dp"
android:orientation="horizontal">

View file

@ -32,10 +32,47 @@
android:id="@+id/P7SendMessageButton"
android:layout_width="80dp"
android:layout_height="match_parent"
android:text="send message"
android:text="@string/send_text"
android:textAllCaps="false"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_marginHorizontal="15dp"
android:layout_marginVertical="5dp"
>
<Button
android:id="@+id/P7RecordAudio"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:text="@string/record_audio"
android:textAllCaps="false"
android:backgroundTint="?colorTertiary"
/>
<Button
android:id="@+id/P7ListenAudio"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:text="@string/play"
android:textAllCaps="false"
android:layout_marginHorizontal="10dp"
android:backgroundTint="?colorSecondary"
android:enabled="false"
/>
<Button
android:id="@+id/P7SendAudio"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:text="@string/send"
android:textAllCaps="false"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="80dp"

View file

@ -59,4 +59,8 @@
<string name="show_callback">显示回调</string>
<string name="save_and_test">测试并保存</string>
<string name="require_api">请输入API Key 和 Chat ID</string>
<string name="send_text">发送文字</string>
<string name="record_audio">录音</string>
<string name="send">发送</string>
<string name="play">播放</string>
</resources>

View file

@ -61,4 +61,8 @@
<string name="show_callback">Show Callback</string>
<string name="save_and_test">Save And Test</string>
<string name="require_api">Please Fill API Key and Chat ID</string>
<string name="send_text">Send text</string>
<string name="record_audio">Record Audio</string>
<string name="send">Send</string>
<string name="play">Play</string>
</resources>

View file

@ -24,6 +24,7 @@ datastoreRxjava3 = "1.1.1"
androidxWork = "2.9.0"
okHttp3 = "4.9.3"
moshiJson = "1.15.1"
ffmpegKit = "6.0-2"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@ -58,6 +59,8 @@ androidx-work = { group = "androidx.work", name="work-runtime", version.ref = "a
okhttp3 = { group = "com.squareup.okhttp3", name="okhttp",version.ref="okHttp3"}
moshi = {group="com.squareup.moshi",name="moshi-kotlin",version.ref="moshiJson"}
ffmpeg-min = {group="com.arthenica",name="ffmpeg-kit-full",version.ref="ffmpegKit"}
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }