fixed response checking (rare crash on some metadata)

improved error reporting
added support for forerunner 945 LTE
This commit is contained in:
memen45
2021-06-23 15:45:24 +02:00
parent 12443f7b4e
commit e2a3eebdb1
12 changed files with 332 additions and 129 deletions

View File

@@ -2,6 +2,11 @@ Version [] -
- (future) added 'Manage...' option for more playlist options
- (future) added alpha podcast implementation (feedback needed)
Version [0.1.10] - 2021-06-23
- fixed response checking (rare crash on some metadata)
- improved error reporting
- added support for forerunner 945 LTE
Version [0.1.9] - 2021-06-02
- fixed support for venu2s (Garmin bug with loading resource strings)

View File

@@ -1,5 +1,5 @@
<!-- This is a generated file. It is highly recommended that you DO NOT edit this file. --><iq:manifest xmlns:iq="http://www.garmin.com/xml/connectiq" version="3">
<iq:application entry="SubMusicApp" id="62436b93f69e4823a8b41854b6684b4a" launcherIcon="@Drawables.LauncherIcon" name="@Strings.AppName" type="audio-content-provider-app" version="0.1.9">
<iq:application entry="SubMusicApp" id="62436b93f69e4823a8b41854b6684b4a" launcherIcon="@Drawables.LauncherIcon" name="@Strings.AppName" type="audio-content-provider-app" version="0.1.10">
<iq:products>
<iq:product id="d2air"/>
<iq:product id="d2delta"/>
@@ -17,6 +17,7 @@
<iq:product id="fr645m"/>
<iq:product id="fr745"/>
<iq:product id="fr945"/>
<iq:product id="fr945lte"/>
<iq:product id="legacyherocaptainmarvel"/>
<iq:product id="legacyherofirstavenger"/>
<iq:product id="legacysagadarthvader"/>

View File

