diff --git a/.gitignore b/.gitignore index 4a4a1a4e..27ebdc9a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,26 +2,31 @@ # # Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore # -/[Ll]ibrary/ -/[Tt]emp/ -/[Oo]bj/ -/[Bb]uild/ -/[Bb]uilds/ -/[Ll]ogs/ -/[Uu]ser[Ss]ettings/ +**/[Ll]ibrary/ +**/[Tt]emp/ +**/[Oo]bj/ +**/[Bb]uild/ +**/[Bb]uilds/ +**/[Ll]ogs/ +**/[Uu]ser[Ss]ettings/ + +**/[Aa]ssets/Samples/ +**/[Aa]ssets/Samples.meta +**/[Aa]ssets/TextMesh Pro/ +**/[Aa]ssets/TextMesh Pro.meta # MemoryCaptures can get excessive in size. # They also could contain extremely sensitive data -/[Mm]emoryCaptures/ +**/[Mm]emoryCaptures/ # Recordings can get excessive in size -/[Rr]ecordings/ +**/[Rr]ecordings/ # 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* +**/[Aa]ssets/Plugins/Editor/JetBrains* # Visual Studio cache directory .vs/ @@ -65,11 +70,11 @@ sysinfo.txt crashlytics-build.properties # Packed Addressables -/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* +**/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* # Temporary auto-generated Android Assets -/[Aa]ssets/[Ss]treamingAssets/aa.meta -/[Aa]ssets/[Ss]treamingAssets/aa/* +**/[Aa]ssets/[Ss]treamingAssets/aa.meta +**/[Aa]ssets/[Ss]treamingAssets/aa/* .DS_Store @@ -81,5 +86,5 @@ downloads~ /Runtime/Plugins/ffi-*/livekit_ffi.h.meta # Ignore temporaries from GameCI -/[Aa]rtifacts/ -/[Cc]odeCoverage/ +**/[Aa]rtifacts/ +**/[Cc]odeCoverage/ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..302b159d --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "attach", + "name": "Rust/C++ Unity auto", + "waitFor": true, + "program": "Unity", + "sourceLanguages": ["rust"] + }, + { + "type": "lldb", + "request": "attach", + "name": "Rust/C++ Unity manual", + "pid": "${command:pickProcess}", // For manual picking, if there are multiple Unity instances running + "sourceLanguages": ["rust"] + } + ] +} \ No newline at end of file diff --git a/BuildScripts~/build_ffi_locally.sh b/BuildScripts~/build_ffi_locally.sh index 4995e666..4bb21883 100755 --- a/BuildScripts~/build_ffi_locally.sh +++ b/BuildScripts~/build_ffi_locally.sh @@ -12,52 +12,91 @@ GREEN='\033[0;32m' RESET='\033[0m' usage() { - echo "Usage: $0 " + echo "Usage: $0 [build_type]" echo "" echo "Platforms:" echo " macos Build for aarch64-apple-darwin" echo " android Build for aarch64-linux-android" + echo " ios Build for aarch64-apple-ios" + echo "" + echo "Build types (optional, defaults to 'debug'):" + echo " release Optimized release build" + echo " debug Debug build" exit 1 } -if [ $# -ne 1 ]; then - echo -e "${RED}Error: Expected exactly one argument.${RESET}" +if [ $# -lt 1 ] || [ $# -gt 2 ]; then + echo -e "${RED}Error: Expected one or two arguments.${RESET}" usage fi PLATFORM="$1" +BUILD_TYPE="${2:-debug}" + +case "$BUILD_TYPE" in + release) + BUILD_FLAG="--release" + BUILD_DIR="release" + ;; + debug) + BUILD_FLAG="" + BUILD_DIR="debug" + ;; + *) + echo -e "${RED}Error: Unknown build type '$BUILD_TYPE'. Expected 'release' or 'debug'.${RESET}" + usage + ;; +esac case "$PLATFORM" in + # MACOS macos) - echo "Building for macOS (aarch64-apple-darwin)..." + echo "Building for macOS (aarch64-apple-darwin) [$BUILD_TYPE]..." cargo build \ --manifest-path "$MANIFEST" \ - --release \ - --workspace \ - -p livekit \ + $BUILD_FLAG \ + -p livekit-ffi \ --target aarch64-apple-darwin BUILD_STATUS=$? - SRC="$BASE_TARGET/aarch64-apple-darwin/release/liblivekit_ffi.dylib" + SRC="$BASE_TARGET/aarch64-apple-darwin/$BUILD_DIR/liblivekit_ffi.dylib" DST="$BASE_DST/ffi-macos-arm64/liblivekit_ffi.dylib" ;; + # ANDROID android) - echo "Building for Android (aarch64-linux-android)..." + echo "Building for Android (aarch64-linux-android) [$BUILD_TYPE]..." pushd "$ROOT/client-sdk-rust~" > /dev/null cargo ndk \ --target aarch64-linux-android \ build \ - --release \ - -p livekit \ - --workspace \ + $BUILD_FLAG \ + -p livekit-ffi \ -v \ --no-default-features \ --features "rustls-tls-webpki-roots" BUILD_STATUS=$? popd > /dev/null - SRC="$BASE_TARGET/aarch64-linux-android/release/liblivekit_ffi.so" + SRC="$BASE_TARGET/aarch64-linux-android/$BUILD_DIR/liblivekit_ffi.so" DST="$BASE_DST/ffi-android-arm64/liblivekit_ffi.so" + JAR_SRC="$BASE_TARGET/aarch64-linux-android/$BUILD_DIR/libwebrtc.jar" + JAR_DST="$BASE_DST/ffi-android-arm64/libwebrtc.jar" + ;; + # IOS + ios) + echo "Building for iOS (aarch64-apple-ios) [$BUILD_TYPE]..." + pushd "$ROOT/client-sdk-rust~/livekit-ffi" > /dev/null + cargo rustc \ + --crate-type staticlib \ + $BUILD_FLAG \ + --target aarch64-apple-ios \ + --no-default-features \ + --features "rustls-tls-webpki-roots" + BUILD_STATUS=$? + popd > /dev/null + + SRC="$BASE_TARGET/aarch64-apple-ios/$BUILD_DIR/liblivekit_ffi.a" + DST="$BASE_DST/ffi-ios-arm64/liblivekit_ffi.a" ;; *) echo -e "${RED}Error: Unknown platform '$PLATFORM'.${RESET}" @@ -70,6 +109,7 @@ if [ $BUILD_STATUS -ne 0 ]; then exit 1 fi +# Copy the built lib echo "" echo "Copying to $DST..." cp -f "$SRC" "$DST" @@ -83,4 +123,18 @@ if [ $? -eq 0 ]; then else echo -e "${RED}Failed to copy $(basename "$DST"). Check that the source file exists and the destination directory is writable.${RESET}" exit 1 +fi + +# For android, also copy the built libwebrtc.jar +if [ "$PLATFORM" = "android" ]; then + echo "" + echo "Copying to $JAR_DST..." + cp -f "$JAR_SRC" "$JAR_DST" + + if [ $? -eq 0 ]; then + echo -e "${GREEN}Copied $(basename "$JAR_DST") successfully.${RESET}" + else + echo -e "${RED}Failed to copy $(basename "$JAR_DST"). Check that the source file exists and the destination directory is writable.${RESET}" + exit 1 + fi fi \ No newline at end of file diff --git a/README.md b/README.md index b01209ac..43690202 100644 --- a/README.md +++ b/README.md @@ -48,15 +48,40 @@ You can use the package manager to import local `client-sdk-unity` into your Uni Or you can import the git url `https://github.com/livekit/client-sdk-unity.git` from the package manager. +## Local development + ### Building Livekit plugins locally For local development, initialize the git submodule containing the Rust code for the LiveKit plugin libraries. -There is a [helper script](https://github.com/livekit/client-sdk-unity/blob/main/BuildScripts~/build_ffi_locally.sh) to build the libraries locally and exchange the downloaded libraries with the build artifacts. +There is a [helper script](https://github.com/livekit/client-sdk-unity/blob/main/BuildScripts~/build_ffi_locally.sh) to build the libraries locally and exchange the downloaded libraries with the local build artifacts in the correct `Runtime/Plugins` folder. Currently, the build script supports the following arguments: - macos - android +- ios + +In the following options: +- debug (default) +- release + +So a build command is for example: +`./BuildScripts~/build_ffi_locally.sh macos release` + +### VSCode setup + +Look at the Unity-SDK.code-workspace setup for VSCode. This will use the Meet Sample as the Unity project and the Unity SDK package as two roots in a multi-root workspace and the Meet.sln as the `dotnet.defaultSolution`, enabling Rust and C# IDE support. + +### Debugging + +For C# debugging, there is a simple attach launch option called `C# Unity`, for example in the `Meet/.vscode/launch.json`. + +For Rust / C++ debugging on MacOS, you need to install the [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb) extension. The debug attach is defined in `.vscode/launch.json`. + +1. Build the livekit-ffi lib locally in debug mode with `./BuildScripts~/build_ffi_locally.sh macos debug` +2. Start the Unity Editor +3. Attach to the Unity Editor process (either auto or manual process picker) +4. Start the Scene in Editor ### iOS diff --git a/Runtime/Scripts/Internal/FFIClient.cs b/Runtime/Scripts/Internal/FFIClient.cs index 0a741b7f..5f83ee7b 100644 --- a/Runtime/Scripts/Internal/FFIClient.cs +++ b/Runtime/Scripts/Internal/FFIClient.cs @@ -143,7 +143,7 @@ private static void InitializeSdk() #endif var sdkVersion = PackageVersion.Get(); - NativeMethods.LiveKitInitialize(FFICallback, captureLogs, "unity", sdkVersion); // TODO: Get SDK version + NativeMethods.LiveKitInitialize(FFICallback, captureLogs, "unity", sdkVersion); Utils.Debug("FFIServer - Initialized"); initialized = true; diff --git a/Runtime/Scripts/Video/YuvToRgbConverter.cs.meta b/Runtime/Scripts/Video/YuvToRgbConverter.cs.meta index 549d7c17..ba8b36be 100644 --- a/Runtime/Scripts/Video/YuvToRgbConverter.cs.meta +++ b/Runtime/Scripts/Video/YuvToRgbConverter.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: de214c4761ddb4f38b8aa37b1f25df40 \ No newline at end of file +guid: de214c4761ddb4f38b8aa37b1f25df40 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/.vscode/extensions.json b/Samples~/Meet/.vscode/extensions.json new file mode 100644 index 00000000..ddb6ff85 --- /dev/null +++ b/Samples~/Meet/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "visualstudiotoolsforunity.vstuc" + ] +} diff --git a/Samples~/Meet/.vscode/launch.json b/Samples~/Meet/.vscode/launch.json new file mode 100644 index 00000000..100fdc99 --- /dev/null +++ b/Samples~/Meet/.vscode/launch.json @@ -0,0 +1,10 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "C# Unity", + "type": "vstuc", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/Samples~/Meet/.vscode/settings.json b/Samples~/Meet/.vscode/settings.json new file mode 100644 index 00000000..eea6aadd --- /dev/null +++ b/Samples~/Meet/.vscode/settings.json @@ -0,0 +1,60 @@ +{ + "files.exclude": { + "**/.DS_Store": true, + "**/.git": true, + "**/.vs": true, + "**/.gitmodules": true, + "**/.vsconfig": true, + "**/*.booproj": true, + "**/*.pidb": true, + "**/*.suo": true, + "**/*.user": true, + "**/*.userprefs": true, + "**/*.unityproj": true, + "**/*.dll": true, + "**/*.exe": true, + "**/*.pdf": true, + "**/*.mid": true, + "**/*.midi": true, + "**/*.wav": true, + "**/*.gif": true, + "**/*.ico": true, + "**/*.jpg": true, + "**/*.jpeg": true, + "**/*.png": true, + "**/*.psd": true, + "**/*.tga": true, + "**/*.tif": true, + "**/*.tiff": true, + "**/*.3ds": true, + "**/*.3DS": true, + "**/*.fbx": true, + "**/*.FBX": true, + "**/*.lxo": true, + "**/*.LXO": true, + "**/*.ma": true, + "**/*.MA": true, + "**/*.obj": true, + "**/*.OBJ": true, + "**/*.asset": true, + "**/*.cubemap": true, + "**/*.flare": true, + "**/*.mat": true, + "**/*.meta": true, + "**/*.prefab": true, + "**/*.unity": true, + "build/": true, + "Build/": true, + "Library/": true, + "library/": true, + "obj/": true, + "Obj/": true, + "Logs/": true, + "logs/": true, + "ProjectSettings/": true, + "UserSettings/": true, + "temp/": true, + "Temp/": true + }, + "dotnet.defaultSolution": "Meet.sln" +} \ No newline at end of file diff --git a/Samples~/Meet/Assets/Editor.meta b/Samples~/Meet/Assets/Editor.meta new file mode 100644 index 00000000..5f888823 --- /dev/null +++ b/Samples~/Meet/Assets/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8ce75b64666c9496d94b032aaabaa776 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/Editor/BuildPostProcessor.cs b/Samples~/Meet/Assets/Editor/BuildPostProcessor.cs new file mode 100644 index 00000000..5b899176 --- /dev/null +++ b/Samples~/Meet/Assets/Editor/BuildPostProcessor.cs @@ -0,0 +1,33 @@ +#if UNITY_IOS + +using UnityEngine; +using UnityEditor; +using UnityEditor.iOS.Xcode; +using UnityEditor.Callbacks; + +public class BuildPostProcessor +{ + [PostProcessBuildAttribute(500)] + public static void OnPostprocessBuild(BuildTarget buildTarget, string pathToBuiltProject) + { + if (buildTarget != BuildTarget.iOS) + { + return; + } + var projPath = pathToBuiltProject + "/Unity-iPhone.xcodeproj/project.pbxproj"; + PBXProject proj = new PBXProject(); + proj.ReadFromFile(projPath); +#if UNITY_2019_3_OR_NEWER + string guid = proj.GetUnityFrameworkTargetGuid(); +#else + string guid = proj.TargetGuidByName("Unity-iPhone"); +#endif + proj.AddBuildProperty(guid, "OTHER_LDFLAGS", "-ObjC"); + string fileGuid = proj.FindFileGuidByProjectPath("Libraries/libiPhone-lib.a"); + proj.RemoveFileFromBuild(guid, fileGuid); + proj.AddFileToBuild(guid, fileGuid); + proj.WriteToFile(projPath); + } +} + +#endif diff --git a/Samples~/Meet/Assets/Editor/BuildPostProcessor.cs.meta b/Samples~/Meet/Assets/Editor/BuildPostProcessor.cs.meta new file mode 100644 index 00000000..393a18ca --- /dev/null +++ b/Samples~/Meet/Assets/Editor/BuildPostProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 86bf4ad98161747bc9f1ebeb95d99721 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/MaterialIcons.meta b/Samples~/Meet/Assets/MaterialIcons.meta new file mode 100644 index 00000000..46ac8a4e --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 1429708ae2e376a469b0488cdcd98d1b +folderAsset: yes +timeCreated: 1585147601 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/MaterialIcons/Editor.meta b/Samples~/Meet/Assets/MaterialIcons/Editor.meta new file mode 100644 index 00000000..b29f7a4d --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons/Editor.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: e7eb0eb9652214c49b7e30cfdd733e38 +folderAsset: yes +timeCreated: 1585147601 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/MaterialIcons/Editor/MaterialIconEditor.cs b/Samples~/Meet/Assets/MaterialIcons/Editor/MaterialIconEditor.cs new file mode 100644 index 00000000..c05eb58f --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons/Editor/MaterialIconEditor.cs @@ -0,0 +1,144 @@ + +using UnityEngine; +using UnityEditor; +using System.Reflection; + +namespace Google.MaterialDesign.Icons +{ + +[CustomEditor(typeof(MaterialIcon), true), CanEditMultipleObjects] +public class MaterialIconEditor : UnityEditor.UI.TextEditor +{ + private static readonly Color darkColor = new Color(0.196f, 0.196f, 0.196f); + private static readonly Color lightColor = new Color(0.804f, 0.804f, 0.804f); + + private SerializedProperty spText; + private SerializedProperty spColor; + private SerializedProperty spRaycastTarget; + private SerializedProperty spAlignment; + private SerializedProperty spFontScale; + + private MaterialIcon icon; + private Font MaterialIconsRegular; + private GUIStyle iconStyle; + private GUIContent iconTooltip = new GUIContent(); + private GUIContent mixedContent = new GUIContent("\u2014", "Mixed Values"); + + protected override void OnEnable() + { + base.OnEnable(); + icon = target as MaterialIcon; + + if(string.IsNullOrEmpty(icon.text)) + { + icon.Init(); + } + + if(icon.font == null) + { + icon.LoadFont(); + } + + MaterialIconsRegular = icon.font; + + iconStyle = new GUIStyle(); + iconStyle.font = MaterialIconsRegular; + iconStyle.fontSize = 42; + iconStyle.alignment = TextAnchor.MiddleCenter; + iconStyle.normal.textColor = iconStyle.active.textColor = iconStyle.focused.textColor = iconStyle.hover.textColor = EditorGUIUtility.isProSkin ? lightColor : darkColor; + + iconTooltip.tooltip = icon.iconUnicode; + + spText = serializedObject.FindProperty("m_Text"); + spColor = serializedObject.FindProperty("m_Color"); + spRaycastTarget = serializedObject.FindProperty("m_RaycastTarget"); + spAlignment = serializedObject.FindProperty("m_FontData.m_Alignment"); + spFontScale = serializedObject.FindProperty("_fontScale"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + if(MaterialIconsRegular == null) + { + EditorGUILayout.Space(); + EditorGUILayout.HelpBox("Could not find \"MaterialIcons-Regular\" font data.", MessageType.Error); + } + + EditorGUILayout.Space(); + + EditorGUI.BeginDisabledGroup(MaterialIconsRegular == null); + + Rect iconRect = GUILayoutUtility.GetRect(EditorGUIUtility.singleLineHeight * 3f, EditorGUIUtility.singleLineHeight * 3f, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(false)); + DoIconControl(iconRect, spText, () => { + MaterialIconSelectionWindow.Init(MaterialIconsRegular, spText.stringValue, (selected) => { + spText.stringValue = selected; + serializedObject.ApplyModifiedProperties(); + iconTooltip.tooltip = icon.iconUnicode; + Repaint(); + }); + }); + + EditorGUI.EndDisabledGroup(); + + EditorGUILayout.Space(); + + EditorGUILayout.PropertyField(spColor); + EditorGUILayout.PropertyField(spRaycastTarget); + + EditorGUILayout.Space(); + + EditorGUILayout.PropertyField(spFontScale); + Rect alignmentRect = GUILayoutUtility.GetRect(EditorGUIUtility.singleLineHeight, EditorGUIUtility.singleLineHeight, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(false)); + DoTextAlignmentControl(alignmentRect, spAlignment); + + serializedObject.ApplyModifiedProperties(); + } + + private void DoIconControl(Rect position, SerializedProperty text, System.Action callback) + { + bool mixedValues = (text.hasMultipleDifferentValues || ((text.stringValue != null) && (text.stringValue.Length > 1))); + GUIContent guiContent = new GUIContent("Icon"); + EditorGUI.BeginProperty(position, guiContent, text); + Rect rect = EditorGUI.PrefixLabel(position, guiContent); + rect.width = rect.height; + if(GUI.Button(rect, mixedValues ? mixedContent : iconTooltip)) + callback.Invoke(); + GUI.Label(rect, mixedValues ? string.Empty : text.stringValue, iconStyle); + EditorGUI.EndProperty(); + } + + /// Reflection for the private synonymous method from the FontDataDrawer class. + private static readonly MethodInfo DoHorizontalAlignmentControl = typeof(UnityEditor.UI.FontDataDrawer).GetMethod("DoHorizontalAligmentControl", BindingFlags.NonPublic | BindingFlags.Static); + + /// Reflection for the private synonymous method from the FontDataDrawer class. + private static readonly MethodInfo DoVerticalAlignmentControl = typeof(UnityEditor.UI.FontDataDrawer).GetMethod("DoVerticalAligmentControl", BindingFlags.NonPublic | BindingFlags.Static); + + /// Workaround for the non-static private synonymous method from the FontDataDrawer class. + private static void DoTextAlignmentControl(Rect position, SerializedProperty alignment) + { + try + { + GUIContent guiContent = new GUIContent("Alignment"); + EditorGUIUtility.SetIconSize(new Vector2(15f, 15f)); + EditorGUI.BeginProperty(position, guiContent, alignment); + Rect rect = EditorGUI.PrefixLabel(position, guiContent); + float size1 = 60f; + float size2 = Mathf.Clamp(rect.width - size1 * 2f, 2f, 10f); + Rect position2 = new Rect(rect.x, rect.y, size1, rect.height); + Rect position3 = new Rect(position2.xMax + size2, rect.y, size1, rect.height); + DoHorizontalAlignmentControl.Invoke(null, new object[] { position2, alignment }); + DoVerticalAlignmentControl.Invoke(null, new object[] { position3, alignment }); + EditorGUI.EndProperty(); + EditorGUIUtility.SetIconSize(Vector2.zero); + } + catch(System.Exception e) + { + Debug.LogException(e); + } + } + +} + +} diff --git a/Samples~/Meet/Assets/MaterialIcons/Editor/MaterialIconEditor.cs.meta b/Samples~/Meet/Assets/MaterialIcons/Editor/MaterialIconEditor.cs.meta new file mode 100644 index 00000000..c48e5e93 --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons/Editor/MaterialIconEditor.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 51028811296326e41a2930e85b26f4b3 +timeCreated: 1585147601 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/MaterialIcons/Editor/MaterialIconSelectionWindow.cs b/Samples~/Meet/Assets/MaterialIcons/Editor/MaterialIconSelectionWindow.cs new file mode 100644 index 00000000..b814f9b8 --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons/Editor/MaterialIconSelectionWindow.cs @@ -0,0 +1,330 @@ + +using UnityEngine; +using UnityEditor; + +using System.IO; +using System.Collections.Generic; +using System.Linq; + +namespace Google.MaterialDesign.Icons +{ + +public class MaterialIconSelectionWindow : EditorWindow +{ + private static readonly Color darkColor = new Color(0.196f, 0.196f, 0.196f); + private static readonly Color lightColor = new Color(0.804f, 0.804f, 0.804f); + + private int iconSize = 58; + private int labelHeight = 24; + private int spacing = 10; + private bool showNames = true; + private Vector2 scrollPos = Vector2.zero; + private string filterText = string.Empty; + private bool filterGotFocus = false; + + private string selected; + private string selectedName; + private bool selectionKeep; + private System.Action onSelectionChanged; + + private Font MaterialIconsRegular; + private CodepointData[] codepointsCollection; + private CodepointData[] filteredCollection; + + private GUIStyle toolbarSeachTextFieldStyle; + private GUIStyle toolbarSeachCancelButtonStyle; + private GUIStyle toolbarSeachCancelButtonEmptyStyle; + private GUIStyle toolbarLabelStyle; + private GUIStyle iconImageStyle; + private GUIStyle iconLabelStyle; + private GUIStyle iconSelectionStyle; + + public void LoadDependencies(Font MaterialIconsRegular) + { + showNames = EditorPrefs.GetBool(typeof(MaterialIconSelectionWindow) + ".showNames", true); + + if(MaterialIconsRegular == null) + return; + + this.MaterialIconsRegular = MaterialIconsRegular; + + string fontPath = AssetDatabase.GetAssetPath(MaterialIconsRegular); + string codepointsPath = Path.GetDirectoryName(fontPath) + "/codepoints"; + + List tempList = new List(); + + foreach(string codepoint in File.ReadAllLines(codepointsPath)) + { + string[] data = codepoint.Split(' '); + tempList.Add(new CodepointData(data[0], data[1])); + } + + codepointsCollection = tempList.ToArray(); + filteredCollection = codepointsCollection; + + var temp = filteredCollection.FirstOrDefault(data => data.codeGUIContent.text == selected); + if(temp != null) + selectedName = temp.name; + } + + public static void Init(Font MaterialIconsRegular, string preSelect, System.Action callback) + { + MaterialIconSelectionWindow window = EditorWindow.GetWindow(true); + window.selected = preSelect; + window.onSelectionChanged = callback; + window.LoadDependencies(MaterialIconsRegular); + } + + private void OnEnable() + { + base.titleContent = new GUIContent("Material Icon Selection"); + base.minSize = new Vector2((iconSize + labelHeight + spacing) * 5f + GUI.skin.verticalScrollbar.fixedWidth + 1f, (iconSize + labelHeight + spacing) * 6f + EditorStyles.toolbar.fixedHeight); + selectionKeep = true; + } + + private void OnGUI() + { + if((toolbarSeachTextFieldStyle == null) || (iconImageStyle == null)) + { + toolbarSeachTextFieldStyle = new GUIStyle("ToolbarSeachTextField"); + toolbarSeachCancelButtonStyle = new GUIStyle("ToolbarSeachCancelButton"); + toolbarSeachCancelButtonEmptyStyle = new GUIStyle("ToolbarSeachCancelButtonEmpty"); + toolbarLabelStyle = new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleCenter }; + iconSelectionStyle = new GUIStyle("selectionrect"); + iconImageStyle = new GUIStyle() { font = MaterialIconsRegular, fontSize = iconSize - spacing - 10, alignment = TextAnchor.MiddleCenter }; + iconLabelStyle = new GUIStyle(EditorStyles.miniLabel) { alignment = TextAnchor.UpperCenter, wordWrap = true }; + iconImageStyle.padding = iconLabelStyle.padding = new RectOffset(); + iconImageStyle.normal.textColor = iconLabelStyle.normal.textColor = EditorGUIUtility.isProSkin ? lightColor : darkColor; + } + + if(MaterialIconsRegular == null) + { + EditorGUILayout.HelpBox("Could not find \"MaterialIcons-Regular\" font data.", MessageType.Error); + return; + } + + if((codepointsCollection == null) || (codepointsCollection.Length == 0)) + { + EditorGUILayout.HelpBox("Could not find \"codepoints\" font data.", MessageType.Error); + return; + } + + OnHeaderGUI(); + OnBodyGUI(); + } + + private void OnHeaderGUI() + { + Rect groupRect = new Rect(0f, 0f, base.position.width, EditorStyles.toolbar.fixedHeight); + GUI.BeginGroup(groupRect); + + if(Event.current.type == EventType.Repaint) + { + EditorStyles.toolbar.Draw(groupRect, false, false, false, false); + } + + Rect filterRect = new Rect(6f, 2f, groupRect.width - 6f - 20f - 64f - 6f, groupRect.height - 2f); + Rect clearRect = new Rect(filterRect.x + filterRect.width, filterRect.y, 20f, filterRect.height); + + EditorGUI.BeginChangeCheck(); + + GUI.SetNextControlName(typeof(MaterialIconSelectionWindow) + ".filterText"); + filterText = EditorGUI.TextField(filterRect, filterText, toolbarSeachTextFieldStyle); + + if(GUI.Button(clearRect, GUIContent.none, string.IsNullOrEmpty(filterText) ? toolbarSeachCancelButtonEmptyStyle : toolbarSeachCancelButtonStyle)) + { + filterText = string.Empty; + GUI.FocusControl(null); + } + + if(!filterGotFocus) + { + EditorGUI.FocusTextInControl(typeof(MaterialIconSelectionWindow) + ".filterText"); + filterGotFocus = true; + } + + if(EditorGUI.EndChangeCheck()) + { + filteredCollection = codepointsCollection.Where(data => string.IsNullOrEmpty(filterText) || data.nameGUIContent.text.IndexOf(filterText, System.StringComparison.OrdinalIgnoreCase) >= 0).ToArray(); + selectionKeep = true; + } + + Rect nameRect = new Rect(clearRect.x + clearRect.width, groupRect.y, 64f, groupRect.height); + EditorGUI.BeginChangeCheck(); + showNames = EditorGUI.Toggle(nameRect, showNames, EditorStyles.toolbarButton); + if(EditorGUI.EndChangeCheck()) + { + GUI.FocusControl(null); + EditorPrefs.SetBool(typeof(MaterialIconSelectionWindow) + ".showNames", showNames); + } + EditorGUI.LabelField(nameRect, "Names", toolbarLabelStyle); + + GUI.EndGroup(); + } + + private void OnBodyGUI() + { + Rect iconRect = new Rect(0f, 0f, iconSize + labelHeight, iconSize); + Rect labelRect = new Rect(0f, 0f, iconRect.width, labelHeight); + if(!showNames) + { + iconRect.width -= labelHeight; + labelRect.height = 0f; + } + Rect buttonRect = new Rect(0f, 0f, iconRect.width + spacing, iconRect.height + labelRect.height + spacing); + + Rect groupRect = new Rect(0f, EditorStyles.toolbar.fixedHeight, base.position.width, base.position.height - EditorStyles.toolbar.fixedHeight); + GUI.BeginGroup(groupRect); + + Rect scrollRect = new Rect(0f, 0f, groupRect.width, groupRect.height); + int columns = Mathf.FloorToInt((scrollRect.width - GUI.skin.verticalScrollbar.fixedWidth) / (iconRect.width + spacing)); + Rect viewRect = new Rect(0f, 0f, scrollRect.width - GUI.skin.verticalScrollbar.fixedWidth, Mathf.Ceil(filteredCollection.Length / (float) columns) * (iconRect.height + labelRect.height + spacing)); + + scrollPos = GUI.BeginScrollView(scrollRect, scrollPos, viewRect); + + for(int i = 0; i < filteredCollection.Length; i += columns) + { + for(int j = 0; j < columns; j++) + { + if((i + j) >= filteredCollection.Length) + break; + + var data = filteredCollection[i + j]; + + iconRect.x = (j * (iconRect.width + spacing)) + (spacing / 2f); + iconRect.y = ((i / (float) columns) * (iconRect.height + labelRect.height + spacing)) + (spacing / 2f); + + labelRect.x = iconRect.x; + labelRect.y = iconRect.y + iconRect.height; + + buttonRect.x = iconRect.x - (spacing / 2f); + buttonRect.y = iconRect.y - (spacing / 2f); + + if(data.name == selectedName) + { + if(Event.current.type == EventType.Repaint) + { + iconSelectionStyle.Draw(buttonRect, false, true, true, true); + } + + if(selectionKeep) + { + if(buttonRect.y + buttonRect.height > scrollPos.y + scrollRect.height) + scrollPos.y = buttonRect.y + buttonRect.height - scrollRect.height; + else if(buttonRect.y < scrollPos.y) + scrollPos.y = buttonRect.y; + + selectionKeep = false; + base.Repaint(); + } + } + + GUI.Label(iconRect, data.codeGUIContent, iconImageStyle); + if(showNames) + GUI.Label(labelRect, data.nameGUIContent, iconLabelStyle); + + if(GUI.Button(buttonRect, GUIContent.none, GUIStyle.none)) + { + GUI.FocusControl(null); + bool shouldClose = data.codeGUIContent.text == selected; + selected = data.codeGUIContent.text; + selectedName = data.name; + onSelectionChanged.Invoke(selected); + if(shouldClose) + base.Close(); + } + } + } + + GUI.EndScrollView(); + GUI.EndGroup(); + + if(Event.current.type == EventType.KeyDown) + { + if(Event.current.keyCode == KeyCode.LeftArrow) + { + SelectRelative(-1); + Event.current.Use(); + } + + if(Event.current.keyCode == KeyCode.RightArrow) + { + SelectRelative(+1); + Event.current.Use(); + } + + if(Event.current.keyCode == KeyCode.UpArrow) + { + SelectRelative(-columns); + Event.current.Use(); + } + + if(Event.current.keyCode == KeyCode.DownArrow) + { + SelectRelative(+columns); + Event.current.Use(); + } + + if(Event.current.keyCode == KeyCode.PageUp) + { + SelectRelative(-(columns * 6)); + Event.current.Use(); + } + + if(Event.current.keyCode == KeyCode.PageDown) + { + SelectRelative(+(columns * 6)); + Event.current.Use(); + } + + if(Event.current.keyCode == KeyCode.Home) + { + SelectAbsolute(0); + Event.current.Use(); + } + + if(Event.current.keyCode == KeyCode.End) + { + SelectAbsolute(filteredCollection.Length - 1); + Event.current.Use(); + } + } + } + + private void SelectRelative(int delta) + { + SelectAbsolute(System.Array.FindIndex(filteredCollection, (data) => data.name == selectedName) + delta); + } + + private void SelectAbsolute(int index) + { + index = Mathf.Clamp(index, 0, filteredCollection.Length - 1); + + selected = filteredCollection[index].codeGUIContent.text; + selectedName = filteredCollection[index].name; + onSelectionChanged.Invoke(selected); + selectionKeep = true; + base.Repaint(); + } + + [System.Serializable] + public class CodepointData + { + public string name { get; private set; } + public string code { get; private set; } + public GUIContent nameGUIContent { get; private set; } + public GUIContent codeGUIContent { get; private set; } + + public CodepointData(string name, string code) + { + this.name = name; + this.code = code; + this.nameGUIContent = new GUIContent(string.Format("{0} ({1})", name.ToLowerInvariant().Replace('_', ' '), code)); + this.codeGUIContent = new GUIContent(char.ConvertFromUtf32(System.Convert.ToInt32(this.code, 16)), this.nameGUIContent.text); + } + + } + +} + +} diff --git a/Samples~/Meet/Assets/MaterialIcons/Editor/MaterialIconSelectionWindow.cs.meta b/Samples~/Meet/Assets/MaterialIcons/Editor/MaterialIconSelectionWindow.cs.meta new file mode 100644 index 00000000..a0be2373 --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons/Editor/MaterialIconSelectionWindow.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 92727dd8d82a3da4bae52be1180138e8 +timeCreated: 1585147601 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/MaterialIcons/Editor/MenuOptions.cs b/Samples~/Meet/Assets/MaterialIcons/Editor/MenuOptions.cs new file mode 100644 index 00000000..73588937 --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons/Editor/MenuOptions.cs @@ -0,0 +1,43 @@ + +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using UnityEditor; + +namespace Google.MaterialDesign.Icons +{ + +internal static class MenuOptions +{ + [MenuItem("GameObject/Google/Material Icon", false, 10)] + public static void CreateMaterialIcon(MenuCommand menuCommand) + { + GameObject parent = menuCommand.context as GameObject; + + if((parent == null) || (parent.GetComponentsInParent(true).Length == 0)) + { + GameObject canvas = new GameObject("Canvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster)); + canvas.layer = LayerMask.NameToLayer("UI"); + canvas.GetComponent().renderMode = RenderMode.ScreenSpaceOverlay; + GameObjectUtility.SetParentAndAlign(canvas, parent); + Undo.RegisterCreatedObjectUndo(canvas, "Create " + canvas.name); + + if(GameObject.FindObjectOfType() == null) + { + GameObject eventSystem = new GameObject("EventSystem", typeof(EventSystem), typeof(StandaloneInputModule)); + GameObjectUtility.SetParentAndAlign(eventSystem, parent); + Undo.RegisterCreatedObjectUndo(eventSystem, "Create " + eventSystem.name); + } + + parent = canvas; + } + + GameObject gameObject = new GameObject("MaterialIcon", typeof(MaterialIcon)); + gameObject.layer = LayerMask.NameToLayer("UI"); + GameObjectUtility.SetParentAndAlign(gameObject, parent); + Undo.RegisterCreatedObjectUndo(gameObject, "Create " + gameObject.name); + Selection.activeObject = gameObject; + } +} + +} diff --git a/Samples~/Meet/Assets/MaterialIcons/Editor/MenuOptions.cs.meta b/Samples~/Meet/Assets/MaterialIcons/Editor/MenuOptions.cs.meta new file mode 100644 index 00000000..f91d2c19 --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons/Editor/MenuOptions.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 43dd1868dd14adf48939a5153365212b +timeCreated: 1614183199 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/MaterialIcons/LICENSE b/Samples~/Meet/Assets/MaterialIcons/LICENSE new file mode 100644 index 00000000..323d087e --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Samples~/Meet/Assets/MaterialIcons/LICENSE.meta b/Samples~/Meet/Assets/MaterialIcons/LICENSE.meta new file mode 100644 index 00000000..611e2d07 --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons/LICENSE.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4ff2670dd0fd741a198aa64f2ebcce6b +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/MaterialIcons/MaterialIcon.cs b/Samples~/Meet/Assets/MaterialIcons/MaterialIcon.cs new file mode 100644 index 00000000..1bc72e9c --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons/MaterialIcon.cs @@ -0,0 +1,100 @@ + +using UnityEngine; +using UnityEngine.UI; + +namespace Google.MaterialDesign.Icons +{ + +public class MaterialIcon : Text +{ + [SerializeField, Range(0f, 10f)] + private float _fontScale = 1f; + + public float fontScale + { + get { return this._fontScale; } + set { this._fontScale = value; UpdateFontSize(); } + } + + public string iconUnicode + { + get { return System.Convert.ToString(char.ConvertToUtf32(base.text, 0), 16); } + set { base.text = char.ConvertFromUtf32(System.Convert.ToInt32(value, 16)); } + } + + protected override void Start() + { + base.Start(); + + if(string.IsNullOrEmpty(base.text)) + { + Init(); + } + + #if UNITY_EDITOR + if(base.font == null) + { + LoadFont(); + } + #endif + } + + #if UNITY_EDITOR + protected override void Reset() + { + base.Reset(); + Init(); + LoadFont(); + } + + protected override void OnValidate() + { + base.OnValidate(); + base.SetLayoutDirty(); + UpdateFontSize(); + } + + /// Searches for the \"MaterialIcons-Regular\" font inside the project. + public void LoadFont() + { + foreach(string guid in UnityEditor.AssetDatabase.FindAssets("t:Font MaterialIcons-Regular")) + { + string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(guid); + + if(assetPath.EndsWith(".ttf", System.StringComparison.OrdinalIgnoreCase) && System.IO.File.Exists(System.IO.Path.GetDirectoryName(assetPath) + "/codepoints")) + { + base.font = UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath); + break; + } + } + } + #endif + + /// Properly initializes base Text class. + public void Init() + { + base.text = "\ue84d"; + base.font = null; + base.color = new Color(0.196f, 0.196f, 0.196f, 1.000f); + base.material = null; + base.alignment = TextAnchor.MiddleCenter; + base.supportRichText = false; + base.horizontalOverflow = HorizontalWrapMode.Overflow; + base.verticalOverflow = VerticalWrapMode.Overflow; + base.fontSize = Mathf.FloorToInt(Mathf.Min(base.rectTransform.rect.width, base.rectTransform.rect.height)); + } + + protected override void OnRectTransformDimensionsChange() + { + base.OnRectTransformDimensionsChange(); + UpdateFontSize(); + } + + private void UpdateFontSize() + { + base.fontSize = Mathf.FloorToInt(Mathf.Min(base.rectTransform.rect.width, base.rectTransform.rect.height) * this._fontScale); + } + +} + +} diff --git a/Samples~/Meet/Assets/MaterialIcons/MaterialIcon.cs.meta b/Samples~/Meet/Assets/MaterialIcons/MaterialIcon.cs.meta new file mode 100644 index 00000000..0fd86888 --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons/MaterialIcon.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: b616e0308c79a454ebc4f2af9f4e7668 +timeCreated: 1585147601 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/MaterialIcons/MaterialIcons-Regular.ttf b/Samples~/Meet/Assets/MaterialIcons/MaterialIcons-Regular.ttf new file mode 100644 index 00000000..9d09b0fe Binary files /dev/null and b/Samples~/Meet/Assets/MaterialIcons/MaterialIcons-Regular.ttf differ diff --git a/Samples~/Meet/Assets/MaterialIcons/MaterialIcons-Regular.ttf.meta b/Samples~/Meet/Assets/MaterialIcons/MaterialIcons-Regular.ttf.meta new file mode 100644 index 00000000..188a0d27 --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons/MaterialIcons-Regular.ttf.meta @@ -0,0 +1,23 @@ +fileFormatVersion: 2 +guid: 7aa8a800795d9664a864b2be473b4518 +timeCreated: 1585147601 +licenseType: Free +TrueTypeFontImporter: + externalObjects: {} + serializedVersion: 4 + fontSize: 16 + forceTextureCase: -2 + characterSpacing: 0 + characterPadding: 1 + includeFontData: 1 + fontName: Material Icons + fontNames: + - Material Icons + fallbackFontReferences: [] + customCharacters: + fontRenderingMode: 0 + ascentCalculationMode: 1 + useLegacyBoundsCalculation: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/MaterialIcons/codepoints b/Samples~/Meet/Assets/MaterialIcons/codepoints new file mode 100644 index 00000000..d08d0f64 --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons/codepoints @@ -0,0 +1,2234 @@ +10k e951 +10mp e952 +11mp e953 +123 eb8d +12mp e954 +13mp e955 +14mp e956 +15mp e957 +16mp e958 +17mp e959 +18_up_rating f8fd +18mp e95a +19mp e95b +1k e95c +1k_plus e95d +1x_mobiledata efcd +20mp e95e +21mp e95f +22mp e960 +23mp e961 +24mp e962 +2k e963 +2k_plus e964 +2mp e965 +30fps efce +30fps_select efcf +360 e577 +3d_rotation e84d +3g_mobiledata efd0 +3k e966 +3k_plus e967 +3mp e968 +3p efd1 +4g_mobiledata efd2 +4g_plus_mobiledata efd3 +4k e072 +4k_plus e969 +4mp e96a +5g ef38 +5k e96b +5k_plus e96c +5mp e96d +60fps efd4 +60fps_select efd5 +6_ft_apart f21e +6k e96e +6k_plus e96f +6mp e970 +7k e971 +7k_plus e972 +7mp e973 +8k e974 +8k_plus e975 +8mp e976 +9k e977 +9k_plus e978 +9mp e979 +abc eb94 +ac_unit eb3b +access_alarm e190 +access_alarms e191 +access_time e192 +access_time_filled efd6 +accessibility e84e +accessibility_new e92c +accessible e914 +accessible_forward e934 +account_balance e84f +account_balance_wallet e850 +account_box e851 +account_circle e853 +account_tree e97a +ad_units ef39 +adb e60e +add e145 +add_a_photo e439 +add_alarm e193 +add_alert e003 +add_box e146 +add_business e729 +add_call e0e8 +add_card eb86 +add_chart e97b +add_circle e147 +add_circle_outline e148 +add_comment e266 +add_home f8eb +add_home_work f8ed +add_ic_call e97c +add_link e178 +add_location e567 +add_location_alt ef3a +add_moderator e97d +add_photo_alternate e43e +add_reaction e1d3 +add_road ef3b +add_shopping_cart e854 +add_task f23a +add_to_drive e65c +add_to_home_screen e1fe +add_to_photos e39d +add_to_queue e05c +addchart ef3c +adf_scanner eada +adjust e39e +admin_panel_settings ef3d +adobe ea96 +ads_click e762 +agriculture ea79 +air efd8 +airline_seat_flat e630 +airline_seat_flat_angled e631 +airline_seat_individual_suite e632 +airline_seat_legroom_extra e633 +airline_seat_legroom_normal e634 +airline_seat_legroom_reduced e635 +airline_seat_recline_extra e636 +airline_seat_recline_normal e637 +airline_stops e7d0 +airlines e7ca +airplane_ticket efd9 +airplanemode_active e195 +airplanemode_inactive e194 +airplanemode_off e194 +airplanemode_on e195 +airplay e055 +airport_shuttle eb3c +alarm e855 +alarm_add e856 +alarm_off e857 +alarm_on e858 +album e019 +align_horizontal_center e00f +align_horizontal_left e00d +align_horizontal_right e010 +align_vertical_bottom e015 +align_vertical_center e011 +align_vertical_top e00c +all_inbox e97f +all_inclusive eb3d +all_out e90b +alt_route f184 +alternate_email e0e6 +amp_stories ea13 +analytics ef3e +anchor f1cd +android e859 +animation e71c +announcement e85a +aod efda +apartment ea40 +api f1b7 +app_blocking ef3f +app_registration ef40 +app_settings_alt ef41 +app_shortcut eae4 +apple ea80 +approval e982 +apps e5c3 +apps_outage e7cc +architecture ea3b +archive e149 +area_chart e770 +arrow_back e5c4 +arrow_back_ios e5e0 +arrow_back_ios_new e2ea +arrow_circle_down f181 +arrow_circle_left eaa7 +arrow_circle_right eaaa +arrow_circle_up f182 +arrow_downward e5db +arrow_drop_down e5c5 +arrow_drop_down_circle e5c6 +arrow_drop_up e5c7 +arrow_forward e5c8 +arrow_forward_ios e5e1 +arrow_left e5de +arrow_outward f8ce +arrow_right e5df +arrow_right_alt e941 +arrow_upward e5d8 +art_track e060 +article ef42 +aspect_ratio e85b +assessment e85c +assignment e85d +assignment_add f848 +assignment_ind e85e +assignment_late e85f +assignment_return e860 +assignment_returned e861 +assignment_turned_in e862 +assist_walker f8d5 +assistant e39f +assistant_direction e988 +assistant_navigation e989 +assistant_photo e3a0 +assured_workload eb6f +atm e573 +attach_email ea5e +attach_file e226 +attach_money e227 +attachment e2bc +attractions ea52 +attribution efdb +audio_file eb82 +audiotrack e3a1 +auto_awesome e65f +auto_awesome_mosaic e660 +auto_awesome_motion e661 +auto_delete ea4c +auto_fix_high e663 +auto_fix_normal e664 +auto_fix_off e665 +auto_graph e4fb +auto_mode ec20 +auto_stories e666 +autofps_select efdc +autorenew e863 +av_timer e01b +baby_changing_station f19b +back_hand e764 +backpack f19c +backspace e14a +backup e864 +backup_table ef43 +badge ea67 +bakery_dining ea53 +balance eaf6 +balcony e58f +ballot e172 +bar_chart e26b +barcode_reader f85c +batch_prediction f0f5 +bathroom efdd +bathtub ea41 +battery_0_bar ebdc +battery_1_bar ebd9 +battery_2_bar ebe0 +battery_3_bar ebdd +battery_4_bar ebe2 +battery_5_bar ebd4 +battery_6_bar ebd2 +battery_alert e19c +battery_charging_full e1a3 +battery_full e1a4 +battery_saver efde +battery_std e1a5 +battery_unknown e1a6 +beach_access eb3e +bed efdf +bedroom_baby efe0 +bedroom_child efe1 +bedroom_parent efe2 +bedtime ef44 +bedtime_off eb76 +beenhere e52d +bento f1f4 +bike_scooter ef45 +biotech ea3a +blender efe3 +blind f8d6 +blinds e286 +blinds_closed ec1f +block e14b +block_flipped ef46 +bloodtype efe4 +bluetooth e1a7 +bluetooth_audio e60f +bluetooth_connected e1a8 +bluetooth_disabled e1a9 +bluetooth_drive efe5 +bluetooth_searching e1aa +blur_circular e3a2 +blur_linear e3a3 +blur_off e3a4 +blur_on e3a5 +bolt ea0b +book e865 +book_online f217 +bookmark e866 +bookmark_add e598 +bookmark_added e599 +bookmark_border e867 +bookmark_outline e867 +bookmark_remove e59a +bookmarks e98b +border_all e228 +border_bottom e229 +border_clear e22a +border_color e22b +border_horizontal e22c +border_inner e22d +border_left e22e +border_outer e22f +border_right e230 +border_style e231 +border_top e232 +border_vertical e233 +boy eb67 +branding_watermark e06b +breakfast_dining ea54 +brightness_1 e3a6 +brightness_2 e3a7 +brightness_3 e3a8 +brightness_4 e3a9 +brightness_5 e3aa +brightness_6 e3ab +brightness_7 e3ac +brightness_auto e1ab +brightness_high e1ac +brightness_low e1ad +brightness_medium e1ae +broadcast_on_home f8f8 +broadcast_on_personal f8f9 +broken_image e3ad +browse_gallery ebd1 +browser_not_supported ef47 +browser_updated e7cf +brunch_dining ea73 +brush e3ae +bubble_chart e6dd +bug_report e868 +build e869 +build_circle ef48 +bungalow e591 +burst_mode e43c +bus_alert e98f +business e0af +business_center eb3f +cabin e589 +cable efe6 +cached e86a +cake e7e9 +calculate ea5f +calendar_month ebcc +calendar_today e935 +calendar_view_day e936 +calendar_view_month efe7 +calendar_view_week efe8 +call e0b0 +call_end e0b1 +call_made e0b2 +call_merge e0b3 +call_missed e0b4 +call_missed_outgoing e0e4 +call_received e0b5 +call_split e0b6 +call_to_action e06c +camera e3af +camera_alt e3b0 +camera_enhance e8fc +camera_front e3b1 +camera_indoor efe9 +camera_outdoor efea +camera_rear e3b2 +camera_roll e3b3 +cameraswitch efeb +campaign ef49 +cancel e5c9 +cancel_presentation e0e9 +cancel_schedule_send ea39 +candlestick_chart ead4 +car_crash ebf2 +car_rental ea55 +car_repair ea56 +card_giftcard e8f6 +card_membership e8f7 +card_travel e8f8 +carpenter f1f8 +cases e992 +casino eb40 +cast e307 +cast_connected e308 +cast_for_education efec +castle eab1 +catching_pokemon e508 +category e574 +celebration ea65 +cell_tower ebba +cell_wifi e0ec +center_focus_strong e3b4 +center_focus_weak e3b5 +chair efed +chair_alt efee +chalet e585 +change_circle e2e7 +change_history e86b +charging_station f19d +chat e0b7 +chat_bubble e0ca +chat_bubble_outline e0cb +check e5ca +check_box e834 +check_box_outline_blank e835 +check_circle e86c +check_circle_outline e92d +checklist e6b1 +checklist_rtl e6b3 +checkroom f19e +chevron_left e5cb +chevron_right e5cc +child_care eb41 +child_friendly eb42 +chrome_reader_mode e86d +church eaae +circle ef4a +circle_notifications e994 +class e86e +clean_hands f21f +cleaning_services f0ff +clear e14c +clear_all e0b8 +close e5cd +close_fullscreen f1cf +closed_caption e01c +closed_caption_disabled f1dc +closed_caption_off e996 +cloud e2bd +cloud_circle e2be +cloud_done e2bf +cloud_download e2c0 +cloud_off e2c1 +cloud_queue e2c2 +cloud_sync eb5a +cloud_upload e2c3 +cloudy_snowing e810 +co2 e7b0 +co_present eaf0 +code e86f +code_off e4f3 +coffee efef +coffee_maker eff0 +collections e3b6 +collections_bookmark e431 +color_lens e3b7 +colorize e3b8 +comment e0b9 +comment_bank ea4e +comments_disabled e7a2 +commit eaf5 +commute e940 +compare e3b9 +compare_arrows e915 +compass_calibration e57c +compost e761 +compress e94d +computer e30a +confirmation_num e638 +confirmation_number e638 +connect_without_contact f223 +connected_tv e998 +connecting_airports e7c9 +construction ea3c +contact_emergency f8d1 +contact_mail e0d0 +contact_page f22e +contact_phone e0cf +contact_support e94c +contactless ea71 +contacts e0ba +content_copy e14d +content_cut e14e +content_paste e14f +content_paste_go ea8e +content_paste_off e4f8 +content_paste_search ea9b +contrast eb37 +control_camera e074 +control_point e3ba +control_point_duplicate e3bb +conveyor_belt f867 +cookie eaac +copy_all e2ec +copyright e90c +coronavirus f221 +corporate_fare f1d0 +cottage e587 +countertops f1f7 +create e150 +create_new_folder e2cc +credit_card e870 +credit_card_off e4f4 +credit_score eff1 +crib e588 +crisis_alert ebe9 +crop e3be +crop_16_9 e3bc +crop_3_2 e3bd +crop_5_4 e3bf +crop_7_5 e3c0 +crop_din e3c1 +crop_free e3c2 +crop_landscape e3c3 +crop_original e3c4 +crop_portrait e3c5 +crop_rotate e437 +crop_square e3c6 +cruelty_free e799 +css eb93 +currency_bitcoin ebc5 +currency_exchange eb70 +currency_franc eafa +currency_lira eaef +currency_pound eaf1 +currency_ruble eaec +currency_rupee eaf7 +currency_yen eafb +currency_yuan eaf9 +curtains ec1e +curtains_closed ec1d +cyclone ebd5 +dangerous e99a +dark_mode e51c +dashboard e871 +dashboard_customize e99b +data_array ead1 +data_exploration e76f +data_object ead3 +data_saver_off eff2 +data_saver_on eff3 +data_thresholding eb9f +data_usage e1af +dataset f8ee +dataset_linked f8ef +date_range e916 +deblur eb77 +deck ea42 +dehaze e3c7 +delete e872 +delete_forever e92b +delete_outline e92e +delete_sweep e16c +delivery_dining ea72 +density_large eba9 +density_medium eb9e +density_small eba8 +departure_board e576 +description e873 +deselect ebb6 +design_services f10a +desk f8f4 +desktop_access_disabled e99d +desktop_mac e30b +desktop_windows e30c +details e3c8 +developer_board e30d +developer_board_off e4ff +developer_mode e1b0 +device_hub e335 +device_thermostat e1ff +device_unknown e339 +devices e1b1 +devices_fold ebde +devices_other e337 +dew_point f879 +dialer_sip e0bb +dialpad e0bc +diamond ead5 +difference eb7d +dining eff4 +dinner_dining ea57 +directions e52e +directions_bike e52f +directions_boat e532 +directions_boat_filled eff5 +directions_bus e530 +directions_bus_filled eff6 +directions_car e531 +directions_car_filled eff7 +directions_ferry e532 +directions_off f10f +directions_railway e534 +directions_railway_filled eff8 +directions_run e566 +directions_subway e533 +directions_subway_filled eff9 +directions_train e534 +directions_transit e535 +directions_transit_filled effa +directions_walk e536 +dirty_lens ef4b +disabled_by_default f230 +disabled_visible e76e +disc_full e610 +discord ea6c +discount ebc9 +display_settings eb97 +diversity_1 f8d7 +diversity_2 f8d8 +diversity_3 f8d9 +dnd_forwardslash e611 +dns e875 +do_disturb f08c +do_disturb_alt f08d +do_disturb_off f08e +do_disturb_on f08f +do_not_disturb e612 +do_not_disturb_alt e611 +do_not_disturb_off e643 +do_not_disturb_on e644 +do_not_disturb_on_total_silence effb +do_not_step f19f +do_not_touch f1b0 +dock e30e +document_scanner e5fa +domain e7ee +domain_add eb62 +domain_disabled e0ef +domain_verification ef4c +done e876 +done_all e877 +done_outline e92f +donut_large e917 +donut_small e918 +door_back effc +door_front effd +door_sliding effe +doorbell efff +double_arrow ea50 +downhill_skiing e509 +download f090 +download_done f091 +download_for_offline f000 +downloading f001 +drafts e151 +drag_handle e25d +drag_indicator e945 +draw e746 +drive_eta e613 +drive_file_move e675 +drive_file_move_outline e9a1 +drive_file_move_rtl e76d +drive_file_rename_outline e9a2 +drive_folder_upload e9a3 +dry f1b3 +dry_cleaning ea58 +duo e9a5 +dvr e1b2 +dynamic_feed ea14 +dynamic_form f1bf +e_mobiledata f002 +earbuds f003 +earbuds_battery f004 +east f1df +eco ea35 +edgesensor_high f005 +edgesensor_low f006 +edit e3c9 +edit_attributes e578 +edit_calendar e742 +edit_document f88c +edit_location e568 +edit_location_alt e1c5 +edit_note e745 +edit_notifications e525 +edit_off e950 +edit_road ef4d +edit_square f88d +egg eacc +egg_alt eac8 +eject e8fb +elderly f21a +elderly_woman eb69 +electric_bike eb1b +electric_bolt ec1c +electric_car eb1c +electric_meter ec1b +electric_moped eb1d +electric_rickshaw eb1e +electric_scooter eb1f +electrical_services f102 +elevator f1a0 +email e0be +emergency e1eb +emergency_recording ebf4 +emergency_share ebf6 +emoji_emotions ea22 +emoji_events ea23 +emoji_flags ea1a +emoji_food_beverage ea1b +emoji_nature ea1c +emoji_objects ea24 +emoji_people ea1d +emoji_symbols ea1e +emoji_transportation ea1f +energy_savings_leaf ec1a +engineering ea3d +enhance_photo_translate e8fc +enhanced_encryption e63f +equalizer e01d +error e000 +error_outline e001 +escalator f1a1 +escalator_warning f1ac +euro ea15 +euro_symbol e926 +ev_station e56d +event e878 +event_available e614 +event_busy e615 +event_note e616 +event_repeat eb7b +event_seat e903 +exit_to_app e879 +expand e94f +expand_circle_down e7cd +expand_less e5ce +expand_more e5cf +explicit e01e +explore e87a +explore_off e9a8 +exposure e3ca +exposure_minus_1 e3cb +exposure_minus_2 e3cc +exposure_neg_1 e3cb +exposure_neg_2 e3cc +exposure_plus_1 e3cd +exposure_plus_2 e3ce +exposure_zero e3cf +extension e87b +extension_off e4f5 +face e87c +face_2 f8da +face_3 f8db +face_4 f8dc +face_5 f8dd +face_6 f8de +face_retouching_natural ef4e +face_retouching_off f007 +facebook f234 +fact_check f0c5 +factory ebbc +family_restroom f1a2 +fast_forward e01f +fast_rewind e020 +fastfood e57a +favorite e87d +favorite_border e87e +favorite_outline e87e +fax ead8 +featured_play_list e06d +featured_video e06e +feed f009 +feedback e87f +female e590 +fence f1f6 +festival ea68 +fiber_dvr e05d +fiber_manual_record e061 +fiber_new e05e +fiber_pin e06a +fiber_smart_record e062 +file_copy e173 +file_download e2c4 +file_download_done e9aa +file_download_off e4fe +file_open eaf3 +file_present ea0e +file_upload e2c6 +file_upload_off f886 +filter e3d3 +filter_1 e3d0 +filter_2 e3d1 +filter_3 e3d2 +filter_4 e3d4 +filter_5 e3d5 +filter_6 e3d6 +filter_7 e3d7 +filter_8 e3d8 +filter_9 e3d9 +filter_9_plus e3da +filter_alt ef4f +filter_alt_off eb32 +filter_b_and_w e3db +filter_center_focus e3dc +filter_drama e3dd +filter_frames e3de +filter_hdr e3df +filter_list e152 +filter_list_alt e94e +filter_list_off eb57 +filter_none e3e0 +filter_tilt_shift e3e2 +filter_vintage e3e3 +find_in_page e880 +find_replace e881 +fingerprint e90d +fire_extinguisher f1d8 +fire_hydrant f1a3 +fire_hydrant_alt f8f1 +fire_truck f8f2 +fireplace ea43 +first_page e5dc +fit_screen ea10 +fitbit e82b +fitness_center eb43 +flag e153 +flag_circle eaf8 +flaky ef50 +flare e3e4 +flash_auto e3e5 +flash_off e3e6 +flash_on e3e7 +flashlight_off f00a +flashlight_on f00b +flatware f00c +flight e539 +flight_class e7cb +flight_land e904 +flight_takeoff e905 +flip e3e8 +flip_camera_android ea37 +flip_camera_ios ea38 +flip_to_back e882 +flip_to_front e883 +flood ebe6 +flourescent f00d +fluorescent ec31 +flutter_dash e00b +fmd_bad f00e +fmd_good f00f +foggy e818 +folder e2c7 +folder_copy ebbd +folder_delete eb34 +folder_off eb83 +folder_open e2c8 +folder_shared e2c9 +folder_special e617 +folder_zip eb2c +follow_the_signs f222 +font_download e167 +font_download_off e4f9 +food_bank f1f2 +forest ea99 +fork_left eba0 +fork_right ebac +forklift f868 +format_align_center e234 +format_align_justify e235 +format_align_left e236 +format_align_right e237 +format_bold e238 +format_clear e239 +format_color_fill e23a +format_color_reset e23b +format_color_text e23c +format_indent_decrease e23d +format_indent_increase e23e +format_italic e23f +format_line_spacing e240 +format_list_bulleted e241 +format_list_bulleted_add f849 +format_list_numbered e242 +format_list_numbered_rtl e267 +format_overline eb65 +format_paint e243 +format_quote e244 +format_shapes e25e +format_size e245 +format_strikethrough e246 +format_textdirection_l_to_r e247 +format_textdirection_r_to_l e248 +format_underline e249 +format_underlined e249 +fort eaad +forum e0bf +forward e154 +forward_10 e056 +forward_30 e057 +forward_5 e058 +forward_to_inbox f187 +foundation f200 +free_breakfast eb44 +free_cancellation e748 +front_hand e769 +front_loader f869 +fullscreen e5d0 +fullscreen_exit e5d1 +functions e24a +g_mobiledata f010 +g_translate e927 +gamepad e30f +games e021 +garage f011 +gas_meter ec19 +gavel e90e +generating_tokens e749 +gesture e155 +get_app e884 +gif e908 +gif_box e7a3 +girl eb68 +gite e58b +golf_course eb45 +gpp_bad f012 +gpp_good f013 +gpp_maybe f014 +gps_fixed e1b3 +gps_not_fixed e1b4 +gps_off e1b5 +grade e885 +gradient e3e9 +grading ea4f +grain e3ea +graphic_eq e1b8 +grass f205 +grid_3x3 f015 +grid_4x4 f016 +grid_goldenratio f017 +grid_off e3eb +grid_on e3ec +grid_view e9b0 +group e7ef +group_add e7f0 +group_off e747 +group_remove e7ad +group_work e886 +groups f233 +groups_2 f8df +groups_3 f8e0 +h_mobiledata f018 +h_plus_mobiledata f019 +hail e9b1 +handshake ebcb +handyman f10b +hardware ea59 +hd e052 +hdr_auto f01a +hdr_auto_select f01b +hdr_enhanced_select ef51 +hdr_off e3ed +hdr_off_select f01c +hdr_on e3ee +hdr_on_select f01d +hdr_plus f01e +hdr_strong e3f1 +hdr_weak e3f2 +headphones f01f +headphones_battery f020 +headset e310 +headset_mic e311 +headset_off e33a +healing e3f3 +health_and_safety e1d5 +hearing e023 +hearing_disabled f104 +heart_broken eac2 +heat_pump ec18 +height ea16 +help e887 +help_center f1c0 +help_outline e8fd +hevc f021 +hexagon eb39 +hide_image f022 +hide_source f023 +high_quality e024 +highlight e25f +highlight_alt ef52 +highlight_off e888 +highlight_remove e888 +hiking e50a +history e889 +history_edu ea3e +history_toggle_off f17d +hive eaa6 +hls eb8a +hls_off eb8c +holiday_village e58a +home e88a +home_filled e9b2 +home_max f024 +home_mini f025 +home_repair_service f100 +home_work ea09 +horizontal_distribute e014 +horizontal_rule f108 +horizontal_split e947 +hot_tub eb46 +hotel e53a +hotel_class e743 +hourglass_bottom ea5c +hourglass_disabled ef53 +hourglass_empty e88b +hourglass_full e88c +hourglass_top ea5b +house ea44 +house_siding f202 +houseboat e584 +how_to_reg e174 +how_to_vote e175 +html eb7e +http e902 +https e88d +hub e9f4 +hvac f10e +ice_skating e50b +icecream ea69 +image e3f4 +image_aspect_ratio e3f5 +image_not_supported f116 +image_search e43f +imagesearch_roller e9b4 +import_contacts e0e0 +import_export e0c3 +important_devices e912 +inbox e156 +incomplete_circle e79b +indeterminate_check_box e909 +info e88e +info_outline e88f +input e890 +insert_chart e24b +insert_chart_outlined e26a +insert_comment e24c +insert_drive_file e24d +insert_emoticon e24e +insert_invitation e24f +insert_link e250 +insert_page_break eaca +insert_photo e251 +insights f092 +install_desktop eb71 +install_mobile eb72 +integration_instructions ef54 +interests e7c8 +interpreter_mode e83b +inventory e179 +inventory_2 e1a1 +invert_colors e891 +invert_colors_off e0c4 +invert_colors_on e891 +ios_share e6b8 +iron e583 +iso e3f6 +javascript eb7c +join_full eaeb +join_inner eaf4 +join_left eaf2 +join_right eaea +kayaking e50c +kebab_dining e842 +key e73c +key_off eb84 +keyboard e312 +keyboard_alt f028 +keyboard_arrow_down e313 +keyboard_arrow_left e314 +keyboard_arrow_right e315 +keyboard_arrow_up e316 +keyboard_backspace e317 +keyboard_capslock e318 +keyboard_command eae0 +keyboard_command_key eae7 +keyboard_control e5d3 +keyboard_control_key eae6 +keyboard_double_arrow_down ead0 +keyboard_double_arrow_left eac3 +keyboard_double_arrow_right eac9 +keyboard_double_arrow_up eacf +keyboard_hide e31a +keyboard_option eadf +keyboard_option_key eae8 +keyboard_return e31b +keyboard_tab e31c +keyboard_voice e31d +king_bed ea45 +kitchen eb47 +kitesurfing e50d +label e892 +label_important e937 +label_important_outline e948 +label_off e9b6 +label_outline e893 +lan eb2f +landscape e3f7 +landslide ebd7 +language e894 +laptop e31e +laptop_chromebook e31f +laptop_mac e320 +laptop_windows e321 +last_page e5dd +launch e895 +layers e53b +layers_clear e53c +leaderboard f20c +leak_add e3f8 +leak_remove e3f9 +leave_bags_at_home f21b +legend_toggle f11b +lens e3fa +lens_blur f029 +library_add e02e +library_add_check e9b7 +library_books e02f +library_music e030 +light f02a +light_mode e518 +lightbulb e0f0 +lightbulb_circle ebfe +lightbulb_outline e90f +line_axis ea9a +line_style e919 +line_weight e91a +linear_scale e260 +link e157 +link_off e16f +linked_camera e438 +liquor ea60 +list e896 +list_alt e0ee +live_help e0c6 +live_tv e639 +living f02b +local_activity e53f +local_airport e53d +local_atm e53e +local_attraction e53f +local_bar e540 +local_cafe e541 +local_car_wash e542 +local_convenience_store e543 +local_dining e556 +local_drink e544 +local_fire_department ef55 +local_florist e545 +local_gas_station e546 +local_grocery_store e547 +local_hospital e548 +local_hotel e549 +local_laundry_service e54a +local_library e54b +local_mall e54c +local_movies e54d +local_offer e54e +local_parking e54f +local_pharmacy e550 +local_phone e551 +local_pizza e552 +local_play e553 +local_police ef56 +local_post_office e554 +local_print_shop e555 +local_printshop e555 +local_restaurant e556 +local_see e557 +local_shipping e558 +local_taxi e559 +location_city e7f1 +location_disabled e1b6 +location_history e55a +location_off e0c7 +location_on e0c8 +location_pin f1db +location_searching e1b7 +lock e897 +lock_clock ef57 +lock_open e898 +lock_outline e899 +lock_person f8f3 +lock_reset eade +login ea77 +logo_dev ead6 +logout e9ba +looks e3fc +looks_3 e3fb +looks_4 e3fd +looks_5 e3fe +looks_6 e3ff +looks_one e400 +looks_two e401 +loop e028 +loupe e402 +low_priority e16d +loyalty e89a +lte_mobiledata f02c +lte_plus_mobiledata f02d +luggage f235 +lunch_dining ea61 +lyrics ec0b +macro_off f8d2 +mail e158 +mail_lock ec0a +mail_outline e0e1 +male e58e +man e4eb +man_2 f8e1 +man_3 f8e2 +man_4 f8e3 +manage_accounts f02e +manage_history ebe7 +manage_search f02f +map e55b +maps_home_work f030 +maps_ugc ef58 +margin e9bb +mark_as_unread e9bc +mark_chat_read f18b +mark_chat_unread f189 +mark_email_read f18c +mark_email_unread f18a +mark_unread_chat_alt eb9d +markunread e159 +markunread_mailbox e89b +masks f218 +maximize e930 +media_bluetooth_off f031 +media_bluetooth_on f032 +mediation efa7 +medical_information ebed +medical_services f109 +medication f033 +medication_liquid ea87 +meeting_room eb4f +memory e322 +menu e5d2 +menu_book ea19 +menu_open e9bd +merge eb98 +merge_type e252 +message e0c9 +messenger e0ca +messenger_outline e0cb +mic e029 +mic_external_off ef59 +mic_external_on ef5a +mic_none e02a +mic_off e02b +microwave f204 +military_tech ea3f +minimize e931 +minor_crash ebf1 +miscellaneous_services f10c +missed_video_call e073 +mms e618 +mobile_friendly e200 +mobile_off e201 +mobile_screen_share e0e7 +mobiledata_off f034 +mode f097 +mode_comment e253 +mode_edit e254 +mode_edit_outline f035 +mode_fan_off ec17 +mode_night f036 +mode_of_travel e7ce +mode_standby f037 +model_training f0cf +monetization_on e263 +money e57d +money_off e25c +money_off_csred f038 +monitor ef5b +monitor_heart eaa2 +monitor_weight f039 +monochrome_photos e403 +mood e7f2 +mood_bad e7f3 +moped eb28 +more e619 +more_horiz e5d3 +more_time ea5d +more_vert e5d4 +mosque eab2 +motion_photos_auto f03a +motion_photos_off e9c0 +motion_photos_on e9c1 +motion_photos_pause f227 +motion_photos_paused e9c2 +motorcycle e91b +mouse e323 +move_down eb61 +move_to_inbox e168 +move_up eb64 +movie e02c +movie_creation e404 +movie_edit f840 +movie_filter e43a +moving e501 +mp e9c3 +multiline_chart e6df +multiple_stop f1b9 +multitrack_audio e1b8 +museum ea36 +music_note e405 +music_off e440 +music_video e063 +my_library_add e02e +my_library_books e02f +my_library_music e030 +my_location e55c +nat ef5c +nature e406 +nature_people e407 +navigate_before e408 +navigate_next e409 +navigation e55d +near_me e569 +near_me_disabled f1ef +nearby_error f03b +nearby_off f03c +nest_cam_wired_stand ec16 +network_cell e1b9 +network_check e640 +network_locked e61a +network_ping ebca +network_wifi e1ba +network_wifi_1_bar ebe4 +network_wifi_2_bar ebd6 +network_wifi_3_bar ebe1 +new_label e609 +new_releases e031 +newspaper eb81 +next_plan ef5d +next_week e16a +nfc e1bb +night_shelter f1f1 +nightlife ea62 +nightlight f03d +nightlight_round ef5e +nights_stay ea46 +no_accounts f03e +no_adult_content f8fe +no_backpack f237 +no_cell f1a4 +no_crash ebf0 +no_drinks f1a5 +no_encryption e641 +no_encryption_gmailerrorred f03f +no_flash f1a6 +no_food f1a7 +no_luggage f23b +no_meals f1d6 +no_meals_ouline f229 +no_meeting_room eb4e +no_photography f1a8 +no_sim e0cc +no_stroller f1af +no_transfer f1d5 +noise_aware ebec +noise_control_off ebf3 +nordic_walking e50e +north f1e0 +north_east f1e1 +north_west f1e2 +not_accessible f0fe +not_interested e033 +not_listed_location e575 +not_started f0d1 +note e06f +note_add e89c +note_alt f040 +notes e26c +notification_add e399 +notification_important e004 +notifications e7f4 +notifications_active e7f7 +notifications_none e7f5 +notifications_off e7f6 +notifications_on e7f7 +notifications_paused e7f8 +now_wallpaper e1bc +now_widgets e1bd +numbers eac7 +offline_bolt e932 +offline_pin e90a +offline_share e9c5 +oil_barrel ec15 +on_device_training ebfd +ondemand_video e63a +online_prediction f0eb +opacity e91c +open_in_browser e89d +open_in_full f1ce +open_in_new e89e +open_in_new_off e4f6 +open_with e89f +other_houses e58c +outbond f228 +outbound e1ca +outbox ef5f +outdoor_grill ea47 +outgoing_mail f0d2 +outlet f1d4 +outlined_flag e16e +output ebbe +padding e9c8 +pages e7f9 +pageview e8a0 +paid f041 +palette e40a +pallet f86a +pan_tool e925 +pan_tool_alt ebb9 +panorama e40b +panorama_fish_eye e40c +panorama_fisheye e40c +panorama_horizontal e40d +panorama_horizontal_select ef60 +panorama_photosphere e9c9 +panorama_photosphere_select e9ca +panorama_vertical e40e +panorama_vertical_select ef61 +panorama_wide_angle e40f +panorama_wide_angle_select ef62 +paragliding e50f +park ea63 +party_mode e7fa +password f042 +pattern f043 +pause e034 +pause_circle e1a2 +pause_circle_filled e035 +pause_circle_outline e036 +pause_presentation e0ea +payment e8a1 +payments ef63 +paypal ea8d +pedal_bike eb29 +pending ef64 +pending_actions f1bb +pentagon eb50 +people e7fb +people_alt ea21 +people_outline e7fc +percent eb58 +perm_camera_mic e8a2 +perm_contact_cal e8a3 +perm_contact_calendar e8a3 +perm_data_setting e8a4 +perm_device_info e8a5 +perm_device_information e8a5 +perm_identity e8a6 +perm_media e8a7 +perm_phone_msg e8a8 +perm_scan_wifi e8a9 +person e7fd +person_2 f8e4 +person_3 f8e5 +person_4 f8e6 +person_add e7fe +person_add_alt ea4d +person_add_alt_1 ef65 +person_add_disabled e9cb +person_off e510 +person_outline e7ff +person_pin e55a +person_pin_circle e56a +person_remove ef66 +person_remove_alt_1 ef67 +person_search f106 +personal_injury e6da +personal_video e63b +pest_control f0fa +pest_control_rodent f0fd +pets e91d +phishing ead7 +phone e0cd +phone_android e324 +phone_bluetooth_speaker e61b +phone_callback e649 +phone_disabled e9cc +phone_enabled e9cd +phone_forwarded e61c +phone_in_talk e61d +phone_iphone e325 +phone_locked e61e +phone_missed e61f +phone_paused e620 +phonelink e326 +phonelink_erase e0db +phonelink_lock e0dc +phonelink_off e327 +phonelink_ring e0dd +phonelink_setup e0de +photo e410 +photo_album e411 +photo_camera e412 +photo_camera_back ef68 +photo_camera_front ef69 +photo_filter e43b +photo_library e413 +photo_size_select_actual e432 +photo_size_select_large e433 +photo_size_select_small e434 +php eb8f +piano e521 +piano_off e520 +picture_as_pdf e415 +picture_in_picture e8aa +picture_in_picture_alt e911 +pie_chart e6c4 +pie_chart_outline f044 +pie_chart_outlined e6c5 +pin f045 +pin_drop e55e +pin_end e767 +pin_invoke e763 +pinch eb38 +pivot_table_chart e9ce +pix eaa3 +place e55f +plagiarism ea5a +play_arrow e037 +play_circle e1c4 +play_circle_fill e038 +play_circle_filled e038 +play_circle_outline e039 +play_disabled ef6a +play_for_work e906 +play_lesson f047 +playlist_add e03b +playlist_add_check e065 +playlist_add_check_circle e7e6 +playlist_add_circle e7e5 +playlist_play e05f +playlist_remove eb80 +plumbing f107 +plus_one e800 +podcasts f048 +point_of_sale f17e +policy ea17 +poll e801 +polyline ebbb +polymer e8ab +pool eb48 +portable_wifi_off e0ce +portrait e416 +post_add ea20 +power e63c +power_input e336 +power_off e646 +power_settings_new e8ac +precision_manufacturing f049 +pregnant_woman e91e +present_to_all e0df +preview f1c5 +price_change f04a +price_check f04b +print e8ad +print_disabled e9cf +priority_high e645 +privacy_tip f0dc +private_connectivity e744 +production_quantity_limits e1d1 +propane ec14 +propane_tank ec13 +psychology ea4a +psychology_alt f8ea +public e80b +public_off f1ca +publish e255 +published_with_changes f232 +punch_clock eaa8 +push_pin f10d +qr_code ef6b +qr_code_2 e00a +qr_code_scanner f206 +query_builder e8ae +query_stats e4fc +question_answer e8af +question_mark eb8b +queue e03c +queue_music e03d +queue_play_next e066 +quick_contacts_dialer e0cf +quick_contacts_mail e0d0 +quickreply ef6c +quiz f04c +quora ea98 +r_mobiledata f04d +radar f04e +radio e03e +radio_button_checked e837 +radio_button_off e836 +radio_button_on e837 +radio_button_unchecked e836 +railway_alert e9d1 +ramen_dining ea64 +ramp_left eb9c +ramp_right eb96 +rate_review e560 +raw_off f04f +raw_on f050 +read_more ef6d +real_estate_agent e73a +rebase_edit f846 +receipt e8b0 +receipt_long ef6e +recent_actors e03f +recommend e9d2 +record_voice_over e91f +rectangle eb54 +recycling e760 +reddit eaa0 +redeem e8b1 +redo e15a +reduce_capacity f21c +refresh e5d5 +remember_me f051 +remove e15b +remove_circle e15c +remove_circle_outline e15d +remove_done e9d3 +remove_from_queue e067 +remove_moderator e9d4 +remove_red_eye e417 +remove_road ebfc +remove_shopping_cart e928 +reorder e8fe +repartition f8e8 +repeat e040 +repeat_on e9d6 +repeat_one e041 +repeat_one_on e9d7 +replay e042 +replay_10 e059 +replay_30 e05a +replay_5 e05b +replay_circle_filled e9d8 +reply e15e +reply_all e15f +report e160 +report_gmailerrorred f052 +report_off e170 +report_problem e8b2 +request_page f22c +request_quote f1b6 +reset_tv e9d9 +restart_alt f053 +restaurant e56c +restaurant_menu e561 +restore e8b3 +restore_from_trash e938 +restore_page e929 +reviews f054 +rice_bowl f1f5 +ring_volume e0d1 +rocket eba5 +rocket_launch eb9b +roller_shades ec12 +roller_shades_closed ec11 +roller_skating ebcd +roofing f201 +room e8b4 +room_preferences f1b8 +room_service eb49 +rotate_90_degrees_ccw e418 +rotate_90_degrees_cw eaab +rotate_left e419 +rotate_right e41a +roundabout_left eb99 +roundabout_right eba3 +rounded_corner e920 +route eacd +router e328 +rowing e921 +rss_feed e0e5 +rsvp f055 +rtt e9ad +rule f1c2 +rule_folder f1c9 +run_circle ef6f +running_with_errors e51d +rv_hookup e642 +safety_check ebef +safety_divider e1cc +sailing e502 +sanitizer f21d +satellite e562 +satellite_alt eb3a +save e161 +save_alt e171 +save_as eb60 +saved_search ea11 +savings e2eb +scale eb5f +scanner e329 +scatter_plot e268 +schedule e8b5 +schedule_send ea0a +schema e4fd +school e80c +science ea4b +score e269 +scoreboard ebd0 +screen_lock_landscape e1be +screen_lock_portrait e1bf +screen_lock_rotation e1c0 +screen_rotation e1c1 +screen_rotation_alt ebee +screen_search_desktop ef70 +screen_share e0e2 +screenshot f056 +screenshot_monitor ec08 +scuba_diving ebce +sd e9dd +sd_card e623 +sd_card_alert f057 +sd_storage e1c2 +search e8b6 +search_off ea76 +security e32a +security_update f058 +security_update_good f059 +security_update_warning f05a +segment e94b +select_all e162 +self_improvement ea78 +sell f05b +send e163 +send_and_archive ea0c +send_time_extension eadb +send_to_mobile f05c +sensor_door f1b5 +sensor_occupied ec10 +sensor_window f1b4 +sensors e51e +sensors_off e51f +sentiment_dissatisfied e811 +sentiment_neutral e812 +sentiment_satisfied e813 +sentiment_satisfied_alt e0ed +sentiment_very_dissatisfied e814 +sentiment_very_satisfied e815 +set_meal f1ea +settings e8b8 +settings_accessibility f05d +settings_applications e8b9 +settings_backup_restore e8ba +settings_bluetooth e8bb +settings_brightness e8bd +settings_cell e8bc +settings_display e8bd +settings_ethernet e8be +settings_input_antenna e8bf +settings_input_component e8c0 +settings_input_composite e8c1 +settings_input_hdmi e8c2 +settings_input_svideo e8c3 +settings_overscan e8c4 +settings_phone e8c5 +settings_power e8c6 +settings_remote e8c7 +settings_suggest f05e +settings_system_daydream e1c3 +settings_voice e8c8 +severe_cold ebd3 +shape_line f8d3 +share e80d +share_arrival_time e524 +share_location f05f +shelves f86e +shield e9e0 +shield_moon eaa9 +shop e8c9 +shop_2 e19e +shop_two e8ca +shopify ea9d +shopping_bag f1cc +shopping_basket e8cb +shopping_cart e8cc +shopping_cart_checkout eb88 +short_text e261 +shortcut f060 +show_chart e6e1 +shower f061 +shuffle e043 +shuffle_on e9e1 +shutter_speed e43d +sick f220 +sign_language ebe5 +signal_cellular_0_bar f0a8 +signal_cellular_4_bar e1c8 +signal_cellular_alt e202 +signal_cellular_alt_1_bar ebdf +signal_cellular_alt_2_bar ebe3 +signal_cellular_connected_no_internet_0_bar f0ac +signal_cellular_connected_no_internet_4_bar e1cd +signal_cellular_no_sim e1ce +signal_cellular_nodata f062 +signal_cellular_null e1cf +signal_cellular_off e1d0 +signal_wifi_0_bar f0b0 +signal_wifi_4_bar e1d8 +signal_wifi_4_bar_lock e1d9 +signal_wifi_bad f063 +signal_wifi_connected_no_internet_4 f064 +signal_wifi_off e1da +signal_wifi_statusbar_4_bar f065 +signal_wifi_statusbar_connected_no_internet_4 f066 +signal_wifi_statusbar_null f067 +signpost eb91 +sim_card e32b +sim_card_alert e624 +sim_card_download f068 +single_bed ea48 +sip f069 +skateboarding e511 +skip_next e044 +skip_previous e045 +sledding e512 +slideshow e41b +slow_motion_video e068 +smart_button f1c1 +smart_display f06a +smart_screen f06b +smart_toy f06c +smartphone e32c +smoke_free eb4a +smoking_rooms eb4b +sms e625 +sms_failed e626 +snapchat ea6e +snippet_folder f1c7 +snooze e046 +snowboarding e513 +snowing e80f +snowmobile e503 +snowshoeing e514 +soap f1b2 +social_distance e1cb +solar_power ec0f +sort e164 +sort_by_alpha e053 +sos ebf7 +soup_kitchen e7d3 +source f1c4 +south f1e3 +south_america e7e4 +south_east f1e4 +south_west f1e5 +spa eb4c +space_bar e256 +space_dashboard e66b +spatial_audio ebeb +spatial_audio_off ebe8 +spatial_tracking ebea +speaker e32d +speaker_group e32e +speaker_notes e8cd +speaker_notes_off e92a +speaker_phone e0d2 +speed e9e4 +spellcheck e8ce +splitscreen f06d +spoke e9a7 +sports ea30 +sports_bar f1f3 +sports_baseball ea51 +sports_basketball ea26 +sports_cricket ea27 +sports_esports ea28 +sports_football ea29 +sports_golf ea2a +sports_gymnastics ebc4 +sports_handball ea33 +sports_hockey ea2b +sports_kabaddi ea34 +sports_martial_arts eae9 +sports_mma ea2c +sports_motorsports ea2d +sports_rugby ea2e +sports_score f06e +sports_soccer ea2f +sports_tennis ea32 +sports_volleyball ea31 +square eb36 +square_foot ea49 +ssid_chart eb66 +stacked_bar_chart e9e6 +stacked_line_chart f22b +stadium eb90 +stairs f1a9 +star e838 +star_border e83a +star_border_purple500 f099 +star_half e839 +star_outline f06f +star_purple500 f09a +star_rate f0ec +stars e8d0 +start e089 +stay_current_landscape e0d3 +stay_current_portrait e0d4 +stay_primary_landscape e0d5 +stay_primary_portrait e0d6 +sticky_note_2 f1fc +stop e047 +stop_circle ef71 +stop_screen_share e0e3 +storage e1db +store e8d1 +store_mall_directory e563 +storefront ea12 +storm f070 +straight eb95 +straighten e41c +stream e9e9 +streetview e56e +strikethrough_s e257 +stroller f1ae +style e41d +subdirectory_arrow_left e5d9 +subdirectory_arrow_right e5da +subject e8d2 +subscript f111 +subscriptions e064 +subtitles e048 +subtitles_off ef72 +subway e56f +summarize f071 +sunny e81a +sunny_snowing e819 +superscript f112 +supervised_user_circle e939 +supervisor_account e8d3 +support ef73 +support_agent f0e2 +surfing e515 +surround_sound e049 +swap_calls e0d7 +swap_horiz e8d4 +swap_horizontal_circle e933 +swap_vert e8d5 +swap_vert_circle e8d6 +swap_vertical_circle e8d6 +swipe e9ec +swipe_down eb53 +swipe_down_alt eb30 +swipe_left eb59 +swipe_left_alt eb33 +swipe_right eb52 +swipe_right_alt eb56 +swipe_up eb2e +swipe_up_alt eb35 +swipe_vertical eb51 +switch_access_shortcut e7e1 +switch_access_shortcut_add e7e2 +switch_account e9ed +switch_camera e41e +switch_left f1d1 +switch_right f1d2 +switch_video e41f +synagogue eab0 +sync e627 +sync_alt ea18 +sync_disabled e628 +sync_lock eaee +sync_problem e629 +system_security_update f072 +system_security_update_good f073 +system_security_update_warning f074 +system_update e62a +system_update_alt e8d7 +system_update_tv e8d7 +tab e8d8 +tab_unselected e8d9 +table_bar ead2 +table_chart e265 +table_restaurant eac6 +table_rows f101 +table_view f1be +tablet e32f +tablet_android e330 +tablet_mac e331 +tag e9ef +tag_faces e420 +takeout_dining ea74 +tap_and_play e62b +tapas f1e9 +task f075 +task_alt e2e6 +taxi_alert ef74 +telegram ea6b +temple_buddhist eab3 +temple_hindu eaaf +terminal eb8e +terrain e564 +text_decrease eadd +text_fields e262 +text_format e165 +text_increase eae2 +text_rotate_up e93a +text_rotate_vertical e93b +text_rotation_angledown e93c +text_rotation_angleup e93d +text_rotation_down e93e +text_rotation_none e93f +text_snippet f1c6 +textsms e0d8 +texture e421 +theater_comedy ea66 +theaters e8da +thermostat f076 +thermostat_auto f077 +thumb_down e8db +thumb_down_alt e816 +thumb_down_off_alt e9f2 +thumb_up e8dc +thumb_up_alt e817 +thumb_up_off_alt e9f3 +thumbs_up_down e8dd +thunderstorm ebdb +tiktok ea7e +time_to_leave e62c +timelapse e422 +timeline e922 +timer e425 +timer_10 e423 +timer_10_select f07a +timer_3 e424 +timer_3_select f07b +timer_off e426 +tips_and_updates e79a +tire_repair ebc8 +title e264 +toc e8de +today e8df +toggle_off e9f5 +toggle_on e9f6 +token ea25 +toll e8e0 +tonality e427 +topic f1c8 +tornado e199 +touch_app e913 +tour ef75 +toys e332 +track_changes e8e1 +traffic e565 +train e570 +tram e571 +transcribe f8ec +transfer_within_a_station e572 +transform e428 +transgender e58d +transit_enterexit e579 +translate e8e2 +travel_explore e2db +trending_down e8e3 +trending_flat e8e4 +trending_neutral e8e4 +trending_up e8e5 +trip_origin e57b +trolley f86b +troubleshoot e1d2 +try f07c +tsunami ebd8 +tty f1aa +tune e429 +tungsten f07d +turn_left eba6 +turn_right ebab +turn_sharp_left eba7 +turn_sharp_right ebaa +turn_slight_left eba4 +turn_slight_right eb9a +turned_in e8e6 +turned_in_not e8e7 +tv e333 +tv_off e647 +two_wheeler e9f9 +type_specimen f8f0 +u_turn_left eba1 +u_turn_right eba2 +umbrella f1ad +unarchive e169 +undo e166 +unfold_less e5d6 +unfold_less_double f8cf +unfold_more e5d7 +unfold_more_double f8d0 +unpublished f236 +unsubscribe e0eb +upcoming f07e +update e923 +update_disabled e075 +upgrade f0fb +upload f09b +upload_file e9fc +usb e1e0 +usb_off e4fa +vaccines e138 +vape_free ebc6 +vaping_rooms ebcf +verified ef76 +verified_user e8e8 +vertical_align_bottom e258 +vertical_align_center e259 +vertical_align_top e25a +vertical_distribute e076 +vertical_shades ec0e +vertical_shades_closed ec0d +vertical_split e949 +vibration e62d +video_call e070 +video_camera_back f07f +video_camera_front f080 +video_chat f8a0 +video_collection e04a +video_file eb87 +video_label e071 +video_library e04a +video_settings ea75 +video_stable f081 +videocam e04b +videocam_off e04c +videogame_asset e338 +videogame_asset_off e500 +view_agenda e8e9 +view_array e8ea +view_carousel e8eb +view_column e8ec +view_comfortable e42a +view_comfy e42a +view_comfy_alt eb73 +view_compact e42b +view_compact_alt eb74 +view_cozy eb75 +view_day e8ed +view_headline e8ee +view_in_ar e9fe +view_kanban eb7f +view_list e8ef +view_module e8f0 +view_quilt e8f1 +view_sidebar f114 +view_stream e8f2 +view_timeline eb85 +view_week e8f3 +vignette e435 +villa e586 +visibility e8f4 +visibility_off e8f5 +voice_chat e62e +voice_over_off e94a +voicemail e0d9 +volcano ebda +volume_down e04d +volume_down_alt e79c +volume_mute e04e +volume_off e04f +volume_up e050 +volunteer_activism ea70 +vpn_key e0da +vpn_key_off eb7a +vpn_lock e62f +vrpano f082 +wallet f8ff +wallet_giftcard e8f6 +wallet_membership e8f7 +wallet_travel e8f8 +wallpaper e1bc +warehouse ebb8 +warning e002 +warning_amber f083 +wash f1b1 +watch e334 +watch_later e924 +watch_off eae3 +water f084 +water_damage f203 +water_drop e798 +waterfall_chart ea00 +waves e176 +waving_hand e766 +wb_auto e42c +wb_cloudy e42d +wb_incandescent e42e +wb_iridescent e436 +wb_shade ea01 +wb_sunny e430 +wb_twighlight ea02 +wb_twilight e1c6 +wc e63d +web e051 +web_asset e069 +web_asset_off e4f7 +web_stories e595 +webhook eb92 +wechat ea81 +weekend e16b +west f1e6 +whatsapp ea9c +whatshot e80e +wheelchair_pickup f1ab +where_to_vote e177 +widgets e1bd +width_full f8f5 +width_normal f8f6 +width_wide f8f7 +wifi e63e +wifi_1_bar e4ca +wifi_2_bar e4d9 +wifi_calling ef77 +wifi_calling_3 f085 +wifi_channel eb6a +wifi_find eb31 +wifi_lock e1e1 +wifi_off e648 +wifi_password eb6b +wifi_protected_setup f0fc +wifi_tethering e1e2 +wifi_tethering_error ead9 +wifi_tethering_error_rounded f086 +wifi_tethering_off f087 +wind_power ec0c +window f088 +wine_bar f1e8 +woman e13e +woman_2 f8e7 +woo_commerce ea6d +wordpress ea9f +work e8f9 +work_history ec09 +work_off e942 +work_outline e943 +workspace_premium e7af +workspaces e1a0 +workspaces_filled ea0d +workspaces_outline ea0f +wrap_text e25b +wrong_location ef78 +wysiwyg f1c3 +yard f089 +youtube_searched_for e8fa +zoom_in e8ff +zoom_in_map eb2d +zoom_out e900 +zoom_out_map e56b \ No newline at end of file diff --git a/Samples~/Meet/Assets/MaterialIcons/codepoints.meta b/Samples~/Meet/Assets/MaterialIcons/codepoints.meta new file mode 100644 index 00000000..38e6dd27 --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons/codepoints.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 817e1122394eb924f99d9ce8f83a8cbb +timeCreated: 1585147601 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/MaterialIcons/readme.txt b/Samples~/Meet/Assets/MaterialIcons/readme.txt new file mode 100644 index 00000000..69cd0ba5 --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons/readme.txt @@ -0,0 +1,2 @@ +Unity Material Icons by convalise +https://github.com/convalise/unity-material-icons \ No newline at end of file diff --git a/Samples~/Meet/Assets/MaterialIcons/readme.txt.meta b/Samples~/Meet/Assets/MaterialIcons/readme.txt.meta new file mode 100644 index 00000000..0beac41a --- /dev/null +++ b/Samples~/Meet/Assets/MaterialIcons/readme.txt.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 01049a3a711aca1419616464c867c8d6 +timeCreated: 1585342395 +licenseType: Free +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/Plugins.meta b/Samples~/Meet/Assets/Plugins.meta new file mode 100644 index 00000000..69d137e3 --- /dev/null +++ b/Samples~/Meet/Assets/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 055f124ecbe9e4345b243e8b3a12bf37 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/Plugins/Android.meta b/Samples~/Meet/Assets/Plugins/Android.meta new file mode 100644 index 00000000..85c0f80e --- /dev/null +++ b/Samples~/Meet/Assets/Plugins/Android.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8c4955416798d47f1a68faee16877bb9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/Plugins/Android/AndroidManifest.xml b/Samples~/Meet/Assets/Plugins/Android/AndroidManifest.xml new file mode 100644 index 00000000..94ea9440 --- /dev/null +++ b/Samples~/Meet/Assets/Plugins/Android/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples~/Meet/Assets/Plugins/Android/AndroidManifest.xml.meta b/Samples~/Meet/Assets/Plugins/Android/AndroidManifest.xml.meta new file mode 100644 index 00000000..881d5871 --- /dev/null +++ b/Samples~/Meet/Assets/Plugins/Android/AndroidManifest.xml.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 79197a715cbe14e38bfb401a95f67c08 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/Resources.meta b/Samples~/Meet/Assets/Resources.meta new file mode 100644 index 00000000..d42cc313 --- /dev/null +++ b/Samples~/Meet/Assets/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 96f2e0babbdce4cc3aeb9175ad85687e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/Resources/LiveKitSdkVersionInfo.txt b/Samples~/Meet/Assets/Resources/LiveKitSdkVersionInfo.txt new file mode 100644 index 00000000..785cda80 --- /dev/null +++ b/Samples~/Meet/Assets/Resources/LiveKitSdkVersionInfo.txt @@ -0,0 +1 @@ +1.3.3 \ No newline at end of file diff --git a/Samples~/Meet/Assets/Resources/LiveKitSdkVersionInfo.txt.meta b/Samples~/Meet/Assets/Resources/LiveKitSdkVersionInfo.txt.meta new file mode 100644 index 00000000..ff9b916b --- /dev/null +++ b/Samples~/Meet/Assets/Resources/LiveKitSdkVersionInfo.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: fef5c02499bec4f45a7084f97d21bc23 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/Runtime.meta b/Samples~/Meet/Assets/Runtime.meta new file mode 100644 index 00000000..658476d0 --- /dev/null +++ b/Samples~/Meet/Assets/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5fdc80459dcb4466094c77659127edb4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/Runtime/LivekitSamples.cs b/Samples~/Meet/Assets/Runtime/LivekitSamples.cs new file mode 100644 index 00000000..19d17247 --- /dev/null +++ b/Samples~/Meet/Assets/Runtime/LivekitSamples.cs @@ -0,0 +1,495 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using LiveKit; +using LiveKit.Proto; +using Google.MaterialDesign.Icons; +using RoomOptions = LiveKit.RoomOptions; +using Application = UnityEngine.Application; + +#if PLATFORM_ANDROID +using UnityEngine.Android; +#endif + +/// +/// Manages a LiveKit room connection with local/remote audio and video tracks. +/// +public class LivekitSamples : MonoBehaviour +{ + private const string LocalVideoTrackName = "my-video-track"; + private const string LocalAudioTrackName = "my-audio-track"; + + private const string MicOnIcon = "e029"; + private const string MicOffIcon = "e02b"; + private const string CamOnIcon = "e04b"; + private const string CamOffIcon = "e04c"; + + [Header("LiveKit Connection")] + [SerializeField] private string url = "ws://localhost:7880"; + [SerializeField] private string token = "YOUR_TOKEN"; + + [Header("UI Buttons")] + [SerializeField] private Button cameraButton; + [SerializeField] private Button microphoneButton; + [SerializeField] private Button startCallButton; + [SerializeField] private Button endCallButton; + [SerializeField] private Button publishDataButton; + + [Header("Video Layout")] + [SerializeField] private GridLayoutGroup videoTrackParent; + [SerializeField] private int frameRate = 30; + + private Room _room; + private WebCamTexture _webCamTexture; + private Transform _audioTrackParent; + private List