はじめに

アンドロイドスマホアプリI'm a bikerについての開発ドキュメントを公開します。

 

I'm a bikerとは

I'm a biker(これ以降本アプリ)は、Stravaに記録されている自分が乗っているロードバイクの走行データを使って簡易的なサービスを提供するためのアンドロイドスマホアプリです。

Stravaは世界有数のフィットネスプラットフォームで、30種類以上のスポーツタイプを記録でき、新しいトレーニング場所の発見や、コミュニティとの繋がり、進捗管理をサポートするウェッブサービスです。

本アプリは、Stravaの数あるスポーツタイプの中でロードバイクにのみに特化し、記録されているアクティビティのデータをダウンロードし、以下の簡易的なサービスを提供します。

  • ユーザーが使用したロードバイクの情報を保存し管理します。
  • Strava APIを使用してアクティビティのデータを取得し管理します。
  • 取得したアクティビティデータは、スマホのデータベースとして保存し、スクロールビュー上に表示します。
  • アクティビティデータ内のマップのポリラインデータからGoogle Map上に走行ルートを描画し、そのスクリーンショットを画像としてスマホに保存し、データベースとリンクすることでスクロールビュー上に表示します。
  • ロードバイクのメンテナンス情報を保存し管理します。
Strava API

Stravaが提供するAPI(Application Programming Interface)で、アプリケーションの開発者が様々なStravaデータセットに対するアクセスを可能にするインターフェースが公開されています。

 

ポリラインデータ

Strava APIで取得することが出来るデータセットには、アスリートがロードバイクで走行したルートに関するマップ情報があります。 このうち走行ルートのポリラインデータを使うことで、Google Map上に走行ルートの軌跡を描画することが出来ます。 本アプリケーションは、このポリラインデータを使ったGoogle Map上の走行ルートをスクリーンショットで画像として保存し、データベースのスクロールビュー上に表示することで、どの辺を走行したのかを一目で分かるようにしています。

 

メンテナンス情報

ユーザーは、使用するロードーバイクを登録することが出来ます。登録したロードバイク情報には、タイヤやチェーンなどの消耗品に対する交換時期を設定することが出来ます。 本アプリケーションは、走行距離に基づいて消耗品の交換時期をチェックし、ユーザーに通知することでロードバイクのメンテナンス時期を知らせることが出来ます。

 

Android Studio

本アプリは、Android用のスマホアプリです。その開発には、Android Studioを使用しました。 Android Studioは Androidアプリ開発用の公式の統合開発環境(IDE)です。 詳細は、Android Studioを参照してください。

開発環境は、Windows 11のパソコンです。この時アカウントに日本語を使っていると、Android Studio実行時に正しく機能しないことがあるため、英語名でのアカウントを作成する必要があります。 日本語のアカウントでAndroid Studioをインストールすると、この最初の段階で相当トラブルことになります。

また、Android Studioには、Android Emulatorが附属しています。これを使うと、様々なAndroidスマホやタブレットのデバイスを画面上で仮想的に実現し、アプリを評価することが出来ます。 ただし、これらをエミュレートするには、デバイス一つに対して相当ディスク容量を使います。簡単に10GBを超えてしまい、初期設定はCドライブであるため、あっという間にディスクフルになってしまいます。

Android Studioには、実機(スマホ)でアプリを実行することも出来ます。私の場合、普段使いのスマホ以外にSIMなしの古いスマホがあったので、それを評価用に使っています。 Emulatorを使うより、起動も速いし問題なく評価が出来ます。

 

実機でアプリを動かす

次の手順で行います。

 

 

 

 

 

Android Studioでアプリを実行すると、プログラムがスマホにアップロードされて、スマホ上でアプリが実行され、デバッグも行うことが出来ます。 また、Android Studio上での実行を停止した後であっても、スマホ単体でアプリを実行することも出来ます。

  • スマホで設定デバイス情報を開きます。
  • ビルド番号を7回タップします。
  • 設定に戻って、システムを開きます。
  • 開発者向けオプションが追加されているので、これを開きます。
  • ワイヤレスデバッグを開いて、ワイヤレスデバッグの使用をオンにします。
  • パソコンでAndroid Studioを起動し、Device ManagerPair Devices Using Wi-Fiをクリックすると、QRコード画面が表示されます。
  • スマホに戻って、ワイヤレスデバッグQRコードによるデバイスのペア設定をクリックして、下記のQRコードを読み込みます。
  • 接続に成功すると、Android StudioのDevice Managerにスマホ名が追加されます。

認証コードを取得する

Stravaから認証コードを取得するには、Strava APIを利用してStravaのホームページから情報を取得する必要があります。 ここでは、その技術的方法を説明します。 Strava APIに関しては、Getting Startedのドキュメントを参照ください。
それでは、Stravaから認証コードを取得する方法を説明します。

  • Getting StartedのD. How to AuthenticateからURLは次に定義されている。
    URL:http://www.strava.com/oauth/authorize?client_id=[REPLACE_WITH_YOUR_CLIENT_ID]&response_type=code&redirect_uri=http://localhost/exchange_token&approval_prompt=force&scope=read> 

    実際のアドレスは、https://www.strava.com/oauth/mobile/authorizeに置き換えます。
  • パラメータの詳細は、Details About Requesting Accessを参照ください。
  • [REPLACE_WITH_YOUR_CLIENT_ID]は、本アプリの使い方はじめにで取得したクライアントIDを設定します。
  • response_typeは、"code"を設定することで、認証コードを要求します。
  • redirect_uriは、認証コードを返送先を指定するもので、"http://localhost"を設定します。
  • approval_promptは、"force"か"auto"ですが、"auto"を設定します。
  • scopeは、"activity:read_all"を設定して問題ありません。

