0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python版を踏まえて――Kotlin × Android で “Twitter 投稿アプリ” を作る

Posted at

はじめに

前回の記事ではTwitter依存解消のため、 Python + Tkinter を使ってTwitterに投稿できるシンプルなデスクトップアプリを作成しました。今回はその延長として、Androidアプリ をKotlinで実装しました。
タイムラインを閲覧せず、投稿だけをモバイルで完結させたいというニーズに対応するものです。

対象読者

  • Android Studio が動く環境を持っている方
  • Twitterに投稿するシンプルな機能だけを使いたい方

ゴール・動作概要

  • アプリ上でテキストを入力し、「ツイートする」ボタンを押すと投稿できる
  • Twitter API v2 の投稿に対応するため、jp.takke.twitter4j-v2 という非公式のラッパーライブラリを導入しています(Twitter4J の拡張版)。公式APIとは異なり一部制約や仕様差があるため、実運用では事前検証をおすすめします

screenshot_20250507-053703_720.png

必要な準備

1. Twitter API キーの取得

Twitter Developer Portalにて、以下の情報を取得してください。

  • Consumer Key
  • Consumer Secret
  • Access Token
  • Access Secret

取得方法は「Twitter API認証情報の取得方法」に書いてあります

※本稿ではコード中に直接記載していますが、実運用では BuildConfiglocal.properties を使って外部管理することを推奨します。

2. 開発環境

項目 内容
Android Studio Meerkat 2024.3.1 Patch 1
Kotlin 2.0.21
AGP (Android Gradle Plugin) 8.9.1
minSdk / targetSdk 24 / 35

プロジェクト構成

app/
├─ manifests/
│  └─ AndroidManifest.xml
├─ java/com/example/simpletweetpost/
│  ├─ MainActivity.kt
│  └─ test/ExampleUnitTest.kt
│  └─ androidTest/ExampleInstrumentedTest.kt
├─ res/
│  ├─ layout/activity_main.xml
│  ├─ values/
│  │  ├─ strings.xml
│  │  ├─ colors.xml
│  │  └─ themes.xml, themes.xml (night)
│  ├─ drawable/
│  └─ mipmap/
│      └─ ic_launcher などアイコン類
├─ build.gradle.kts
├─ libs.versions.toml

test / androidTest フォルダには自動生成されたテストファイルが含まれます。本アプリでは利用していませんが、ユニットテストやUIテストを導入する際のベースになります。

実装コード

MainActivity.kt

package com.example.simpletweetpost

import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import twitter4j.TwitterFactory
import twitter4j.conf.ConfigurationBuilder
import twitter4j.v2 // 重要:拡張プロパティのインポート

class MainActivity : AppCompatActivity() {

    // Twitter API 認証情報
    private val CONSUMER_KEY    = "YOUR_CONSUMER_KEY"
    private val CONSUMER_SECRET = "YOUR_CONSUMER_SECRET"
    private val ACCESS_TOKEN    = "YOUR_ACCESS_TOKEN"
    private val ACCESS_SECRET   = "YOUR_ACCESS_SECRET"

    private lateinit var tweetEditText: EditText
    private lateinit var tweetButton: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // UIコンポーネントの取得
        tweetEditText = findViewById(R.id.tweetEditText)
        tweetButton = findViewById(R.id.tweetButton)

        // ツイートボタンのクリックリスナー
        tweetButton.setOnClickListener {
            val tweetText = tweetEditText.text.toString().trim()

            if (tweetText.isEmpty()) {
                showAlertDialog("警告", "ツイート内容が空です。入力してください。")
                return@setOnClickListener
            }

            postTweet(tweetText)
        }
    }

    private fun postTweet(message: String) {
        // UIスレッドをブロックしないようにコルーチンを使用
        CoroutineScope(Dispatchers.IO).launch {
            try {
                // Twitter認証設定
                val cb = ConfigurationBuilder()
                cb.setOAuthConsumerKey(CONSUMER_KEY)
                cb.setOAuthConsumerSecret(CONSUMER_SECRET)
                cb.setOAuthAccessToken(ACCESS_TOKEN)
                cb.setOAuthAccessTokenSecret(ACCESS_SECRET)
                cb.setJSONStoreEnabled(true) // JSONサポートを有効に

                val twitter = TwitterFactory(cb.build()).instance

                // Twitter API v2を使ってツイートを投稿
                val response = twitter.v2.createTweet(text = message)

                // UIスレッドに戻ってダイアログを表示
                withContext(Dispatchers.Main) {
                    showAlertDialog("成功", "ツイートが投稿されました!\nツイートID: ${response.id}")
                    tweetEditText.text.clear()
                }
            } catch (e: Exception) {
                withContext(Dispatchers.Main) {
                    showAlertDialog("エラー", "ツイートの投稿中にエラーが発生しました: ${e.message}")
                }
            }
        }
    }

    private fun showAlertDialog(title: String, message: String) {
        AlertDialog.Builder(this)
            .setTitle(title)
            .setMessage(message)
            .setPositiveButton("OK", null)
            .show()
    }
}

(投稿成功後のダイアログを表示させない場合は showAlert() 呼び出しを削除)

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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/labelTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="@string/title"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/tweetEditText"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_marginTop="8dp"
        android:background="@android:drawable/editbox_background"
        android:gravity="top|start"
        android:hint="@string/input_hint"
        android:inputType="textMultiLine"
        android:padding="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/labelTextView" />

    <Button
        android:id="@+id/tweetButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="@string/tweet_button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tweetEditText" />

</androidx.constraintlayout.widget.ConstraintLayout>

strings.xml

<resources>
    <string name="app_name">SimpleTweetPost</string>
    <string name="input_hint">ここにツイート内容を入力</string>
    <string name="title">ツイート内容を入力してください:</string>
    <string name="tweet_button">ツイートする</string>
</resources>

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.SimpleTweetPost"
        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>

build.gradle.kts(抜粋)

dependencies {
    implementation("org.twitter4j:twitter4j-core:4.0.7")
    implementation("io.github.takke:jp.takke.twitter4j-v2:1.4.1")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
    // その他の依存は libs.versions.toml に記載
}

実装のポイント

GUI: ConstraintLayout + EditText + Button の最小構成

認証処理: Twitter4J に API キーを設定して OAuth 認証

非同期処理: Coroutine で非同期処理を実装、UIスレッドをブロックしない

結果表示: 投稿完了/失敗を AlertDialog で通知

ビルドと実行手順

必要に応じて API キーを環境に応じて埋め込む

Android Studio で実行

入力 → 投稿ボタン → 結果表示

まとめ

Kotlin + Twitter4J でシンプルなTwitter投稿アプリが構築可能

投稿専用アプリなら、最小限の構成で十分動作する

実運用時はAPIキーの管理と投稿制限(Rate Limit)に注意すること

注意

無料プランでは、API経由での投稿上限が 1日あたり17件 に制限されています(2025/04/21時点)。上限を超える可能性がある場合は、有料プランの導入や投稿タイミングの調整を検討してください。

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?