Edited at

カスタムURLスキームの処理でハマった話

More than 1 year has passed since last update.


はじめに

カスタムURLスキームに応じて、Android アプリに何かをさせたい時ってありますよね。そういう時はアクティビティに 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:scheme="smpl" />
</intent-filter>


アクティビティで intent.dataString を取得して、スキームに応じた処理をすると思います。


何が問題か

カスタムURLスキームに応じて何かをする場合、いくつかのケースが考えられます。

状態
やること

起動していない
起動してスキームに応じた画面を表示

バックグラウンドにいる
復帰してスキームに応じた画面を表示

この二つの違いは onCreate 以降で intent を取得するか、onNewIntentintent を取得するかだと思います。

ぼくがハマったのは次の条件を満たしたケースです。


  • カスタムURLスキームで起動

  • バックキーでバックグラウンドに遷移

  • タスクから選択して復帰

問題は、タスクから選択して復帰した時に intent.dataString を取得すると、カスタムURLスキームで起動した時と同じ intent.dataString が取得できてしまうという点です。


どう対応したか

だいぶハマったのですが、結論を述べると intent.sourceBounds の有無で「タスクから選択して復帰」したかどうかを判定しました。


端末によって動作が異なる

val dataString = intent?.let {

when (Intent.ACTION_VIEW == it.action && it.sourceBounds == null) {
true -> it.dataString
else -> null
}
}

sourceBounds は画面座標を返します。タスクから選択した時はタップした座標が sourceBounds の戻り値として取得でき、何らかの intent を取得して起動する時は null が取得できます。


追記

上の方法では端末によって intent.sourceBounds が取得できないケースがあり、意図した動作をしないことがわかりました。ただしくは intent.flags を取得して FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY かどうかを判定します。


意図通りに動作する

val dataString = intent?.let {

when (Intent.ACTION_VIEW == it.action && (Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY.and(intent.flags)) == 0) {
true -> it.dataString
else -> null
}
}


参考