キャラセリフ稼働可能
This commit is contained in:
@@ -1,24 +1,52 @@
|
|||||||
package com.example.curation_train_app
|
package com.example.curation_train_app
|
||||||
|
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import org.json.JSONArray
|
||||||
|
|
||||||
object AiReplyParser {
|
object AiReplyParser {
|
||||||
|
|
||||||
fun parse(json: String): AiReply {
|
fun extractTextFromResponse(json: String): String {
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
|
// 配列としてパース
|
||||||
|
val arr = JSONObject("{\"a\":$json}").getJSONArray("a")
|
||||||
|
val obj = arr.getJSONObject(0)
|
||||||
|
|
||||||
val obj = JSONObject(json)
|
// textだけ取得
|
||||||
|
var text = obj.getString("text")
|
||||||
|
|
||||||
// GPT から返ってくるのは「text」だけ
|
// ---- 余計なものを完全除去 ----
|
||||||
val text = obj.optString("text", "(解析エラー)")
|
text = text
|
||||||
|
.replace("text\":", "") // "text":
|
||||||
|
.replace("\"text\":\"", "") // "text":"
|
||||||
|
.replace("{\"text\":\"", "") // {"text":"
|
||||||
|
.replace("\"}", "") // "}
|
||||||
|
.replace("}", "") // }
|
||||||
|
.replace("{", "") // {
|
||||||
|
.replace("\\n", "") // 改行タグ
|
||||||
|
.replace("\n", "")
|
||||||
|
.replace("emotion", "") // emotion行
|
||||||
|
.replace("normal", "")
|
||||||
|
.replace("happy", "")
|
||||||
|
.replace("angry", "")
|
||||||
|
.replace("surprised", "")
|
||||||
|
.replace("confused", "")
|
||||||
|
.replace("sleepy", "")
|
||||||
|
.replace("worry", "")
|
||||||
|
.replace("think", "")
|
||||||
|
.replace("“", "")
|
||||||
|
.replace("”", "")
|
||||||
|
.replace("\"", "")
|
||||||
|
.replace(",", "")
|
||||||
|
.replace(":", "")
|
||||||
|
|
||||||
// emotion は入っていないため、常に NORMAL を返す
|
.trim()
|
||||||
val emotion = EmotionType.NORMAL
|
|
||||||
|
|
||||||
AiReply(text, emotion)
|
text
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
AiReply("(JSON解析エラー)", EmotionType.NORMAL)
|
json
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,4 +20,7 @@ class AkaneResponder : CharacterResponder {
|
|||||||
"なんか気になる情報があるよねっ!気をつけていこーっ!"
|
"なんか気になる情報があるよねっ!気をつけていこーっ!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun reply(text: String): String = respond(text, InfoType.NORMAL)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,19 +25,15 @@ class CharacterReplyManager {
|
|||||||
?: return "キャラが見つかりません。"
|
?: return "キャラが見つかりません。"
|
||||||
return responder.respond(info, type)
|
return responder.respond(info, type)
|
||||||
}
|
}
|
||||||
|
fun replyWithAI(characterId: String, info: String, type: InfoType): String {
|
||||||
fun replyWithAI(characterId: String, info: String, type: InfoType): AiReply {
|
|
||||||
val character = CharacterProfiles.getProfile(characterId)
|
val character = CharacterProfiles.getProfile(characterId)
|
||||||
?: return AiReply("(エラー:キャラが不明です)", EmotionType.NORMAL)
|
?: return "(エラー:キャラが不明です)"
|
||||||
|
|
||||||
|
|
||||||
val prompt = PromptBuilder.buildPrompt(character, info, type)
|
val prompt = PromptBuilder.buildPrompt(character, info, type)
|
||||||
|
|
||||||
val ai = AiClient(apiKey = API_KEY)
|
val ai = AiClient("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")
|
||||||
val raw = ai.requestCharacterReply(prompt)
|
return ai.requestCharacterReply(prompt)
|
||||||
|
|
||||||
// JSON パース
|
|
||||||
return AiReplyParser.parse(raw)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,172 @@ package com.example.curation_train_app
|
|||||||
object DummyLines {
|
object DummyLines {
|
||||||
|
|
||||||
fun linesOf(region: String): List<String> {
|
fun linesOf(region: String): List<String> {
|
||||||
return when (region) {
|
return when(region) {
|
||||||
"北海道" -> listOf("JR北海道全線", "札幌市営地下鉄", "道南いさりび鉄道")
|
|
||||||
"東北" -> listOf("JR東日本(東北)", "仙台市地下鉄", "青い森鉄道")
|
// -------------------------
|
||||||
"関東" -> listOf("JR東日本(関東)", "東京メトロ", "京急", "東急", "小田急", "京王")
|
// 北海道
|
||||||
"中部" -> listOf("JR東海", "名鉄", "近鉄(名古屋)")
|
// -------------------------
|
||||||
"関西" -> listOf("JR西日本(関西)", "大阪メトロ", "阪急", "阪神", "京阪", "南海", "近鉄(関西)")
|
"hokkaido" -> listOf(
|
||||||
"中国" -> listOf("JR西日本(中国)", "広島電鉄", "一畑電車")
|
"JR北海道(函館本線)",
|
||||||
"四国" -> listOf("JR四国")
|
"JR北海道(千歳線)",
|
||||||
"九州" -> listOf("JR九州", "西鉄")
|
"JR北海道(札沼線・学園都市線)",
|
||||||
|
"JR北海道(根室本線)",
|
||||||
|
"JR北海道(宗谷本線)",
|
||||||
|
"札幌市営地下鉄 南北線",
|
||||||
|
"札幌市営地下鉄 東西線",
|
||||||
|
"札幌市営地下鉄 東豊線"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -------------------------
|
||||||
|
// 東北
|
||||||
|
// -------------------------
|
||||||
|
"tohoku" -> listOf(
|
||||||
|
"JR東日本(東北本線)",
|
||||||
|
"JR東日本(常磐線)",
|
||||||
|
"JR東日本(奥羽本線)",
|
||||||
|
"JR東日本(仙山線)",
|
||||||
|
"JR東日本(磐越西線)",
|
||||||
|
"仙台市地下鉄 南北線",
|
||||||
|
"仙台市地下鉄 東西線"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -------------------------
|
||||||
|
// 関東
|
||||||
|
// -------------------------
|
||||||
|
"kanto" -> listOf(
|
||||||
|
// JR
|
||||||
|
"JR東日本(山手線)",
|
||||||
|
"JR東日本(中央線快速)",
|
||||||
|
"JR東日本(総武線各停)",
|
||||||
|
"JR東日本(京浜東北線)",
|
||||||
|
"JR東日本(常磐線)",
|
||||||
|
"JR東日本(埼京線)",
|
||||||
|
"JR東日本(横須賀線)",
|
||||||
|
"JR東日本(湘南新宿ライン)",
|
||||||
|
"JR東日本(京葉線)",
|
||||||
|
"JR東日本(武蔵野線)",
|
||||||
|
|
||||||
|
// 私鉄
|
||||||
|
"東京メトロ(丸ノ内線)",
|
||||||
|
"東京メトロ(東西線)",
|
||||||
|
"東京メトロ(日比谷線)",
|
||||||
|
"東京メトロ(半蔵門線)",
|
||||||
|
"東京メトロ(南北線)",
|
||||||
|
"都営地下鉄(浅草線)",
|
||||||
|
"都営地下鉄(三田線)",
|
||||||
|
"東武東上線",
|
||||||
|
"東武伊勢崎線(スカイツリーライン)",
|
||||||
|
"西武池袋線",
|
||||||
|
"西武新宿線",
|
||||||
|
"小田急小田原線",
|
||||||
|
"京王京王線",
|
||||||
|
"東急東横線",
|
||||||
|
"東急田園都市線",
|
||||||
|
"京急本線",
|
||||||
|
"相鉄本線",
|
||||||
|
"つくばエクスプレス"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -------------------------
|
||||||
|
// 中部(東海)
|
||||||
|
// -------------------------
|
||||||
|
"chubu" -> listOf(
|
||||||
|
// JR
|
||||||
|
"JR東海(東海道本線 静岡)",
|
||||||
|
"JR東海(東海道本線 名古屋)",
|
||||||
|
"JR東海(中央本線)",
|
||||||
|
"JR東海(関西本線)",
|
||||||
|
"JR東海(高山本線)",
|
||||||
|
"JR東海(御殿場線)",
|
||||||
|
|
||||||
|
// 名古屋の私鉄
|
||||||
|
"名古屋市営地下鉄 東山線",
|
||||||
|
"名古屋市営地下鉄 名城線",
|
||||||
|
"名古屋市営地下鉄 桜通線",
|
||||||
|
"名鉄名古屋本線",
|
||||||
|
"名鉄犬山線",
|
||||||
|
"名鉄常滑線",
|
||||||
|
"近鉄名古屋線"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------
|
||||||
|
// 関西(あなたの本命)
|
||||||
|
// -------------------------
|
||||||
|
"kansai" -> listOf(
|
||||||
|
// JR西日本
|
||||||
|
"JR西日本(阪和線)",
|
||||||
|
"JR西日本(大阪環状線)",
|
||||||
|
"JR西日本(大和路線)",
|
||||||
|
"JR西日本(関空・紀州路快速)",
|
||||||
|
"JR西日本(京都線)",
|
||||||
|
"JR西日本(神戸線)",
|
||||||
|
"JR西日本(宝塚線)",
|
||||||
|
"JR西日本(琵琶湖線)",
|
||||||
|
|
||||||
|
// 南海
|
||||||
|
"南海本線",
|
||||||
|
"南海空港線",
|
||||||
|
"南海高野線",
|
||||||
|
"南海加太線",
|
||||||
|
|
||||||
|
// 泉北高速
|
||||||
|
"泉北高速鉄道線",
|
||||||
|
|
||||||
|
// Osaka Metro
|
||||||
|
"Osaka Metro 御堂筋線",
|
||||||
|
"Osaka Metro 四つ橋線",
|
||||||
|
"Osaka Metro 谷町線",
|
||||||
|
"Osaka Metro 堺筋線",
|
||||||
|
|
||||||
|
// 私鉄大手
|
||||||
|
"近鉄奈良線",
|
||||||
|
"近鉄大阪線",
|
||||||
|
"京阪本線",
|
||||||
|
"阪急京都線",
|
||||||
|
"阪急神戸線",
|
||||||
|
"阪急宝塚線",
|
||||||
|
"阪神本線",
|
||||||
|
"阪神なんば線"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -------------------------
|
||||||
|
// 中国
|
||||||
|
// -------------------------
|
||||||
|
"chugoku" -> listOf(
|
||||||
|
"JR西日本(山陽本線)",
|
||||||
|
"JR西日本(可部線)",
|
||||||
|
"JR西日本(伯備線)",
|
||||||
|
"広島電鉄(市内線)",
|
||||||
|
"岡山電気軌道",
|
||||||
|
"一畑電車"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------
|
||||||
|
// 四国
|
||||||
|
// -------------------------
|
||||||
|
"shikoku" -> listOf(
|
||||||
|
"JR四国(予讃線)",
|
||||||
|
"JR四国(高徳線)",
|
||||||
|
"JR四国(徳島線)",
|
||||||
|
"JR四国(土讃線)"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -------------------------
|
||||||
|
// 九州
|
||||||
|
// -------------------------
|
||||||
|
"kyushu" -> listOf(
|
||||||
|
"JR九州(鹿児島本線)",
|
||||||
|
"JR九州(長崎本線)",
|
||||||
|
"JR九州(日豊本線)",
|
||||||
|
"JR九州(久大本線)",
|
||||||
|
"福岡市地下鉄 空港線",
|
||||||
|
"福岡市地下鉄 箱崎線",
|
||||||
|
"福岡市地下鉄 七隈線",
|
||||||
|
"西鉄天神大牟田線"
|
||||||
|
)
|
||||||
|
|
||||||
else -> listOf("該当する路線がありません")
|
else -> listOf("該当する路線がありません")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,4 +14,15 @@ object FollowSettings {
|
|||||||
val prefs = context.getSharedPreferences("follow", Context.MODE_PRIVATE)
|
val prefs = context.getSharedPreferences("follow", Context.MODE_PRIVATE)
|
||||||
prefs.edit().putStringSet("lines", lines.toSet()).apply()
|
prefs.edit().putStringSet("lines", lines.toSet()).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun loadNotifySettings(context: Context): Set<String> {
|
||||||
|
val prefs = context.getSharedPreferences("follow", Context.MODE_PRIVATE)
|
||||||
|
return prefs.getStringSet("notify", emptySet()) ?: emptySet()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveNotifySettings(context: Context, types: Set<String>) {
|
||||||
|
val prefs = context.getSharedPreferences("follow", Context.MODE_PRIVATE)
|
||||||
|
prefs.edit().putStringSet("notify", types).apply()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,4 +20,6 @@ class HiyoriResponder : CharacterResponder {
|
|||||||
"気になったことがあるの?よかったら教えてね。いっしょに見てみるよ。"
|
"気になったことがあるの?よかったら教えてね。いっしょに見てみるよ。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun reply(text: String): String = respond(text, InfoType.NORMAL)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ enum class InfoType {
|
|||||||
EVENT, // イベント・臨時列車
|
EVENT, // イベント・臨時列車
|
||||||
WEATHER, // 天候影響
|
WEATHER, // 天候影響
|
||||||
NEW_TRAIN, // 新型車両関連
|
NEW_TRAIN, // 新型車両関連
|
||||||
OTHER // 分類できない場合
|
OTHER, // 分類できない場合
|
||||||
|
|
||||||
|
NORMAL
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.widget.TextView
|
|||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
|
import android.widget.CheckBox
|
||||||
|
|
||||||
class LinePriorityActivity : ComponentActivity() {
|
class LinePriorityActivity : ComponentActivity() {
|
||||||
|
|
||||||
@@ -34,6 +35,20 @@ class LinePriorityActivity : ComponentActivity() {
|
|||||||
|
|
||||||
val boxes = listOf(box1, box2, box3, box4, box5, box6)
|
val boxes = listOf(box1, box2, box3, box4, box5, box6)
|
||||||
|
|
||||||
|
val saved = FollowSettings.loadNotifySettings(this)
|
||||||
|
|
||||||
|
val cbDelay = findViewById<CheckBox>(R.id.checkboxDelay)
|
||||||
|
val cbOuting = findViewById<CheckBox>(R.id.checkboxOuting)
|
||||||
|
val cbCampaign = findViewById<CheckBox>(R.id.checkboxCampaign)
|
||||||
|
val cbOther = findViewById<CheckBox>(R.id.checkboxOther)
|
||||||
|
|
||||||
|
// 保存済みの状態を反映
|
||||||
|
cbDelay.isChecked = saved.contains("delay")
|
||||||
|
cbOuting.isChecked = saved.contains("outing")
|
||||||
|
cbCampaign.isChecked = saved.contains("campaign")
|
||||||
|
cbOther.isChecked = saved.contains("other")
|
||||||
|
|
||||||
|
|
||||||
// ▼▼ 空き枠を探して路線名をセット ▼▼
|
// ▼▼ 空き枠を探して路線名をセット ▼▼
|
||||||
for (box in boxes) {
|
for (box in boxes) {
|
||||||
val t = box.text.toString()
|
val t = box.text.toString()
|
||||||
@@ -49,4 +64,25 @@ class LinePriorityActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
// ▲▲ 空き枠を探してセット ▲▲
|
// ▲▲ 空き枠を探してセット ▲▲
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
|
||||||
|
val selected = mutableSetOf<String>()
|
||||||
|
|
||||||
|
if (findViewById<CheckBox>(R.id.checkboxDelay).isChecked)
|
||||||
|
selected.add("delay")
|
||||||
|
|
||||||
|
if (findViewById<CheckBox>(R.id.checkboxOuting).isChecked)
|
||||||
|
selected.add("outing")
|
||||||
|
|
||||||
|
if (findViewById<CheckBox>(R.id.checkboxCampaign).isChecked)
|
||||||
|
selected.add("campaign")
|
||||||
|
|
||||||
|
if (findViewById<CheckBox>(R.id.checkboxOther).isChecked)
|
||||||
|
selected.add("other")
|
||||||
|
|
||||||
|
FollowSettings.saveNotifySettings(this, selected)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.example.curation_train_app
|
|||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.widget.Button
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
@@ -13,6 +14,8 @@ class LineSelectActivity : ComponentActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_line_select)
|
setContentView(R.layout.activity_line_select)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ★ 地域名を受け取る
|
// ★ 地域名を受け取る
|
||||||
val region = intent.getStringExtra("region") ?: "未指定"
|
val region = intent.getStringExtra("region") ?: "未指定"
|
||||||
|
|
||||||
@@ -31,6 +34,19 @@ class LineSelectActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onLineSelected(id: String) {
|
||||||
|
val lines = FollowSettings.loadLines(this).toMutableList()
|
||||||
|
|
||||||
|
if (!lines.contains(id)) {
|
||||||
|
lines.add(id)
|
||||||
|
FollowSettings.saveLines(this, lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 優先度設定画面へ遷移
|
// 優先度設定画面へ遷移
|
||||||
private fun openPriorityScreen(line: String) {
|
private fun openPriorityScreen(line: String) {
|
||||||
val intent = Intent(this, LinePriorityActivity::class.java)
|
val intent = Intent(this, LinePriorityActivity::class.java)
|
||||||
|
|||||||
@@ -15,126 +15,40 @@ import retrofit2.Retrofit
|
|||||||
import retrofit2.converter.scalars.ScalarsConverterFactory
|
import retrofit2.converter.scalars.ScalarsConverterFactory
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
|
private var lastTrafficStatus: String = ""
|
||||||
|
private var rssList: List<NewsItem> = emptyList()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
|
|
||||||
// --- キャラ初期画像 ---
|
// --- キャラ初期画像 ---
|
||||||
val charId = CharacterSettings.loadCharacter(this)
|
setInitialCharacterImage()
|
||||||
val imageView = findViewById<ImageView>(R.id.imageCharacter)
|
|
||||||
|
|
||||||
val drawableId = when (charId) {
|
// --- 運行情報 & RSS ---
|
||||||
"reimu" -> R.drawable.reimu_normal
|
|
||||||
"marisa" -> R.drawable.marisa_normal
|
|
||||||
"flandre" -> R.drawable.flandre_normal
|
|
||||||
"sanae" -> R.drawable.sanae_normal
|
|
||||||
"akane" -> R.drawable.akane_normal
|
|
||||||
"momoka" -> R.drawable.momoka_normal
|
|
||||||
"sayaka" -> R.drawable.sayaka_normal
|
|
||||||
"hiyori" -> R.drawable.hiyori_normal
|
|
||||||
else -> R.drawable.reimu_normal
|
|
||||||
}
|
|
||||||
imageView.setImageResource(drawableId)
|
|
||||||
|
|
||||||
// ----- キャラ応答ボタン -----
|
|
||||||
val btn = findViewById<Button>(R.id.btnTestReply)
|
|
||||||
val inputText = findViewById<EditText>(R.id.inputText)
|
|
||||||
val commentView = findViewById<TextView>(R.id.textCharacterComment)
|
|
||||||
|
|
||||||
btn.setOnClickListener {
|
|
||||||
val text = inputText.text.toString()
|
|
||||||
val charId = CharacterSettings.loadCharacter(this)
|
|
||||||
val infoType = InfoClassifier.classify(text)
|
|
||||||
|
|
||||||
Thread {
|
|
||||||
val result = CharacterReplyManager().replyWithAI(charId, text, infoType)
|
|
||||||
runOnUiThread {
|
|
||||||
commentView.text = result.text
|
|
||||||
updateCharacterExpression(charId, result.emotion)
|
|
||||||
}
|
|
||||||
}.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- 運行情報 & RSS 読み込み ---
|
|
||||||
loadTraffic()
|
loadTraffic()
|
||||||
loadRss()
|
loadRss()
|
||||||
|
|
||||||
// --- RecyclerView 初期設定 ---
|
// RecyclerView
|
||||||
val recyclerNews = findViewById<RecyclerView>(R.id.recyclerNews)
|
val recyclerNews = findViewById<RecyclerView>(R.id.recyclerNews)
|
||||||
recyclerNews.layoutManager = LinearLayoutManager(this)
|
recyclerNews.layoutManager = LinearLayoutManager(this)
|
||||||
|
|
||||||
// 路線設定画面へ遷移
|
// 路線設定
|
||||||
val btnOpenRegion = findViewById<TextView>(R.id.textFavorite)
|
findViewById<TextView>(R.id.textFavorite).setOnClickListener {
|
||||||
btnOpenRegion.setOnClickListener {
|
startActivity(Intent(this, RegionSelectActivity::class.java))
|
||||||
val intent = Intent(this, RegionSelectActivity::class.java)
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
applyCharacterLayoutDefault()
|
applyCharacterLayoutDefault()
|
||||||
|
|
||||||
// 詳細画面へ遷移
|
|
||||||
val titleView = findViewById<TextView>(R.id.textTitle)
|
|
||||||
titleView.setOnClickListener {
|
|
||||||
val text = titleView.tag as? String ?: "(詳細情報なし)"
|
|
||||||
val intent = Intent(this, TrafficDetailActivity::class.java)
|
|
||||||
intent.putExtra("text", text)
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------
|
// =========================================================
|
||||||
|
// 運行情報ロード
|
||||||
private fun applyCharacterLayoutDefault() {
|
// =========================================================
|
||||||
val img = findViewById<ImageView>(R.id.imageCharacter)
|
|
||||||
val scale = resources.displayMetrics.density
|
|
||||||
|
|
||||||
val widthDp = 110
|
|
||||||
val heightDp = 110
|
|
||||||
val offsetXdp = -8
|
|
||||||
val offsetYdp = -18
|
|
||||||
|
|
||||||
img.layoutParams.width = (widthDp * scale).toInt()
|
|
||||||
img.layoutParams.height = (heightDp * scale).toInt()
|
|
||||||
|
|
||||||
val lp = img.layoutParams as ViewGroup.MarginLayoutParams
|
|
||||||
lp.marginStart = (offsetXdp * scale).toInt()
|
|
||||||
lp.topMargin = (offsetYdp * scale).toInt()
|
|
||||||
|
|
||||||
img.layoutParams = lp
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------
|
|
||||||
|
|
||||||
private fun loadRss() {
|
|
||||||
Thread {
|
|
||||||
try {
|
|
||||||
val api = RssApi()
|
|
||||||
val feed = api.loadRss()
|
|
||||||
|
|
||||||
val list = feed?.channel?.items?.map { item ->
|
|
||||||
NewsItem(
|
|
||||||
company = "JR西日本",
|
|
||||||
title = item.title ?: "",
|
|
||||||
body = item.description ?: ""
|
|
||||||
)
|
|
||||||
} ?: emptyList()
|
|
||||||
|
|
||||||
runOnUiThread {
|
|
||||||
val recyclerNews = findViewById<RecyclerView>(R.id.recyclerNews)
|
|
||||||
recyclerNews.adapter = NewsAdapter(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------
|
|
||||||
|
|
||||||
private fun loadTraffic() {
|
private fun loadTraffic() {
|
||||||
Thread {
|
Thread {
|
||||||
try {
|
try {
|
||||||
@@ -144,27 +58,19 @@ class MainActivity : ComponentActivity() {
|
|||||||
.build()
|
.build()
|
||||||
|
|
||||||
val client = retrofit.create(HtmlClient::class.java)
|
val client = retrofit.create(HtmlClient::class.java)
|
||||||
val response = client.getHtml().execute()
|
val html = client.getHtml().execute().body()?.string() ?: return@Thread
|
||||||
val html = response.body()?.string() ?: return@Thread
|
|
||||||
|
|
||||||
val doc = Jsoup.parse(html)
|
val doc = Jsoup.parse(html)
|
||||||
val lines = doc.select("div.jisyo")
|
|
||||||
|
|
||||||
var hasDelay = false
|
var hasDelay = false
|
||||||
var hasSuspension = false
|
var hasSuspension = false
|
||||||
var hasInfo = false
|
var hasInfo = false
|
||||||
|
|
||||||
val items = mutableListOf<NewsItem>()
|
val lines = doc.select("div.jisyo")
|
||||||
|
|
||||||
for (line in lines) {
|
for (line in lines) {
|
||||||
val title = line.select("h2").text()
|
val c = line.classNames()
|
||||||
val body = line.select("p.gaiyo").text()
|
if (c.contains("stop")) hasSuspension = true
|
||||||
val classes = line.classNames()
|
if (c.contains("delay")) hasDelay = true
|
||||||
|
if (c.contains("info")) hasInfo = true
|
||||||
if (classes.contains("stop")) hasSuspension = true
|
|
||||||
if (classes.contains("delay")) hasDelay = true
|
|
||||||
if (classes.contains("info")) hasInfo = true
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val statusText = when {
|
val statusText = when {
|
||||||
@@ -174,11 +80,28 @@ class MainActivity : ComponentActivity() {
|
|||||||
else -> "平常運転"
|
else -> "平常運転"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastTrafficStatus = statusText
|
||||||
|
val infoText = "現在の運行状況:$statusText"
|
||||||
|
|
||||||
|
// UI 反映
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
val statusView = findViewById<TextView>(R.id.trafficStatus)
|
findViewById<TextView>(R.id.trafficStatus).text =
|
||||||
statusView.text = "設定した路線:$statusText"
|
"設定した路線:$statusText"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 平常時は RSS の情報からコメント生成 =====
|
||||||
|
if (lastTrafficStatus == "平常運転") {
|
||||||
|
if (rssList.isNotEmpty()) {
|
||||||
|
val importantItem = pickImportantRssItem(rssList)
|
||||||
|
|
||||||
|
if (importantItem != null) {
|
||||||
|
val infoText = "${importantItem.title}\n${importantItem.body}"
|
||||||
|
generateAutoCommentFromInfo(infoText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 遅延・見合わせは優先
|
||||||
|
generateAutoCommentFromInfo(infoText)
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -187,33 +110,42 @@ class MainActivity : ComponentActivity() {
|
|||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------
|
// =========================================================
|
||||||
|
// RSSロード
|
||||||
|
// =========================================================
|
||||||
|
private fun loadRss() {
|
||||||
|
Thread {
|
||||||
|
try {
|
||||||
|
val api = RssApi()
|
||||||
|
val feed = api.loadRss()
|
||||||
|
|
||||||
fun emotionToExpression(type: EmotionType): String {
|
rssList = feed?.channel?.items?.map { item ->
|
||||||
return when (type) {
|
NewsItem(
|
||||||
EmotionType.HAPPY -> "happy"
|
company = "JR西日本",
|
||||||
EmotionType.ANGRY -> "angry"
|
title = item.title ?: "",
|
||||||
EmotionType.SLEEPY -> "sleepy"
|
body = item.description ?: ""
|
||||||
EmotionType.SURPRISED -> "surprised"
|
)
|
||||||
EmotionType.CONFUSED -> "confused"
|
} ?: emptyList()
|
||||||
EmotionType.THINK -> "think"
|
|
||||||
EmotionType.WORRY -> "worry"
|
runOnUiThread {
|
||||||
else -> "normal"
|
val recycler = findViewById<RecyclerView>(R.id.recyclerNews)
|
||||||
|
recycler.adapter = NewsAdapter(rssList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 平常運転のとき → RSSの重要情報でコメント生成
|
||||||
|
if (lastTrafficStatus == "平常運転" && rssList.isNotEmpty()) {
|
||||||
|
val importantItem = pickImportantRssItem(rssList)
|
||||||
|
|
||||||
|
if (importantItem != null) {
|
||||||
|
val infoText = "${importantItem.title}\n${importantItem.body}"
|
||||||
|
generateAutoCommentFromInfo(infoText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateCharacterExpression(charId: String, type: EmotionType) {
|
} catch (e: Exception) {
|
||||||
val imageView = findViewById<ImageView>(R.id.imageCharacter)
|
e.printStackTrace()
|
||||||
val expression = emotionToExpression(type)
|
|
||||||
val resName = "${charId}_${expression}"
|
|
||||||
val resId = resources.getIdentifier(resName, "drawable", packageName)
|
|
||||||
|
|
||||||
if (resId != 0)
|
|
||||||
imageView.setImageResource(resId)
|
|
||||||
else {
|
|
||||||
val normalId = resources.getIdentifier("${charId}_normal", "drawable", packageName)
|
|
||||||
imageView.setImageResource(normalId)
|
|
||||||
}
|
}
|
||||||
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
@@ -235,7 +167,123 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
imageView.setImageResource(drawableId)
|
imageView.setImageResource(drawableId)
|
||||||
|
|
||||||
|
// ---- キャラ切り替え後、自動で最新コメントを再生成 ----
|
||||||
|
// ---- キャラ切り替え後、自動で最新コメントを再生成 ----
|
||||||
|
if (lastTrafficStatus == "平常運転" && rssList.isNotEmpty()) {
|
||||||
|
|
||||||
|
// ★ここを変更(ランダム or 後でフィルタできるように)
|
||||||
|
val item = pickRandomRssItem(rssList)
|
||||||
|
|
||||||
|
if (item != null) {
|
||||||
|
val infoText = "${item.title}\n${item.body}"
|
||||||
|
generateAutoCommentFromInfo(infoText)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 遅延・運転見合わせならその情報を優先
|
||||||
|
val infoText = "現在の運行状況: $lastTrafficStatus"
|
||||||
|
generateAutoCommentFromInfo(infoText)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun classifyRssType(title: String, body: String): String {
|
||||||
|
return when {
|
||||||
|
title.contains("遅れ") || title.contains("運転") -> "delay"
|
||||||
|
title.contains("イベント") || body.contains("イベント") -> "outing"
|
||||||
|
title.contains("キャンペーン") -> "campaign"
|
||||||
|
else -> "other"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateExpressionByKeyword(text: String) {
|
||||||
|
val imageView = findViewById<ImageView>(R.id.imageCharacter)
|
||||||
|
val charId = CharacterSettings.loadCharacter(this)
|
||||||
|
|
||||||
|
val isDelay = text.contains("遅延") || text.contains("遅れ")
|
||||||
|
val isStop = text.contains("運転見合わせ") || text.contains("運休")
|
||||||
|
|
||||||
|
val expression = when {
|
||||||
|
isStop -> "angry" // 運転見合わせ=怒り
|
||||||
|
isDelay -> "worry" // 遅延=困り顔
|
||||||
|
else -> "normal"
|
||||||
|
}
|
||||||
|
|
||||||
|
val resId = resources.getIdentifier("${charId}_${expression}", "drawable", packageName)
|
||||||
|
imageView.setImageResource(resId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pickImportantRssItem(list: List<NewsItem>): NewsItem? {
|
||||||
|
if (list.isEmpty()) return null
|
||||||
|
|
||||||
|
val keywords = listOf("工事", "運休", "変更", "改良", "新設", "増発", "減便")
|
||||||
|
|
||||||
|
// ① 重要記事を抽出
|
||||||
|
val important = list.filter { item ->
|
||||||
|
keywords.any { kw ->
|
||||||
|
item.title.contains(kw) || item.body.contains(kw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ② 重要記事が複数 → ランダムに1件
|
||||||
|
if (important.isNotEmpty()) {
|
||||||
|
return important.random()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ③ 重要記事ゼロ → 全RSSからランダム
|
||||||
|
return list.random()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateAutoCommentFromInfo(text: String) {
|
||||||
|
Thread {
|
||||||
|
try {
|
||||||
|
val charId = CharacterSettings.loadCharacter(this)
|
||||||
|
val infoType = InfoClassifier.classify(text)
|
||||||
|
|
||||||
|
val json = CharacterReplyManager().replyWithAI(
|
||||||
|
characterId = charId,
|
||||||
|
info = text,
|
||||||
|
type = infoType
|
||||||
|
)
|
||||||
|
|
||||||
|
val clean = AiReplyParser.extractTextFromResponse(json)
|
||||||
|
|
||||||
|
runOnUiThread {
|
||||||
|
findViewById<TextView>(R.id.textAutoComment).text = clean
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) { e.printStackTrace() }
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setInitialCharacterImage() {
|
||||||
|
val charId = CharacterSettings.loadCharacter(this)
|
||||||
|
val imageView = findViewById<ImageView>(R.id.imageCharacter)
|
||||||
|
val drawable = resources.getIdentifier("${charId}_smile", "drawable", packageName)
|
||||||
|
imageView.setImageResource(drawable)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyCharacterLayoutDefault() {
|
||||||
|
val img = findViewById<ImageView>(R.id.imageCharacter)
|
||||||
|
val scale = resources.displayMetrics.density
|
||||||
|
val lp = img.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
|
lp.marginStart = (-8 * scale).toInt()
|
||||||
|
lp.topMargin = (-18 * scale).toInt()
|
||||||
|
img.layoutParams = lp
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pickRandomRssItem(list: List<NewsItem>): NewsItem? {
|
||||||
|
if (list.isEmpty()) return null
|
||||||
|
return list.random() // ← ランダム取得
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// コメント生成
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -20,4 +20,7 @@ class MarisaResponder : CharacterResponder {
|
|||||||
"ふむ?何か気になることがあるんだな。他にも情報があれば教えてくれよ。"
|
"ふむ?何か気になることがあるんだな。他にも情報があれば教えてくれよ。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun reply(text: String): String = respond(text, InfoType.NORMAL)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,4 +20,6 @@ class MomokaResponder : CharacterResponder{
|
|||||||
"気になることがあったんだね。よかったら、もう少し教えてねっ!"
|
"気になることがあったんだね。よかったら、もう少し教えてねっ!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun reply(text: String): String = respond(text, InfoType.NORMAL)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.example.curation_train_app
|
||||||
|
|
||||||
|
data class RailLine(
|
||||||
|
val id: String, // "hanwa"
|
||||||
|
val name: String, // "阪和線"
|
||||||
|
val company: String // "jr_west"
|
||||||
|
)
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.example.curation_train_app
|
||||||
|
|
||||||
|
object RailLineData {
|
||||||
|
val lines = listOf(
|
||||||
|
RailLine("hanwa", "阪和線", "jr_west"),
|
||||||
|
RailLine("kyoto", "JR京都線", "jr_west"),
|
||||||
|
RailLine("kobes", "JR神戸線", "jr_west"),
|
||||||
|
RailLine("takarazuka", "JR宝塚線", "jr_west"),
|
||||||
|
|
||||||
|
// 南海
|
||||||
|
RailLine("nankai_main", "南海本線", "nankai"),
|
||||||
|
RailLine("koya", "高野線", "nankai"),
|
||||||
|
RailLine("senboku", "泉北高速線", "senboku"),
|
||||||
|
|
||||||
|
// ここにどんどん追加できる
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -20,4 +20,8 @@ class ReimuResponder : CharacterResponder {
|
|||||||
"気になったのね。内容をもう少し教えてくれれば、ちゃんと見てみるわよ。"
|
"気になったのね。内容をもう少し教えてくれれば、ちゃんと見てみるわよ。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun reply(text: String): String = respond(text, InfoType.NORMAL)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,4 +20,7 @@ class SanaeResponder : CharacterResponder {
|
|||||||
"気になる点がありましたか?よければ詳しく教えてくださいね。"
|
"気になる点がありましたか?よければ詳しく教えてくださいね。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun reply(text: String): String = respond(text, InfoType.NORMAL)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,4 +20,7 @@ class SayakResponder : CharacterResponder {
|
|||||||
"気になる点があるのね。もう少し詳しく教えてくれれば、整理してお話しできると思うわ。"
|
"気になる点があるのね。もう少し詳しく教えてくれれば、整理してお話しできると思うわ。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun reply(text: String): String = respond(text, InfoType.NORMAL)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,4 +20,6 @@ class flandreResponder : CharacterResponder {
|
|||||||
"なになに?気になることがあるの?フランに教えてほしいのだ!"
|
"なになに?気になることがあるの?フランに教えてほしいのだ!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun reply(text: String): String = respond(text, InfoType.NORMAL)
|
||||||
}
|
}
|
||||||
|
|||||||
10
app/src/main/res/drawable/bg_comment_bubble.xml
Normal file
10
app/src/main/res/drawable/bg_comment_bubble.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#FFFFFF"/>
|
||||||
|
<corners android:radius="12dp"/>
|
||||||
|
<padding
|
||||||
|
android:left="8dp"
|
||||||
|
android:top="8dp"
|
||||||
|
android:right="8dp"
|
||||||
|
android:bottom="8dp"/>
|
||||||
|
<stroke android:width="1dp" android:color="#DDDDDD"/>
|
||||||
|
</shape>
|
||||||
@@ -97,4 +97,44 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layoutNotify"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="お知らせ設定"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:padding="4dp"/>
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/checkboxDelay"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="運行・遅延情報"/>
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/checkboxOuting"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="お出かけ関連情報"/>
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/checkboxCampaign"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="キャンペーン・お楽しみ情報"/>
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/checkboxOther"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="その他"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -129,77 +129,42 @@
|
|||||||
android:padding="8dp" />
|
android:padding="8dp" />
|
||||||
|
|
||||||
|
|
||||||
<!-- ▼▼ キャラ応答テストUI ▼▼ -->
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/testArea"
|
android:id="@+id/commentArea"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="horizontal"
|
||||||
android:padding="16dp"
|
android:padding="8dp">
|
||||||
android:background="#EEEEEE">
|
|
||||||
|
|
||||||
<EditText
|
<!-- 吹き出し(白枠) -->
|
||||||
android:id="@+id/inputText"
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="テスト用の文章を入力" />
|
android:background="@drawable/bg_comment_bubble"
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/inputCharacter"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="キャラID(例:reimu / akane)" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btnTestReply"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="キャラにしゃべらせる" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textTestResult"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="※結果がここに表示されます"
|
|
||||||
android:paddingTop="8dp" />
|
|
||||||
|
|
||||||
<!-- ▼▼ キャラ表示エリア(正しい構造) ▼▼ -->
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/bottomArea"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="180dp"
|
|
||||||
android:padding="12dp">
|
android:padding="12dp">
|
||||||
|
|
||||||
<!-- 左側のセリフ枠 -->
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/textCharacterComment"
|
android:id="@+id/textAutoComment"
|
||||||
android:layout_width="250dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/comment_background_white"
|
android:textSize="15sp" />
|
||||||
android:padding="13dp"
|
</LinearLayout>
|
||||||
android:text="ちょっとした一言"
|
|
||||||
android:textSize="13sp"
|
|
||||||
android:layout_gravity="center_vertical|start"
|
|
||||||
android:translationX="0dp"
|
|
||||||
android:translationY="0dp" />
|
|
||||||
|
|
||||||
<!-- 右下のキャラ画像 -->
|
<!-- キャラ画像(右寄せ) -->
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/imageCharacter"
|
android:id="@+id/imageCharacter"
|
||||||
android:layout_width="200dp"
|
android:layout_width="110dp"
|
||||||
android:layout_height="250dp"
|
android:layout_height="110dp"
|
||||||
android:adjustViewBounds="false"
|
android:layout_marginStart="6dp"
|
||||||
android:scaleType="fitCenter"
|
android:layout_gravity="bottom" />
|
||||||
android:src="@drawable/akane_01_icon"
|
</LinearLayout>
|
||||||
android:layout_gravity="bottom|end"
|
|
||||||
android:layout_margin="8dp"
|
|
||||||
android:cropToPadding="false"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
<!-- ▼▼ キャラ応答テストUI ▼▼ -->
|
||||||
<!-- ▲▲ キャラ表示エリア ▲▲ -->
|
<!-- ▲▲ キャラ表示エリア ▲▲ -->
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
<!-- ▲▲ キャラ応答テストUI ▲▲ -->
|
<!-- ▲▲ キャラ応答テストUI ▲▲ -->
|
||||||
|
|
||||||
<!-- ▲▲ キャラ応答テストUI ▲▲ -->
|
<!-- ▲▲ キャラ応答テストUI ▲▲ -->
|
||||||
|
|||||||
@@ -119,56 +119,56 @@
|
|||||||
android:id="@+id/btnReimu"
|
android:id="@+id/btnReimu"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="🔴 霊夢(キャラ紹介)"
|
android:text="🔴 霊夢(落ち着いた案内役)"
|
||||||
android:layout_marginTop="4dp"/>
|
android:layout_marginTop="4dp"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btnMarisa"
|
android:id="@+id/btnMarisa"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="🟡 魔理沙(キャラ紹介)"
|
android:text="🟡 魔理沙(専門的な深掘り担当)"
|
||||||
android:layout_marginTop="4dp"/>
|
android:layout_marginTop="4dp"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btnFrandle"
|
android:id="@+id/btnFrandle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="🟠 フラン(キャラ紹介)"
|
android:text="🟠 フラン(明るい元気ムードメーカー)"
|
||||||
android:layout_marginTop="4dp"/>
|
android:layout_marginTop="4dp"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btnSanae"
|
android:id="@+id/btnSanae"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="🟢 早苗(キャラ紹介)"
|
android:text="🟢 早苗(専門的な深掘り担当)"
|
||||||
android:layout_marginTop="4dp"/>
|
android:layout_marginTop="4dp"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btnAkane"
|
android:id="@+id/btnAkane"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="🟨 あかね(キャラ紹介)"
|
android:text="🟨 あかね(素直なリアクション枠)"
|
||||||
android:layout_marginTop="4dp"/>
|
android:layout_marginTop="4dp"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btnMomoka"
|
android:id="@+id/btnMomoka"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="🍑 ももか(キャラ紹介)"
|
android:text="🍑 ももか(素直なリアクション枠)"
|
||||||
android:layout_marginTop="4dp"/>
|
android:layout_marginTop="4dp"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btnSayaka"
|
android:id="@+id/btnSayaka"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="🟦 さやか(キャラ紹介)"
|
android:text="🟦 さやか(穏やかな整理・補足役)"
|
||||||
android:layout_marginTop="4dp"/>
|
android:layout_marginTop="4dp"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btnHiyori"
|
android:id="@+id/btnHiyori"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="🟧 ひより(キャラ紹介)"
|
android:text="🟧 ひより(柔らかい反応の癒し枠)"
|
||||||
android:layout_marginTop="4dp"/>
|
android:layout_marginTop="4dp"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
Reference in New Issue
Block a user