Unity ver2020.3.38f1 Personal

GoogleCredential credential = GoogleCredential.FromJson(jsonText).CreateScoped(
                DriveService.Scope.DriveReadonly
            	// ,DriveService.Scope.Drive
);

アカウント情報を利用して GoogleDrive にアクセスすると、Android だと以下のエラーとなります。

2023/09/29 21:44:05.541 4080 4294 Info Unity error:Error deserializing JSON credential data.

実際のエラーは、以下のエラーでした。対応策は一番最後を参照して下さい。

2023/09/29 21:44:05.538 4080 4294 Info Unity Error Newtonsoft.Json.JsonSerializationException: Unable to find a constructor to use for type Google.Apis.Auth.OAuth2.JsonCredentialParameters. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'type', line 2, position 7.
2023/09/29 21:44:05.538 4080 4294 Info Unity   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject (Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonObjectContract objectContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, Newtonsoft.Json.Serialization.JsonProperty containerProperty, System.String id, System.Boolean& createdFromNonDefaultCreator) [0x00000] in <00000000000000000000000000000000>:0 
2023/09/29 21:44:05.538 4080 4294 Info Unity   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerCo
2023/09/29 21:44:05.540 4080 4294 Info Unity finally

簡単に言うと、コンストラクターが見つからなかったようです。

上記のソースは、Unity のエミュレータでは動作しているので認証情報のJSONに間違いはないです。

Android 特有のエラーと考えられます。

 

ソースを clone して確認すると、以下のソースでエラーがあるのが確認できます。

https://github.com/googleapis/google-api-dotnet-client

./Src/Support/Google.Apis.Auth/OAuth2/DefaultCredentialProvider.cs

google-api-dotnet-client % grep -r "Error deserializing JSON credential data" .                 
./Src/Support/Google.Apis.Auth/OAuth2/DefaultCredentialProvider.cs:                throw new InvalidOperationException("Error deserializing JSON credential data.", e);
./Src/Support/Google.Apis.Auth/OAuth2/DefaultCredentialProvider.cs:                throw new InvalidOperationException("Error deserializing JSON credential data.", e);
./Src/Support/Google.Apis.Auth/OAuth2/DefaultCredentialProvider.cs:                throw new InvalidOperationException("Error deserializing JSON credential data.", e);
falco@M1 google-api-dotnet-client % 

以下のコードでエラーが発生していることが考えられます。

JsonCredentialParameters credentialParameters;
try
{   
    credentialParameters = NewtonsoftJsonSerializer.Instance.Deserialize<JsonCredentialParameters>(json);
}       
catch (Exception e)
{
    throw new InvalidOperationException("Error deserializing JSON credential data.", e);
}
return CreateDefaultCredentialFromParameters(credentialParameters);

ネット検索すると以下のコードを追加しているようなので、追加 (Assets/link.xml)

https://forum.unity.com/threads/error-deserializing-json-credential-data-google-api.1367799/

<linker>
    <assembly fullname="Google.Apis.Auth">
        <type fullname="Google.Apis.Auth.OAuth2.JsonCredentialParameters" preserve="all" />
    </assembly>
</linker>

これで実行すると、次に以下のエラーが発生

2023/09/29 22:21:09.984 11023 11057 Info Unity Error System.NotSupportedException: System.Reflection.Emit.DynamicMethod::.ctor
2023/09/29 22:21:09.984 11023 11057 Info Unity   at System.Reflection.Emit.DynamicMethod..ctor (System.String name, System.Type returnType, System.Type[] parameterTypes, System.Type owner, System.Boolean skipVisibility) [0x00000] in <00000000000000000000000000000000>:0 
2023/09/29 22:21:09.984 11023 11057 Info Unity   at Newtonsoft.Json.Utilities.DynamicReflectionDelegateFactory.CreateDynamicMethod (System.String name, System.Type returnType, System.Type[] parameterTypes, System.Type owner) [0x00000] in <00000000000000000000000000000000>:0 
2023/09/29 22:21:09.984 11023 11057 Info Unity   at Newtonsoft.Json.Utilities.DynamicReflectionDelegateFactory.CreateDefaultConstructor[T] (System.Type type) [0x00000] in <00000000000000000000000000000000>:0 
2023/09/29 22:21:09.984 11023 11057 Info Unity   at Newtonsoft.Json.Serialization.DefaultContractResolver.InitializeContract (Newtonsoft.Json.Serialization.JsonContract contract) [0x00000] in <00000000000000000000000000000000>:0 
2023/09/29 22:21:09.984 11023 11057 Info Unity   at Newtonsoft.Json.Serialization.DefaultContractResolver.CreateObjectContract (System.Type objectType) [0x00000] in <00000000000000000000000000