このURLを元に、startActivityを起動します。

 

    val intentUri = "https://www.strava.com/oauth/mobile/authorize".toUri()
        .buildUpon()
        .appendQueryParameter("client_id",       gbv_clientId)
        .appendQueryParameter("redirect_uri",    REDIRECT_URI)
        .appendQueryParameter("response_type",   RESPONSE_TYPE)
        .appendQueryParameter("approval_prompt", APPROVAL_PROMPT)
        .appendQueryParameter("scope",           SCOPE)
        .build()

    val intent = Intent(Intent.ACTION_VIEW, intentUri)
    startActivity(intent)

 

Webブラウザで次の画面が表示されます。

 

このStravaの画面に従って、認証コードを取得します。 詳しくは、本アプリの使い方認証コードの取得を参照してください。

次にStravaからアクセストークンを取得する方法を説明します。

 

アクセストークンを取得する

Stravaからアクティビティのデータを取得するには、Strava APIを利用してStravaのホームページから情報を取得する必要があります。
本アプリでは、Retrofitを使ったHTTP通信を行い、そのレスポンスでデータを取得しています。 Retrofitは、HTTPクライアント通信ができるライブラリで、使用できるHTTPメソッドには、GET、POST、PUT、PATCH、DELETE、OPTIONS、HEADがあります。 Retrofitでは、使用するAPIをインターフェースとアノテーションにて定義します。 Retrofitの実態は、OkHttpのラッパーであり、OkHttpClientを使用して機能を拡張することができます。

  • アプリをインターネットに接続する許可を与えるため、AndroidManifest.xmlに以下のタグを追加します。
    <uses-permission android:name="android.permission.INTERNET"/>
  • build.gradle.ktdにOkHttpモジュールへの参照を追記します。
    implementation(libs.okhttp)
  • Tokenの取得には、非同期通信(enqueue)を使います。
    val client = OkHttpClient()
    val request = Request.Builder()
        .url("https://www.strava.com/api/v3/oauth/token")
        .post(formBody)
        .header("Content-Type", "application/x-www-form-urlencoded")
        .build()
    try {
        val call = client.newCall(request)
        call.enqueue(object : Callback {
            override fun onFailure(call: Call, ex: IOException) {}
            @RequiresApi(Build.VERSION_CODES.O)
            override fun onResponse(call: Call, response: Response) {
                val jsonData = response.body!!.string()
                if (!response.isSuccessful) {
                    // 受信エラー処理
                    return
                }
                // JSONによるデータ抽出処理
            }
        })
    } catch (ex: Exception) {
        // 例外処理
    }
     
  • 取得したデータは、JSONによって処理し抽出します。
    例えば、Tokenを処理する場合は、次のようにプロファイルを定義します。
    data class TokenProfile(
        @SerializedName("token_type")    var tokenType: String,
        @SerializedName("expires_at")    var expiresAt: Int,
        @SerializedName("expires_in")    var expiresIn: Int,
        @SerializedName("refresh_token") var refreshToken: String,
        @SerializedName("access_token")  var accessToken: String,
        @SerializedName("athlete")       var athlete: TokenAthlete?
    )   
    これを使って、次のコードでデータを抽出します。
    var profile = Gson().fromJson(jsonData, TokenProfile::class.java)
    
    例えば、アクセストークンは、profile.accessTokenを使ってデータを取得できます。

アクティビティデータを取得する

  • 走行データ等のアクティビティのデータの取得には、同期通信(execute)を使います。
    これは、これは大量のデータを分割受信し、データを加工する処理を繰り返し行うようにするためです。
    例えば、Strava APIのList Athlete Activitiesを使ってデータを取得する場合は
    val stravaUri = "https://www.strava.com/api/v3/athlete/activities".toUri()
        .buildUpon()
        .appendQueryParameter("page", pageNo)
        .appendQueryParameter("per_page", noOfPage)
        .build()
    val request = Request.Builder()
        .url(stravaUri.toString())
        .header("Authorization", "Bearer $gbv_accessToken")
        .build()
    
    responseData = ""
    try {
        runBlocking {
            val response = httpGET(request).await()
            if (response!!.isSuccessful) {
                responseData = response.body!!.string()
            } else {
                // 受信エラー処理
            }
        }
    } catch (ex: Exception) {
        // 例外処理
    }
    
  • responseDataにpageNoに該当するページのnoOfPageに相当するデータが格納されます。
    これを全ページ分繰り返して、全データを取得します。取得したデータは、JSONを使ってデータを抽出します。

Vol.2へ

Strava APIを使ったアンドロイドスマホアプリを作る Vol.2