first commit
This commit is contained in:
@@ -51,6 +51,16 @@ dependencies {
|
||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
implementation("androidx.recyclerview:recyclerview:1.3.2")
|
||||
implementation("com.squareup.retrofit2:retrofit:2.9.0")
|
||||
implementation("com.squareup.retrofit2:converter-simplexml:2.9.0")
|
||||
dependencies {
|
||||
implementation("com.squareup.retrofit2:retrofit:2.9.0")
|
||||
implementation("com.squareup.retrofit2:converter-simplexml:2.9.0")
|
||||
implementation("com.squareup.okhttp3:okhttp:4.11.0")
|
||||
implementation("com.squareup.moshi:moshi-kotlin:1.15.0")
|
||||
|
||||
}
|
||||
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
@@ -22,6 +25,12 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".RegionSelectActivity" />
|
||||
<activity android:name=".LineSelectActivity" />
|
||||
<activity android:name=".LinePriorityActivity" />
|
||||
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
class AkaneResponder : CharacterResponder {
|
||||
|
||||
override val profile = CharacterProfiles.akane
|
||||
|
||||
override fun respond(info: String, type: InfoType): String {
|
||||
return when (type) {
|
||||
|
||||
InfoType.DELAY ->
|
||||
"えっ!?遅延!? 急ぐなら別ルートも考えたほうがいいかもだよっ!!"
|
||||
|
||||
InfoType.REVISION ->
|
||||
"うわーっ!ダイヤ改正だって!? 新しい運用とかワクワクするじゃんっ!"
|
||||
|
||||
InfoType.EVENT ->
|
||||
"イベント列車!? こういうのめっちゃ楽しみだよねっ!!"
|
||||
|
||||
else ->
|
||||
"なんか気になる情報があるよねっ!気をつけていこーっ!"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
object CharacterProfiles {
|
||||
|
||||
data class CharacterProfile(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val speakingStyle: String, // 話し方・口調の説明
|
||||
val personality: String, // 性格・立ち位置
|
||||
val knowledgeLevel: String // 鉄道知識レベル
|
||||
)
|
||||
|
||||
// ───────────────────────────────
|
||||
// 🔴1. 霊夢(メイン進行・解説)
|
||||
// ───────────────────────────────
|
||||
val reimu = CharacterProfile(
|
||||
id = "reimu",
|
||||
name = "霊夢",
|
||||
speakingStyle =
|
||||
"落ち着いた女言葉。断定を避け、やわらかい表現。敬語は使わない。" +
|
||||
"語尾は自然な範囲で「〜よ」「〜わよ」「〜よね」を用いる。",
|
||||
personality =
|
||||
"穏やか、冷静、全体進行役。説明整理が得意で、視聴者に安心感を与える。優しい。",
|
||||
knowledgeLevel = "中程度の鉄道知識"
|
||||
)
|
||||
|
||||
// ───────────────────────────────
|
||||
// 🟡2. 魔理沙(補足・深掘り担当)
|
||||
// ───────────────────────────────
|
||||
val marisa = CharacterProfile(
|
||||
id = "marisa",
|
||||
name = "魔理沙",
|
||||
speakingStyle =
|
||||
"元気な口調。「〜だぜ」は乱発せず、強調・納得・気付きの場面でだけ自然に使う。",
|
||||
personality =
|
||||
"深掘り・解説の補足、技術寄りの説明が得意。少しテンション高めで勢いがある。",
|
||||
knowledgeLevel = "高い鉄道知識"
|
||||
)
|
||||
|
||||
// ───────────────────────────────
|
||||
// 🟠3. フラン(明るく元気)
|
||||
// ───────────────────────────────
|
||||
val flan = CharacterProfile(
|
||||
id = "flan",
|
||||
name = "フラン",
|
||||
speakingStyle =
|
||||
"明るく元気。テンションが高いときだけ「なのだー」「のだー」が出る。" +
|
||||
"普段は普通の話し方で語尾を毎回つけない。",
|
||||
personality =
|
||||
"ムードメーカー。感情表現が大きく、明るい。盛り上げ役。",
|
||||
knowledgeLevel = "中〜低の鉄道知識(感覚派)"
|
||||
)
|
||||
|
||||
// ───────────────────────────────
|
||||
// 🟢4. 早苗(丁寧で落ち着いた補足役)
|
||||
// ───────────────────────────────
|
||||
val sanae = CharacterProfile(
|
||||
id = "sanae",
|
||||
name = "早苗",
|
||||
speakingStyle =
|
||||
"丁寧で落ち着いた語り。霊夢寄りの柔らかい話し方。敬語あり。",
|
||||
personality =
|
||||
"冷静な補足役。情景や背景知識を丁寧に伝える。視点が穏やか。",
|
||||
knowledgeLevel = "中程度の鉄道知識"
|
||||
)
|
||||
|
||||
// ───────────────────────────────
|
||||
// 🟨5. あかね(テンション高い・視聴者代表)
|
||||
// ───────────────────────────────
|
||||
val akane = CharacterProfile(
|
||||
id = "akane",
|
||||
name = "あかね",
|
||||
speakingStyle =
|
||||
"元気で明るい。テンション高め。語尾に小さい「っ」が入ることがある。" +
|
||||
"自分のことを「HN君」と呼ぶ。",
|
||||
personality =
|
||||
"視聴者代表。リアクション担当。疑問を素直に口に出す。場を明るくする。",
|
||||
knowledgeLevel = "低〜中の鉄道知識。難しい話は苦手。"
|
||||
)
|
||||
|
||||
// ───────────────────────────────
|
||||
// 🟦6. さやか(冷静・整理役)
|
||||
// ───────────────────────────────
|
||||
val sayaka = CharacterProfile(
|
||||
id = "sayaka",
|
||||
name = "さやか",
|
||||
speakingStyle =
|
||||
"落ち着いた女言葉。霊夢寄りの丁寧な話し方。語尾は崩れない。敬語は使わない",
|
||||
personality =
|
||||
"冷静な説明・整理役。感情を抑えめに、的確に指摘する。",
|
||||
knowledgeLevel = "中程度の鉄道知識"
|
||||
)
|
||||
|
||||
// ───────────────────────────────
|
||||
// 🟧7. ひより(穏やか・控えめ)
|
||||
// ───────────────────────────────
|
||||
val hiyori = CharacterProfile(
|
||||
id = "hiyori",
|
||||
name = "ひより",
|
||||
speakingStyle =
|
||||
"やわらかく、控えめな話し方。語彙は優しめ。崩れすぎない。",
|
||||
personality =
|
||||
"優しく控えめで、聞き役になることもある。しっかり者だが遠慮がち。",
|
||||
knowledgeLevel = "中〜低の鉄道知識"
|
||||
)
|
||||
|
||||
// ───────────────────────────────
|
||||
// 🍑8. ももか(甘めの口調・テンション高め)
|
||||
// ───────────────────────────────
|
||||
val momoka = CharacterProfile(
|
||||
id = "momoka",
|
||||
name = "ももか",
|
||||
speakingStyle =
|
||||
"かわいめの語尾・柔らかい話し方。テンション高めの時だけ「っ」が入る。",
|
||||
personality =
|
||||
"甘めの雰囲気で明るい。感情が前に出るタイプ。",
|
||||
knowledgeLevel = "低〜中の鉄道知識"
|
||||
)
|
||||
|
||||
// ───────────────────────────────
|
||||
// ID → キャラ検索
|
||||
// ───────────────────────────────
|
||||
fun getProfile(id: String): CharacterProfile? {
|
||||
return when (id.lowercase()) {
|
||||
"reimu" -> reimu
|
||||
"marisa" -> marisa
|
||||
"flan" -> flan
|
||||
"sanae" -> sanae
|
||||
"akane" -> akane
|
||||
"sayaka" -> sayaka
|
||||
"hiyori" -> hiyori
|
||||
"momoka" -> momoka
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
import com.example.curation_train_app.ai.PromptBuilder
|
||||
import com.example.curation_train_app.ai.AiClient
|
||||
private const val API_KEY = "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"
|
||||
|
||||
|
||||
|
||||
class CharacterReplyManager {
|
||||
|
||||
private val responders = mapOf(
|
||||
"reimu" to ReimuResponder(),
|
||||
"akane" to AkaneResponder(),
|
||||
"marisa" to MarisaResponder(),
|
||||
"sayaka" to SayakResponder(),
|
||||
"sanae" to SanaeResponder(),
|
||||
"momoka" to MomokaResponder(),
|
||||
"flandre" to flandreResponder(),
|
||||
"hiyori" to HiyoriResponder()
|
||||
// ← 6人追加予定
|
||||
)
|
||||
|
||||
fun reply(characterId: String, info: String, type: InfoType): String {
|
||||
val responder = responders[characterId]
|
||||
?: return "キャラが見つかりません。"
|
||||
return responder.respond(info, type)
|
||||
}
|
||||
fun replyWithAI(characterId: String, info: String, type: InfoType): String {
|
||||
val character = CharacterProfiles.getProfile(characterId)
|
||||
?: return "(エラー:キャラが不明です)"
|
||||
|
||||
|
||||
val prompt = PromptBuilder.buildPrompt(character, info, type)
|
||||
|
||||
val ai = AiClient(API_KEY)
|
||||
return ai.requestCharacterReply(prompt)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
interface CharacterResponder {
|
||||
val profile: CharacterProfiles.CharacterProfile
|
||||
fun respond(info: String, type: InfoType): String
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
object DummyLines {
|
||||
|
||||
fun linesOf(region: String): List<String> {
|
||||
return when (region) {
|
||||
"北海道" -> listOf("JR北海道全線", "札幌市営地下鉄", "道南いさりび鉄道")
|
||||
"東北" -> listOf("JR東日本(東北)", "仙台市地下鉄", "青い森鉄道")
|
||||
"関東" -> listOf("JR東日本(関東)", "東京メトロ", "京急", "東急", "小田急", "京王")
|
||||
"中部" -> listOf("JR東海", "名鉄", "近鉄(名古屋)")
|
||||
"関西" -> listOf("JR西日本(関西)", "大阪メトロ", "阪急", "阪神", "京阪", "南海", "近鉄(関西)")
|
||||
"中国" -> listOf("JR西日本(中国)", "広島電鉄", "一畑電車")
|
||||
"四国" -> listOf("JR四国")
|
||||
"九州" -> listOf("JR九州", "西鉄")
|
||||
else -> listOf("該当する路線がありません")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
|
||||
class FilterSettingActivity : ComponentActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_filter_setting)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
class HiyoriResponder : CharacterResponder {
|
||||
|
||||
override val profile = CharacterProfiles.hiyori
|
||||
|
||||
override fun respond(info: String, type: InfoType): String {
|
||||
return when (type) {
|
||||
|
||||
InfoType.DELAY ->
|
||||
"遅延しているみたい…焦らずに、少し余裕を持って動けるといいね。"
|
||||
|
||||
InfoType.REVISION ->
|
||||
"ダイヤが変わるんだね…。知らないと戸惑うから、確認しておくと安心だよ。"
|
||||
|
||||
InfoType.EVENT ->
|
||||
"イベント列車…!楽しそうだね。時間が合えば、ちょっと見に行ってみたいな。"
|
||||
|
||||
else ->
|
||||
"気になったことがあるの?よかったら教えてね。いっしょに見てみるよ。"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
object InfoClassifier {
|
||||
|
||||
fun classify(text: String): InfoType {
|
||||
|
||||
val t = text.lowercase()
|
||||
|
||||
return when {
|
||||
// 遅延系
|
||||
t.contains("遅延") || t.contains("遅れ") || t.contains("delay") ->
|
||||
InfoType.DELAY
|
||||
|
||||
// ダイヤ改正 / 運転変更
|
||||
t.contains("ダイヤ") || t.contains("時刻表") || t.contains("運転変更") ->
|
||||
InfoType.REVISION
|
||||
|
||||
// 注意報・警報・天候・安全
|
||||
t.contains("強風") || t.contains("大雨") ||
|
||||
t.contains("警報") || t.contains("注意喚起") ||
|
||||
t.contains("事故") || t.contains("運休") ->
|
||||
InfoType.WEATHER
|
||||
|
||||
// その他
|
||||
else -> InfoType.OTHER
|
||||
}
|
||||
}
|
||||
}
|
||||
12
app/src/main/java/com/example/curation_train_app/InfoType.kt
Normal file
12
app/src/main/java/com/example/curation_train_app/InfoType.kt
Normal file
@@ -0,0 +1,12 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
enum class InfoType {
|
||||
DELAY, // 遅延
|
||||
SUSPENSION, // 運休
|
||||
REVISION, // ダイヤ改正
|
||||
CONSTRUCTION, // 工事
|
||||
EVENT, // イベント・臨時列車
|
||||
WEATHER, // 天候影響
|
||||
NEW_TRAIN, // 新型車両関連
|
||||
OTHER // 分類できない場合
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.TextView
|
||||
import androidx.activity.ComponentActivity
|
||||
import android.content.Intent
|
||||
import android.widget.Button
|
||||
|
||||
class LinePriorityActivity : ComponentActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_line_priority)
|
||||
|
||||
val btnOpenFilter = findViewById<Button>(R.id.btnOpenFilter)
|
||||
btnOpenFilter.setOnClickListener {
|
||||
val intent = Intent(this, FilterSettingActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
|
||||
val lineName = intent.getStringExtra("line_name") ?: "不明な路線"
|
||||
|
||||
val selectedLine = findViewById<TextView>(R.id.textSelectedLine)
|
||||
selectedLine.text = "選択された路線:$lineName"
|
||||
|
||||
// 優先度6枠まとめて取得
|
||||
val box1 = findViewById<TextView>(R.id.priority1)
|
||||
val box2 = findViewById<TextView>(R.id.priority2)
|
||||
val box3 = findViewById<TextView>(R.id.priority3)
|
||||
val box4 = findViewById<TextView>(R.id.priority4)
|
||||
val box5 = findViewById<TextView>(R.id.priority5)
|
||||
val box6 = findViewById<TextView>(R.id.priority6)
|
||||
|
||||
val boxes = listOf(box1, box2, box3, box4, box5, box6)
|
||||
|
||||
// ▼▼ 空き枠を探して路線名をセット ▼▼
|
||||
for (box in boxes) {
|
||||
val t = box.text.toString()
|
||||
|
||||
// 初期状態(1〜6)または空欄の場合は代入
|
||||
if (t == "1" || t == "2" || t == "3" ||
|
||||
t == "4" || t == "5" || t == "6" ||
|
||||
t.isBlank()
|
||||
) {
|
||||
box.text = lineName
|
||||
break
|
||||
}
|
||||
}
|
||||
// ▲▲ 空き枠を探してセット ▲▲
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.widget.TextView
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
class LineSelectActivity : ComponentActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_line_select)
|
||||
|
||||
// ★ 地域名を受け取る
|
||||
val region = intent.getStringExtra("region") ?: "未指定"
|
||||
|
||||
// ★ タイトル反映
|
||||
val title = findViewById<TextView>(R.id.textRegionTitle)
|
||||
title.text = "◆ 選択されている地域:$region"
|
||||
|
||||
// ★ 該当地域の路線リストを取得(あとでDBやJSONに移動予定)
|
||||
val lines = DummyLines.linesOf(region)
|
||||
|
||||
// ★ リスト表示
|
||||
val recycler = findViewById<RecyclerView>(R.id.recyclerLines)
|
||||
recycler.layoutManager = LinearLayoutManager(this)
|
||||
recycler.adapter = LineSelectAdapter(lines) { lineName ->
|
||||
openPriorityScreen(lineName)
|
||||
}
|
||||
}
|
||||
|
||||
// 優先度設定画面へ遷移
|
||||
private fun openPriorityScreen(line: String) {
|
||||
val intent = Intent(this, LinePriorityActivity::class.java)
|
||||
intent.putExtra("line_name", line)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
class LineSelectAdapter(
|
||||
private val items: List<String>,
|
||||
private val onClick: (String) -> Unit
|
||||
) : RecyclerView.Adapter<LineSelectAdapter.ViewHolder>() {
|
||||
|
||||
class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
|
||||
val text: TextView = v.findViewById(R.id.textLineName)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.item_line_name, parent, false)
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val name = items[position]
|
||||
holder.text.text = name
|
||||
holder.itemView.setOnClickListener { onClick(name) }
|
||||
}
|
||||
}
|
||||
@@ -4,33 +4,120 @@ import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import android.content.Intent
|
||||
import android.widget.LinearLayout
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.view.ViewGroup
|
||||
import com.example.curation_train_app.ai.AiClient
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
// ここで R.id.recyclerNews を使う
|
||||
val recyclerView = findViewById<RecyclerView>(R.id.recyclerNews)
|
||||
val btn = findViewById<Button>(R.id.btnTestReply)
|
||||
val inputText = findViewById<EditText>(R.id.inputText)
|
||||
val inputCharacter = findViewById<EditText>(R.id.inputCharacter)
|
||||
val textResult = findViewById<TextView>(R.id.textTestResult)
|
||||
|
||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
btn.setOnClickListener {
|
||||
val text = inputText.text.toString()
|
||||
val charId = inputCharacter.text.toString()
|
||||
|
||||
val newsList: List<NewsItem> = listOf(
|
||||
NewsItem(
|
||||
company = "△△電鉄",
|
||||
title = "○○線で新型車両がデビューします!",
|
||||
body = "2025年12月より○○線で新型車両が営業運転を開始します。ダイヤ変更にご注意ください。",
|
||||
imageRes = R.drawable.ic_launcher_foreground
|
||||
),
|
||||
NewsItem(
|
||||
company = "JR◆◆",
|
||||
title = "○○線で駅ホーム延伸工事を実施",
|
||||
body = "工事期間中は一部列車で停車位置が変更となります。係員の案内に従ってご利用ください。",
|
||||
imageRes = R.drawable.ic_launcher_foreground
|
||||
)
|
||||
// …以下、必要な分だけ続ける
|
||||
)
|
||||
val type = InfoClassifier.classify(text)
|
||||
|
||||
recyclerView.adapter = NewsAdapter(newsList)
|
||||
Thread {
|
||||
val reply = CharacterReplyManager().replyWithAI(charId, text, type)
|
||||
|
||||
runOnUiThread {
|
||||
val commentView = findViewById<TextView>(R.id.textCharacterComment)
|
||||
|
||||
val cleanText = if (reply.trim().startsWith("{") || reply.trim().startsWith("[")) {
|
||||
AiClient(apiKey = "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").extractText(json = reply)
|
||||
} else {
|
||||
reply
|
||||
}
|
||||
|
||||
commentView.text = cleanText
|
||||
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
loadRss()
|
||||
|
||||
// ▼ リリース枠(表示テスト) ▼
|
||||
val releaseBox = findViewById<LinearLayout>(R.id.layoutRelease)
|
||||
val releaseTitle = findViewById<TextView>(R.id.textReleaseTitle)
|
||||
val releaseBody = findViewById<TextView>(R.id.textReleaseBody)
|
||||
|
||||
releaseTitle.text = "JR西日本:新着情報"
|
||||
releaseBody.text = "最新リリースがここに入る"
|
||||
|
||||
releaseBox.visibility = View.VISIBLE
|
||||
// ▲ リリース枠 ▲
|
||||
|
||||
// ▼ 地域セレクト画面へ ▼
|
||||
val btnOpenRegion = findViewById<TextView>(R.id.textFavorite)
|
||||
btnOpenRegion.setOnClickListener {
|
||||
val intent = Intent(this, RegionSelectActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
// ▲ 地域セレクト画面へ ▲
|
||||
|
||||
// ▼ キャラ画像の初期配置 ▼
|
||||
applyCharacterLayoutDefault()
|
||||
}
|
||||
|
||||
// ▼ キャラ画像の位置調整関数 ▼
|
||||
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
|
||||
img.requestLayout()
|
||||
}
|
||||
|
||||
// ▼ RSS 読み込み ▼
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
class MarisaResponder : CharacterResponder {
|
||||
|
||||
override val profile = CharacterProfiles.marisa
|
||||
|
||||
override fun respond(info: String, type: InfoType): String {
|
||||
return when (type) {
|
||||
|
||||
InfoType.DELAY ->
|
||||
"遅延か。原因が気になるところだぜ。無理せず、回避ルートを考えるのもアリだな。"
|
||||
|
||||
InfoType.REVISION ->
|
||||
"ダイヤ改正だな。運用の流れが結構変わる時期なんだぜ。新しい列車の動きも要チェックだ。"
|
||||
|
||||
InfoType.EVENT ->
|
||||
"イベント列車か。撮るならポイント選びが肝心だぜ。準備して臨むといいと思うぜ。"
|
||||
|
||||
else ->
|
||||
"ふむ?何か気になることがあるんだな。他にも情報があれば教えてくれよ。"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
class MomokaResponder : CharacterResponder{
|
||||
|
||||
override val profile = CharacterProfiles.momoka
|
||||
|
||||
override fun respond(info: String, type: InfoType): String {
|
||||
return when (type) {
|
||||
|
||||
InfoType.DELAY ->
|
||||
"えっ…遅れてるの?大丈夫かな…。のんびり落ち着いていこうねっ。"
|
||||
|
||||
InfoType.REVISION ->
|
||||
"ダイヤが変わるんだね!ちょっとドキドキしちゃうよ〜。"
|
||||
|
||||
InfoType.EVENT ->
|
||||
"イベント列車!?わぁ〜楽しそう!時間が合ったら絶対見たいなっ!"
|
||||
|
||||
else ->
|
||||
"気になることがあったんだね。よかったら、もう少し教えてねっ!"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,6 @@ data class NewsItem(
|
||||
val company: String,
|
||||
val title: String,
|
||||
val body: String,
|
||||
val imageRes: Int
|
||||
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import android.widget.Button
|
||||
|
||||
class RegionSelectActivity : ComponentActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_region_select)
|
||||
|
||||
fun go(region: String) {
|
||||
val intent = Intent(this, LineSelectActivity::class.java)
|
||||
intent.putExtra("region", region)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
findViewById<Button>(R.id.btnNorth).setOnClickListener { go("北海道") }
|
||||
findViewById<Button>(R.id.btnTohoku).setOnClickListener { go("東北") }
|
||||
findViewById<Button>(R.id.btnKanto).setOnClickListener { go("関東") }
|
||||
findViewById<Button>(R.id.btnChubu).setOnClickListener { go("中部") }
|
||||
findViewById<Button>(R.id.btnKansai).setOnClickListener { go("関西") }
|
||||
findViewById<Button>(R.id.btnChugoku).setOnClickListener { go("中国") }
|
||||
findViewById<Button>(R.id.btnShikoku).setOnClickListener { go("四国") }
|
||||
findViewById<Button>(R.id.btnKyushu).setOnClickListener { go("九州") }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
class ReimuResponder : CharacterResponder {
|
||||
|
||||
override val profile = CharacterProfiles.reimu
|
||||
|
||||
override fun respond(info: String, type: InfoType): String {
|
||||
return when (type) {
|
||||
|
||||
InfoType.DELAY ->
|
||||
"遅延みたいね。急がなければ、少し余裕を持って動くのがいいわよ。落ち着いていきましょう。"
|
||||
|
||||
InfoType.REVISION ->
|
||||
"ダイヤ改正があるみたいね。大きな変化がある時期だから、いつもより確認しておくと安心よ。"
|
||||
|
||||
InfoType.EVENT ->
|
||||
"イベント列車が走るみたいね。せっかくだから、時間が合えば見に行くと楽しいわよ。"
|
||||
|
||||
else ->
|
||||
"気になったのね。内容をもう少し教えてくれれば、ちゃんと見てみるわよ。"
|
||||
}
|
||||
}
|
||||
}
|
||||
20
app/src/main/java/com/example/curation_train_app/RssApi.kt
Normal file
20
app/src/main/java/com/example/curation_train_app/RssApi.kt
Normal file
@@ -0,0 +1,20 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.simplexml.SimpleXmlConverterFactory
|
||||
|
||||
class RssApi {
|
||||
|
||||
private val retrofit = Retrofit.Builder()
|
||||
.baseUrl("https://www.westjr.co.jp/press/article/index.xml")
|
||||
.addConverterFactory(SimpleXmlConverterFactory.create())
|
||||
.build()
|
||||
|
||||
private val client = retrofit.create(RssClient::class.java)
|
||||
|
||||
fun loadRss(): RssFeed? {
|
||||
val response = client.getFeed().execute()
|
||||
return response.body()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.GET
|
||||
|
||||
interface RssClient {
|
||||
@GET("article/index.xml")
|
||||
fun getFeed(): Call<RssFeed>
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
import org.simpleframework.xml.Element
|
||||
import org.simpleframework.xml.ElementList
|
||||
import org.simpleframework.xml.Root
|
||||
|
||||
@Root(name = "rss", strict = false)
|
||||
data class RssFeed(
|
||||
@field:Element(name = "channel")
|
||||
var channel: RssChannel? = null
|
||||
)
|
||||
|
||||
@Root(name = "channel", strict = false)
|
||||
data class RssChannel(
|
||||
@field:ElementList(name = "item", inline = true, required = false)
|
||||
var items: List<RssItem>? = null
|
||||
)
|
||||
|
||||
@Root(name = "item", strict = false)
|
||||
data class RssItem(
|
||||
@field:Element(name = "title", required = false)
|
||||
var title: String = "",
|
||||
|
||||
@field:Element(name = "description", required = false)
|
||||
var description: String = "",
|
||||
|
||||
@field:Element(name = "link", required = false)
|
||||
var link: String = "",
|
||||
|
||||
@field:Element(name = "pubDate", required = false)
|
||||
var pubDate: String = ""
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
class SanaeResponder : CharacterResponder {
|
||||
|
||||
override val profile = CharacterProfiles.sanae
|
||||
|
||||
override fun respond(info: String, type: InfoType): String {
|
||||
return when (type) {
|
||||
|
||||
InfoType.DELAY ->
|
||||
"遅延が出ているようですね。無理のない範囲で、時間に余裕を持って動かれると良いと思いますよ。"
|
||||
|
||||
InfoType.REVISION ->
|
||||
"ダイヤ改正が予定されているみたいですね。列車の動きが変わるので、ご注意くださいね。"
|
||||
|
||||
InfoType.EVENT ->
|
||||
"イベント列車ですか。季節感や特別感があって素敵ですよね。機会があれば見に行きたいですね。"
|
||||
|
||||
else ->
|
||||
"気になる点がありましたか?よければ詳しく教えてくださいね。"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
class SayakResponder : CharacterResponder {
|
||||
|
||||
override val profile = CharacterProfiles.sayaka
|
||||
|
||||
override fun respond(info: String, type: InfoType): String {
|
||||
return when (type) {
|
||||
|
||||
InfoType.DELAY ->
|
||||
"遅延が出ているようね。急がないのであれば、少し余裕を持って動くと安心よ。"
|
||||
|
||||
InfoType.REVISION ->
|
||||
"ダイヤ改正の情報ね。列車の動きが変わることもあるから、事前に確認しておくと良いわ。"
|
||||
|
||||
InfoType.EVENT ->
|
||||
"イベント列車が走るみたいね。珍しい機会だし、時間が合えば見てみるのもいいと思うわよ。"
|
||||
|
||||
else ->
|
||||
"気になる点があるのね。もう少し詳しく教えてくれれば、整理してお話しできると思うわ。"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.example.curation_train_app.ai
|
||||
|
||||
import okhttp3.*
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.json.JSONObject
|
||||
|
||||
class AiClient(private val apiKey: String) {
|
||||
|
||||
private val client = OkHttpClient()
|
||||
|
||||
fun requestCharacterReply(prompt: String): String {
|
||||
val url = "https://api.openai.com/v1/responses"
|
||||
|
||||
val json = JSONObject().apply {
|
||||
put("model", "gpt-4.1-mini") // ⭐ ChatGPT 5.2 mini 相当
|
||||
put("input", prompt) // ⭐ messages ではなく input!!
|
||||
put("max_output_tokens", 150)
|
||||
}
|
||||
|
||||
val body = json.toString()
|
||||
.toRequestBody("application/json".toMediaType())
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.post(body)
|
||||
.addHeader("Authorization", "Bearer $apiKey")
|
||||
.build()
|
||||
|
||||
client.newCall(request).execute().use { response ->
|
||||
val responseBody = response.body?.string()
|
||||
?: return "(AIエラー:応答なし)"
|
||||
|
||||
val obj = JSONObject(responseBody)
|
||||
|
||||
// ⭐ 新APIは “output” 配列の中に text が入っている
|
||||
val outputArray = obj.getJSONArray("output")
|
||||
val outputObj = outputArray.getJSONObject(0)
|
||||
|
||||
// ⭐ content だけ取り出す
|
||||
val text = outputObj.getString("content")
|
||||
|
||||
return text
|
||||
}
|
||||
}
|
||||
fun extractText(json: String): String {
|
||||
return try {
|
||||
val obj = org.json.JSONObject(json)
|
||||
obj.optString("text", json)
|
||||
} catch (_: Exception) {
|
||||
json
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.example.curation_train_app.ai
|
||||
|
||||
import com.example.curation_train_app.CharacterProfiles
|
||||
import com.example.curation_train_app.InfoType
|
||||
|
||||
object PromptBuilder {
|
||||
|
||||
fun buildPrompt(
|
||||
character: CharacterProfiles.CharacterProfile,
|
||||
info: String,
|
||||
type: InfoType
|
||||
): String {
|
||||
|
||||
return """
|
||||
あなたは以下のキャラクターになりきって返答してください。
|
||||
|
||||
【キャラ設定】
|
||||
名前:${character.name}
|
||||
話し方:${character.speakingStyle}
|
||||
性格:${character.personality}
|
||||
知識レベル:${character.knowledgeLevel}
|
||||
|
||||
【状況】
|
||||
鉄道情報の種類:$type
|
||||
内容:$info
|
||||
|
||||
【出力条件】
|
||||
・キャラになりきる
|
||||
・短く、自然に、感情をほんの少し込める
|
||||
・ユーザーへ助言する感じで
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.example.curation_train_app
|
||||
|
||||
class flandreResponder : CharacterResponder {
|
||||
|
||||
override val profile = CharacterProfiles.flan
|
||||
|
||||
override fun respond(info: String, type: InfoType): String {
|
||||
return when (type) {
|
||||
|
||||
InfoType.DELAY ->
|
||||
"えっ、遅れてるの?大丈夫かな?ちょっと心配だよ!"
|
||||
|
||||
InfoType.REVISION ->
|
||||
"ダイヤが変わっちゃうんだ!なんだかワクワクしてきちゃうかも!"
|
||||
|
||||
InfoType.EVENT ->
|
||||
"イベント列車!?絶対楽しいやつじゃん!時間が合えば見に行きたいのだー!"
|
||||
|
||||
else ->
|
||||
"なになに?気になることがあるの?フランに教えてほしいのだ!"
|
||||
}
|
||||
}
|
||||
}
|
||||
32
app/src/main/res/layout/activity_filter_setting.xml
Normal file
32
app/src/main/res/layout/activity_filter_setting.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="情報フィルター設定(仮)"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkOfficial"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="公式の情報のみ" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkDelay"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="運行・遅延情報" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkOuting"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="お出かけ関連情報" />
|
||||
</LinearLayout>
|
||||
100
app/src/main/res/layout/activity_line_priority.xml
Normal file
100
app/src/main/res/layout/activity_line_priority.xml
Normal file
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#E8F8E5">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- タイトル -->
|
||||
<TextView
|
||||
android:id="@+id/textSelectedLine"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="選択された路線:"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:paddingBottom="16dp" />
|
||||
|
||||
<!-- 優先度の枠(1~6) -->
|
||||
<TextView
|
||||
android:id="@+id/priority1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="8dp"
|
||||
android:background="#FFFFFF"
|
||||
android:text="1"
|
||||
android:textSize="16sp"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/priority2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="8dp"
|
||||
android:background="#FFFFFF"
|
||||
android:text="2"
|
||||
android:textSize="16sp"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/priority3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="8dp"
|
||||
android:background="#FFFFFF"
|
||||
android:text="3"
|
||||
android:textSize="16sp"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/priority4"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="8dp"
|
||||
android:background="#FFFFFF"
|
||||
android:text="4"
|
||||
android:textSize="16sp"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/priority5"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="8dp"
|
||||
android:background="#FFFFFF"
|
||||
android:text="5"
|
||||
android:textSize="16sp"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/priority6"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="8dp"
|
||||
android:background="#FFFFFF"
|
||||
android:text="6"
|
||||
android:textSize="16sp"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnOpenFilter"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="情報フィルターへ"
|
||||
android:layout_marginTop="16dp"/>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
75
app/src/main/res/layout/activity_line_select.xml
Normal file
75
app/src/main/res/layout/activity_line_select.xml
Normal file
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp"
|
||||
android:orientation="vertical"
|
||||
android:background="#E8F8E5">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textRegionTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="◆ 選択されている地域:"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:paddingBottom="12dp"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerLines"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:padding="4dp" />
|
||||
|
||||
<!-- ▼▼ お知らせ設定エリア ▼▼ -->
|
||||
<LinearLayout
|
||||
android:id="@+id/noticeSettingBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:minHeight="120dp"
|
||||
android:background="@drawable/comment_background_white">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="■ お知らせ設定"
|
||||
android:textStyle="bold"
|
||||
android:textSize="14sp"
|
||||
android:paddingBottom="8dp"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkNoticeTrain"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="運行・遅延情報"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkNoticeOuting"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="お出かけ関連情報"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkNoticeCampaign"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="キャンペーン・お楽しみ情報"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkNoticeService"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="サービス・アプリ関連"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- ▲▲ キャラ選択エリア ▲▲ -->
|
||||
|
||||
<!-- ▲▲ お知らせ設定エリア ▲▲ -->
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical"
|
||||
android:background="#EAF7EF">
|
||||
|
||||
<!-- タイトル行 -->
|
||||
|
||||
<!-- タイトル行(白いカード背景) -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:background="#EAF7EF">
|
||||
android:layout_margin="12dp"
|
||||
android:background="@drawable/comment_background_white"
|
||||
android:elevation="2dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textTitle"
|
||||
@@ -20,7 +24,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="○○線:平常運転"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
android:textStyle="bold"
|
||||
android:paddingBottom="8dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textFavorite"
|
||||
@@ -28,13 +33,49 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="お気に入り登録・設定"
|
||||
android:gravity="center"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:background="#F0F0F0"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="6dp"
|
||||
android:background="#F7F7F7"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 運行情報カード(白背景) -->
|
||||
|
||||
|
||||
|
||||
<!-- ▼▼ リリース情報ボックス(1つだけ) ▼▼ -->
|
||||
<!-- リリース情報カード(白背景) -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutRelease"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp"
|
||||
android:layout_margin="12dp"
|
||||
android:background="@drawable/comment_background_white"
|
||||
android:elevation="2dp"
|
||||
android:visibility="visible">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textReleaseTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="リリース情報"
|
||||
android:textStyle="bold"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textReleaseBody"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="ここにリリース本文が入ります"
|
||||
android:textSize="13sp"
|
||||
android:layout_marginTop="4dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- ▲▲ リリース情報ボックス ▲▲ -->
|
||||
|
||||
|
||||
<!-- 運行情報カード -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutNotice"
|
||||
android:layout_width="match_parent"
|
||||
@@ -43,7 +84,8 @@
|
||||
android:padding="12dp"
|
||||
android:layout_margin="12dp"
|
||||
android:background="@drawable/comment_background_white"
|
||||
android:elevation="2dp">
|
||||
android:elevation="2dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textNoticeTitle"
|
||||
@@ -62,7 +104,8 @@
|
||||
android:layout_marginTop="4dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 運行情報一覧(スクロール) -->
|
||||
|
||||
<!-- 運行情報リスト -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerNews"
|
||||
android:layout_width="match_parent"
|
||||
@@ -70,33 +113,88 @@
|
||||
android:layout_weight="1"
|
||||
android:padding="8dp" />
|
||||
|
||||
<!-- 下部:一言 & あかね -->
|
||||
|
||||
<!-- ▼▼ キャラ応答テストUI ▼▼ -->
|
||||
<LinearLayout
|
||||
android:id="@+id/testArea"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:background="#EEEEEE">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/inputText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="テスト用の文章を入力" />
|
||||
|
||||
<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" />
|
||||
|
||||
</LinearLayout>
|
||||
<!-- ▲▲ キャラ応答テストUI ▲▲ -->
|
||||
|
||||
<!-- ▼ 下部キャラ表示 ▼ -->
|
||||
|
||||
<!-- ▼ 下部キャラ表示 ▼ -->
|
||||
<FrameLayout
|
||||
android:id="@+id/bottomArea"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="16dp"
|
||||
android:gravity="bottom">
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- コメントテキスト(左側) -->
|
||||
<TextView
|
||||
android:id="@+id/textCharacterComment"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/comment_background_white"
|
||||
android:padding="8dp"
|
||||
android:text="ちょっとした一言(例:今日から●●線のダイヤが変わってるから気をつけてね!)"
|
||||
android:textSize="13sp" />
|
||||
|
||||
android:padding="13dp"
|
||||
android:text="ちょっとした一言"
|
||||
android:textSize="13sp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="110dp"
|
||||
android:translationX="-8dp"
|
||||
android:translationY="0dp" />
|
||||
<!-- キャラ(右下固定・サイズ自由) -->
|
||||
<ImageView
|
||||
android:id="@+id/imageCharacter"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="140dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_width="280dp"
|
||||
android:layout_height="300dp"
|
||||
android:src="@drawable/akane_01_icon"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="fitCenter" />
|
||||
</LinearLayout>
|
||||
android:adjustViewBounds="false"
|
||||
android:scaleType="fitCenter"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:translationX="8dp"
|
||||
android:translationY="-4dp" />
|
||||
|
||||
</FrameLayout>
|
||||
<!-- ▲ 下部キャラ表示 ▲ -->
|
||||
|
||||
|
||||
<!-- ▲ 下部キャラ表示 ▲ -->
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
<!-- 下部キャラ表示 -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
179
app/src/main/res/layout/activity_region_select.xml
Normal file
179
app/src/main/res/layout/activity_region_select.xml
Normal file
@@ -0,0 +1,179 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#E8F8E5">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- タイトル(お気に入り路線登録) -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="お気に入り路線登録"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:paddingBottom="12dp"/>
|
||||
|
||||
<!-- 8地域グリッド -->
|
||||
<GridLayout
|
||||
android:id="@+id/gridRegions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:columnCount="2"
|
||||
android:rowCount="4"
|
||||
android:alignmentMode="alignMargins"
|
||||
android:columnOrderPreserved="false"
|
||||
android:useDefaultMargins="true">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnNorth"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:text="北海道"
|
||||
android:textSize="18sp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnTohoku"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:text="東北"
|
||||
android:textSize="18sp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnKanto"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:text="関東"
|
||||
android:textSize="18sp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnChubu"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:text="中部"
|
||||
android:textSize="18sp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnKansai"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:text="関西"
|
||||
android:textSize="18sp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnChugoku"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:text="中国"
|
||||
android:textSize="18sp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnShikoku"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:text="四国"
|
||||
android:textSize="18sp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnKyushu"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:text="九州"
|
||||
android:textSize="18sp"/>
|
||||
|
||||
</GridLayout>
|
||||
|
||||
<!-- ▼▼ キャラ選択エリア ▼▼ -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutCharacters"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:background="@drawable/comment_background_white"
|
||||
android:layout_marginTop="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="ナビゲーター設定"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:paddingBottom="8dp"/>
|
||||
|
||||
<!-- 8キャラボタン -->
|
||||
<Button
|
||||
android:id="@+id/btnReimu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="🔴 霊夢(キャラ紹介)"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnMarisa"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="🟡 魔理沙(キャラ紹介)"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnFrandle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="🟠 フラン(キャラ紹介)"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnSanae"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="🟢 早苗(キャラ紹介)"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnAkane"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="🟨 あかね(キャラ紹介)"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnMomoka"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="🍑 ももか(キャラ紹介)"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnSayaka"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="🟦 さやか(キャラ紹介)"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnHiyori"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="🟧 ひより(キャラ紹介)"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
12
app/src/main/res/layout/item_line_name.xml
Normal file
12
app/src/main/res/layout/item_line_name.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/textLineName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:textSize="16sp"
|
||||
android:background="#FFFFFF"
|
||||
android:text="路線名"
|
||||
android:layout_marginBottom="6dp"/>
|
||||
@@ -1,3 +1,4 @@
|
||||
<resources>
|
||||
<string name="app_name">curation_train_app</string>
|
||||
<string name="openai_key">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</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user