エラーの原因は以下の模様

Error System.NotSupportedException: System.Reflection.Emit.DynamicMethod::.ctor

再度ネット検索すると、以下がヒット

https://forum.unity.com/threads/notsupportedexception-with-log-detail.543644/

System.Reflection.Emit is not supported with IL2CPP. You will need to use a JSON library that does not require use of it to work with Hololens. I believe that Newtonsoft JSON does have a mode that works with AOT runtimes like IL2CPP.
System.Reflection.Emit は IL2CPP ではサポートされていません。 Hololens と連携するには、JSON ライブラリを使用する必要がありません。 Newtonsoft JSON には、IL2CPP のような AOT ランタイムで動作するモードがあると思います。

結局のところ、Scripting Backend を IL2CPP -> Mono にすることで、正常に動作する事が確認出来ました。

Thanks Unity forum.

これで解決と思いきや、iOS  でも確認しようと設定を切り替えると、iOS は、IL2CPP 固定となり振り出しに戻る。

「 Newtonsoft JSON には、IL2CPP のような AOT ランタイムで動作するモードがあると思います。」

との事なので、結局のところ IL2CPP を使う事で、ストリッピング機能によりリフレクションで関節的にコールしているメソッド等が削除される事が原因のようなので、これを抑止する事にします。

Newtonsoft.Json には、IL2CPP版がありこちらをインストールします。

Newtonsoft.Json-for-Unity

Assets 配下の Link.xml に以下を記述することで、Android、iOS で動作します。

<linker>
    <assembly fullname="Google.Apis.Auth" preserve="all" />
    <assembly fullname="Google.Apis.Drive.v3" preserve="all" />
    <assembly fullname="Newtonsoft.Json" preserve="all" />
</linker>

以下、抜粋のGoogle Drive にアクセスするソースです。

void Start()
{
    ListFilesRecursively("folderId");
}

private void InitGooglDrive()
{
    String jsonText = Resources.Load<TextAsset>("GoogleAuth/service_account").text;
    Debug.Log($"json:{jsonText}");

    JsonCredentialParameters credentialParameters;

    try {
        credentialParameters = NewtonsoftJsonSerializer.Instance.Deserialize<JsonCredentialParameters>(jsonText);
    } catch (Exception e) {
        Debug.Log($"Error {e}");
    } finally {
        Debug.Log($"finally");
    }

    // OAuth 2.0認証をセットアップ
    Debug.Log("Call FromStream");
    GoogleCredential credential = GoogleCredential.FromJson(jsonText).CreateScoped(
        DriveService.Scope.DriveReadonly
    );

    // Drive APIクライアントを作成
    service = new DriveService(new BaseClientService.Initializer() {
        HttpClientInitializer = credential,
        ApplicationName = "Google shared folder viewer"
    });

}

private async void ListFilesRecursively(string folderId)
{
    InitGooglDrive();
    Debug.Log("folder:" + folderId);
    FilesResource.ListRequest listRequest = service.Files.List();
    listRequest.Q = $"'{folderId}' in parents";
    listRequest.Fields = "nextPageToken, files(id, name, mimeType, modifiedTime, md5Checksum)";

    do {
	var files = listRequest.Execute().Files;
	if (files == null || files.Count == 0) {
	    Debug.Log("No files found in folder: " + folderId);
	    continue;
	}

	foreach (var file in files) {
	    Debug.Log($"File Name: {file.Name}, ID: {file.Id}, MIME Type: {file.MimeType}, createdTime:{file.CreatedTime}");
   	    // フォルダの場合、再帰的にその中身を表示
 	    if (file.MimeType == "application/vnd.google-apps.folder") {
		ListFilesRecursively(file.Id);
  	    }
	 }
         if(listRequest.PageToken != null) {
             // next page token があればスリープ
             await Task.Delay(200);
         }
     } while (!string.IsNullOrEmpty(listRequest.PageToken));
}

 

そのままでは、正常に動作しない。

設定->システム->言語入力->詳細設定->テキスト読み上げ再生->再生 で動作を確認

google service speech を google play で最新更新

 

Container(
          width: double.infinity,
          height: 1,
          color: Colors.black,
        ),

