20200925のAndroidに関する記事は8件です。

Android でFIS_AUTH_ERRORエラー Firebaseプッシュ通知が届かない

はじめに

3864.jpg

運営しているアプリを機種変更してから初めて起動すると突如、このようなエラーに見舞われた。。。。

環境

Ionic 5 + Angular で開発したのネイティブアプリ
Ionic プッシュ通知を送るテスト(Capacitor)
:point_up:この記事と全く同じやり方でプッシュ通知を実装しました。

解決までにやったこと

※結論から言うと、解決はしてません。。。

Google Cloud PlatformでAPIの制限を追加

スクリーンショット 2020-09-25 19.45.24.jpg

ずっと、「キーを制限しない」になっていたが、追加してみた。

The FIS_AUTH_ERROR means Authentication for Firebase installation sdk has failed.

https://stackoverflow.com/questions/60698622/java-io-ioexception-fis-auth-error-in-android-firebase

と言うことらしいので。

Firebase SDK を追加する

こちらを参考に、app/build.gradleに以下を追加

dependencies {
    // 省略
    implementation 'com.google.firebase:firebase-messaging:20.1.2'
    implementation 'com.google.firebase:firebase-analytics:17.5.0'
}

しかしながら、Using Push Notifications with Firebase in an Ionic + Angular Appのドキュメントでは、以下のような記述がある。

We don't need to add any dependencies to our project because Capacitor projects automatically include a version of firebase-messaging in it's build.gradle file.

何もdependenciesに追加する必要はない、と言っているが、、、とりあえず追加してみよう。

ゾウさんボタンでアプリを同期
スクリーンショット 2020-09-25 20.20.58.jpg

google-services.jsonを新しく入れ替える

Firebaseコンソール画面からプロジェクトを選択>設定>マイアプリの項目から新しくgoogle-services.jsonをダウンロード。

そして置き換える。

エラーは消えたが、プッシュ通知を目視することはできなかった!!!:pensive:

