Sibainu Relax Room

愛犬の柴犬とともに過ごす部屋

Android ブラウザからAndroidアプリを開く

柴犬は熟睡しています。気持ちよさそうです。

概要

次のURLで「Androidブラウザからのアプリ起動」のコミュニティを見て実行したくなりました。

https://groups.google.com/g/android-group-japan/c/Uyw6SAa-dxc

その中で次のような記事がありましたので、参考にしました。

これを基に調べてみたところ、本家 Android Developers URL「Android アプリリンクの処理」に詳しい説明がありました。

https://developer.android.com/training/app-links?hl=ja

ここで使っているのは「ディープリンク」と呼ばれているものらしいです。

ディープリンクは、ユーザーをアプリの特定の部分に直接誘導する、あらゆるスキームの URI です。ディープリンクを作成するには、アプリ内の適切なアクティビティにユーザーを誘導するインテント フィルタを追加します。次のコード スニペットをご覧ください


<activity
    android:name=".MyMapActivity"
    android:exported="true"
    ...>
    <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="geo" />
    </intent-filter>
</activity>

次のURLはディープリンクの詳しい説明です。

https://developer.android.com/training/app-links/deep-linking?hl=ja

data構文

<data android:scheme="string"
      android:host="string"
      android:port="string"
      android:path="string"
      android:pathPattern="string"
      android:pathPrefix="string"
      android:pathSuffix="string"
      android:pathAdvancedPattern="string"
      android:mimeType="string" />

URI

<scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>|<pathAdvancedPattern>|<pathSuffix>]

android:scheme

URI のスキーマ部分。これは、URI を指定するうえで最小限の必須属性です。フィルタに対して少なくとも 1 つの scheme 属性を設定する必要があります。この属性を設定していないと、他の URI 属性は意味を持ちません。 

android:host

URI 認証局のホスト部分。フィルタに対して scheme 属性も指定されていないと、この属性は意味を持ちません。複数のサブドメインにマッチさせるには、アスタリスク(*)を使用して、ホスト内の 0 個以上の文字にマッチさせます。たとえばホスト *.google.com は、www.google.com、.google.com、developer.google.com にマッチします。アスタリスクは host 属性の最初の文字にしか使えません。

path と pathPrefix

path 属性は、Intent オブジェクトの完全パスに対してマッチングされる完全パスを指定します。pathPrefix 属性は、Intent オブジェクトのパスの最初の部分のみに対してマッチングされる部分パスを指定します。

他のアプリと連携する

表題のことについて。、次の本家のURLで詳しい説明があります。

https://developer.android.com/training/basics/intents?hl=ja

いろいろ試行錯誤しましたが、なんとかできましたので記録することにします。

WEBのみでは断片的で覚えにくいので最初に購入した Kotlin の本です。

Project の全体

こんな感じです。

AndroidManifest.xml

最初、この MainActivity だけにしたのですが、実行すると Unable to determine activity name とエラーになりました。

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <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="sibainu"
                    android:host="relax"
                    android:path="/room"/>
            </intent-filter>
        </activity>

ここは、任意の名称でよいので区別しやすいものにします。
android:scheme、android:host は必須で、android:path は任意です。

                <data
                    android:scheme="sibainu"
                    android:host="relax"
                    android:path="/room"/>

メインとなる Activity がないというエラーのようですので、ダミーで TopActivity を作りました。

        <activity
            android:name=".TopActivity"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

ですので、全体のコードはつぎのようになりました。

copy

<?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" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

    <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.Javascriptsample2"
        tools:targetApi="31">

        <activity
            android:name=".TopActivity"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <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="sibainu"
                    android:host="relax"
                    android:path="/room"/>
            </intent-filter>
        </activity>

    </application>

</manifest>

HTML

ブラウザ側の HTML はリンクを次のような形にします。

      var scheme = 'sibainu';
      var host = 'relax';
      var path = '/room';

      var url = scheme + '://' + host + path;
      url += '?param1=hoho';
      url += '&param2=gege';
      url += '&param3=fufu';
      url += '&param4=gaga';

AndroidManifest.xml での値を使います。

                <data
                    android:scheme="sibainu"
                    android:host="relax"
                    android:path="/room"/>

HTMLは次のような簡単なものです。

copy

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0,
  maximum-scale=1.0, user-scalable=no" />
  <style>
    #sample {
      border: 1px solid #999;
      width: 140px;
      text-align: center;
      padding: 10px;
      color: #666;
      background-color: #eee;
      cursor: pointer;
    }
  </style>
  <script type="text/javascript">

    var init = function () {
      var scheme = 'sibainu';
      var host = 'relax';
      var path = '/room';

      var url = scheme + '://' + host + path;
      url += '?param1=hoho';
      url += '&param2=gege';
      url += '&param3=fufu';
      url += '&param4=gaga';

      var target = document.getElementById("samplehref");
      target.href = url;
      alert(url);
      document.getElementById("sample").addEventListener("click", function () {
        location.replace(url);
      }, false);
    };

    window.addEventListener("load", init);

  </script>
</head>

<body>

  <br>
  <br>
  <br>
  <br>
  <p id="sample">Androidアプリの起動</p>
  <br>
  <br>
  <br>
  <br>
  <a id="samplehref" href="">Androidアプリの起動</a>
</body>

</html>

TopActivity.java

ダミーのようなものですので簡単です。
onCreate に finish() を書いていますので、瞬間フラッシュのようになります。

copy

package org.sibainu.relax.room.javascriptsample2;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

public class TopActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_top);
        finish();
    }
}

MainActivity.java

パラメータの受け取りは次のようにします。

        Intent intent = getIntent();
        String action = intent.getAction();

        if (Intent.ACTION_VIEW.equals(action)) {
            //URIを取得
            Uri uri = intent.getData();
            //URIのクエリー文字によってやりたい処理を変えたりとか。
            String str1 = uri.getQueryParameter("param1");
            String str2 = uri.getQueryParameter("param2");
            String str3 = uri.getQueryParameter("param3");
            String str4 = uri.getQueryParameter("param4");

            省略
        }

全体は次のようになります。

copy

package org.sibainu.relax.room.javascriptsample2;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = getIntent();
        String action = intent.getAction();

        if (Intent.ACTION_VIEW.equals(action)) {
            //URIを取得
            Uri uri = intent.getData();
            //URIのクエリー文字によってやりたい処理を変えたりとか。
            String str1 = uri.getQueryParameter("param1");
            String str2 = uri.getQueryParameter("param2");
            String str3 = uri.getQueryParameter("param3");
            String str4 = uri.getQueryParameter("param4");

            String str = str1 + "\n";
            str += str2 + "\n";
            str += str3 + "\n";
            str += str4;

            TextView tv;
            tv = findViewById(R.id.textView);
            tv.setText(str);
        }
    }
}

実行

最初に Androidアプリを起動します。瞬間的に開いて閉じます。

次にブラウザを開いてリンクをクリックしますと、右のようにレイアウト activity_main.xml が開いて送られてきたパラーメータが表示されました。

activity_main.xml

TextView がひとつの簡単なレイアウトです。

copy

<?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"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="212dp"
        android:layout_height="76dp"
        android:layout_marginTop="32dp"
        android:text="TextView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

ここまでとします。