完全無料!ChatGPTに訊きながらコピペで日めくりアプリを作ったよ|自作アプリで余ったスマホが大活躍!|Abdroid Studio|Google Sheets API|Arrows Fit F-01H

(PR) 記事内に広告が含まれています。
スポンサーリンク

YouTubeに投稿した動画のフォローアップ記事になるよ

コピペ用のコードは、こちらから

手っ取り早く動かしたい人向けに、コードを記載します。

Android Studioで作成したプロジェクトのコード

Android Studio のプロジェクト設定
Android Studioのプロジェクト設定
activity_main.xml
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="16dp">
    
        <!-- 1. 和暦年・月 -->
        <TextView
            android:id="@+id/tvWareki"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:gravity="center_horizontal"
            android:text="令和 10年 10月"
            android:textSize="48sp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            tools:ignore="HardcodedText" />
    
        <!-- 2. 日付 -->
        <TextView
            android:id="@+id/tvDate"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:gravity="center_horizontal"
            android:text="04"
            android:textSize="220sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/tvWareki"
            tools:ignore="HardcodedText" />
    
        <!-- 3. 曜日 -->
        <TextView
            android:id="@+id/tvWeekday"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="日曜日"
            android:textSize="50sp"
            app:layout_constraintTop_toBottomOf="@id/tvDate"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            tools:ignore="HardcodedText" />
    
        <!-- 4. メッセージ領域 -->
        <LinearLayout
            android:id="@+id/llMessages"
            android:orientation="vertical"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toBottomOf="@id/tvWeekday"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:paddingTop="12dp">
    
            <TextView
                android:id="@+id/msg01"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="メッセージ01*────────*"
                android:textSize="23sp"
                tools:ignore="HardcodedText" />
    
            <TextView
                android:id="@+id/msg02"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="メッセージ02*────────*"
                android:textSize="23sp"
                tools:ignore="HardcodedText" />
    
            <TextView
                android:id="@+id/msg03"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="メッセージ03*────────*"
                android:textSize="23sp"
                tools:ignore="HardcodedText" />
        </LinearLayout>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
    MainActivity.kt
    package com.example.himekuricalendar
    
    import android.os.Bundle
    import android.os.Handler
    import android.os.Looper
    import android.widget.TextView
    import androidx.appcompat.app.AppCompatActivity
    import org.json.JSONObject
    import java.io.BufferedReader
    import java.io.InputStreamReader
    import java.net.HttpURLConnection
    import java.net.URL
    import java.text.SimpleDateFormat
    import java.util.*
    import java.util.concurrent.Executors
    import java.util.concurrent.TimeUnit
    
    class MainActivity : AppCompatActivity() {
    
        private lateinit var tvWareki: TextView
        private lateinit var tvDate: TextView
        private lateinit var tvWeekday: TextView
        private lateinit var msg01: TextView
        private lateinit var msg02: TextView
        private lateinit var msg03: TextView
    
        private val handler = Handler(Looper.getMainLooper())
    
        // テスト中は1分間隔、本番は20分間隔へ
        private val updateIntervalMillis = TimeUnit.MINUTES.toMillis(20)
    
        private val updateTask = object : Runnable {
            override fun run() {
                updateDateTimeViews()
                fetchMessagesFromApi()
                handler.postDelayed(this, updateIntervalMillis)
            }
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            // View の取得
            tvWareki = findViewById(R.id.tvWareki)
            tvDate = findViewById(R.id.tvDate)
            tvWeekday = findViewById(R.id.tvWeekday)
            msg01 = findViewById(R.id.msg01)
            msg02 = findViewById(R.id.msg02)
            msg03 = findViewById(R.id.msg03)
    
            // メッセージ領域初期化
            msg01.text = ""
            msg02.text = ""
            msg03.text = ""
    
            // 初回更新 & タイマー開始
            updateDateTimeViews()
            fetchMessagesFromApi()
            handler.postDelayed(updateTask, updateIntervalMillis)
        }
    
        private fun updateDateTimeViews() {
            val now = Calendar.getInstance()
    
            // 和暦(例: "令和 7年 5月")
            val wareki = getJapaneseEraWithSpaces(now)
            tvWareki.text = wareki
    
            // 日 表示(ゼロパディングなし)
            tvDate.text = now.get(Calendar.DAY_OF_MONTH).toString()
    
            // 曜日
            val weekdayFormat = SimpleDateFormat("EEEE", Locale.JAPAN)
            tvWeekday.text = weekdayFormat.format(now.time)
        }
    
        private fun fetchMessagesFromApi() {
            val apiUrl = "https://script.google.com/macros/s/AKfycb.../exec" // 自分のWebアプリURLに置き換える
    
            Executors.newSingleThreadExecutor().execute {
                try {
                    val url = URL(apiUrl)
                    val conn = url.openConnection() as HttpURLConnection
                    conn.requestMethod = "GET"
                    conn.connectTimeout = 5000
                    conn.readTimeout = 5000
    
                    val reader = BufferedReader(InputStreamReader(conn.inputStream))
                    val response = reader.readText()
                    reader.close()
    
                    val json = JSONObject(response)
                    val messages = json.getJSONArray("messages")
    
                    runOnUiThread {
                        msg01.text = if (messages.length() > 0) messages.getString(0) else ""
                        msg02.text = if (messages.length() > 1) messages.getString(1) else ""
                        msg03.text = if (messages.length() > 2) messages.getString(2) else ""
                    }
    
                } catch (e: Exception) {
                    e.printStackTrace()
                    runOnUiThread {
                        msg01.text = "メッセージ取得エラー"
                        msg02.text = ""
                        msg03.text = ""
                    }
                }
            }
        }
    
        private fun getJapaneseEraWithSpaces(calendar: Calendar): String {
            val year = calendar.get(Calendar.YEAR)
            val month = calendar.get(Calendar.MONTH) + 1
            return when {
                year >= 2019 -> "令和 ${year - 2018}年 ${month}月"
                year >= 1989 -> "平成 ${year - 1988}年 ${month}月"
                year >= 1926 -> "昭和 ${year - 1925}年 ${month}月"
                year >= 1912 -> "大正 ${year - 1911}年 ${month}月"
                else -> "明治以前"
            }
        }
    
        override fun onDestroy() {
            super.onDestroy()
            handler.removeCallbacks(updateTask)
        }
    }
    
    AndroidManifest.xml
    <?xml version="1.0" encoding="utf-8"?>
    <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"
            android:fullBackupContent="@xml/backup_rules"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/Theme.HimekuriCalendar"
            tools:targetApi="31">
            <activity
                android:name=".MainActivity"
                android:exported="true">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>

    Google Sheetの App Script

    function doGet() {
      const sheetId = 'あなたのスプレッドシートID'; // 必ずここを自身のシートIDに置き換えてください
      const sheetName = 'メッセージ';
    
      try {
        const ss = SpreadsheetApp.openById(sheetId);
        const sheet = ss.getSheetByName(sheetName);
        if (!sheet) return ContentService.createTextOutput('シートが見つかりません');
    
        const data = sheet.getDataRange().getValues(); // A列〜C列をすべて取得
        const now = new Date();
    
        const messages = data
          .slice(1) // ヘッダー除外
          .map(row => {
            const datePart = row[0]; // A列:日付
            const timePart = row[1]; // B列:時刻(Date型、ただし年月日が1970-01-01になる可能性あり)
            const message = row[2];  // C列:メッセージ
    
            // 日付と時刻を統合
            const fullDate = new Date(
              datePart.getFullYear(),
              datePart.getMonth(),
              datePart.getDate(),
              timePart.getHours(),
              timePart.getMinutes(),
              timePart.getSeconds()
            );
    
            return {
              datetime: fullDate,
              message: message
            };
          })
          .filter(item => item.datetime >= now)
          .sort((a, b) => a.datetime - b.datetime)
          .slice(0, 3)
          .map(item => {
            const d = item.datetime;
            const day = ('0' + d.getDate()).slice(-2);
            const weekdays = ['日', '月', '火', '水', '木', '金', '土'];
            const weekday = weekdays[d.getDay()];
            const formatted = `${day}日(${weekday}) ${item.message}`;
            return formatted;
          });
    
        const result = { messages };
    
        return ContentService
          .createTextOutput(JSON.stringify(result))
          .setMimeType(ContentService.MimeType.JSON);
    
      } catch (e) {
        return ContentService.createTextOutput('エラー: ' + e.message);
      }
    }

    ChatGPTに送信したプロンプト

    最初期のプロンプト

    actyvity_main.xmlを調整後、日付を表示するロジックを再生成

    Google Sheets APIの生成の後、表示機能を組み込んだロジックを生成

    コメント