Skip to content

Commit

Permalink
Merge pull request #605 from dfinity/unity-ii-integration
Browse files Browse the repository at this point in the history
Unity Android Internet Identity integration
  • Loading branch information
vincent-dfinity authored Aug 18, 2023
2 parents aefe0a2 + 459bbcb commit a3727f0
Show file tree
Hide file tree
Showing 172 changed files with 18,213 additions and 0 deletions.
25 changes: 25 additions & 0 deletions native-apps/unity_android_applink/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Internet Identity Integration
This sample shows one way to integrate Internet Identity with Android apps. It contains two parts: a dapp with II integrated, and an Unity Project which interacts with this dapp.

## ii_integration_page
It's an example that integrates with Internet Identity, with the backend and frontend. It derives from the [Internet Identity integration sample](https://github.com/dfinity/examples/tree/master/motoko/internet_identity_integration) demo with some modifications.
Please refer to [README](./ii_integration_page/README.md) for details.

## android_integration
This is a Unity project with [ICP.NET](https://github.com/BoomDAO/ICP.NET) embedded, which is a C# agent that is able to communicate with the IC from C#. Please refer to [README](./android_integration/README.md) for details.

## Workflow
Before continuing, please read through the [Android App Links](https://developer.android.com/studio/write/app-link-indexing) to understand how Android App Links works.

Here is the basic workflow that how to integrate with Internet Identity from a Unity Android game. The basic idea is to open the Web Browser from the game, login in with II in the browser, and pass the DelegationIdentity back to the game.

The steps in detail are described below:

1. Set up an [Internet Identity integration dapp](#ii_integration_page) which supports logging in with II, with an `assetlinks.json` file associated.
Please refer to [ii_integration_page](./ii_integration_page/README.md) to set up the dapp.
2. Run a Unity game on Android, which is built from [android_integration sample](#android_integration).
Please refer to [android_integration](./android_integration/README.md) to build the Unity Android game.
3. Launch the Web Browser from the game to open the dapp frontend deployed in #1, with the public key of `Ed25519Identity` as a parameter.
4. Login with your Internet Identity in the Web Browser.
5. Launch the application via App Links, and pass the `DelegationIdentity` back to the game as the URL parameter.
6. Call the backend canister with the `DelegationIdentity` to greet.
61 changes: 61 additions & 0 deletions native-apps/unity_android_applink/android_integration/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# This .gitignore file should be placed at the root of your Unity project directory
#
# Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore
#
/[Ll]ibrary/
/[Tt]emp/
/[Oo]bj/
/[Bb]uild/
/[Bb]uilds/
/[Ll]ogs/
/[Mm]emoryCaptures/

# Asset meta data should only be ignored when the corresponding asset is also ignored
!/[Aa]ssets/**/*.meta

# Uncomment this line if you wish to ignore the asset store tools plugin
# /[Aa]ssets/AssetStoreTools*

# Autogenerated Jetbrains Rider plugin
[Aa]ssets/Plugins/Editor/JetBrains*

# Visual Studio cache directory
.vs/

# Gradle cache directory
.gradle/
gradle/

# Autogenerated VS/MD/Consulo solution and project files
ExportedObj/
.consulo/
*.csproj
*.unityproj
*.sln
*.suo
*.tmp
*.user
*.userprefs
*.pidb
*.booproj
*.svd
*.pdb
*.mdb
*.opendb
*.VC.db

# Unity3D generated meta files
*.pidb.meta
*.pdb.meta
*.mdb.meta

# Unity3D generated file on crash reports
sysinfo.txt

# Builds
*.apk
*.unitypackage

# Crashlytics generated file
crashlytics-build.properties

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.icgamekit.plugin;

import android.content.Intent;
import android.net.Uri;
import android.util.Log;

import com.unity3d.player.UnityPlayer;

import java.io.File;
import java.io.FileOutputStream;

public class ICGameKitPlugin {
static final String TAG_PLUGIN = "ICGameKitPlugin";

public static ICGameKitPlugin sCurrentPlugin;

public static ICGameKitPlugin initImpl() {
if (sCurrentPlugin != null)
return sCurrentPlugin;

sCurrentPlugin = new ICGameKitPlugin();

return sCurrentPlugin;
}

public void openBrowser(String url) {
Log.i(TAG_PLUGIN, url);

//String url = "https://6x7nu-oaaaa-aaaan-qdaua-cai.ic0.app";
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
UnityPlayer.currentActivity.startActivity(intent);
}

public void sendMessage(String[] arguments) {
if (arguments == null || arguments.length != 2)
return;

String gameObjectName = arguments[0];
String methodName = arguments[1];

if (gameObjectName == null || gameObjectName.length() == 0
|| methodName == null || methodName.length() == 0)
return;

Uri uri = UnityPlayer.currentActivity.getIntent().getData();
if (uri == null)
return;

String url = uri.toString();
int index = url.indexOf("delegation=");
if (index == -1)
return;

String delegation = url.substring(index);
//Log.i(TAG_PLUGIN, delegation);

// Write to a temporary file to internal storage and read it back from C# side.
// The reason is we can only pass 1024 bytes as string back to the C# side, but the params string with delegation is more than 3k bytes.
String delegationPath = UnityPlayer.currentActivity.getFilesDir().getPath() + "/delegation.file";
File delegationFile = new File(delegationPath);
try {
if (delegationFile.exists())
delegationFile.delete();

FileOutputStream fileOutputStream = new FileOutputStream(delegationFile);
fileOutputStream.write(delegation.getBytes());
fileOutputStream.flush();
fileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}

// Pass the delegation path back to C#.
UnityPlayer.UnitySendMessage(gameObjectName, methodName, delegationPath);
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#if UNITY_ANDROID
using System;
using System.IO;
using System.Xml;
using UnityEditor.Android;

namespace IC.GameKit
{
public class AndroidPostProcessor : IPostGenerateGradleAndroidProject
{
const string kAndroidNamespaceURI = "http://schemas.android.com/apk/res/android";

// Android URL Scheme, you can add more parameters like port etc.
const string kURLScheme = "https";
const string kURLHost = "6x7nu-oaaaa-aaaan-qdaua-cai.icp0.io";
const string kURLPath = "/authorize";

public int callbackOrder { get { return 0; } }

public void OnPostGenerateGradleAndroidProject(string projectPath)
{
InjectAndroidManifest(projectPath);
}

private void InjectAndroidManifest(string projectPath)
{
var manifestPath = projectPath + "/src/main/AndroidManifest.xml";
if (!File.Exists(manifestPath))
throw new FileNotFoundException(manifestPath + " doesn't exist.");

var manifestXmlDoc = new XmlDocument();
manifestXmlDoc.Load(manifestPath);

// Check if the url host exists.
if (manifestXmlDoc.InnerXml.Contains(kURLHost))
return;

AppendAndroidIntentFilter(manifestPath, manifestXmlDoc);

manifestXmlDoc.Save(manifestPath);
}

internal static void AppendAndroidIntentFilter(string manifestPath, XmlDocument xmlDoc)
{
var activityNode = xmlDoc.SelectSingleNode("manifest/application/activity");
if (activityNode == null)
throw new ArgumentException(string.Format("Missing 'activity' node in '{0}'.", manifestPath));

var intentFilterNode = xmlDoc.CreateElement("intent-filter");
intentFilterNode.SetAttribute("autoVerify", kAndroidNamespaceURI, "true");

var actionNode = xmlDoc.CreateElement("action");
actionNode.SetAttribute("name", kAndroidNamespaceURI, "android.intent.action.VIEW");
intentFilterNode.AppendChild(actionNode);

var categoryNode1 = xmlDoc.CreateElement("category");
categoryNode1.SetAttribute("name", kAndroidNamespaceURI, "android.intent.category.DEFAULT");
intentFilterNode.AppendChild(categoryNode1);

var categoryNode2 = xmlDoc.CreateElement("category");
categoryNode2.SetAttribute("name", kAndroidNamespaceURI, "android.intent.category.BROWSABLE");
intentFilterNode.AppendChild(categoryNode2);

var dataNode = xmlDoc.CreateElement("data");
dataNode.SetAttribute("scheme", kAndroidNamespaceURI, kURLScheme);
dataNode.SetAttribute("host", kAndroidNamespaceURI, kURLHost);
dataNode.SetAttribute("path", kAndroidNamespaceURI, kURLPath);

intentFilterNode.AppendChild(dataNode);

activityNode.AppendChild(intentFilterNode);
}
}
}
#endif

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Loading

0 comments on commit a3727f0

Please sign in to comment.