全体のソース

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Draw 3 Lines Across Screen Width'),
        ),
        body: Column(
          children: [
            SizedBox(height: 10,),
            Container(
              width: double.infinity,
              height: 5,
              color: Colors.blue,
            ),
            SizedBox(height: 10,),
            Container(
              width: double.infinity,
              height: 5,
              color: Colors.red,
            ),
            SizedBox(height: 10,),
            Container(
              width: double.infinity,
              height: 5,
              color: Colors.green,
            ),
          ],
        ),
      ),
    );
  }
}

flutter で、versionName versionCode の設定するには?

pubspec.yaml にて

# flutter で、versionName versionCode の設定する
version: 1.2.0+4

versionName が 1.2.0

versionCode が4 になります。

iOS だとbuild-name build-number となります。

コマンド実行で指定するには

flutter build appbundle --build-name=1.3.0 --build-number=4  
flutter build ios --build-name=1.3.0 --build-number=4

 

 

ImageMagick なる外部ライブラリーがありこれを使おうかと色々調べたがあまりAndroid C#で利用している情報が見つからず結果断念。

https://github.com/MolotovCherry/Android-ImageMagick7

 

代わりに ImageSharp を利用することに

Visual Stuido の NuGet より、SixLabors.ImageSharp を選択して、Version 1.0.4 を指定してダウンロード

(最新だと、netstandard2.0  をサポートしていない?)

Package より netstandard2.0 フォルダーより以下のファイルをAssets/Plugins に保存

SixLabors.ImageSharp.dll
System.Numerics.Vectors.dll
System.Buffers.dll
System.Runtime.CompilerServices.Unsafe.dll
System.Memory.dll
System.Text.Encoding.CodePages.dll

あとは、Android でビルド

以下が画像を変換するコード(楽ちん)

  // 色々な画像をPNGに変換 
    public static byte[] ConvertToPNG(byte[] imageData)
    {
        using (var image = SixLabors.ImageSharp.Image.Load(imageData)) {
            using (var outputStream = new MemoryStream()) {
                image.Save(outputStream, new PngEncoder());
                return outputStream.ToArray();
            }
        }
    }

iOS でももちろん動作します。

https://developers.google.com/cardboard/develop/unity/quickstart

 

Unity ver.2020.3.38

Mac ver.12.6

Android Sdk ver.30

 

Google Cardboard  のデモをAndroid 用にビルドしたら以下のエラー

> A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
> Android resource linking failed
/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:3:5-94: AAPT: error: resource android:color/system_neutral1_1000 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:4:5-94: AAPT: error: resource android:color/system_neutral1_900 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:5:5-93: AAPT: error: resource android:color/system_neutral1_0 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:6:5-94: AAPT: error: resource android:color/system_neutral1_800 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:7:5-94: AAPT: error: resource android:color/system_neutral1_700 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:8:5-94: AAPT: error: resource android:color/system_neutral1_600 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:9:5-94: AAPT: error: resource android:color/system_neutral1_500 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:10:5-94: AAPT: error: resource android:color/system_neutral1_400 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:11:5-94: AAPT: error: resource android:color/system_neutral1_300 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:12:5-94: AAPT: error: resource android:color/system_neutral1_200 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:13:5-94: AAPT: error: resource android:color/system_neutral1_100 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:14:5-93: AAPT: error: resource android:color/system_neutral1_50 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:15:5-93: AAPT: error: resource android:color/system_neutral1_10 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:16:5-102: AAPT: error: resource android:color/system_neutral2_1000 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:17:5-102: AAPT: error: resource android:color/system_neutral2_900 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:18:5-101: AAPT: error: resource android:color/system_neutral2_0 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:19:5-102: AAPT: error: resource android:color/system_neutral2_800 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:20:5-102: AAPT: error: resource android:color/system_neutral2_700 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:21:5-102: AAPT: error: resource android:color/system_neutral2_600 not found.

/Users/falco/.gradle/caches/transforms-2/files-2.1/a0ebba694f8e6a95bc427eaef4894530/material-1.6.1/res/values-v31/values-v31.xml:22:5-102: AAPT: error: resource android:color/system_neutral2_500 not found.

リソースが見つからない…

 

対応としては、ライブラリーをダウングレード

// implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.gms:play-services-vision:20.1.3'
// implementation 'com.google.android.material:material:1.6.1'
implementation 'com.google.protobuf:protobuf-javalite:3.19.4'

implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.4.0'

これでビルド出来た。

