react-native-audio-browser 0.2.0-next.13 → 0.2.0-next.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +3 -0
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +44 -0
- package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +0 -2
- package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +1 -1
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +21 -22
- package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +8 -5
- package/android/src/main/res/values/strings.xml +37 -2
- package/android/src/test/java/com/audiobrowser/TestFixtures.kt +1 -2
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +9 -0
- package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +2 -1
- package/ios/Browser/JsonModels.swift +0 -5
- package/ios/Extension/Section+Copying.swift +1 -3
- package/ios/HybridAudioBrowser.swift +2 -1
- package/ios/Model/NitroTypeStubs.swift +0 -1
- package/ios/Tests/ResolvedTrackCopyingTests.swift +2 -2
- package/lib/commonjs/features/browser-config.js +1 -0
- package/lib/commonjs/features/browser-config.js.map +1 -1
- package/lib/module/features/browser-config.js +1 -0
- package/lib/module/features/browser-config.js.map +1 -1
- package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-native.d.ts +2 -1
- package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-nodes.d.ts +14 -9
- package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
- package/lib/typescript/src/types/browser.d.ts +36 -0
- package/lib/typescript/src/types/browser.d.ts.map +1 -1
- package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +2 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__string__.hpp +102 -0
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +1 -0
- package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +14 -1
- package/nitrogen/generated/android/c++/JSection.hpp +1 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__string__.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +7 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Section.kt +2 -5
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +9 -0
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +50 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__string__.swift +54 -0
- package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +48 -1
- package/nitrogen/generated/ios/swift/Section.swift +1 -19
- package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +6 -2
- package/nitrogen/generated/shared/c++/Section.hpp +1 -5
- package/package.json +1 -1
- package/src/features/browser-config.ts +1 -0
- package/src/types/browser-native.ts +5 -1
- package/src/types/browser-nodes.ts +14 -9
- package/src/types/browser.ts +38 -0
- package/website/guide/android-auto.md +1 -1
- package/website/guide/browser.md +19 -19
|
@@ -18,19 +18,13 @@ public extension Section {
|
|
|
18
18
|
/**
|
|
19
19
|
* Create a new instance of `Section`.
|
|
20
20
|
*/
|
|
21
|
-
init(title: String?,
|
|
21
|
+
init(title: String?, style: SectionStyle?, path: String?, children: [Track]) {
|
|
22
22
|
self.init({ () -> bridge.std__optional_std__string_ in
|
|
23
23
|
if let __unwrappedValue = title {
|
|
24
24
|
return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
|
|
25
25
|
} else {
|
|
26
26
|
return .init()
|
|
27
27
|
}
|
|
28
|
-
}(), { () -> bridge.std__optional_std__string_ in
|
|
29
|
-
if let __unwrappedValue = subtitle {
|
|
30
|
-
return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
|
|
31
|
-
} else {
|
|
32
|
-
return .init()
|
|
33
|
-
}
|
|
34
28
|
}(), { () -> bridge.std__optional_SectionStyle_ in
|
|
35
29
|
if let __unwrappedValue = style {
|
|
36
30
|
return bridge.create_std__optional_SectionStyle_(__unwrappedValue)
|
|
@@ -64,18 +58,6 @@ public extension Section {
|
|
|
64
58
|
}()
|
|
65
59
|
}
|
|
66
60
|
|
|
67
|
-
@inline(__always)
|
|
68
|
-
var subtitle: String? {
|
|
69
|
-
return { () -> String? in
|
|
70
|
-
if bridge.has_value_std__optional_std__string_(self.__subtitle) {
|
|
71
|
-
let __unwrapped = bridge.get_std__optional_std__string_(self.__subtitle)
|
|
72
|
-
return String(__unwrapped)
|
|
73
|
-
} else {
|
|
74
|
-
return nil
|
|
75
|
-
}
|
|
76
|
-
}()
|
|
77
|
-
}
|
|
78
|
-
|
|
79
61
|
@inline(__always)
|
|
80
62
|
var style: SectionStyle? {
|
|
81
63
|
return self.__style.value
|
|
@@ -78,16 +78,17 @@ namespace margelo::nitro::audiobrowser {
|
|
|
78
78
|
std::optional<bool> singleTrack SWIFT_PRIVATE;
|
|
79
79
|
std::optional<std::function<std::shared_ptr<Promise<std::shared_ptr<Promise<void>>>>(const TrackLoadEvent& /* event */)>> handleTrackLoad SWIFT_PRIVATE;
|
|
80
80
|
std::optional<bool> androidControllerOfflineError SWIFT_PRIVATE;
|
|
81
|
+
std::optional<std::function<std::shared_ptr<Promise<std::string>>()>> viewMoreTitle SWIFT_PRIVATE;
|
|
81
82
|
std::optional<std::string> carPlayLoadingTitle SWIFT_PRIVATE;
|
|
82
83
|
std::optional<std::function<std::shared_ptr<Promise<std::optional<std::string>>>(const Track& /* track */)>> resolveAlbumPath SWIFT_PRIVATE;
|
|
83
84
|
std::optional<std::function<std::shared_ptr<Promise<std::optional<FormattedNavigationError>>>(const FormatNavigationErrorParams& /* params */)>> formatNavigationError SWIFT_PRIVATE;
|
|
84
85
|
|
|
85
86
|
public:
|
|
86
87
|
NativeBrowserConfiguration() = default;
|
|
87
|
-
explicit NativeBrowserConfiguration(std::optional<std::string> path, std::optional<TransformableRequestConfig> request, std::optional<std::function<std::shared_ptr<Promise<std::shared_ptr<Promise<TransformableRequestConfig>>>>()>> requestResolver, std::optional<TransformableRequestConfig> browse, std::optional<std::function<std::shared_ptr<Promise<std::shared_ptr<Promise<TransformableRequestConfig>>>>()>> browseResolver, std::optional<MediaRequestConfig> media, std::optional<ArtworkRequestConfig> artwork, std::optional<ArtworkRequestConfig> nowPlayingArtwork, std::optional<std::vector<NativeRouteEntry>> routes, std::optional<bool> singleTrack, std::optional<std::function<std::shared_ptr<Promise<std::shared_ptr<Promise<void>>>>(const TrackLoadEvent& /* event */)>> handleTrackLoad, std::optional<bool> androidControllerOfflineError, std::optional<std::string> carPlayLoadingTitle, std::optional<std::function<std::shared_ptr<Promise<std::optional<std::string>>>(const Track& /* track */)>> resolveAlbumPath, std::optional<std::function<std::shared_ptr<Promise<std::optional<FormattedNavigationError>>>(const FormatNavigationErrorParams& /* params */)>> formatNavigationError): path(path), request(request), requestResolver(requestResolver), browse(browse), browseResolver(browseResolver), media(media), artwork(artwork), nowPlayingArtwork(nowPlayingArtwork), routes(routes), singleTrack(singleTrack), handleTrackLoad(handleTrackLoad), androidControllerOfflineError(androidControllerOfflineError), carPlayLoadingTitle(carPlayLoadingTitle), resolveAlbumPath(resolveAlbumPath), formatNavigationError(formatNavigationError) {}
|
|
88
|
+
explicit NativeBrowserConfiguration(std::optional<std::string> path, std::optional<TransformableRequestConfig> request, std::optional<std::function<std::shared_ptr<Promise<std::shared_ptr<Promise<TransformableRequestConfig>>>>()>> requestResolver, std::optional<TransformableRequestConfig> browse, std::optional<std::function<std::shared_ptr<Promise<std::shared_ptr<Promise<TransformableRequestConfig>>>>()>> browseResolver, std::optional<MediaRequestConfig> media, std::optional<ArtworkRequestConfig> artwork, std::optional<ArtworkRequestConfig> nowPlayingArtwork, std::optional<std::vector<NativeRouteEntry>> routes, std::optional<bool> singleTrack, std::optional<std::function<std::shared_ptr<Promise<std::shared_ptr<Promise<void>>>>(const TrackLoadEvent& /* event */)>> handleTrackLoad, std::optional<bool> androidControllerOfflineError, std::optional<std::function<std::shared_ptr<Promise<std::string>>()>> viewMoreTitle, std::optional<std::string> carPlayLoadingTitle, std::optional<std::function<std::shared_ptr<Promise<std::optional<std::string>>>(const Track& /* track */)>> resolveAlbumPath, std::optional<std::function<std::shared_ptr<Promise<std::optional<FormattedNavigationError>>>(const FormatNavigationErrorParams& /* params */)>> formatNavigationError): path(path), request(request), requestResolver(requestResolver), browse(browse), browseResolver(browseResolver), media(media), artwork(artwork), nowPlayingArtwork(nowPlayingArtwork), routes(routes), singleTrack(singleTrack), handleTrackLoad(handleTrackLoad), androidControllerOfflineError(androidControllerOfflineError), viewMoreTitle(viewMoreTitle), carPlayLoadingTitle(carPlayLoadingTitle), resolveAlbumPath(resolveAlbumPath), formatNavigationError(formatNavigationError) {}
|
|
88
89
|
|
|
89
90
|
public:
|
|
90
|
-
// NativeBrowserConfiguration is not equatable because these properties are not equatable: request, requestResolver, browse, browseResolver, media, artwork, nowPlayingArtwork, routes, handleTrackLoad, resolveAlbumPath, formatNavigationError
|
|
91
|
+
// NativeBrowserConfiguration is not equatable because these properties are not equatable: request, requestResolver, browse, browseResolver, media, artwork, nowPlayingArtwork, routes, handleTrackLoad, viewMoreTitle, resolveAlbumPath, formatNavigationError
|
|
91
92
|
};
|
|
92
93
|
|
|
93
94
|
} // namespace margelo::nitro::audiobrowser
|
|
@@ -112,6 +113,7 @@ namespace margelo::nitro {
|
|
|
112
113
|
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "singleTrack"))),
|
|
113
114
|
JSIConverter<std::optional<std::function<std::shared_ptr<Promise<std::shared_ptr<Promise<void>>>>(const margelo::nitro::audiobrowser::TrackLoadEvent&)>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "handleTrackLoad"))),
|
|
114
115
|
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "androidControllerOfflineError"))),
|
|
116
|
+
JSIConverter<std::optional<std::function<std::shared_ptr<Promise<std::string>>()>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "viewMoreTitle"))),
|
|
115
117
|
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "carPlayLoadingTitle"))),
|
|
116
118
|
JSIConverter<std::optional<std::function<std::shared_ptr<Promise<std::optional<std::string>>>(const margelo::nitro::audiobrowser::Track&)>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "resolveAlbumPath"))),
|
|
117
119
|
JSIConverter<std::optional<std::function<std::shared_ptr<Promise<std::optional<margelo::nitro::audiobrowser::FormattedNavigationError>>>(const margelo::nitro::audiobrowser::FormatNavigationErrorParams&)>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "formatNavigationError")))
|
|
@@ -131,6 +133,7 @@ namespace margelo::nitro {
|
|
|
131
133
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "singleTrack"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.singleTrack));
|
|
132
134
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "handleTrackLoad"), JSIConverter<std::optional<std::function<std::shared_ptr<Promise<std::shared_ptr<Promise<void>>>>(const margelo::nitro::audiobrowser::TrackLoadEvent&)>>>::toJSI(runtime, arg.handleTrackLoad));
|
|
133
135
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "androidControllerOfflineError"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.androidControllerOfflineError));
|
|
136
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "viewMoreTitle"), JSIConverter<std::optional<std::function<std::shared_ptr<Promise<std::string>>()>>>::toJSI(runtime, arg.viewMoreTitle));
|
|
134
137
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "carPlayLoadingTitle"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.carPlayLoadingTitle));
|
|
135
138
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "resolveAlbumPath"), JSIConverter<std::optional<std::function<std::shared_ptr<Promise<std::optional<std::string>>>(const margelo::nitro::audiobrowser::Track&)>>>::toJSI(runtime, arg.resolveAlbumPath));
|
|
136
139
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "formatNavigationError"), JSIConverter<std::optional<std::function<std::shared_ptr<Promise<std::optional<margelo::nitro::audiobrowser::FormattedNavigationError>>>(const margelo::nitro::audiobrowser::FormatNavigationErrorParams&)>>>::toJSI(runtime, arg.formatNavigationError));
|
|
@@ -156,6 +159,7 @@ namespace margelo::nitro {
|
|
|
156
159
|
if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "singleTrack")))) return false;
|
|
157
160
|
if (!JSIConverter<std::optional<std::function<std::shared_ptr<Promise<std::shared_ptr<Promise<void>>>>(const margelo::nitro::audiobrowser::TrackLoadEvent&)>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "handleTrackLoad")))) return false;
|
|
158
161
|
if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "androidControllerOfflineError")))) return false;
|
|
162
|
+
if (!JSIConverter<std::optional<std::function<std::shared_ptr<Promise<std::string>>()>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "viewMoreTitle")))) return false;
|
|
159
163
|
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "carPlayLoadingTitle")))) return false;
|
|
160
164
|
if (!JSIConverter<std::optional<std::function<std::shared_ptr<Promise<std::optional<std::string>>>(const margelo::nitro::audiobrowser::Track&)>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "resolveAlbumPath")))) return false;
|
|
161
165
|
if (!JSIConverter<std::optional<std::function<std::shared_ptr<Promise<std::optional<margelo::nitro::audiobrowser::FormattedNavigationError>>>(const margelo::nitro::audiobrowser::FormatNavigationErrorParams&)>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "formatNavigationError")))) return false;
|
|
@@ -47,14 +47,13 @@ namespace margelo::nitro::audiobrowser {
|
|
|
47
47
|
struct Section final {
|
|
48
48
|
public:
|
|
49
49
|
std::optional<std::string> title SWIFT_PRIVATE;
|
|
50
|
-
std::optional<std::string> subtitle SWIFT_PRIVATE;
|
|
51
50
|
std::optional<SectionStyle> style SWIFT_PRIVATE;
|
|
52
51
|
std::optional<std::string> path SWIFT_PRIVATE;
|
|
53
52
|
std::vector<Track> children SWIFT_PRIVATE;
|
|
54
53
|
|
|
55
54
|
public:
|
|
56
55
|
Section() = default;
|
|
57
|
-
explicit Section(std::optional<std::string> title, std::optional<
|
|
56
|
+
explicit Section(std::optional<std::string> title, std::optional<SectionStyle> style, std::optional<std::string> path, std::vector<Track> children): title(title), style(style), path(path), children(children) {}
|
|
58
57
|
|
|
59
58
|
public:
|
|
60
59
|
friend bool operator==(const Section& lhs, const Section& rhs) = default;
|
|
@@ -71,7 +70,6 @@ namespace margelo::nitro {
|
|
|
71
70
|
jsi::Object obj = arg.asObject(runtime);
|
|
72
71
|
return margelo::nitro::audiobrowser::Section(
|
|
73
72
|
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "title"))),
|
|
74
|
-
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "subtitle"))),
|
|
75
73
|
JSIConverter<std::optional<margelo::nitro::audiobrowser::SectionStyle>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "style"))),
|
|
76
74
|
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "path"))),
|
|
77
75
|
JSIConverter<std::vector<margelo::nitro::audiobrowser::Track>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "children")))
|
|
@@ -80,7 +78,6 @@ namespace margelo::nitro {
|
|
|
80
78
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::audiobrowser::Section& arg) {
|
|
81
79
|
jsi::Object obj(runtime);
|
|
82
80
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "title"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.title));
|
|
83
|
-
obj.setProperty(runtime, PropNameIDCache::get(runtime, "subtitle"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.subtitle));
|
|
84
81
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "style"), JSIConverter<std::optional<margelo::nitro::audiobrowser::SectionStyle>>::toJSI(runtime, arg.style));
|
|
85
82
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "path"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.path));
|
|
86
83
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "children"), JSIConverter<std::vector<margelo::nitro::audiobrowser::Track>>::toJSI(runtime, arg.children));
|
|
@@ -95,7 +92,6 @@ namespace margelo::nitro {
|
|
|
95
92
|
return false;
|
|
96
93
|
}
|
|
97
94
|
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "title")))) return false;
|
|
98
|
-
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "subtitle")))) return false;
|
|
99
95
|
if (!JSIConverter<std::optional<margelo::nitro::audiobrowser::SectionStyle>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "style")))) return false;
|
|
100
96
|
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "path")))) return false;
|
|
101
97
|
if (!JSIConverter<std::vector<margelo::nitro::audiobrowser::Track>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "children")))) return false;
|
package/package.json
CHANGED
|
@@ -289,6 +289,7 @@ export function toNativeConfig(
|
|
|
289
289
|
singleTrack: config.singleTrack,
|
|
290
290
|
handleTrackLoad: config.handleTrackLoad,
|
|
291
291
|
androidControllerOfflineError: config.androidControllerOfflineError,
|
|
292
|
+
viewMoreTitle: config.viewMoreTitle,
|
|
292
293
|
carPlayLoadingTitle: config.carPlayLoadingTitle,
|
|
293
294
|
resolveAlbumPath: config.resolveAlbumPath,
|
|
294
295
|
formatNavigationError: config.formatNavigationError
|
|
@@ -14,7 +14,8 @@ import type {
|
|
|
14
14
|
HandleTrackLoadCallback,
|
|
15
15
|
MediaRequestConfig,
|
|
16
16
|
SearchSourceCallback,
|
|
17
|
-
TransformableRequestConfig
|
|
17
|
+
TransformableRequestConfig,
|
|
18
|
+
ViewMoreTitleCallback
|
|
18
19
|
} from './browser'
|
|
19
20
|
import type { ResolvedTrack } from './browser-nodes'
|
|
20
21
|
|
|
@@ -73,6 +74,9 @@ export interface NativeBrowserConfiguration {
|
|
|
73
74
|
singleTrack?: boolean
|
|
74
75
|
handleTrackLoad?: HandleTrackLoadCallback
|
|
75
76
|
androidControllerOfflineError?: boolean
|
|
77
|
+
// Title of the "view more" row appended to a pathed section where the header
|
|
78
|
+
// can't be tapped (Android Auto). Absent = native's "View more" default.
|
|
79
|
+
viewMoreTitle?: ViewMoreTitleCallback
|
|
76
80
|
|
|
77
81
|
// CarPlay options
|
|
78
82
|
carPlayLoadingTitle?: string
|
|
@@ -293,11 +293,6 @@ export type TrackArtwork = string | ArtworkVariants
|
|
|
293
293
|
export interface Section {
|
|
294
294
|
/** Header text. Absent = headerless group. */
|
|
295
295
|
title?: string
|
|
296
|
-
/**
|
|
297
|
-
* Secondary line for the section's navigation surface — e.g. the label
|
|
298
|
-
* of the "view all" link a tile section gets on Android Auto.
|
|
299
|
-
*/
|
|
300
|
-
subtitle?: string
|
|
301
296
|
/**
|
|
302
297
|
* Presentation, separated from content. Each surface renders the
|
|
303
298
|
* declared layout's nearest supported form; on CarPlay
|
|
@@ -308,10 +303,20 @@ export interface Section {
|
|
|
308
303
|
*/
|
|
309
304
|
style?: SectionStyle
|
|
310
305
|
/**
|
|
311
|
-
* Navigation target for the section
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
306
|
+
* Navigation target for the section's "view all" affordance — the
|
|
307
|
+
* fuller page this section is a teaser for. Absent = a self-contained
|
|
308
|
+
* section: the header is a plain label, not a link.
|
|
309
|
+
*
|
|
310
|
+
* The surface picks the form (ADR 0010), and each carries the copy
|
|
311
|
+
* itself — a `path` never adds words to the section:
|
|
312
|
+
*
|
|
313
|
+
* - **CarPlay** — a tile section's row draws a disclosure chevron and
|
|
314
|
+
* the whole row navigates. A list section renders no affordance:
|
|
315
|
+
* CarPlay has no "see all" idiom for one, and an invented row would
|
|
316
|
+
* spend an item from the template's cap.
|
|
317
|
+
* - **Android Auto** — no header is tappable at any display, so the
|
|
318
|
+
* section gets an appended browsable row, titled by the global
|
|
319
|
+
* `viewMoreTitle` browser option ("View more" by default).
|
|
315
320
|
*/
|
|
316
321
|
path?: string
|
|
317
322
|
/** The section's tracks. */
|
package/src/types/browser.ts
CHANGED
|
@@ -931,6 +931,41 @@ export type BrowserConfiguration = {
|
|
|
931
931
|
*/
|
|
932
932
|
androidControllerOfflineError?: boolean
|
|
933
933
|
|
|
934
|
+
/**
|
|
935
|
+
* Titles the "view more" row appended to a `Section` that carries a
|
|
936
|
+
* `Section.path` on surfaces whose section header cannot be tapped —
|
|
937
|
+
* Android Auto, today the only one.
|
|
938
|
+
*
|
|
939
|
+
* Leave it unset and the row is titled "View more" — the library's in-code
|
|
940
|
+
* default, the same arrangement `formatNavigationError` has. Set this to
|
|
941
|
+
* localize it: it is re-resolved once per content generation, so
|
|
942
|
+
* `invalidateAllContent()` after an in-app language switch picks up the new
|
|
943
|
+
* copy without reconfiguring the browser. That is why it is a callback rather
|
|
944
|
+
* than a plain string — an app whose locale is its own, not the system's,
|
|
945
|
+
* cannot express that with a value frozen at configure time.
|
|
946
|
+
*
|
|
947
|
+
* One string for every section by design — the row is a fallback for a
|
|
948
|
+
* surface that cannot draw a link in its header, so per-section wording would
|
|
949
|
+
* be ceremony most consumers never see rendered.
|
|
950
|
+
*
|
|
951
|
+
* For wording that varies by section — "View all 42 albums", "See every
|
|
952
|
+
* episode" — author the link yourself instead: append an ordinary browsable
|
|
953
|
+
* {@link Track} to the section's `children`, titled however you like, and
|
|
954
|
+
* leave `Section.path` unset so this row doesn't also appear. Since the
|
|
955
|
+
* row is Android-only, that branch is an ordinary platform check on the
|
|
956
|
+
* content you emit; CarPlay never sees the extra row.
|
|
957
|
+
*
|
|
958
|
+
* @default 'View more'
|
|
959
|
+
*
|
|
960
|
+
* @example
|
|
961
|
+
* ```typescript
|
|
962
|
+
* viewMoreTitle: () => t('View more')
|
|
963
|
+
* ```
|
|
964
|
+
*
|
|
965
|
+
* @platform android
|
|
966
|
+
*/
|
|
967
|
+
viewMoreTitle?: ViewMoreTitleCallback
|
|
968
|
+
|
|
934
969
|
/**
|
|
935
970
|
* Title shown (as the list's centered empty state) on CarPlay screens whose
|
|
936
971
|
* content is still loading — browse destinations while they resolve, and the
|
|
@@ -1093,3 +1128,6 @@ export type FormatNavigationErrorCallback = (
|
|
|
1093
1128
|
* or `undefined` to do nothing. See `resolveAlbumPath`.
|
|
1094
1129
|
*/
|
|
1095
1130
|
export type ResolveAlbumPathCallback = (track: Track) => string | undefined
|
|
1131
|
+
|
|
1132
|
+
/** Supplies the title of the appended "view more" row. See `viewMoreTitle`. */
|
|
1133
|
+
export type ViewMoreTitleCallback = () => string
|
|
@@ -66,7 +66,7 @@ Android Auto reads a page's layout from the **parent item** that opens it — be
|
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
- **`style.display` on the parent item** — `'list'` (rows) or `'grid'` (tiles). Below the root this is the only layout signal Android Auto honors, and it is only sent when you declare it — leave it off and the page renders as a list. In dev mode the library warns when an all-grid page is opened from a parent item with no declared `display`. (A section's "view all" page needs no declaration of its own: it reuses the section's `style`.)
|
|
69
|
-
- **Sections** — declare `sections` on the page to group its tracks under headers; each [`Section`](/api/types/browser-nodes/#section)'s `title` renders as a header above its children
|
|
69
|
+
- **Sections** — declare `sections` on the page to group its tracks under headers; each [`Section`](/api/types/browser-nodes/#section)'s `title` renders as a header above its children. A section that carries a `path` also gets a browsable "view more" row appended after its children, navigating there. No header is tappable on Android Auto at any `display`, so the row is the only affordance the section's `path` has here; its title comes from the [`viewMoreTitle`](/api/types/browser/#viewmoretitle) browser option, defaulting to `"View more"` — one wording for every section. For per-section copy (`"View all 42 albums"`), leave `path` unset and append your own browsable `Track` to `children` instead, titled however you like; this page's content is Android-only anyway, so CarPlay never sees it.
|
|
70
70
|
|
|
71
71
|
::: warning AAOS lays out the whole page from the parent item's `display`
|
|
72
72
|
On AOSP-based Android Automotive units — including mainstream cars' OEM skins — the parent item's `display` is the **only** below-root layout signal honored: the whole page renders as list or grid, uniformly. Per-section `display` differences (a single-line grid above a list, say) render on CarPlay and projected Android Auto, but an AAOS unit shows the page in the single declared layout. There is no way to describe a mixed page there — pick the layout that suits its dominant section, or split the content across pages. `gridWrap: false` also has no single-line container there: the grid wraps, showing more, never less.
|
package/website/guide/browser.md
CHANGED
|
@@ -76,7 +76,7 @@ A **page** is a [`ResolvedTrack`](/api/types/browser-nodes/#resolvedtrack): it r
|
|
|
76
76
|
}
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
A Section is `{ title?,
|
|
79
|
+
A Section is `{ title?, style?, path?, children }`: `title` renders as a header above its tracks, `style` is a declaration block whose `display` picks the children's layout (`'list'` rows — the default — or `'grid'` artwork tiles; `gridWrap: false` keeps the grid to a single line), and `path` is the navigation target for the section's "view all" affordance. See [Presentation](#presentation) for how each surface renders the declarations.
|
|
80
80
|
|
|
81
81
|
A flat page doesn't have to spell that out: authoring a plain `children: Track[]` — as most examples in this guide do — is sugar for one untitled section. Resolved output always carries `sections`; `children` is never populated on a resolved page.
|
|
82
82
|
|
|
@@ -512,24 +512,24 @@ The `code` values: `content-not-found`, `network-error`, `http-error`, `callback
|
|
|
512
512
|
|
|
513
513
|
Presentation lives in `style` blocks, separate from content, plus a few Track fields and config options. Each surface applies the style properties it supports and ignores the rest — an unsupported property is a no-op, never an error.
|
|
514
514
|
|
|
515
|
-
| Field | On | Effect
|
|
516
|
-
| ------------------------------------------------ | ------------------------ |
|
|
517
|
-
| `title` | a section | header above its tracks
|
|
518
|
-
| `style: { display: 'grid' }` | a section | wrapping artwork-tile grid
|
|
519
|
-
| `style: { display: 'grid', gridWrap: false }` | a section | a single line of artwork tiles
|
|
520
|
-
| `path`
|
|
521
|
-
| `style` (same block) | a page | declaration for the whole page — a section overrides it for its own children
|
|
522
|
-
| `style: { display: 'grid' }` | a browsable track | the layout of the page it opens — Android Auto reads it from the parent item
|
|
523
|
-
| `style: { artworkRendering: 'stencil' }` | a track / section / page | tint monochrome artwork to the surface appearance
|
|
524
|
-
| `style: { imageShape: 'circular' }` | a track / section / page | crop grid-tile artwork circular (people) instead of rounded
|
|
525
|
-
| `style: { accessorySymbol: 'lock.fill' }` | a track / section / page | SF Symbol accessory on rows and tiles (`'none'` restores the derived one)
|
|
526
|
-
| `style: { gridTile: 'card' }` | a section / page | the grid's tile kind: `'plain'` (default), `'card'`, or `'condensed'`
|
|
527
|
-
| `style: { cardTint: '#1e3a8a' }` (+ `cardImage`) | a track / section / page | card color and image mode (`'background'` fills the card full-height)
|
|
528
|
-
| `disabled: true` | a track | unavailable: never plays; grayed where drawable, hidden elsewhere
|
|
529
|
-
| `artwork: 'sf:heart.fill'` | any item | SF Symbol icon (supports `?bg=…&fg=…`)
|
|
530
|
-
| `live: true` | a track | live indicator
|
|
531
|
-
| `carPlaySiriListButton: 'top' \| 'bottom'` | a page | place the Siri cell on the page
|
|
532
|
-
| `albumPath` + `resolveAlbumPath` | track + config | make the now-playing album line tappable
|
|
515
|
+
| Field | On | Effect | Platform |
|
|
516
|
+
| ------------------------------------------------ | ------------------------ | --------------------------------------------------------------------------------- | ------------------- |
|
|
517
|
+
| `title` | a section | header above its tracks | all |
|
|
518
|
+
| `style: { display: 'grid' }` | a section | wrapping artwork-tile grid | all\* |
|
|
519
|
+
| `style: { display: 'grid', gridWrap: false }` | a section | a single line of artwork tiles | all\* |
|
|
520
|
+
| `path` | a section | "view all" target — a tappable header on CarPlay, an appended row on Android Auto | all |
|
|
521
|
+
| `style` (same block) | a page | declaration for the whole page — a section overrides it for its own children | all |
|
|
522
|
+
| `style: { display: 'grid' }` | a browsable track | the layout of the page it opens — Android Auto reads it from the parent item | Android Auto / AAOS |
|
|
523
|
+
| `style: { artworkRendering: 'stencil' }` | a track / section / page | tint monochrome artwork to the surface appearance | CarPlay |
|
|
524
|
+
| `style: { imageShape: 'circular' }` | a track / section / page | crop grid-tile artwork circular (people) instead of rounded | CarPlay (iOS 26+) |
|
|
525
|
+
| `style: { accessorySymbol: 'lock.fill' }` | a track / section / page | SF Symbol accessory on rows and tiles (`'none'` restores the derived one) | CarPlay |
|
|
526
|
+
| `style: { gridTile: 'card' }` | a section / page | the grid's tile kind: `'plain'` (default), `'card'`, or `'condensed'` | CarPlay (iOS 26+) |
|
|
527
|
+
| `style: { cardTint: '#1e3a8a' }` (+ `cardImage`) | a track / section / page | card color and image mode (`'background'` fills the card full-height) | CarPlay (iOS 26+) |
|
|
528
|
+
| `disabled: true` | a track | unavailable: never plays; grayed where drawable, hidden elsewhere | all |
|
|
529
|
+
| `artwork: 'sf:heart.fill'` | any item | SF Symbol icon (supports `?bg=…&fg=…`) | iOS |
|
|
530
|
+
| `live: true` | a track | live indicator | iOS |
|
|
531
|
+
| `carPlaySiriListButton: 'top' \| 'bottom'` | a page | place the Siri cell on the page | iOS |
|
|
532
|
+
| `albumPath` + `resolveAlbumPath` | track + config | make the now-playing album line tappable | CarPlay |
|
|
533
533
|
|
|
534
534
|
::: info Caveats from the table
|
|
535
535
|
**`albumPath` requires `album`** — CarPlay renders the tappable line from the album metadata, so without an `album` there is no line to tap. **\*Layouts render each platform's nearest supported form** — a single-line grid (`gridWrap: false`) on CarPlay shows the tiles that fit (roughly eight, width-dependent) and truncates the rest; a wrapping grid takes as many lines as it needs on iOS 26+ and renders as a list before that. Android Auto's grid always wraps, so it shows more, never less. No car surface scrolls horizontally; an app UI typically renders `gridWrap: false` as a horizontal scroller. Truncation is visual only: tapping a tile queues the whole section (see [Playback behavior](#playback-behavior)). Item properties like `artworkRendering`, `imageShape`, and `accessorySymbol` are inherited — `track ?? section ?? page`, per property — so a section-wide value can be overridden per item; `accessorySymbol: 'none'` is the per-item escape from an inherited symbol, and where the accessory draws is the platform's choice per surface (trailing on list rows, leading the title on tiles). `display` is never inherited: it always describes the children of whatever it's set on. On a page or section it lays out that page or section; on a browsable track it declares the layout of the page that track opens — Android Auto needs it there, or that page renders as a list. `gridWrap` is inert unless `display` is `'grid'`. One AAOS caveat: AOSP-based Automotive units honor only the parent item's `display`, so a page mixing grid and list sections renders uniformly there — see [Android Auto → Browse display](/guide/android-auto#browse-display).
|