@@ -3,6 +3,7 @@ using Toybox.Cryptography;
using Toybox.StringUtil;
using Toybox.Communications;
using Toybox.Lang;
using Toybox.Media;
class AmpacheAPI extends Api {
@@ -66,7 +67,7 @@ class AmpacheAPI extends Api {
System.println("AmpacheAPI::onHandshake with responseCode " + responseCode + " payload " + data);
// errors are filtered first
var error = checkDictionaryResponse(responseCode, data);
var error = Api.checkDictionaryResponse(responseCode, data);
if (error) {
d_fallback.invoke(error);
return;
@@ -170,7 +171,7 @@ class AmpacheAPI extends Api {
:mediaEncoding => encoding,
:fileDownloadProgressCallback => method(:onProgress),
};
Communications.makeWebRequest(url(), params, options, self.method(:onStream));
Communications.makeWebRequest(url(), params, options, self.method(:onContentResponse));
}
function get_art(callback, params) {
@@ -192,23 +193,23 @@ class AmpacheAPI extends Api {
Communications.makeImageRequest(url(), params, options, self.method(:onGet_art));
}
function onStream(responseCode, data) {
System.println("AmpacheAPI::onStream with responseCode: " + responseCode);
// function onStream(responseCode, data) {
// System.println("AmpacheAPI::onStream with responseCode: " + responseCode);
// check if request was successful and response is ok
var error = checkResponse(responseCode, data);
if (error) {
d_fallback.invoke(error);
return;
}
d_callback.invoke(data.getId());
}
// // check if request was successful and response is ok
// var error = checkResponse(responseCode, data);
// if (error) {
// d_fallback.invoke(error);
// return;
// }
// d_callback.invoke(data.getId());
// }
function onGet_art(responseCode, data) {
System.println("AmpacheAPI::onGet_art with responseCode: " + responseCode + " and " + data);
// check if request was successful and response is ok
var error = checkResponse(responseCode, data);
var error = Api.checkImageResponse(responseCode, data);
if (error) {
d_fallback.invoke(error);
return;
@@ -236,7 +237,7 @@ class AmpacheAPI extends Api {
System.println("AmpacheAPI::onArrayResponse with responseCode: " + responseCode + ", payload " + data);
// errors are filtered first
var error = checkArrayResponse(responseCode, data);
var error = Api.checkArrayResponse(responseCode, data);
if (error) {
d_fallback.invoke(error);
return;
@@ -244,14 +245,14 @@ class AmpacheAPI extends Api {
d_callback.invoke(data);
}
function checkArrayResponse(responseCode, data) {
var error = checkResponse(responseCode, data);
if (error) { return error; }
// function checkArrayResponse(responseCode, data) {
// var error = checkResponse(responseCode, data);
// if (error) { return error; }
// finally, expecting array
if (!(data instanceof Lang.Array)) { return new AmpacheError(null); }
return null;
}
// // finally, expecting array
// if (!(data instanceof Lang.Array)) { return new AmpacheError(null); }
// return null;
// }
/*
* onDictionaryResponse
@@ -262,34 +263,60 @@ class AmpacheAPI extends Api {
System.println("AmpacheAPI::onDictionaryResponse with responseCode " + responseCode + " payload " + data);
// errors are filtered first
var error = checkDictionaryResponse(responseCode, data);
var error = Api.checkDictionaryResponse(responseCode, data);
if (error) {
d_fallback.invoke(error);
return;
}
d_callback.invoke(data);
}
function checkDictionaryResponse(responseCode, data) {
var error = checkResponse(responseCode, data);
if (error) { return error; }
// function checkDictionaryResponse(responseCode, data) {
// var error = checkResponse(responseCode, data);
// if (error) { return error; }
// finally, expecting Dictionary
if (!(data instanceof Lang.Dictionary)) { return new AmpacheError(null); }
return null;
}
// // finally, expecting Dictionary
// if (!(data instanceof Lang.Dictionary)) { return new AmpacheError(null); }
// return null;
// }
/*
* checkResponse
* onContentResponse
*
* returns response / api errors if found
* Default handler for actions that return a ContentRef
*/
function checkResponse(responseCode, data) {
var error = Api.checkResponse(responseCode, data);
if (error) { return error; }
function onContentResponse(responseCode, data) {
System.println("AmpacheAPI::onContentResponse with responseCode " + responseCode + " payload " + data);
// errors are filtered first
var error = Api.checkContentResponse(responseCode, data);
if (error) {
d_fallback.invoke(error);
return;
}
d_callback.invoke(data);
}
// /*
// * checkResponse
// *
// * returns response / api errors if found
// */
// function checkResponse(responseCode, data) {
// var error = Api.checkResponse(responseCode, data);
// if (error) { return error; }
// return AmpacheError.is(responseCode, data);
// }
/*
* @override Api.checkApiError
*
* returns ampache api error if found (used from base class Api)
*/
function checkApiError(responseCode, data) {
return AmpacheError.is(responseCode, data);
}
// converts rfc3339 formatted timestamp to Time::Moment (null on error)
function parseISODate(date) {
@@ -406,6 +433,7 @@ class AmpacheAPI extends Api {
}
function deleteSession() {
System.println("AmpacheAPI::deleteSession()");
// reset the session
d_expire = new Time.Moment(0);
Application.Storage.deleteValue("AMPACHE_API_SESSION");

View File

@@ -13,7 +13,7 @@ class AmpacheError extends SubMusic.ApiError {
// BAD_REQUEST = 4710,
// FAILED_ACCESS = 4742,
// }
enum {
static enum {
ACCESS_CONTROL = 400,
HANDSHAKE = 401,
FEATURE_MISSING = 403,
@@ -23,16 +23,16 @@ class AmpacheError extends SubMusic.ApiError {
BAD_REQUEST = 410,
FAILED_ACCESS = 442,
}
private var d_code = null;
private var d_type = null;
private var d_msg = "";
static private var s_name = "Ampache";
static private var s_name = "AmpacheError";
function initialize(error_obj) {
// if null error_obj, response is malformed
if (error_obj) {
d_code = error_obj["code"];
d_type = error_obj["code"].toNumber();
d_msg = error_obj["message"];
// TODO for Ampache 5:
// d_code = error_obj["errorCode"]; // Ampache5
@@ -40,32 +40,37 @@ class AmpacheError extends SubMusic.ApiError {
}
// default is unknown
var type = SubMusic.ApiError.UNKNOWN;
if (d_code == HANDSHAKE) {
type = SubMusic.ApiError.LOGIN;
} else if (d_code == FAILED_ACCESS) {
type = SubMusic.ApiError.ACCESS;
} else if (d_code == NOT_FOUND) {
type = SubMusic.ApiError.NOTFOUND;
} else if ((d_code == ACCESS_CONTROL) || (d_code == FEATURE_MISSING) || (d_code == METHOD_MISSING) || (d_code == METHOD_DPRCTD)) {
type = SubMusic.ApiError.SERVERCLIENT;
} else if (d_code == BAD_REQUEST) {
type = SubMusic.ApiError.BADREQUEST;
var apitype = SubMusic.ApiError.UNKNOWN;
if (d_type == HANDSHAKE) {
apitype= SubMusic.ApiError.LOGIN;
} else if (d_type == FAILED_ACCESS) {
apitype= SubMusic.ApiError.ACCESS;
} else if (d_type == NOT_FOUND) {
apitype = SubMusic.ApiError.NOTFOUND;
} else if ((d_type == ACCESS_CONTROL) || (d_type == FEATURE_MISSING) || (d_type == METHOD_MISSING) || (d_type == METHOD_DPRCTD)) {
apitype = SubMusic.ApiError.SERVERCLIENT;
} else if (d_type == BAD_REQUEST) {
apitype = SubMusic.ApiError.BADREQUEST;
}
SubMusic.ApiError.initialize(type);
SubMusic.ApiError.initialize(apitype);
System.println(s_name + "::" + AmpacheError.typeToString(d_type));
}
function shortString() {
return SubMusic.ApiError.shortString() + " " + d_code;
return s_name + "::" + d_type;
}
function toString() {
return d_msg;
return SubMusic.ApiError.toString() +
" --> " +
s_name + "::" + AmpacheError.typeToString(d_type) +
": " + d_msg;
}
function code() {
return d_code;
function type() {
return d_type;
}
static function is(responseCode, data) {
@@ -78,4 +83,25 @@ class AmpacheError extends SubMusic.ApiError {
}
return new AmpacheError(data["error"]);
}
static function typeToString(type) {
if (type == ACCESS_CONTROL) {
return "ACCESS_CONTROL";
} else if (type == HANDSHAKE) {
return "HANDSHAKE";
} else if (type == FEATURE_MISSING) {
return "FEATURE_MISSING";
} else if (type == NOT_FOUND) {
return "NOT_FOUND";
} else if (type == METHOD_MISSING) {
return "METHOD_MISSING";
} else if (type == METHOD_DPRCTD) {
return "METHOD_DPRCTD";
} else if (type == BAD_REQUEST) {
return "BAD_REQUEST";
} else if (type == FAILED_ACCESS) {
return "FAILED_ACCESS";
}
return "Unknown";
}
}

View File

@@ -264,9 +264,9 @@ class AmpacheProvider {
do_();
}
function on_do_stream(refId) {
function on_do_stream(contentRef) {
d_action = null;
d_callback.invoke(refId);
d_callback.invoke(contentRef.getId());
}
function on_do_get_art(artwork) {
@@ -325,7 +325,7 @@ class AmpacheProvider {
// if handshake error on otherwise valid session, delete session and retry handshake
if ((error instanceof AmpacheError)
&& (error.code() == AmpacheError.HANDSHAKE)
&& (error.type() == AmpacheError.HANDSHAKE)
&& d_api.session(null)) {
d_api.deleteSession();

View File

@@ -1,3 +1,9 @@
using Toybox.Lang;
using Toybox.Media;
using Toybox.System;
using Toybox.WatchUi;
using SubMusic;
class Api {
private var d_client; // the client name
@@ -61,10 +67,53 @@ class Api {
*
* returns http/sdk errors if found
*/
static function checkResponse(responseCode, data) {
function checkResponse(responseCode, data) {
var error = SubMusic.HttpError.is(responseCode);
if (error) { return error; }
return SubMusic.GarminSdkError.is(responseCode);
error = SubMusic.GarminSdkError.is(responseCode);
if (error) { return error; }
return self.checkApiError(responseCode, data);
}
function checkApiError(responseCode, data) {
System.println("WARNING: Api::checkApiError() was called, but should not be called");
return null;
}
function checkDictionaryResponse(responseCode, data) {
var error = checkResponse(responseCode, data);
if (error) { return error; }
// check type of received object
if (data instanceof Lang.Dictionary) { return null; }
return new SubMusic.ApiError(SubMusic.ApiError.BADRESPONSE);
}
function checkArrayResponse(responseCode, data) {
var error = checkResponse(responseCode, data);
if (error) { return error; }
// check type of received object
if (data instanceof Lang.Array) { return null; }
return new SubMusic.ApiError(SubMusic.ApiError.BADRESPONSE);
}
function checkContentResponse(responseCode, data) {
var error = checkResponse(responseCode, data);
if (error) { return error; }
// check type of received object
if (data instanceof Media.ContentRef) { return null; }
return new SubMusic.ApiError(SubMusic.ApiError.BADRESPONSE);
}
function checkImageResponse(responseCode, data) {
var error = checkResponse(responseCode, data);
if (error) { return error; }
// check type of received object
if (data instanceof WatchUi.BitmapResource) { return null; }
return new SubMusic.ApiError(SubMusic.ApiError.BADRESPONSE);
}
function onProgress(totalBytesTransferred, fileSize) {

View File

@@ -52,7 +52,7 @@ class SubsonicAPI extends Api {
System.println("SubsonicAPI::onResponse( responseCode: " + responseCode + ", data: " + data + ")");
// check if request was successful and response is ok
var error = checkResponse(responseCode, data);
var error = Api.checkDictionaryResponse(responseCode, data);
if (error) {
d_fallback.invoke(error); // add function name and variables available ?
return;
@@ -78,7 +78,7 @@ class SubsonicAPI extends Api {
System.println("SubsonicAPI::onGetPlaylists( responseCode: " + responseCode + ", data: " + data + ")");
// check if request was successful and response is ok
var error = checkResponse(responseCode, data);
var error = Api.checkDictionaryResponse(responseCode, data);
if (error) {
d_fallback.invoke(error); // add function name and variables available ?
return;
@@ -123,7 +123,7 @@ class SubsonicAPI extends Api {
System.println("Subsonic::onGetPlaylist(responseCode: " + responseCode + ", data: " + data);
// check if request was successful and response is ok
var error = checkResponse(responseCode, data);
var error = Api.checkDictionaryResponse(responseCode, data);
if (error) {
d_fallback.invoke(error); // add function name and variables available ?
return;
@@ -163,12 +163,12 @@ class SubsonicAPI extends Api {
System.println("SubsonicAPI::onStream with responseCode: " + responseCode);
// check if request was successful and response is ok
var error = checkResponse(responseCode, data);
var error = Api.checkContentResponse(responseCode, data);
if (error) {
d_fallback.invoke(error);
return;
}
d_callback.invoke(data.getId());
d_callback.invoke(data);
}
/**
@@ -204,17 +204,23 @@ class SubsonicAPI extends Api {
System.println("SubsonicAPI::onGetCoverArt with responseCode: " + responseCode + " and " + data);
// check if request was successful and response is ok
var error = checkResponse(responseCode, data);
// var error = checkResponse(responseCode, data);
var error = Api.checkImageResponse(responseCode, data);
if (error) {
d_fallback.invoke(error);
d_fallback.invoke(error);
return;
}
d_callback.invoke(data);
}
function checkResponse(responseCode, data) {
var error = Api.checkResponse(responseCode, data);
if (error) { return error; }
// function checkResponse(responseCode, data) {
// var error = Api.checkResponse(responseCode, data);
// if (error) { return error; }
// return SubsonicError.is(responseCode, data);
// }
// @override
function checkApiError(responseCode, data) {
return SubsonicError.is(responseCode, data);
}

View File

@@ -3,7 +3,7 @@ using Toybox.Lang;
class SubsonicError extends SubMusic.ApiError {
enum {
static enum {
GENERIC = 0, // 0 A generic error.
MISSING_PARAM = 10, // 10 Required parameter is missing.
INCOMPAT_CLIENT = 20, // 20 Incompatible Subsonic REST protocol version. Client must upgrade.
@@ -14,46 +14,51 @@ class SubsonicError extends SubMusic.ApiError {
TRIAL_OVER = 60, // 60 The trial period for the Subsonic server is over. Please upgrade to Subsonic Premium. Visit subsonic.org for details.
NOT_FOUND = 70, // 70 The requested data was not found.
}
private var d_code = null;
private var d_type = null;
private var d_msg = "";
static private var s_name = "Subsonic";
static private var s_name = "SubsonicError";
function initialize(error_obj) {
// if null error_obj, response is malformed
if (error_obj) {
d_code = error_obj["code"];
d_type = error_obj["code"];
d_msg = error_obj["message"];
}
// default is unknown
var type = SubMusic.ApiError.UNKNOWN;
if ((d_code == WRONG_CREDS) || (d_code == TOKEN_SUPPORT) || (d_code == TRIAL_OVER)) {
type = SubMusic.ApiError.LOGIN;
} else if (d_code == NOT_AUTHORIZED) {
type = SubMusic.ApiError.ACCESS;
} else if (d_code == NOT_FOUND) {
type = SubMusic.ApiError.NOTFOUND;
} else if ((d_code == INCOMPAT_CLIENT) || (d_code == INCOMPAT_SERVER)) {
type = SubMusic.ApiError.SERVERCLIENT;
} else if (d_code == MISSING_PARAM) {
type = SubMusic.ApiError.BADREQUEST;
var apitype = SubMusic.ApiError.UNKNOWN;
if ((d_type == WRONG_CREDS) || (d_type == TOKEN_SUPPORT) || (d_type == TRIAL_OVER)) {
apitype = SubMusic.ApiError.LOGIN;
} else if (d_type == NOT_AUTHORIZED) {
apitype = SubMusic.ApiError.ACCESS;
} else if (d_type == NOT_FOUND) {
apitype = SubMusic.ApiError.NOTFOUND;
} else if ((d_type == INCOMPAT_CLIENT) || (d_type == INCOMPAT_SERVER)) {
apitype = SubMusic.ApiError.SERVERCLIENT;
} else if (d_type == MISSING_PARAM) {
apitype = SubMusic.ApiError.BADREQUEST;
}
SubMusic.ApiError.initialize(type);
SubMusic.ApiError.initialize(apitype);
System.println(s_name + "::" + SubsonicError.typeToString(d_type));
}
function shortString() {
return SubMusic.ApiError.shortString() + " " + d_code;
return s_name + "::" + d_type;
}
function toString() {
return d_msg;
return SubMusic.ApiError.toString() +
" --> " +
s_name + "::" + SubsonicError.typeToString(d_type) +
": " + d_msg;
}
function code() {
return d_code;
function type() {
return d_type;
}
static function is(responseCode, data) {
@@ -70,4 +75,27 @@ class SubsonicError extends SubMusic.ApiError {
}
return new SubsonicError(data["subsonic-response"]["error"]);
}
static function typeToString(type) {
if (type == GENERIC) {
return "GENERIC";
} else if (type == MISSING_PARAM) {
return "MISSING_PARAM";
} else if (type == INCOMPAT_CLIENT) {
return "INCOMPAT_CLIENT";
} else if (type == INCOMPAT_SERVER) {
return "INCOMPAT_SERVER";
} else if (type == WRONG_CREDS) {
return "WRONG_CREDS";
} else if (type == TOKEN_SUPPORT) {
return "TOKEN_SUPPORT";
} else if (type == NOT_AUTHORIZED) {
return "NOT_AUTHORIZED";
} else if (type == TRIAL_OVER) {
return "TRIAL_OVER";
} else if (type == NOT_FOUND) {
return "NOT_FOUND";
}
return "Unknown";
}
}

View File

@@ -209,8 +209,8 @@ class SubsonicProvider {
d_callback.invoke(songs);
}
function onStream(refId) {
d_callback.invoke(refId);
function onStream(contentRef) {
d_callback.invoke(contentRef.getId());
}
function onGetCoverArt(artwork) {

View File

@@ -143,7 +143,7 @@ class PlaylistSync extends Deferrable {
}
function onError(error) {
System.println("PlaylistSync::onError(" + error.shortString() + ")");
System.println("PlaylistSync::onError(" + error.shortString() + " : " + error.toString() + ")");
// indicate failed sync
// d_playlist.setError(error); TODO
@@ -165,15 +165,27 @@ class PlaylistSync extends Deferrable {
d_playlist.setSynced(!d_failed);
// update playlist info if not found
if ((error instanceof SubMusic.ApiError)
&& (error.type() == SubMusic.ApiError.NOTFOUND)) {
d_playlist.setRemote(false);
Deferrable.complete();
return;
}
// other errors will break the sync by default
Deferrable.cancel(error);
return;
// if ((error instanceof SubMusic.ApiError)
// && (error.type() == SubMusic.ApiError.NOTFOUND)) {
// d_playlist.setRemote(false);
// Deferrable.complete();
// return;
// }
if (!(error instanceof SubMusic.ApiError)) {
// other errors will break the sync by default
Deferrable.cancel(error);
return;
}
var apiError as SubMusic.ApiError = error;
if (apiError.api_type() == SubMusic.ApiError.NOTFOUND) {
d_playlist.setRemote(false);
Deferrable.complete();
return;
}
Deferrable.cancel(error);
return;
}
}

View File

@@ -1,13 +1,17 @@
using Toybox.Communications;
module SubMusic {
class Error {
enum {
static enum {
HTTP,
API,
}
static private var s_name = "Error";
private var d_type;
function initialize(type) {
System.println(s_name + "::" + Error.typeToString(type));
d_type = type;
}
@@ -16,17 +20,26 @@ module SubMusic {
}
function shortString() {
return "Error";
return s_name + " " + Error.typeToString(d_type);
}
function toString() {
return "";
return s_name + " " + Error.typeToString(d_type);
}
static function typeToString(type) {
if (type == HTTP) {
return "HTTP";
} else if (type == API) {
return "API";
}
return "Unknown Error";
}
}
class ApiError extends Error {
enum {
static enum {
LOGIN,
ACCESS,
NOTFOUND,
@@ -36,23 +49,35 @@ module SubMusic {
UNKNOWN,
}
private var d_type;
static private var s_name = "API";
static private var s_name = "ApiError";
function initialize(type) {
Error.initialize(Error.API);
System.println(s_name + "::" + ApiError.typeToString(type));
d_type = type;
}
function shortString() {
return "API::" + typeToString(d_type) + " " + Error.shortString();
return s_name + "::" + ApiError.typeToString(d_type);
}
function toString() {
return SubMusic.Error.toString() +
" --> " +
s_name + "::" + ApiError.typeToString(d_type);
}
function type() {
return d_type;
}
function api_type() {
return ApiError.type();
}
function typeToString(type) {
static function typeToString(type) {
if (type == LOGIN) {
return "\"LOGIN\"";
@@ -69,23 +94,24 @@ module SubMusic {
} else if (type == UNKNOWN) {
return "\"UNKNOWN\"";
}
return "Unknown";
return "Unknown ApiError";
}
}
class HttpError extends Error {
enum {
static enum {
BAD_REQUEST = 400,
NOT_FOUND = 404,
}
private var d_type;
static private var s_name = "HTTP";
static private var s_name = "HttpError";
function initialize(type) {
Error.initialize(Error.HTTP);
d_type = type;
System.println(HttpError.toString());
}
static function is(responseCode) {
@@ -99,17 +125,23 @@ module SubMusic {
}
function shortString() {
return Error.shortString() + "::" + s_name + "::" + d_type.toString();
return s_name + "::" + d_type.toString();
}
function toString() {
return Error.toString() +
" --> " +
s_name + "::" + HttpError.typeToString(d_type);
}
function toString() {
static function typeToString(type) {
if (d_type == BAD_REQUEST) {
if (type == BAD_REQUEST) {
return "BAD_REQUEST";
} else if (d_type == NOT_FOUND) {
} else if (type == NOT_FOUND) {
return "NOT_FOUND";
}
return "Unknown";
return "Unknown HttpError";
}
}
@@ -117,11 +149,13 @@ module SubMusic {
// enum for possible errors can be found in module Communications
private var d_responseCode;
static private var s_name = "GarminSdkError";
function initialize(responseCode) {
Error.initialize(Error.HTTP);
d_responseCode = responseCode;
System.println(GarminSdkError.toString());
}
static function is(responseCode) {
@@ -139,15 +173,17 @@ module SubMusic {
}
function shortString() {
return d_responseCode.toString();
return s_name + "::" + d_responseCode.toString();
}
function toString() {
return respCodeToString(d_responseCode);
return Error.toString() +
" --> " +
s_name + "::" + GarminSdkError.respCodeToString(d_responseCode);
}
// move to somewhere else later, but now this is one of the two places this is used
function respCodeToString(responseCode) {
static function respCodeToString(responseCode) {
if (responseCode == Communications.UNKNOWN_ERROR) {
return "\"UNKNOWN_ERROR\"";
} else if (responseCode == Communications.BLE_ERROR) {
@@ -188,8 +224,20 @@ module SubMusic {
return "\"STORAGE_FULL\"";
} else if (responseCode == Communications.SECURE_CONNECTION_REQUIRED) {
return "\"SECURE_CONNECTION_REQUIRED\"";
} else if (responseCode == Communications.UNSUPPORTED_CONTENT_TYPE_IN_RESPONSE) {
return "\"UNSUPPORTED_CONTENT_TYPE_IN_RESPONSE\"";
} else if (responseCode == Communications.REQUEST_CANCELLED) {
return "\"REQUEST_CANCELLED\"";
} else if (responseCode == Communications.REQUEST_CONNECTION_DROPPED) {
return "\"REQUEST_CONNECTION_DROPPED\"";
} else if (responseCode == Communications.UNABLE_TO_PROCESS_MEDIA) {
return "\"UNABLE_TO_PROCESS_MEDIA\"";
} else if (responseCode == Communications.UNABLE_TO_PROCESS_IMAGE) {
return "\"UNABLE_TO_PROCESS_IMAGE\"";
} else if (responseCode == Communications.UNABLE_TO_PROCESS_HLS) {
return "\"UNABLE_TO_PROCESS_HLS\"";
}
return "Unknown";
return "Unknown GarminSdkError";
}
}
}

View File

@@ -18,8 +18,8 @@ class SubMusicVersion {
private var d_major = 0;
private var d_minor = 1;
private var d_patch = 9;
private var d_name = "juliett";
private var d_patch = 10;
private var d_name = "kilo";
function initialize(storage) {
if (storage == null) {