ConstraintLayout の基本的な考え方

<android.support.design.button.MaterialButton
    android:id="@+id/button1"

    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="1"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toTopOf="@id/button2" 
/>

layout_constraintBottom_toTopOf=”@id/button2″ は

@+id/button1 の constraintBottom を @id/button2 の toTop に合わせる

 

Unity で Android エミュレータを使用するとき、Android studio を起動するのが、ちょっとじゃまなので、コマンドラインからエミュレータを起動

% jdk 1.8
/usr/libexec/java_home -v1.8
openjdk version "1.8.0_302"
OpenJDK Runtime Environment (Temurin)(build 1.8.0_302-b08)
OpenJDK 64-Bit Server VM (Temurin)(build 25.302-b08, mixed mode)
// alias setting
% alias emulator="$ANDROID_HOME/emulator/emulator"
// name list show
% emulator -list-avds
Pixel_4_XL_API_21
Pixel_4_XL_API_23
Pixel_4_XL_API_25
Pixel_4_XL_API_27
Pixel_5_API_32
Pixel_5_API_33
// execute emulator @name
% emulator @Pixel_4_XL_API_23

 

Android のフレームワークの処理を確認したい時に必要な環境

  • Android エミューレータ
  • Source for Android
  • Google Apis * System image [Google play * System image ではない]

上記の環境を Android SDK よりダウンロードして、エミュレーターを起動

Android Studio の [Attach Debugger to Android Process] をクリック

[Show all processes] をチェック

[Create New] を選択、[system_process] を選択して [OK] をクリック

後は、普通のデバックと同様に、フレームワークのソースを参照出来るしブレイクポイントを設定出来ます。

※重要なのは「Google Apis * System image」を使用する事です。

Google play * System image を使用すると、[Show all processes]  チェックしても[system_process] は表示されません。

 

java.lang.IllegalStateException: Pre-packaged database has an invalid schema: WORDS(mobile.falco.EnglishTrainer.db.model.Words).
 Expected:
TableInfo{name='WORDS', columns={average=Column{name='average', type='INTEGER', affinity='3', notNull=notNull, primaryKeyPosition=0, defaultValue='0'}, ngCount=Column{name='ngCount', type='INTEGER', affinity='3', notNull=notNull, primaryKeyPosition=0, defaultValue='0'}, japanese=Column{name='japanese', type='TEXT', affinity='2', notNull=notNull, primaryKeyPosition=0, defaultValue='null'}, english=Column{name='english', type='TEXT', affinity='2', notNull=notNull, primaryKeyPosition=0, defaultValue='null'}, kigou=Column{name='kigou', type='TEXT', affinity='2', notNull=notNull, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=notNull, primaryKeyPosition=1, defaultValue='null'}, ipa=Column{name='ipa', type='TEXT', affinity='2', notNull=notNull, primaryKeyPosition=0, defaultValue='null'}, okCount=Column{name='okCount', type='INTEGER', affinity='3', notNull=notNull, primaryKeyPosition=0, defaultValue='0'}}, foreignKeys=[], indices=[]}
 Found:
TableInfo{name='WORDS', columns={}, foreignKeys=[], indices=[]}
    at androidx.room.RoomOpenHelper.checkIdentity(RoomOpenHelper.kt:159)
    at androidx.room.RoomOpenHelper.onOpen(RoomOpenHelper.kt:128)
    at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onOpen(FrameworkSQLiteOpenHelper.java:326)
    at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:432)
    at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:321)
    at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableOrReadableDatabase(FrameworkSQLiteOpenHelper.java:273)
    at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.innerGetDatabase(FrameworkSQLiteOpenHelper.java:225)
    at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getSupportDatabase(FrameworkSQLiteOpenHelper.java:183)
    at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:133)
    at androidx.room.SQLiteCopyOpenHelper.getWritableDatabase(SQLiteCopyOpenHelper.kt:71)
    at androidx.room.RoomDatabase.inTransaction(RoomDatabase.kt:634)
    at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.kt:430)

Android11 で実行すると上記のエラーとなる。

Android6 では問題なし。

database = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "hatuon_.db")
    // .fallbackToDestructiveMigration()
    .createFromAsset("database/hatuon.db")
    .allowMainThreadQueries()
    .setJournalMode(RoomDatabase.JournalMode.TRUNCATE)
    .build()

database の名称を huton.db -> hatuon_.db にすることで正常に起動

© 2024 Falco Tech Blog Suffusion theme by Sayontan Sinha