准备网页以提供应用
為應用服務設置網頁¶
應用用戶按照以下步驟安裝和運行應用:
- 在應用程式服務的網頁上點擊應用程式啟動按鈕,下載並安裝Crossplay Launcher。
- 運行Crossplay Launcher。
- Crossplay Launcher會自動下載並安裝應用程式。
- Crossplay Launcher會自動更新並運行應用程式。
以下是使用Crossplay Launcher從網頁服務應用程序的整個過程。
服務應用程式的網頁可以通過以下兩種方式提供:
- 遊戲 (https://withhive.com/)
- 由開發該應用程式的公司建立的網頁。
本指南提供了示例和指导,说明在使用Crossplay Launcher从网页服务应用时,网页上需要实现什么内容,由应用开发者构建。
檢查應用程式服務條件¶
當用戶訪問網頁時,檢查用戶的PC環境,例如OS版本,是否受支持。
- 支持的OS:Windows 10或更高版本
檢查條件後,如果有任何問題,應該向用戶顯示錯誤彈出窗口。
實現應用啟動按鈕和跨平台啟動器下載彈出窗口¶
實現一個彈出窗口,用於下載Crossplay Launcher安裝文件。彈出窗口必須包含安裝Crossplay Launcher的指南。
在網頁上點擊應用啟動按鈕會顯示彈出窗口。彈出窗口應該有一個按鈕來下載Crossplay Launcher安裝文件。
檢查 Crossplay Launcher 安裝情況¶
當用戶在網頁上點擊應用啟動按鈕時,有必要檢查用戶的 PC 上是否安裝了 Crossplay Launcher。在網頁上執行以下 JavaScript 代碼以檢查用戶的 PC 上是否安裝了 Crossplay Launcher。
JavaScript 代碼
!function (b, a) {
"object" == typeof exports && "object" == typeof module
? module.exports = a()
: "function" == typeof define && define.amd
? define("customProtocolCheck", [], a)
: "object" == typeof exports
? exports.customProtocolCheck = a()
: b.customProtocolCheck = a()
}(window, function () {
return function (b) {
var c = {};
function a(d) {
if (c[d])
return c[d].exports;
var e = c[d] = {
i: d,
l: !1,
exports: {}
};
return b[d].call(e.exports, e, e.exports, a),
e.l = !0,
e.exports
}
return a.m = b,
a.c = c,
a.d = function (b, c, d) {
a.o(b, c) || Object.defineProperty(b, c, {
enumerable: !0,
get: d
})
},
a.r = function (a) {
"undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(
a,
Symbol.toStringTag,
{value: "Module"}
),
Object.defineProperty(a, "__esModule", {
value: !0
})
},
a.t = function (b, c) {
if (
1 & c && (b = a(b)),
8 & c || 4 & c && "object" == typeof b && b && b.__esModule
)
return b;
var d = Object.create(null);
if (a.r(d), Object.defineProperty(d, "default", {
enumerable: !0,
value: b
}), 2 & c && "string" != typeof b)
for (var e in b)
a.d(d, e, (function (a) {
return b[a]
}).bind(null, e));
return d
},
a.n = function (c) {
var b = c && c.__esModule
? function () {
return c.default
}
: function () {
return c
};
return a.d(b, "a", b),
b
},
a.o = function (a, b) {
return Object
.prototype
.hasOwnProperty
.call(a, b)
},
a.p = "",
a(a.s = 0)
}({
"./index.js": function (module, exports) {
eval(
'var browser = {\n getUserAgent: function getUserAgent() {\n return window.' +
'navigator.userAgent;\n },\n userAgentContains: function userAgentContains(br' +
'owserName) {\n browserName = browserName.toLowerCase();\n return this.ge' +
'tUserAgent().toLowerCase().indexOf(browserName) > -1;\n },\n isOSX: function' +
' isOSX() {\n return this.userAgentContains("Macintosh");\n },\n isFirefox' +
': function isFirefox() {\n return this.userAgentContains("firefox");\n },' +
'\n isInternetExplorer: function isInternetExplorer() {\n return this.userA' +
'gentContains("trident");\n },\n\n /**\r\n * Detects IE 11 and older\r\n ' +
'* @return {Boolean} Returns true when IE 11 and older\r\n */\n isIE: functi' +
'on isIE() {\n var ua = this.getUserAgent().toLowerCase(); // Test values.\n' +
' // Uncomment to check result\n // IE 10\n // ua = \'Mozilla/5.0 (com' +
'patible; MSIE 10.0; Windows NT 6.2; Trident/6.0)\';\n // IE 11\n // ua =' +
' \'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko/20100101 Fire' +
'fox/12.0\';\n\n var msie = ua.indexOf("msie");\n\n if (msie > 0) {\n ' +
' // IE 10 or older\n return true;\n }\n\n var trident = ua.indexOf' +
'("trident/");\n\n if (trident > 0) {\n // IE 11\n return true;\n ' +
' } // other browser\n\n\n return false;\n },\n isEdge: function isEdge(' +
') {\n var ua = this.getUserAgent().toLowerCase(); // Test values.\n // U' +
'ncomment to check result\n // Edge\n // ua = \'Mozilla/5.0 (Windows NT 1' +
'0.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 S' +
'afari/537.36 Edge/12.10240\';\n\n var edge = ua.indexOf("edge");\n\n if ' +
'(edge > 0) {\n return true;\n }\n\n return false;\n },\n isChrome' +
': function isChrome() {\n // IE11 returns undefined for window.chrome\n ' +
'// and new Opera 30 outputs true for window.chrome\n // but needs to check ' +
'if window.opr is not undefined\n // and new IE Edge outputs to true for win' +
'dow.chrome\n // and if not iOS Chrome check\n var isChromium = window.ch' +
'rome;\n var winNav = window.navigator;\n var vendorName = winNav.vendor;' +
'\n var isOpera = typeof window.opr !== "undefined";\n var isIEedge = win' +
'Nav.userAgent.indexOf("Edge") > -1;\n var isIOSChrome = winNav.userAgent.ma' +
'tch("CriOS");\n return isChromium !== null && typeof isChromium !== "undefi' +
'ned" && vendorName === "Google Inc." && isOpera === false && isIEedge === fals' +
'e || isIOSChrome;\n },\n isWhale: function isWhale() {\n // IE11 returns ' +
'undefined for window.chrome\n // and new Opera 30 outputs true for window.c' +
'hrome\n // but needs to check if window.opr is not undefined\n // and ne' +
'w IE Edge outputs to true for window.chrome\n // and if not iOS Chrome chec' +
'k\n var isChromium = window.chrome;\n var winNav = window.navigator;\n ' +
' var vendorName = winNav.vendor;\n var isOpera = typeof window.opr !== "un' +
'defined";\n var isIEedge = winNav.userAgent.indexOf("Edge") > -1;\n var ' +
'isIOSChrome = winNav.userAgent.match("CriOS");\n return isChromium !== null' +
' && typeof isChromium !== "undefined" && vendorName === "NAVER Corp." && isOpe' +
'ra === false && isIEedge === false || isIOSChrome;\n },\n isOpera: function ' +
'isOpera() {\n return this.userAgentContains(" OPR/");\n }\n};\nvar DEFAULT' +
'_CUSTOM_PROTOCOL_FAIL_CALLBACK_TIMEOUT;\n\nvar registerEvent = function regist' +
'erEvent(target, eventType, cb) {\n if (target.addEventListener) {\n target' +
'.addEventListener(eventType, cb);\n return {\n remove: function remove' +
'() {\n target.removeEventListener(eventType, cb);\n }\n };\n }' +
' else {\n target.attachEvent(eventType, cb);\n return {\n remove: f' +
'unction remove() {\n target.detachEvent(eventType, cb);\n }\n }' +
';\n }\n};\n\nvar createHiddenIframe = function createHiddenIframe(target, uri' +
') {\n var iframe = document.createElement("iframe");\n iframe.src = uri;\n ' +
'iframe.id = "hiddenIframe";\n iframe.style.display = "none";\n target.append' +
'Child(iframe);\n return iframe;\n};\n\nvar openUriWithHiddenFrame = function ' +
'openUriWithHiddenFrame(uri, failCb, successCb) {\n var timeout = setTimeout(f' +
'unction () {\n failCb();\n handler.remove();\n }, DEFAULT_CUSTOM_PROTOC' +
'OL_FAIL_CALLBACK_TIMEOUT);\n var iframe = document.querySelector("#hiddenIfra' +
'me");\n\n if (!iframe) {\n iframe = createHiddenIframe(document.body, "abo' +
'ut:blank");\n }\n\n onBlur = function onBlur() {\n clearTimeout(timeout);' +
'\n handler.remove();\n successCb();\n };\n\n var handler = registerEve' +
'nt(window, "blur", onBlur);\n iframe.contentWindow.location.href = uri;\n};\n' +
'\nvar openUriWithTimeoutHack = function openUriWithTimeoutHack(uri, failCb, su' +
'ccessCb) {\n var timeout = setTimeout(function () {\n failCb();\n handl' +
'er.remove();\n }, DEFAULT_CUSTOM_PROTOCOL_FAIL_CALLBACK_TIMEOUT); //handle pa' +
'ge running in an iframe (blur must be registered with top level window)\n\n v' +
'ar target = window;\n\n while (target.parent && target != target.parent) {\n ' +
' target = target.parent;\n }\n\n onBlur = function onBlur() {\n clearTi' +
'meout(timeout);\n handler.remove();\n successCb();\n };\n\n var handle' +
'r = registerEvent(target, "blur", onBlur);\n window.location = uri;\n};\n\nva' +
'r openUriUsingFirefox = function openUriUsingFirefox(uri, failCb, successCb) {' +
'\n var iframe = document.querySelector("#hiddenIframe");\n\n if (!iframe) {' +
'\n iframe = createHiddenIframe(document.body, "about:blank");\n }\n\n try' +
' {\n iframe.contentWindow.location.href = uri;\n successCb();\n } catch' +
' (e) {\n if (e.name == "NS_ERROR_UNKNOWN_PROTOCOL") {\n failCb();\n ' +
' }\n }\n};\n\nvar openUriWithMsLaunchUri = function openUriWithMsLaunchUri(ur' +
'i, failCb, successCb) {\n navigator.msLaunchUri(uri, successCb, failCb);\n};' +
'\n\nvar getBrowserVersion = function getBrowserVersion() {\n var ua = window.' +
'navigator.userAgent;\n var tem,\n M = ua.match(/(opera|chrome|safari|fir' +
'efox|msie|trident(?=\\/))\\/?\\s*(\\d+)/i) || [];\n\n if (/trident/i.test(M[1' +
'])) {\n tem = /\\brv[ :]+(\\d+)/g.exec(ua) || [];\n return parseFloat(te' +
'm[1]) || "";\n }\n\n if (M[1] === "Chrome") {\n tem = ua.match(/\\b(OPR|E' +
'dge)\\/(\\d+)/);\n\n if (tem != null) {\n return parseFloat(tem[2]);\n' +
' }\n }\n\n M = M[2] ? [M[1], M[2]] : [window.navigator.appName, window.na' +
'vigator.appVersion, "-?"];\n if ((tem = ua.match(/version\\/(\\d+)/i)) != nul' +
'l) M.splice(1, 1, tem[1]);\n return parseFloat(M[1]);\n};\n\nvar protocolChec' +
'k = function protocolCheck(uri, failCb, successCb) {\n var timeout = argument' +
's.length > 3 && arguments[3] !== undefined ? arguments[3] : 2000;\n var unsup' +
'portedCb = arguments.length > 4 ? arguments[4] : undefined;\n\n var failCallb' +
'ack = function failCallback() {\n failCb && failCb();\n };\n\n var succes' +
'sCallback = function successCallback() {\n successCb && successCb();\n };' +
'\n\n var unsupportedCallback = function unsupportedCallback() {\n unsuppor' +
'tedCb && unsupportedCb();\n };\n\n var openUri = function openUri() {\n i' +
'f (browser.isFirefox()) {\n var browserVersion = getBrowserVersion();\n\n' +
' if (browserVersion >= 64) {\n openUriWithHiddenFrame(uri, failCal' +
'lback, successCallback);\n } else {\n openUriUsingFirefox(uri, fai' +
'lCallback, successCallback);\n }\n } else if (browser.isWhale()) {\n ' +
' openUriWithTimeoutHack(uri, failCallback, successCallback);\n } else if' +
' (browser.isChrome()) {\n openUriWithTimeoutHack(uri, failCallback, succe' +
'ssCallback);\n } else if (browser.isOSX()) {\n openUriWithHiddenFrame(' +
'uri, failCallback, successCallback);\n } else {\n //not supported, imp' +
'lement please\n unsupportedCallback();\n }\n };\n\n if (timeout) {\n' +
' DEFAULT_CUSTOM_PROTOCOL_FAIL_CALLBACK_TIMEOUT = timeout;\n }\n\n if (bro' +
'wser.isEdge() || browser.isIE()) {\n //for IE and Edge in Win 8 and Win 10' +
'\n openUriWithMsLaunchUri(uri, failCb, successCb);\n } else {\n if (doc' +
'ument.hasFocus()) {\n openUri();\n } else {\n var focusHandler = ' +
'registerEvent(window, "focus", function () {\n focusHandler.remove();\n' +
' openUri();\n });\n }\n }\n};\n\nmodule.exports = protocolChec' +
'k;\n\n//# sourceURL=webpack://customProtocolCheck/./index.js?'
)
},
0: function (module, exports, __webpack_require__) {
eval(
'module.exports = __webpack_require__(/*! /Users/shahv/Viresh/work/rnd/custom-p' +
'rotocol-check/index.js */"./index.js");\n\n\n//# sourceURL=webpack://customPro' +
'tocolCheck/multi_./index.js?'
)
}
})
})
如果Crossplay Launcher尚未安裝在用戶的PC上,請實現功能同時安裝Crossplay Launcher和應用程序。如果Crossplay Launcher已經安裝,請實現功能運行Crossplay Launcher。
同時安裝 Crossplay Launcher 和應用程式¶
在安裝 Crossplay Launcher 的過程中,應用程式也可以自動安裝並運行。
實現下載 Crossplay Launcher 安裝檔案的事件:下載安裝檔案¶
在實現上述指南以創建應用啟動按鈕和彈出窗口後,實現用戶在彈出窗口上點擊下載按鈕時下載Crossplay Launcher安裝檔案hivecrossplay-fn.qpyou.cn/hivecrossplay/p/w/Installer.exe的功能。
實現下載 Crossplay Launcher 安裝文件的事件:自動應用安裝¶
當用戶點擊下載按鈕時,實現將 自動安裝 URI 複製到用戶的 PC 剪貼板的功能。在安裝 Crossplay Launcher 後,Crossplay Launcher 會自動搜索剪貼板中的遊戲安裝 URI。如果找到,它會安裝對應於 URI 的應用。如果在剪貼板中未找到遊戲安裝 URI,則僅在用戶的 PC 上安裝 Crossplay Launcher。要安裝應用,用戶需要返回網頁並再次點擊應用啟動按鈕。請參考 運行 Crossplay Launcher 獲取更多詳細信息。
獲取自動安裝 URI¶
從Hive 控制台 (沙盒或商業) > 跨平台啟動器 > 應用管理 > 下載設置 > 啟動器安裝/啟動 URI獲取自動安裝URI。
# Example
hivelauncher:?app_id=com.com2us.hivesdk.windows.microsoftstore.global.normal&start_point=9 f387268b8ea6c3e0016c8fd41562ed53d4c38338
將 URI 複製到剪貼簿¶
實現將獲得的自動安裝 URI 複製到用戶的 PC 剪貼簿空間。
運行跨平台啟動器¶
一旦跨平台啟動器安裝在用戶的PC上,當用戶在網頁上點擊應用啟動按鈕的事件發生時,執行以下JavaScript代碼。JavaScript代碼使用啟動URI啟動跨平台啟動器,並且跨平台啟動器會自動安裝並運行應用。
獲取啟動 URI¶
從URI中獲取啟動Hive 控制台(沙盒或實時) > 跨平台啟動器 > 應用管理 > 下載設置 > 啟動器安裝/啟動 URI。
# Example
hivelauncher:?app_id=com.com2us.hivesdk.windows.microsoftstore.global.normal&start_point=9
實現並運行 Javascript 代碼¶
當用戶的 PC 上安裝了 Crossplay Launcher 時,實現當用戶在網頁上點擊應用啟動按鈕時執行 JavaScript 代碼。
event.preventDefault
? event.preventDefault()
: (event.returnValue = false);
window.customProtocolCheck({
# Implement executing the launch URI.
# The launch URI launches the Crossplay Launcher. The Crossplay Launcher automatically installs and runs the app.
}, function (e) {
console.log("FAIL");
{
# Handling Crossplay Launcher Execution Failure
}
}, function (e) {
console.log("SUCCESS");
}, 2500, function (e) {
console.log("Not Supported");
{
# Handling No Response After 2500ms from Crossplay Launcher Execution Request
}
});
以下是按下“在PC上播放”按钮时的结果示例,这会触发JavaScript代码执行。当Crossplay Launcher运行时,浏览器通知会提示是否执行Crossplay Updater,该更新程序会更新Crossplay Launcher本身。
Note
Due to the difference between JAVA's URLEncoder.encode()
and JavaScript's encodeURIComponent()
, when implementing the execution of the app in JAVA, the replaceAll("\+", "%20")
operation is required.