2020-09-25 19:35:13.665 15955-15955/com.hCalendar.app I/Capacitor/Console: File: http://localhost/common.js - Line 371 - Msg: プッシュ通知を受け取ったよ: {"id":"0:1601030113498713%b2260e4cb2260e4c","data":{},"title":"寒くなりましたね。。","body":"人肌を重ねてあったまりましょう"}
2020-09-25 19:35:13.811 15955-16488/com.hCalendar.app D/FA: Connected to remote service
2020-09-25 19:35:48.363 15955-15955/com.hCalendar.app I/Ads: Use RequestConfiguration.Builder().setTestDeviceIds(Arrays.asList("01C5204CC8216A22615E0A6DF7BDDD97") to get test ads on this device.
2020-09-25 19:35:48.393 15955-16288/com.hCalendar.app W/m.hCalendar.ap: Accessing hidden method Lsun/misc/Unsafe;->getObject(Ljava/lang/Object;J)Ljava/lang/Object; (greylist, linking, allowed)
2020-09-25 19:35:48.393 15955-16288/com.hCalendar.app W/m.

結局エラーは消えたが、プッシュ通知は確認できなかった。。。(コンソール上のログでは確認できてるので、一応届いてはいる???:thinking:

うーん。。。どうすればいいんだろう??
どなたか情報提供お願いします:sweat_smile:

参考

https://stackoverflow.com/questions/60698622/java-io-ioexception-fis-auth-error-in-android-firebase

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

12. 【Android/Kotlin】丸いイメージ(CircleImageView)

はじめに

DreamHanksのMOONです。

前回はToastという通知メッセージについて説明ししました。
11. 【Android/Kotlin】Toast

今回はCircleImageViewという外部ライブラリを使用していきます。

ライブラリを追加する方法については下記のリンクで確認してください。
10. 【Android/Kotlin】ライブラリを追加

CircleImageViewとは

皆さんはラインや他のアプリで丸い写真のイメージを見たことがあると思います。

それがCircleImageViewで変換されたイメージです。

CircleImageViewは原本のイメージを丸いイメージに変換してくれるImageViewのライブラリです。

CircleImageView追加

・CircleImageViewのライブラリを追加
3.PNG
buile.gradleにのdependenciesに下記のコードを追加

    implementation 'de.hdodenhof:circleimageview:3.1.0'

・Activityを作成

CircleImageViewActivity.kt
package com.example.practiceapplication

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.*

class CircleImageViewActivity : AppCompatActivity() {

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

    }

}

・レイアウトのxmlファイルを作成
1.PNG

activity_circleimageview.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    tools:context=".CircleImageViewActivity"
    android:gravity="center">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="原本のイメージ"/>

    <ImageView
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:id="@+id/org_iv"
        android:src="@drawable/dreamhanks"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="丸いイメージ"/>

    <de.hdodenhof.circleimageview.CircleImageView
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:src="@drawable/dreamhanks"
        android:id="@+id/circle_iv"/>
</LinearLayout>

CircleImageViewのViewタグを追加します。

アプリ起動

終わりに

今回はCircleImageViewという外部ライブラリを使用してみました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Chrome Custom Tabs で強制的に Chrome で表示する方法

概要

例えば AndroidManifest.xml の <intent-filter>

AndroidManifest.xml
<intent-filter>
    <action android:name="android.intent.action.VIEW" />

    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />

    <data
        android:host="example.com"
        android:scheme="https" />
</intent-filter>

と定義していた場合、Chrome Custom Tabs で https://example.com/ のページを表示しようとすると
intent-filter の条件に一致するため、

こんな感じで Chrome 以外の候補が表示されてしまいます。

この候補モーダルを表示させずに強制的に Chrome で表示する方法です。

方法

MainActivity.kt
val builder: CustomTabsIntent.Builder = CustomTabsIntent.Builder()
val customTabsIntent: CustomTabsIntent = builder.build()

if (packageManager.getLaunchIntentForPackage("com.android.chrome") != null) {
    customTabsIntent.intent.setPackage("com.android.chrome")
}

customTabsIntent.launchUrl(this, Uri.parse(url))

setPackage() で Chrome のパッケージ名を指定してあげるだけです。

念のため getLaunchIntentForPackage() で Chrome が存在しているかチェックしています。
↑ では存在しなかったときの処理はなにも行っていませんが本来であればエラーメッセージを表示するなどしてあげたほうが良いかと思います。

以上です。

参考

[stackoverrun] android - Chromeのカスタムタブとインテントフィルタ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

生成された図で見るDagger Hiltへのマイグレーション

Dagger Hiltへのマイグレーションのコードラボは数ヶ月前にやっていて忘れてきたので、どういう内容だったのか後から振り替えれるようにグラフをおいておきます。
ちょっと先にDagger Hiltのチュートリアル的なのは他の記事などでご確認ください。

https://codelabs.developers.google.com/codelabs/android-dagger-to-hilt/#1

またDagger SPIを使ったいい感じのGradle Pluginも見つけたのでそれで見ていきます。
https://github.com/arunkumar9t2/scabbard

マイグレーション前

Daggerでは、DaggerのComponent、DIコンテナでオブジェクトをインスタンス化するためのロジック(コンストラクションロジック)とインスタンスが保持されています。
AppComponentにはアプリケーション全体で保持するコンストラクションロジックとインスタンスを保持しており、RegistrationComponentには登録のときに使うコンストラクションロジックとインスタンスが入っています。

コンポーネントの木
image.png
今回はSubComponentという仕組みが使われており、コンポーネント同士が依存することができ木構造になっており、RegistrationComponentではAppComponentの内容が使えるようになっています。

コードでのSubComponentの作り方(詳細は説明しません)

@Singleton
@Component(modules = [StorageModule::class, AppSubcomponents::class])
interface AppComponent {
...
     // サブコンポーネントのFactoryを取得できるようにする
    fun registrationComponent(): RegistrationComponent.Factory
    fun loginComponent(): LoginComponent.Factory
...
}
@Module(
    subcomponents = [ // サブコンポーネントを指定する
        RegistrationComponent::class,
        LoginComponent::class,
        UserComponent::class
    ]
)
class AppSubcomponents

@ActivityScope
@Subcomponent
interface RegistrationComponent {
    @Subcomponent.Factory
    interface Factory {
        fun create(): RegistrationComponent
    }
...
}

そしてRegistrationComponentとLoginComponentでは @ActivityScopeと書いており、これを使うことでこのComponentのDIコンテナを指定してインスタンスを配布したり、同じインスタンスを共有したりできるようにしているようです。 (このサンプルでは、ViewModelがこの@ActivityScopeになっている)

@ActivityScope
@Subcomponent
interface LoginComponent {
...
}
@ActivityScope
class RegistrationViewModel @Inject constructor(val userManager: UserManager) {


詳細なグラフ

AppComponent
image.png

RegistrationComponent
image.png

LoginComponent
image.png

UserComponent
com.example.android.dagger.user.UserComponent.png


中間までマイグレーション

Dagger HiltではDaggerのアプリのコンポーネントの構造を標準化するので、このようにActivityComponent(内部的にはActivityRetainedC)などのComponentが追加されています。
中間までのマイグレーションなので、UserComponentが残っています。

コンポーネントの木
image.png

Dagger Hiltのコード生成によって、ActivityComponent(内部的にはActivityRetainedC)など勝手にSubCompopnentが作られます。
しかし、UserComponentは自分で作ったComopnentなので、生成させられません。
どのようにしたかというと、InstallInでApplicationComponentにModuleを追加し、そこでsubcomponentsを指定しています。

@InstallIn(ApplicationComponent::class)
@Module(
    subcomponents = [
        UserComponent::class
    ]
)
class AppSubcomponents

そしてUserComponent.FactoryをUserManagerにInjectさせることで、UserComponentを作成し利用できます。

@Singleton
class UserManager @Inject constructor(
    private val storage: Storage,
    private val userComponentFactory: UserComponent.Factory
) {

ただ、UserComponentはDagger Hiltの外のComponentなので、このUserComponentの管理は自分で行う必要があります。

@Singleton
class UserManager @Inject constructor(
    private val storage: Storage,
    private val userComponentFactory: UserComponent.Factory
) {


    var userComponent: UserComponent? = null
        private set
...

    fun registerUser(username: String, password: String) {
...
        userJustLoggedIn()
    }

    fun loginUser(username: String, password: String): Boolean {
...
        userJustLoggedIn()
        return true
    }

    fun logout() {
        // When the user logs out, we remove the instance of UserComponent from memory
        userComponent = null
    }

    private fun userJustLoggedIn() {
        userComponent = userComponentFactory.create()
    }
}

ここでUserComponentによるInjectを使う上で一つ問題があります。
MainActivityでUserComponentによるInjectが使うためには、UserManagerのインスタンスが必要です。UserComponentのDIコンテナでInjectしたいのでMainActivityには@AndroidEntryPointをつけてHiltのInjectを使うことができません。
こういうときにDagger HiltのDIコンテナが持つインスタンスを受け取る方法があります。 EntryPointAccessorを使うというもので、これによってUserManagerを取得して、UserManagerのもつuserComponentでinjectが可能になります。

class MainActivity : AppCompatActivity() {
    @InstallIn(ApplicationComponent::class)
    @EntryPoint
    interface UserManagerEntryPoint {
        fun userManager(): UserManager
    }
...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Grabs instance of UserManager from the application graph
        val entryPoint = EntryPointAccessors.fromApplication(applicationContext, UserManagerEntryPoint::class.java)
        val userManager = entryPoint.userManager()
...
            userManager.userComponent!!.inject(this)
...
    }


詳細なグラフ

AppComponent
image.png

ActivityComponent
image.png

UserComponent
image.png


マイグレーション後

完全にUserComponentが削除されており、HiltのComponentが完全に使われています。
image.png

ちなみに@SingletonなUserRepositoryを使って、その中でログイン中かなどを管理することで、解決しているようです。


詳細なグラフ

image.png
image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

エンジニア就職に向けてマッチングアプリ(Androidアプリ)を作ってみた

はじめに

こんにちは。タイトルにもある通りエンジニア就職のためにマッチングアプリ『Match-com(マッチコン)』を作成しました。
開発期間はおおよそ1ヶ月半です。
1年ちょっと前まではSESに9ヶ月ほど在籍していました。それ以降は少しエンジニアから離れて別のことをやっていましたが最近になってまたこの業界に戻ってきました。(プログラミングの学習自体は続けてました)

どんなアプリ?

feature.png

男性と女性のマッチングアプリです。皆さんも一度は使ったことがあるんじゃないでしょうか。
お互いがいいねをしたらマッチングしてチャットができるようになります。

主な機能
・Firebase
・メールログイン/Facebookログイン
・プロフィール登録
・いいね!
・マッチング
・リアルタイムチャット
・ブロック
・写真登録/プロフィール編集
・新規メッセージとマッチング時のPush通知
・退会

独自の機能を盛り込んで今あるサービスと差別化を図る予定でしたが細かいところまで拘っていたら思った以上に開発に時間がかかってしまいました。
とりあえず最低限の機能は実装して形にはなったので一旦これで就職活動を始めて落ち着いたらまた色々と機能を追加していく予定です。

※現在、丸2日経っても審査中のためまだリリースができていません。リリース次第こちらにURLを貼り付けます。

苦労したこと

バグ
バグには苦労させられました。一度、完成したと思っても開発を進めていくうちにおかしな挙動を発見したりアプリ自体が落ちてしまうこともありました。

達成感に満ち溢れてるところから落とされるのはかなりきついです。何度もアタックしてやっと付き合えた彼女に3日で振られるような、そんな感覚です。

設計の甘さ
今まで自分がやったことのない機能の開発は初めてのことばかりでかなり時間がかかりました。
これまでも簡単なアプリなら作ったことはあったのですが大体今までの知識の流用で事足りていました。

しかし、今回は割と本格的に作ったので色々と調べて自分のコードで動作するように改変しながら実装していきました。特に最初にちゃんとした設計をやっていなかったところもあり、何度も書いたコードを修正したりと設計の重要性を再認識できた一方で変なところで時間が取られてしまったりともう少し効率よく開発できれば良かったと思います。

わからない言葉が多い
調べごとをしているとわからない言葉がたくさん出てきます。必要そうなことは都度調べて開発していたのですがアプリの完成を第一目標にした場合、さすがに全ての言葉を一回一回調べていると時間がなくなるのでとりあえず後回しにした言葉も結構ありました。実際に開発現場でそういう言葉がバンバン飛び交うのを想像すると少し気が重くなりました。

開発してみて良かったこと

ドキュメントを読む癖
今回の開発ではFirebaseを本格導入したのですがそこでドキュメントを読み解く癖がついたのがいい経験になりました。
もちろん、Qiitaの記事や日本語の参考資料など有益なものもたくさんありましたが細かい話になるとドキュメントや英語の記事を読み漁る必要があったので結果的にドキュメントに対するアレルギーというものが大幅に軽減できました。

英語の重要性
よく「エンジニアになるのに英語は必要か」みたいな議論がありますがエンジニアに就職するためなら必ずしも必要というわけではないですがこれから自分でプログラミングを学んでいく上で絶対できた方が効率いいよね、と思う場面は何度もありました。
日本語で探してやっとたどり着いた記事の内容が英語で検索すると一番上にある、なんていうのはザラにあったからです。今回のアプリを日本語の資料だけで実装しろと言われたらかなり時間がかかると思います。

情報を整理する力
簡単な実装ならチュートリアル的なものを見つけてちょこっと修正すればそれで済むと思います。
しかし、複雑なことをしようと思うと当然1つの情報源で全てが解決するということは稀でインターネット上のいくつかの情報を組み合わせて自分のケースに落とし込むというのが必要になってきます。
しっかりとしたアプリケーションを作ろうと思うと、こうした「情報を総合して解釈する能力」というのが自然と身に付いてくると感じました。

やり切った自信
アプリを作る前と後だとだいぶ開発に対しての自信がつきました。
最初は本当に完成させられるのか不安が90%くらいでしたが実際に完成させてみると、ある程度のアプリなら作れそうと思えるようになりました。
やはり教材でずっと勉強しているよりも何かを作った方が応用力がつくので結果的に実力向上に結びつきやすいです。結局は作りながら学ぶのが一番です。
ただ最初の知識が少なすぎるとパワーで無理やり実装してしまうところも出てくるのである程度体系だった知識を学んでから、実際に作ってみて今ある知識に肉付けしていくというやり方でもいいと思います。

アプリ紹介

以上で作ったアプリの簡単な概要はおしまいです。これ以降は作ったアプリの紹介になるので時間がある方は見てみてください。(少し長くなります!)
基本的に上で列挙した機能を順に紹介していくスタイルにしようと思います。直感的にわかるように文字は少なめで画像や動画を多めに使っています。gifではうまくupできなかったのでこちらを参考にしてTwitterに上げたものを使っています。

Firebase

今回はバックエンドにFirebaseを使用しました。NoSQLはRDBのようにテーブルの結合ができなかったため最初は戸惑いましたが慣れてくると非常に使い勝手が良くスムーズに使えるようになりました。

使用したサービス
・Authentication
・Realtime Database
・Cloud Firestore
・Cloud Storage(画像の保存に利用)
・Cloud Messaging(Push通知に利用)
・Dynamic Links(メールリンクに使用)

メールログイン / Facebookログイン

画面はこんな感じです。

login_screen.png

メールアドレスでログインする場合はメールを入力してサインインボタンを押すとパスコードが発行されるので送られてきたメールのリンクからアドレスと発行コードを入力して登録orログインします。

このメールリンクの機能はDynamic Linksを使用しています。
https://firebase.google.com/docs/dynamic-links/android/receive?hl=ja

issue_passcode.png
メールのリンクを開くと入力画面に遷移します。
enter_app.png

Facebookログインの場合は友達が10人以上いないと登録できません。

fb_signin.png

ユーザーの友達の数を取得するのにはFacebookのGraph APIというものを使用しました。
https://developers.facebook.com/docs/graph-api/overview

プロフィール登録

上記の方法でログインが完了するとそのユーザーが新規のユーザーかを判定します。

すでに存在するユーザーであればホームスクリーン、新規のユーザーであればプロフィール登録画面に進みます。ここのプロフィールは全て入力しないとボタンが非活性になります。
(※QiitaでうまくgifをupできなかったためTwitterを使ってます。)

いいね!

ホーム画面はこんな感じです。
discover.png

右スワイプでいいね、左スワイプでごめんね、となります。


写真をタップすると次の写真に移動できます。名前以下のところをタップするとユーザーの詳細画面に飛ぶことができます。

マッチング

相手も自分にいいねをしていた場合、そこでマッチング成立となります。

リアルタイムチャット

こちらがチャット一覧画面です。これとチャット画面に一番時間がかかりました。

「マッチングしたお相手」というのがマッチはしたが会話はしてない人、「会話中のお相手」が実際に会話中の人になります。マッチして24時間以内なら上にNew!とつきます。(黒で塗りつぶしてあるのはフリー素材以外の写真を使用していたからです。)
match_overview.png

「会話中のお相手」の写真の左上にある◯はそのユーザーのログインステータスになります。

緑 → オンライン
黄色 → 最終ログインから24時間以内
灰色 → 最終ログインから24時間以上

メッセージを送ると「会話中のお相手」に移動します。

チャット画面はこんな感じです。
chat_screen.png
意外にメッセージの上にある日付を入れるのに苦労しました笑
別にここは簡単な実装で良かったんですがなるべくreal-world project的なものを作りたかったので地味に拘ってしまいました。今日のメッセージは今日、昨日のものは昨日、それ以外は日付、今年以外のものは年も含めて表示するようにしています。

メッセージはもちろん写真も送信することができます。

またチャット画面のアイコンからを相手ユーザーの詳細画面に飛べます。

ブロック

ユーザーをブロックすると会話中の相手からいなくなります。

写真登録/プロフィール編集

プロフィール編集画面はシンプルなものにしました。
tatuya.png

写真はクロッピングして登録します。

メイン写真と入れ替えることもできます。

削除した写真が2枚目だった場合、3枚目の写真は左につめます。間を写真のない状態にしないということですね。

プロフィール編集ではユーザーの基本的な情報を編集できます。
edit_profile.png

趣味と言語を登録する時は入力したものがチップとなって溜まっていきます。最後に更新ボタンを押して更新完了です。

新規メッセージとマッチング時のPush通知

新着のメッセージを受信するとPush通知が送られてきます。


また文字だけでなく画像もPush通知で送ることができます。

Push通知を本格的に実装しようとすると結構大変でした。普通にやるとアプリを開いている時は受信できるが閉じると受信できなくなってしまうからです。アプリを閉じたときでも受信できるようにするには

1 : FirebaseのCloud Functionsを使用してRealtime Database更新のタイミングでPush通知を送るよう実装する
2 : FirebaseMessagingServiceを継承したクラスを作り、onMessageReceivedメソッドをオーバーライドして通知した際の処理を実装し、デバイス間の通信を実現

このどちらかの方法で実装する必要がありました。自分は2の方法で実装しました。デバイス間の通信にはRetrofitを利用しています。

1の方法でもNode.jsなどでローカルで実装してCLIからFirebaseにdeployすればイケると思います。

退会

最後に退会機能です。これはプロフィール編集画面の一番下から行うことができます。

まとめ

本格的にアプリを開発したのは初めてだったので作り終わったあとの達成感もありましたが同時に疲労感もありました。これからアプリを開発する人はなるべく設計を意識した方がいいと思います。

じゃないと何度もコードを修正したり、依存性の強いコードを書いたりしてしまいます。
Solid原則を常に意識しておくと良いでしょう。こちらの記事が参考になります。

こういうのを読んでみるのもいいかもしれません。(自分はまだ読んだことないですが)
https://www.amazon.co.jp/dp/B07FSBHS2V/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1

アウトプットは今回で十分やったのでしばらくはまたインプットの量を増やして学習を継続していこうと思います。

最後まで読んでいただき、ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

マッチングアプリ(Androidアプリ)を作りました

はじめに

こんにちは。エンジニア就職のためにマッチングアプリ『Match-com(マッチコン)』を作成しました。
開発期間はおおよそ1ヶ月半です。

どんなアプリ?

feature.png

男性と女性のマッチングアプリです。皆さんも一度は使ったことがあるんじゃないでしょうか。
お互いがいいねをしたらマッチングしてチャットができるようになります。

主な機能
・Firebase
・メールログイン/Facebookログイン
・プロフィール登録
・いいね!
・マッチング
・リアルタイムチャット
・ブロック
・写真登録/プロフィール編集
・新規メッセージとマッチング時のPush通知
・退会

独自の機能を盛り込む予定でしたが細かいところまで拘っていたら思った以上に開発に時間がかかってしまいました。
とりあえず最低限の機能は実装して形にはなったので一旦これで就職活動を始めて落ち着いたらまた色々と機能を追加していく予定です。

※現在、丸2日経っても審査中のためまだリリースができていません。リリース次第こちらにURLを貼り付けます。

苦労したこと

バグ
バグには苦労させられました。一度、完成したと思っても開発を進めていくうちにおかしな挙動を発見したりアプリ自体が落ちてしまうこともありました。

達成感に満ち溢れてるところから落とされるのはかなりきついです。何度もアタックしてやっと付き合えた彼女に3日で振られるような、そんな感覚です。

設計の甘さ
今まで自分がやったことのない機能の開発は初めてのことばかりでかなり時間がかかりました。
これまでも簡単なアプリなら作ったことはあったのですが大体今までの知識の流用で事足りていました。

しかし、今回は割と本格的に作ったので色々と調べて自分のコードで動作するように改変しながら実装していきました。特に最初にちゃんとした設計をやっていなかったところもあり、何度も書いたコードを修正したりと設計の重要性を再認識できた一方で変なところで時間が取られてしまったりともう少し効率よく開発できれば良かったと思います。

わからない言葉が多い
調べごとをしているとわからない言葉がたくさん出てきます。必要そうなことは都度調べて開発していたのですがアプリの完成を第一目標にした場合、さすがに全ての言葉を一回一回調べていると時間がなくなるのでとりあえず後回しにした言葉も結構ありました。

開発してみて良かったこと

ドキュメントを読む癖
今回の開発ではFirebaseを本格導入したのですがそこでドキュメントを読み解く癖がついたのがいい経験になりました。
もちろん、Qiitaの記事や日本語の参考資料など有益なものもたくさんありましたが細かい話になるとドキュメントや英語の記事を読み漁る必要があったので結果的にドキュメントに対するアレルギーというものが大幅に軽減できました。

英語の重要性
よく「エンジニアになるのに英語は必要か」みたいな議論がありますがエンジニアに就職するためなら必ずしも必要というわけではないですがこれから自分でプログラミングを学んでいく上で絶対できた方が効率いいよね、と思う場面は何度もありました。
日本語で探してやっとたどり着いた記事の内容が英語で検索すると一番上にある、なんていうのはザラにあったからです。今回のアプリを日本語の資料だけで実装しろと言われたらかなり時間がかかると思います。

情報を整理する力
簡単な実装ならチュートリアル的なものを見つけてちょこっと修正すればそれで済むと思います。
しかし、複雑なことをしようと思うと当然1つの情報源で全てが解決するということは稀でインターネット上のいくつかの情報を組み合わせて自分のケースに落とし込むというのが必要になってきます。
しっかりとしたアプリケーションを作ろうと思うと、こうした「情報を総合して解釈する能力」というのが自然と身に付いてくると感じました。

やり切った自信
アプリを作る前と後だとだいぶ開発に対しての自信がつきました。
最初は本当に完成させられるのか不安が90%くらいでしたが実際に完成させてみると、ある程度のアプリなら作れそうと思えるようになりました。
やはり教材でずっと勉強しているよりも何かを作った方が応用力がつくので結果的に実力向上に結びつきやすいです。結局は作りながら学ぶのが一番です。
ただ最初の知識が少なすぎるとパワーで無理やり実装してしまうところも出てくるのである程度体系だった知識を学んでから、実際に作ってみて今ある知識に肉付けしていくというやり方でもいいと思います。

アプリ紹介

以上で作ったアプリの簡単な概要はおしまいです。これ以降は作ったアプリの紹介になるので時間がある方は見てみてください。(少し長くなります!)
基本的に上で列挙した機能を順に紹介していくスタイルにしようと思います。直感的にわかるように文字は少なめで画像や動画を多めに使っています。gifではうまくupできなかったのでこちらを参考にしてTwitterに上げたものを使っています。

Firebase

今回はバックエンドにFirebaseを使用しました。NoSQLはRDBのようにテーブルの結合ができなかったため最初は戸惑いましたが慣れてくると非常に使い勝手が良くスムーズに使えるようになりました。

使用したサービス
・Authentication
・Realtime Database
・Cloud Firestore
・Cloud Storage(画像の保存に利用)
・Cloud Messaging(Push通知に利用)
・Dynamic Links(メールリンクに使用)

メールログイン / Facebookログイン

画面はこんな感じです。

login_screen.png

メールアドレスでログインする場合はメールを入力してサインインボタンを押すとパスコードが発行されるので送られてきたメールのリンクからアドレスと発行コードを入力して登録orログインします。

このメールリンクの機能はDynamic Linksを使用しています。
https://firebase.google.com/docs/dynamic-links/android/receive?hl=ja

issue_passcode.png
メールのリンクを開くと入力画面に遷移します。
enter_app.png

Facebookログインの場合は友達が10人以上いないと登録できません。

fb_signin.png

ユーザーの友達の数を取得するのにはFacebookのGraph APIというものを使用しました。
https://developers.facebook.com/docs/graph-api/overview

プロフィール登録

上記の方法でログインが完了するとそのユーザーが新規のユーザーかを判定します。

すでに存在するユーザーであればホームスクリーン、新規のユーザーであればプロフィール登録画面に進みます。ここのプロフィールは全て入力しないとボタンが非活性になります。
(※QiitaでうまくgifをupできなかったためTwitterを使ってます。)

いいね!

ホーム画面はこんな感じです。
discover.png

右スワイプでいいね、左スワイプでごめんね、となります。


写真をタップすると次の写真に移動できます。名前以下のところをタップするとユーザーの詳細画面に飛ぶことができます。

マッチング

相手も自分にいいねをしていた場合、そこでマッチング成立となります。

リアルタイムチャット

こちらがチャット一覧画面です。これとチャット画面に一番時間がかかりました。

「マッチングしたお相手」というのがマッチはしたが会話はしてない人、「会話中のお相手」が実際に会話中の人になります。マッチして24時間以内なら上にNew!とつきます。(黒で塗りつぶしてあるのはフリー素材以外の写真を使用していたからです。)
match_overview.png

「会話中のお相手」の写真の左上にある◯はそのユーザーのログインステータスになります。

緑 → オンライン
黄色 → 最終ログインから24時間以内
灰色 → 最終ログインから24時間以上

メッセージを送ると「会話中のお相手」に移動します。

チャット画面はこんな感じです。
chat_screen.png
意外にメッセージの上にある日付を入れるのに苦労しました笑
別にここは簡単な実装で良かったんですがなるべくreal-world project的なものを作りたかったので地味に拘ってしまいました。今日のメッセージは今日、昨日のものは昨日、それ以外は日付、今年以外のものは年も含めて表示するようにしています。

メッセージはもちろん写真も送信することができます。

またチャット画面のアイコンからを相手ユーザーの詳細画面に飛べます。

ブロック

ユーザーをブロックすると会話中の相手からいなくなります。

写真登録/プロフィール編集

プロフィール編集画面はシンプルなものにしました。
tatuya.png

写真はクロッピングして登録します。

メイン写真と入れ替えることもできます。

削除した写真が2枚目だった場合、3枚目の写真は左につめます。間を写真のない状態にしないということですね。

プロフィール編集ではユーザーの基本的な情報を編集できます。
edit_profile.png

趣味と言語を登録する時は入力したものがチップとなって溜まっていきます。最後に更新ボタンを押して更新完了です。

新規メッセージとマッチング時のPush通知

新着のメッセージを受信するとPush通知が送られてきます。


また文字だけでなく画像もPush通知で送ることができます。

Push通知を本格的に実装しようとすると結構大変でした。普通にやるとアプリを開いている時は受信できるが閉じると受信できなくなってしまうからです。アプリを閉じたときでも受信できるようにするには

1 : FirebaseのCloud Functionsを使用してRealtime Database更新のタイミングでPush通知を送るよう実装する
2 : FirebaseMessagingServiceを継承したクラスを作り、onMessageReceivedメソッドをオーバーライドして通知した際の処理を実装し、デバイス間の通信を実現

このどちらかの方法で実装する必要がありました。自分は2の方法で実装しました。デバイス間の通信にはRetrofitを利用しています。

1の方法でもNode.jsなどでローカルで実装してCLIからFirebaseにdeployすればイケると思います。

退会

最後に退会機能です。これはプロフィール編集画面の一番下から行うことができます。

まとめ

本格的にアプリを開発したのは初めてだったので作り終わったあとの達成感もありましたが同時に疲労感もありました。これからアプリを開発する人はなるべく設計を意識した方がいいと思います。

じゃないと何度もコードを修正したり、依存性の強いコードを書いたりしてしまいます。
Solid原則を常に意識しておくと良いでしょう。こちらの記事が参考になります。

こういうのを読んでみるのもいいかもしれません。(自分はまだ読んだことないですが)
https://www.amazon.co.jp/dp/B07FSBHS2V/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1

アウトプットは今回で十分やったのでしばらくはまたインプットの量を増やして学習を継続していこうと思います。

最後まで読んでいただき、ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android SDK platform-tools 旧バージョン インストール

結論

Xの部分を欲しいリビジョン番号に書き換えます。

https://dl.google.com/android/repository/platform-tools_rXX.X.X-darwin.zip

URLの雛形が変わった場合の対応方法

URLの雛形が変わる場合があるので、雛形入手の手順も記載しておきます。

URLの雛形

Android SDK platform-toolsに関する情報は下記を参照します。

https://developer.android.com/studio/releases/platform-tools.html

最新版は画面トップでインストールできるので、まずは最新版インストールのURLを入手しましょう。

image.png

image.png

入手したURL

https://dl.google.com/android/repository/platform-tools-latest-darwin.zip

欲しいリビジョン番号の入手

画面を下にスクロールすると過去のリビジョンについての情報が記載されてます。
欲しい番号を入手しましょう。
※画面上部にある言語設定はEnglishにしましょう。他の言語だと更新されてない場合があります。

https://developer.android.com/studio/releases/platform-tools.html

image.png

URLの雛形のtools-latestの部分を欲しいリビジョン番号で書き換えます。

https://dl.google.com/android/repository/platform-tools_r29.0.6-darwin.zip

これでダウンロードできます。

参考

https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q14182946004

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ubuntu18.04にAndroid Studio4系を入れる

Android Studioは公式サイトから落としてきて展開しても良いが、面倒。

snap install android-studio --classic

snapを使うとすんなり入り、android-studioコマンドで起動できる。

別途、Androidのエミュレータ起動のために関連ライブラリを入れた上で、kvmへのパーミッションが通るようにしたらok。

sudo apt install qemu-kvm -y
sudo adduser $USER kvm
sudo chown $USER /dev/kvm

自分の環境では、PC再起動で反映された。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む