diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index aa70d88c52..7255e1f295 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -22,7 +22,8 @@ android:icon="@drawable/ic_launcher" android:launchMode="singleTop" android:roundIcon="@drawable/ic_launcher"> - + diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java b/android/app/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java index 10cfd85b50..78a6421746 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java @@ -8,6 +8,7 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.util.Log; +import android.view.View; import org.json.JSONException; import org.json.JSONObject; @@ -135,4 +136,13 @@ public class PermissionChecker extends Activity { launchActivityWithPermissions(); } } + + @Override + protected void onResume() { + super.onResume(); + View decorView = getWindow().getDecorView(); + // Hide the status bar. + int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + decorView.setSystemUiVisibility(uiOptions); + } } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java index 9ca6c7c4cc..7bd373cf1d 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java @@ -66,6 +66,7 @@ public class HomeFragment extends Fragment { GridLayoutManager gridLayoutMgr = new GridLayoutManager(getContext(), numberOfColumns); mDomainsView.setLayoutManager(gridLayoutMgr); mDomainAdapter = new DomainAdapter(getContext(), HifiUtils.getInstance().protocolVersionSignature(), nativeGetLastLocation()); + mSwipeRefreshLayout.setRefreshing(true); mDomainAdapter.setClickListener((view, position, domain) -> { new Handler(getActivity().getMainLooper()).postDelayed(() -> { if (mListener != null) { diff --git a/android/app/src/main/java/org/qtproject/qt5/android/bindings/QtActivity.java b/android/app/src/main/java/org/qtproject/qt5/android/bindings/QtActivity.java index 4a460ff9e7..93ae2bc227 100644 --- a/android/app/src/main/java/org/qtproject/qt5/android/bindings/QtActivity.java +++ b/android/app/src/main/java/org/qtproject/qt5/android/bindings/QtActivity.java @@ -60,6 +60,8 @@ import android.view.View; import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityEvent; +import org.qtproject.qt5.android.QtNative; + @SuppressWarnings("unused") public class QtActivity extends Activity { public String APPLICATION_PARAMETERS = null; @@ -103,7 +105,7 @@ public class QtActivity extends Activity { } public boolean super_dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - return super_dispatchPopulateAccessibilityEvent(event); + return super.dispatchPopulateAccessibilityEvent(event); } //--------------------------------------------------------------------------- @@ -362,7 +364,25 @@ public class QtActivity extends Activity { @Override protected void onDestroy() { super.onDestroy(); - QtApplication.invokeDelegate(); + /* + cduarte https://highfidelity.manuscript.com/f/cases/16712/App-freezes-on-opening-randomly + After Qt upgrade to 5.11 we had a black screen crash after closing the application with + the hardware button "Back" and trying to start the app again. It could only be fixed after + totally closing the app swiping it in the list of running apps. + This problem did not happen with the previous Qt version. + After analysing changes we came up with this case and change: + https://codereview.qt-project.org/#/c/218882/ + In summary they've moved libs loading to the same thread as main() and as a matter of correctness + in the onDestroy method in QtActivityDelegate, they exit that thread with `QtNative.m_qtThread.exit();` + That exit call is the main reason of this problem. + + In this fix we just replace the `QtApplication.invokeDelegate();` call that may end using the + entire onDestroy method including that thread exit line for other three lines that purposely + terminate qt (borrowed from QtActivityDelegate::onDestroy as well). + */ + QtNative.terminateQt(); + QtNative.setActivity(null, null); + System.exit(0); } //--------------------------------------------------------------------------- diff --git a/android/app/src/main/res/drawable/launch_screen.xml b/android/app/src/main/res/drawable/launch_screen.xml new file mode 100644 index 0000000000..0693094ffa --- /dev/null +++ b/android/app/src/main/res/drawable/launch_screen.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/activity_splash.xml b/android/app/src/main/res/layout/activity_splash.xml index ed25797917..8bb7cbffb6 100644 --- a/android/app/src/main/res/layout/activity_splash.xml +++ b/android/app/src/main/res/layout/activity_splash.xml @@ -7,9 +7,17 @@ android:layout_height="match_parent" android:background="@android:color/black"> + diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index 15895e4355..7e6cf52d36 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -17,4 +17,5 @@ #FF7171 #99000000 #292929 + #23B2E7 diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 308c438fa6..25823214cd 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -19,6 +19,9 @@ false true + + diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 83dd633d22..07f1eb7e5e 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1223,7 +1223,7 @@ "name": "max_avatar_height", "type": "double", "label": "Maximum Avatar Height (meters)", - "help": "Limits the scale of avatars in your domain. Cannot be greater than 1755.", + "help": "Limits the height of avatars in your domain. Cannot be greater than 1755.", "placeholder": 5.2, "default": 5.2 }, diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index e67ea43158..3888277c00 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -58,7 +58,11 @@ $(document).ready(function(){ } Settings.handlePostSettings = function(formJSON) { - + + if (!verifyAvatarHeights()) { + return false; + } + // check if we've set the basic http password if (formJSON["security"]) { @@ -207,7 +211,7 @@ $(document).ready(function(){ swal({ title: '', type: 'error', - text: "There was a problem retreiving domain information from High Fidelity API.", + text: "There was a problem retrieving domain information from High Fidelity API.", confirmButtonText: 'Try again', showCancelButton: true, closeOnConfirm: false @@ -288,7 +292,7 @@ $(document).ready(function(){ swal({ title: 'Create new domain ID', type: 'input', - text: 'Enter a label this machine.

This will help you identify which domain ID belongs to which machine.

', + text: 'Enter a label for this machine.

This will help you identify which domain ID belongs to which machine.

', showCancelButton: true, confirmButtonText: "Create", closeOnConfirm: false, @@ -669,7 +673,7 @@ $(document).ready(function(){ var spinner = createDomainSpinner(); $('#' + Settings.PLACES_TABLE_ID).after($(spinner)); - var errorEl = createDomainLoadingError("There was an error retreiving your places."); + var errorEl = createDomainLoadingError("There was an error retrieving your places."); $("#" + Settings.PLACES_TABLE_ID).after(errorEl); // do we have a domain ID? @@ -1091,4 +1095,43 @@ $(document).ready(function(){ $('#settings_backup .panel-body').html(html); } + + function verifyAvatarHeights() { + var errorString = ''; + var minAllowedHeight = 0.009; + var maxAllowedHeight = 1755; + var alertCss = { backgroundColor: '#ffa0a0' }; + var minHeightElement = $('input[name="avatars.min_avatar_height"]'); + var maxHeightElement = $('input[name="avatars.max_avatar_height"]'); + + var minHeight = Number(minHeightElement.val()); + var maxHeight = Number(maxHeightElement.val()); + + if (maxHeight < minHeight) { + errorString = 'Maximum avatar height must not be less than minimum avatar height
'; + minHeightElement.css(alertCss); + maxHeightElement.css(alertCss); + }; + if (minHeight < minAllowedHeight) { + errorString += 'Minimum avatar height must not be less than ' + minAllowedHeight + '
'; + minHeightElement.css(alertCss); + } + if (maxHeight > maxAllowedHeight) { + errorString += 'Maximum avatar height must not be greater than ' + maxAllowedHeight + '
'; + maxHeightElement.css(alertCss); + } + + if (errorString.length > 0) { + swal({ + type: 'error', + title: '', + text: errorString, + html: true + }); + return false; + } else { + return true; + } + + } }); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8524c40262..130c2c0b89 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -275,6 +275,13 @@ Menu::Menu() { QString("hifi/tablet/TabletGraphicsPreferences.qml"), "GraphicsPreferencesDialog"); }); + // Settings > Attachments... + action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::Attachments); + connect(action, &QAction::triggered, [] { + qApp->showDialog(QString("hifi/dialogs/AttachmentsDialog.qml"), + QString("hifi/tablet/TabletAttachmentsDialog.qml"), "AttachmentsDialog"); + }); + // Settings > Developer Menu addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menu", 0, false, this, SLOT(toggleDeveloperMenus())); diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 85a83d88d1..c1ba6f0535 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -262,6 +262,9 @@ int main(int argc, const char* argv[]) { // Extend argv to enable WebGL rendering std::vector argvExtended(&argv[0], &argv[argc]); argvExtended.push_back("--ignore-gpu-blacklist"); +#ifdef Q_OS_ANDROID + argvExtended.push_back("--suppress-settings-reset"); +#endif int argcExtended = (int)argvExtended.size(); PROFILE_SYNC_END(startup, "main startup", ""); diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index f692128fa3..2162b78721 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -159,13 +159,12 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See for(var bookmarkName in message.data.bookmarks) { var bookmark = message.data.bookmarks[bookmarkName]; - if (!bookmark.avatarEntites) { // ensure avatarEntites always exist - bookmark.avatarEntites = []; - } - bookmark.avatarEntites.forEach(function(avatarEntity) { - avatarEntity.properties.localRotationAngles = Quat.safeEulerAngles(avatarEntity.properties.localRotation) - }) + if (bookmark.avatarEntites) { + bookmark.avatarEntites.forEach(function(avatarEntity) { + avatarEntity.properties.localRotationAngles = Quat.safeEulerAngles(avatarEntity.properties.localRotation); + }); + } } sendToQml(message) diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index 0c24e64af7..269f7d1d92 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -10,7 +10,7 @@ /* global Script, Controller, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, makeRunningValues, Messages, makeDispatcherModuleParameters, HMD, getGrabPointSphereOffset, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_ON_VALUE, - getEnabledModuleByName, PICK_MAX_DISTANCE, isInEditMode, Picks, makeLaserParams + getEnabledModuleByName, PICK_MAX_DISTANCE, isInEditMode, Picks, makeLaserParams, Entities */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -19,7 +19,7 @@ Script.include("/~/system/libraries/utils.js"); (function () { var MARGIN = 25; - + var TABLET_MATERIAL_ENTITY_NAME = 'Tablet-Material-Entity'; function InEditMode(hand) { this.hand = hand; this.triggerClicked = false; @@ -53,7 +53,7 @@ Script.include("/~/system/libraries/utils.js"); return (HMD.tabletScreenID && objectID === HMD.tabletScreenID) || (HMD.homeButtonID && objectID === HMD.homeButtonID); }; - + this.calculateNewReticlePosition = function(intersection) { var dims = Controller.getViewportDimensions(); this.reticleMaxX = dims.x - MARGIN; @@ -75,10 +75,12 @@ Script.include("/~/system/libraries/utils.js"); } } if (this.selectedTarget.type === Picks.INTERSECTED_ENTITY) { - Messages.sendLocalMessage("entityToolUpdates", JSON.stringify({ - method: "selectEntity", - entityID: this.selectedTarget.objectID - })); + if (!this.isTabletMaterialEntity(this.selectedTarget.objectID)) { + Messages.sendLocalMessage("entityToolUpdates", JSON.stringify({ + method: "selectEntity", + entityID: this.selectedTarget.objectID + })); + } } else if (this.selectedTarget.type === Picks.INTERSECTED_OVERLAY) { Messages.sendLocalMessage("entityToolUpdates", JSON.stringify({ method: "selectOverlay", @@ -88,10 +90,16 @@ Script.include("/~/system/libraries/utils.js"); this.triggerClicked = true; } - + this.sendPointingAtData(controllerData); }; - + + + this.isTabletMaterialEntity = function(entityID) { + return ((entityID === HMD.homeButtonHighlightMaterialID) || + (entityID === HMD.homeButtonUnhighlightMaterialID)); + }; + this.sendPointingAtData = function(controllerData) { var rayPick = controllerData.rayPicks[this.hand]; var hudRayPick = controllerData.hudRayPicks[this.hand]; diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 532f58493f..f83f961438 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -30,6 +30,8 @@ var INCHES_TO_METERS = 1 / 39.3701; var NO_HANDS = -1; var DELAY_FOR_30HZ = 33; // milliseconds +var TABLET_MATERIAL_ENTITY_NAME = 'Tablet-Material-Entity'; + // will need to be recaclulated if dimensions of fbx model change. var TABLET_NATURAL_DIMENSIONS = {x: 32.083, y: 48.553, z: 2.269}; @@ -79,6 +81,19 @@ function calcSpawnInfo(hand, landscape) { }; } + +cleanUpOldMaterialEntities = function() { + var avatarEntityData = MyAvatar.getAvatarEntityData(); + for (var entityID in avatarEntityData) { + var entityName = Entities.getEntityProperties(entityID, ["name"]).name; + + if (entityName === TABLET_MATERIAL_ENTITY_NAME && entityID !== HMD.homeButtonHighlightMaterialID && + entityID !== HMD.homeButtonUnhighlightMaterialID) { + Entities.deleteEntity(entityID); + } + } +}; + /** * WebTablet * @param url [string] url of content to show on the tablet. @@ -134,6 +149,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { } this.cleanUpOldTablets(); + cleanUpOldMaterialEntities(); this.tabletEntityID = Overlays.addOverlay("model", tabletProperties); @@ -180,6 +196,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { this.homeButtonUnhighlightMaterial = Entities.addEntity({ type: "Material", + name: TABLET_MATERIAL_ENTITY_NAME, materialURL: "materialData", localPosition: { x: 0.0, y: 0.0, z: 0.0 }, priority: HIGH_PRIORITY, @@ -199,6 +216,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { this.homeButtonHighlightMaterial = Entities.addEntity({ type: "Material", + name: TABLET_MATERIAL_ENTITY_NAME, materialURL: "materialData", localPosition: { x: 0.0, y: 0.0, z: 0.0 }, priority: LOW_PRIORITY, diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index fb876302dd..24b90e3a44 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -111,6 +111,11 @@ EntityListTool = function(shouldUseEditTabletApp) { return value !== undefined ? value : ""; } + function filterEntity(entityID) { + return ((entityID === HMD.homeButtonHighlightMaterialID) || + (entityID === HMD.homeButtonUnhighlightMaterialID)); + } + that.sendUpdate = function() { var entities = []; @@ -121,6 +126,10 @@ EntityListTool = function(shouldUseEditTabletApp) { ids = Entities.findEntities(MyAvatar.position, searchRadius); } + ids = ids.filter(function(id) { + return !filterEntity(id); + }); + var cameraPosition = Camera.position; for (var i = 0; i < ids.length; i++) { var id = ids[i]; diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 96a3b2b015..6956c7010f 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -381,6 +381,8 @@ SelectionDisplay = (function() { var CTRL_KEY_CODE = 16777249; + var RAIL_AXIS_LENGTH = 10000; + var TRANSLATE_DIRECTION = { X: 0, Y: 1, @@ -616,6 +618,40 @@ SelectionDisplay = (function() { dashed: false }); + var xRailOverlay = Overlays.addOverlay("line3d", { + visible: false, + start: Vec3.ZERO, + end: Vec3.ZERO, + color: { + red: 255, + green: 0, + blue: 0 + }, + ignoreRayIntersection: true // always ignore this + }); + var yRailOverlay = Overlays.addOverlay("line3d", { + visible: false, + start: Vec3.ZERO, + end: Vec3.ZERO, + color: { + red: 0, + green: 255, + blue: 0 + }, + ignoreRayIntersection: true // always ignore this + }); + var zRailOverlay = Overlays.addOverlay("line3d", { + visible: false, + start: Vec3.ZERO, + end: Vec3.ZERO, + color: { + red: 0, + green: 0, + blue: 255 + }, + ignoreRayIntersection: true // always ignore this +}); + var allOverlays = [ handleTranslateXCone, handleTranslateXCylinder, @@ -656,7 +692,11 @@ SelectionDisplay = (function() { handleScaleFLEdge, handleCloner, selectionBox, - iconSelectionBox + iconSelectionBox, + xRailOverlay, + yRailOverlay, + zRailOverlay + ]; var maximumHandleInAllOverlays = handleCloner; @@ -873,11 +913,13 @@ SelectionDisplay = (function() { }; // FUNCTION: MOUSE MOVE EVENT + var lastMouseEvent = null; that.mouseMoveEvent = function(event) { var wantDebug = false; if (wantDebug) { print("=============== eST::MouseMoveEvent BEG ======================="); } + lastMouseEvent = event; if (activeTool) { if (wantDebug) { print(" Trigger ActiveTool(" + activeTool.mode + ")'s onMove"); @@ -999,19 +1041,35 @@ SelectionDisplay = (function() { }; // Control key remains active only while key is held down - that.keyReleaseEvent = function(key) { - if (key.key === CTRL_KEY_CODE) { + that.keyReleaseEvent = function(event) { + if (event.key === CTRL_KEY_CODE) { ctrlPressed = false; that.updateActiveRotateRing(); } + if (activeTool && lastMouseEvent !== null) { + lastMouseEvent.isShifted = event.isShifted; + lastMouseEvent.isMeta = event.isMeta; + lastMouseEvent.isControl = event.isControl; + lastMouseEvent.isAlt = event.isAlt; + activeTool.onMove(lastMouseEvent); + SelectionManager._update(); + } }; // Triggers notification on specific key driven events - that.keyPressEvent = function(key) { - if (key.key === CTRL_KEY_CODE) { + that.keyPressEvent = function(event) { + if (event.key === CTRL_KEY_CODE) { ctrlPressed = true; that.updateActiveRotateRing(); } + if (activeTool && lastMouseEvent !== null) { + lastMouseEvent.isShifted = event.isShifted; + lastMouseEvent.isMeta = event.isMeta; + lastMouseEvent.isControl = event.isControl; + lastMouseEvent.isAlt = event.isAlt; + activeTool.onMove(lastMouseEvent); + SelectionManager._update(); + } }; // NOTE: mousePressEvent and mouseMoveEvent from the main script should call us., so we don't hook these: @@ -1705,6 +1763,14 @@ SelectionDisplay = (function() { }, onEnd: function(event, reason) { pushCommandForSelections(duplicatedEntityIDs); + if (isConstrained) { + Overlays.editOverlay(xRailOverlay, { + visible: false + }); + Overlays.editOverlay(zRailOverlay, { + visible: false + }); + } }, elevation: function(origin, intersection) { return (origin.y - intersection.y) / Vec3.distance(origin, intersection); @@ -1768,10 +1834,46 @@ SelectionDisplay = (function() { vector.x = 0; } if (!isConstrained) { + var xStart = Vec3.sum(startPosition, { + x: -RAIL_AXIS_LENGTH, + y: 0, + z: 0 + }); + var xEnd = Vec3.sum(startPosition, { + x: RAIL_AXIS_LENGTH, + y: 0, + z: 0 + }); + var zStart = Vec3.sum(startPosition, { + x: 0, + y: 0, + z: -RAIL_AXIS_LENGTH + }); + var zEnd = Vec3.sum(startPosition, { + x: 0, + y: 0, + z: RAIL_AXIS_LENGTH + }); + Overlays.editOverlay(xRailOverlay, { + start: xStart, + end: xEnd, + visible: true + }); + Overlays.editOverlay(zRailOverlay, { + start: zStart, + end: zEnd, + visible: true + }); isConstrained = true; } } else { if (isConstrained) { + Overlays.editOverlay(xRailOverlay, { + visible: false + }); + Overlays.editOverlay(zRailOverlay, { + visible: false + }); isConstrained = false; } } diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index 29dc457197..bd6a9c69d5 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -13,7 +13,7 @@ // /* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays, - MyAvatar, Menu, AvatarInputs, Vec3 */ + MyAvatar, Menu, AvatarInputs, Vec3, cleanUpOldMaterialEntities */ (function() { // BEGIN LOCAL_SCOPE var tabletRezzed = false; @@ -31,6 +31,14 @@ Script.include("../libraries/WebTablet.js"); + function cleanupMaterialEntities() { + if (Window.isPhysicsEnabled()) { + cleanUpOldMaterialEntities(); + return; + } + Script.setTimeout(cleanupMaterialEntities, 100); + } + function checkTablet() { if (gTablet === null) { gTablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); @@ -327,4 +335,5 @@ HMD.homeButtonHighlightMaterialID = null; HMD.homeButtonUnhighlightMaterialID = null; }); + Script.setTimeout(cleanupMaterialEntities, 100); }()); // END LOCAL_SCOPE