diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 15b736b08..834af91d8 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1 +1,2 @@
* @DroidKaigi/ConferenceApp2023Reviewer
+/app-ios/ @DroidKaigi/appios
diff --git a/.github/workflows/UnitTest.yml b/.github/workflows/UnitTest.yml
index efe0f18a8..68c268e27 100644
--- a/.github/workflows/UnitTest.yml
+++ b/.github/workflows/UnitTest.yml
@@ -42,7 +42,7 @@ jobs:
- run: ./gradlew testDevDebugUnitTest testDebugUnitTest --stacktrace
- - run: ./gradlew koverHtmlReportDebug koverHtmlReportDevDebug --stacktrace
+ - run: ./gradlew koverHtmlReportDevDebug --stacktrace
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
if: ${{ always() }}
diff --git a/app-android/build.gradle.kts b/app-android/build.gradle.kts
index 690ce4d15..46b004faf 100644
--- a/app-android/build.gradle.kts
+++ b/app-android/build.gradle.kts
@@ -100,3 +100,25 @@ dependencies {
implementation(libs.kermit)
testImplementation(projects.core.testing)
}
+
+// Dependency configuration to aggregate Kover coverage reports
+// TODO: extract report aggregation to build-logic
+dependencies {
+ kover(projects.appIosShared)
+
+ kover(projects.feature.about)
+ kover(projects.feature.contributors)
+ kover(projects.feature.floorMap)
+ kover(projects.feature.main)
+ kover(projects.feature.sessions)
+ kover(projects.feature.sponsors)
+ kover(projects.feature.staff)
+ kover(projects.feature.stamps)
+
+ kover(projects.core.common)
+ kover(projects.core.data)
+ kover(projects.core.designsystem)
+ kover(projects.core.model)
+ kover(projects.core.testing)
+ kover(projects.core.ui)
+}
diff --git a/app-ios/App/DroidKaigi2023/DroidKaigi2023.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/app-ios/App/DroidKaigi2023/DroidKaigi2023.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
index 705fe5e7e..5fe23d9ce 100644
--- a/app-ios/App/DroidKaigi2023/DroidKaigi2023.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/app-ios/App/DroidKaigi2023/DroidKaigi2023.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -36,22 +36,13 @@
"version" : "1.7.2"
}
},
- {
- "identity" : "licenselist",
- "kind" : "remoteSourceControl",
- "location" : "https://github.com/cybozu/LicenseList",
- "state" : {
- "revision" : "4cbf80b821167042e562639dd875c3b81e8ba1c7",
- "version" : "0.2.1"
- }
- },
{
"identity" : "firebase-ios-sdk",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk",
"state" : {
- "revision" : "df2171b0c6afb9e9d4f7e07669d558c510b9f6be",
- "version" : "10.13.0"
+ "revision" : "2bfe6abe1014aafe5cf28401708f7d39f9926a76",
+ "version" : "10.14.0"
}
},
{
@@ -99,6 +90,15 @@
"version" : "3.1.1"
}
},
+ {
+ "identity" : "interop-ios-for-google-sdks",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/interop-ios-for-google-sdks.git",
+ "state" : {
+ "revision" : "2d12673670417654f08f5f90fdd62926dc3a2648",
+ "version" : "100.0.0"
+ }
+ },
{
"identity" : "leveldb",
"kind" : "remoteSourceControl",
@@ -108,6 +108,15 @@
"version" : "1.22.2"
}
},
+ {
+ "identity" : "licenselist",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/cybozu/LicenseList",
+ "state" : {
+ "revision" : "4cbf80b821167042e562639dd875c3b81e8ba1c7",
+ "version" : "0.2.1"
+ }
+ },
{
"identity" : "nanopb",
"kind" : "remoteSourceControl",
@@ -149,8 +158,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser.git",
"state" : {
- "revision" : "fee6933f37fde9a5e12a1e4aeaa93fe60116ff2a",
- "version" : "1.2.2"
+ "revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531",
+ "version" : "1.2.3"
}
},
{
diff --git a/app-ios/App/DroidKaigi2023/license-list.plist b/app-ios/App/DroidKaigi2023/license-list.plist
index d2525adb9..bb5e4d0b2 100644
--- a/app-ios/App/DroidKaigi2023/license-list.plist
+++ b/app-ios/App/DroidKaigi2023/license-list.plist
@@ -1790,6 +1790,214 @@ Fuller's blog</a>
name
GTMSessionFetcher
+
+ licenseBody
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ name
+ InteropForGoogle
+
licenseBody
Copyright (c) 2011 The LevelDB Authors. All rights reserved.
diff --git a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/badge.imageset/badge.pdf b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/badge.imageset/badge.pdf
deleted file mode 100644
index 6d19d8dbd..000000000
Binary files a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/badge.imageset/badge.pdf and /dev/null differ
diff --git a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/map.imageset/Contents.json b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/floor_map.imageset/Contents.json
similarity index 77%
rename from app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/map.imageset/Contents.json
rename to app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/floor_map.imageset/Contents.json
index 60ad78a00..50a6edc76 100644
--- a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/map.imageset/Contents.json
+++ b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/floor_map.imageset/Contents.json
@@ -1,7 +1,7 @@
{
"images" : [
{
- "filename" : "map.pdf",
+ "filename" : "floormap.pdf",
"idiom" : "universal"
}
],
diff --git a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/floor_map.imageset/floormap.pdf b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/floor_map.imageset/floormap.pdf
new file mode 100644
index 000000000..de30cbd6b
Binary files /dev/null and b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/floor_map.imageset/floormap.pdf differ
diff --git a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/floor_map_fill_off.imageset/Contents.json b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/floor_map_fill_off.imageset/Contents.json
new file mode 100644
index 000000000..14768b2fb
--- /dev/null
+++ b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/floor_map_fill_off.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "floormap_fill_off.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/map.imageset/map.pdf b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/floor_map_fill_off.imageset/floormap_fill_off.pdf
similarity index 100%
rename from app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/map.imageset/map.pdf
rename to app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/floor_map_fill_off.imageset/floormap_fill_off.pdf
diff --git a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/info.imageset/info.pdf b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/info.imageset/info.pdf
index 8fabe60d0..9f6382084 100644
Binary files a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/info.imageset/info.pdf and b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/info.imageset/info.pdf differ
diff --git a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/info_fill_off.imageset/Contents.json b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/info_fill_off.imageset/Contents.json
new file mode 100644
index 000000000..78cd4a83f
--- /dev/null
+++ b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/info_fill_off.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "info_fill_off.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/info_fill_off.imageset/info_fill_off.pdf b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/info_fill_off.imageset/info_fill_off.pdf
new file mode 100644
index 000000000..8fabe60d0
Binary files /dev/null and b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/info_fill_off.imageset/info_fill_off.pdf differ
diff --git a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/badge.imageset/Contents.json b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/stamp.imageset/Contents.json
similarity index 79%
rename from app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/badge.imageset/Contents.json
rename to app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/stamp.imageset/Contents.json
index 9f3083259..798efe483 100644
--- a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/badge.imageset/Contents.json
+++ b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/stamp.imageset/Contents.json
@@ -1,7 +1,7 @@
{
"images" : [
{
- "filename" : "badge.pdf",
+ "filename" : "stamp.pdf",
"idiom" : "universal"
}
],
diff --git a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/stamp.imageset/stamp.pdf b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/stamp.imageset/stamp.pdf
new file mode 100644
index 000000000..c514bf0dc
Binary files /dev/null and b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/stamp.imageset/stamp.pdf differ
diff --git a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/stamp_fill_off.imageset/Contents.json b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/stamp_fill_off.imageset/Contents.json
new file mode 100644
index 000000000..f27e6e3d3
--- /dev/null
+++ b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/stamp_fill_off.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "stamp_fill_off.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/stamp_fill_off.imageset/stamp_fill_off.pdf b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/stamp_fill_off.imageset/stamp_fill_off.pdf
new file mode 100644
index 000000000..2e14167d6
Binary files /dev/null and b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/stamp_fill_off.imageset/stamp_fill_off.pdf differ
diff --git a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/timetable_fill_off.imageset/Contents.json b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/timetable_fill_off.imageset/Contents.json
new file mode 100644
index 000000000..76bbf04b8
--- /dev/null
+++ b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/timetable_fill_off.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "timetable_fill_off.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/timetable_fill_off.imageset/timetable_fill_off.pdf b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/timetable_fill_off.imageset/timetable_fill_off.pdf
new file mode 100644
index 000000000..8f284fc37
Binary files /dev/null and b/app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/timetable_fill_off.imageset/timetable_fill_off.pdf differ
diff --git a/app-ios/Modules/Sources/Model/TimetableTimeGroupItems.swift b/app-ios/Modules/Sources/Model/TimetableTimeGroupItems.swift
index ccc9c6a77..c2542bc88 100644
--- a/app-ios/Modules/Sources/Model/TimetableTimeGroupItems.swift
+++ b/app-ios/Modules/Sources/Model/TimetableTimeGroupItems.swift
@@ -1,36 +1,17 @@
import shared
public struct TimetableTimeGroupItems: Identifiable, Equatable, Hashable {
- public struct Duration: Hashable {
- public let startsAt: Kotlinx_datetimeInstant
- public let endsAt: Kotlinx_datetimeInstant
-
- var minute: Int {
- let startMinute = Int(startsAt.epochSeconds) / 60
- let endMinute = Int(endsAt.epochSeconds) / 60
-
- return endMinute - startMinute
- }
-
- public init(startsAt: Kotlinx_datetimeInstant, endsAt: Kotlinx_datetimeInstant) {
- self.startsAt = startsAt
- self.endsAt = endsAt
- }
- }
-
public var id: String {
UUID().uuidString
}
- public var startsAt: Date
- public var endsAt: Date
- public var minute: Int
+ public var startsTimeString: String
+ public var endsTimeString: String
public var items: [TimetableItemWithFavorite]
- public init(duration: Duration, items: [TimetableItemWithFavorite]) {
- self.startsAt = duration.startsAt.toDate()
- self.endsAt = duration.endsAt.toDate()
- self.minute = duration.minute
+ public init(startsTimeString: String, endsTimeString: String, items: [TimetableItemWithFavorite]) {
+ self.startsTimeString = startsTimeString
+ self.endsTimeString = endsTimeString
self.items = items
}
}
diff --git a/app-ios/Modules/Sources/Navigation/RootView.swift b/app-ios/Modules/Sources/Navigation/RootView.swift
index 25efc1d74..60decf25d 100644
--- a/app-ios/Modules/Sources/Navigation/RootView.swift
+++ b/app-ios/Modules/Sources/Navigation/RootView.swift
@@ -34,8 +34,13 @@ public struct RootView: View {
Label {
Text("Timetable")
} icon: {
- Assets.Icons.timetable.swiftUIImage
- .renderingMode(.template)
+ if selection == .timeline {
+ Assets.Icons.timetable.swiftUIImage
+ .renderingMode(.template)
+ } else {
+ Assets.Icons.timetableFillOff.swiftUIImage
+ .renderingMode(.template)
+ }
}
}
FloorMapView()
@@ -44,8 +49,13 @@ public struct RootView: View {
Label {
Text("FloorMap")
} icon: {
- Assets.Icons.map.swiftUIImage
- .renderingMode(.template)
+ if selection == .floorMap {
+ Assets.Icons.floorMap.swiftUIImage
+ .renderingMode(.template)
+ } else {
+ Assets.Icons.floorMapFillOff.swiftUIImage
+ .renderingMode(.template)
+ }
}
}
StampsView()
@@ -54,8 +64,13 @@ public struct RootView: View {
Label {
Text("Stamps")
} icon: {
- Assets.Icons.badge.swiftUIImage
- .renderingMode(.template)
+ if selection == .stamps {
+ Assets.Icons.stamp.swiftUIImage
+ .renderingMode(.template)
+ } else {
+ Assets.Icons.stampFillOff.swiftUIImage
+ .renderingMode(.template)
+ }
}
}
AboutView(
@@ -74,8 +89,13 @@ public struct RootView: View {
Label {
Text("About")
} icon: {
- Assets.Icons.info.swiftUIImage
- .renderingMode(.template)
+ if selection == .about {
+ Assets.Icons.info.swiftUIImage
+ .renderingMode(.template)
+ } else {
+ Assets.Icons.infoFillOff.swiftUIImage
+ .renderingMode(.template)
+ }
}
}
}
diff --git a/app-ios/Modules/Sources/Stamps/StampsView.swift b/app-ios/Modules/Sources/Stamps/StampsView.swift
index 9001cc5df..0a9a47764 100644
--- a/app-ios/Modules/Sources/Stamps/StampsView.swift
+++ b/app-ios/Modules/Sources/Stamps/StampsView.swift
@@ -23,7 +23,7 @@ public struct StampsView: View {
.padding(16)
}
.background(AssetColors.Surface.surface.swiftUIColor)
- .navigationTitle("Badges")
+ .navigationTitle("Stamps")
}
}
}
diff --git a/app-ios/Modules/Sources/Timetable/Bookmark/BookmarkViewModel.swift b/app-ios/Modules/Sources/Timetable/Bookmark/BookmarkViewModel.swift
index b9d4bb894..e911e4520 100644
--- a/app-ios/Modules/Sources/Timetable/Bookmark/BookmarkViewModel.swift
+++ b/app-ios/Modules/Sources/Timetable/Bookmark/BookmarkViewModel.swift
@@ -44,19 +44,17 @@ final class BookmarkViewModel: ObservableObject {
} ?? cachedTimetable.contents
let timetableTimeGroupItems = timetableContents
// .filter { cachedTimetable.bookmarks.contains($0.timetableItem.id) }
- .map {
- TimetableTimeGroupItems.Duration(startsAt: $0.timetableItem.startsAt, endsAt: $0.timetableItem.endsAt)
- }
- .map { duration in
+ .map { content in
let items = timetableContents
.filter { itemWithFavorite in
- itemWithFavorite.timetableItem.startsAt == duration.startsAt && itemWithFavorite.timetableItem.endsAt == duration.endsAt
+ itemWithFavorite.timetableItem.startsTimeString == content.timetableItem.startsTimeString && itemWithFavorite.timetableItem.endsTimeString == content.timetableItem.endsTimeString
}
.sorted {
$0.timetableItem.room.name.currentLangTitle < $1.timetableItem.room.name.currentLangTitle
}
return TimetableTimeGroupItems(
- duration: duration,
+ startsTimeString: content.timetableItem.startsTimeString,
+ endsTimeString: content.timetableItem.endsTimeString,
items: items
)
}
diff --git a/app-ios/Modules/Sources/Timetable/Search/SearchViewModel.swift b/app-ios/Modules/Sources/Timetable/Search/SearchViewModel.swift
index b0983e66e..9faa084e4 100644
--- a/app-ios/Modules/Sources/Timetable/Search/SearchViewModel.swift
+++ b/app-ios/Modules/Sources/Timetable/Search/SearchViewModel.swift
@@ -58,19 +58,17 @@ final class SearchViewModel: ObservableObject {
}
let timetableContents = cachedTimetable.filtered(filters: state.filters).contents
let timetableTimeGroupItems = timetableContents
- .map {
- TimetableTimeGroupItems.Duration(startsAt: $0.timetableItem.startsAt, endsAt: $0.timetableItem.endsAt)
- }
- .map { duration in
+ .map { content in
let items = timetableContents
.filter { itemWithFavorite in
- itemWithFavorite.timetableItem.startsAt == duration.startsAt && itemWithFavorite.timetableItem.endsAt == duration.endsAt
+ itemWithFavorite.timetableItem.startsTimeString == content.timetableItem.startsTimeString && itemWithFavorite.timetableItem.endsTimeString == content.timetableItem.endsTimeString
}
.sorted {
$0.timetableItem.room.name.currentLangTitle < $1.timetableItem.room.name.currentLangTitle
}
return TimetableTimeGroupItems(
- duration: duration,
+ startsTimeString: content.timetableItem.startsTimeString,
+ endsTimeString: content.timetableItem.endsTimeString,
items: items
)
}
diff --git a/app-ios/Modules/Sources/Timetable/SessionTimeView.swift b/app-ios/Modules/Sources/Timetable/SessionTimeView.swift
index cdf77ebc4..c2f4f1b1a 100644
--- a/app-ios/Modules/Sources/Timetable/SessionTimeView.swift
+++ b/app-ios/Modules/Sources/Timetable/SessionTimeView.swift
@@ -1,27 +1,21 @@
+import Model
import SwiftUI
import Theme
-private let formatter: DateFormatter = {
- let formatter = DateFormatter()
- formatter.dateStyle = .none
- formatter.timeStyle = .short
- return formatter
-}()
-
struct SessionTimeView: View {
- var startsAt: Date
- var endsAt: Date
+ var startsTimeString: String
+ var endsTimeString: String
var body: some View {
VStack(spacing: 4) {
- Text(formatter.string(from: startsAt))
+ Text(startsTimeString)
.foregroundStyle(AssetColors.Surface.onSurface.swiftUIColor)
.font(Font(UIFont.systemFont(ofSize: 16, weight: .bold)))
.frame(height: 24)
Rectangle()
.foregroundColor(AssetColors.Outline.outlineVariant.swiftUIColor)
.frame(width: 2, height: 8)
- Text(formatter.string(from: endsAt))
+ Text(endsTimeString)
.foregroundStyle(AssetColors.Secondary.secondary.swiftUIColor)
.font(Font(UIFont.systemFont(ofSize: 16, weight: .bold)))
.frame(height: 24)
@@ -29,6 +23,14 @@ struct SessionTimeView: View {
}
}
- #Preview {
- SessionTimeView(startsAt: .distantPast, endsAt: .distantFuture)
- }
+#if DEBUG
+import shared
+
+#Preview {
+ SessionTimeView(
+ startsTimeString: Timetable.companion.fake().contents.first!.timetableItem.startsTimeString,
+ endsTimeString: Timetable.companion.fake().contents.first!.timetableItem.endsTimeString
+ )
+}
+
+#endif
diff --git a/app-ios/Modules/Sources/Timetable/TimetableListItemView.swift b/app-ios/Modules/Sources/Timetable/TimetableListItemView.swift
index 055a80a7c..ecb9d92c7 100644
--- a/app-ios/Modules/Sources/Timetable/TimetableListItemView.swift
+++ b/app-ios/Modules/Sources/Timetable/TimetableListItemView.swift
@@ -58,6 +58,7 @@ struct TimetableListItemView: View {
}
Spacer().frame(height: 16)
}
+ .frame(maxWidth: .infinity, alignment: .leading)
Button(
action: {
// TODO: favorite action
diff --git a/app-ios/Modules/Sources/Timetable/TimetableListView.swift b/app-ios/Modules/Sources/Timetable/TimetableListView.swift
index 3a72386cb..c1aadd04f 100644
--- a/app-ios/Modules/Sources/Timetable/TimetableListView.swift
+++ b/app-ios/Modules/Sources/Timetable/TimetableListView.swift
@@ -9,8 +9,8 @@ struct TimetableListView: View {
ForEach(timetableTimeGroupItems) { timetableTimeGroupItem in
HStack(alignment: .top, spacing: 16) {
SessionTimeView(
- startsAt: timetableTimeGroupItem.startsAt,
- endsAt: timetableTimeGroupItem.endsAt
+ startsTimeString: timetableTimeGroupItem.startsTimeString,
+ endsTimeString: timetableTimeGroupItem.endsTimeString
)
VStack(spacing: 0) {
ForEach(timetableTimeGroupItem.items, id: \.timetableItem.id.value) { timetableItemWithFavorite in
@@ -37,10 +37,8 @@ import shared
TimetableListView(
timetableTimeGroupItems: [
TimetableTimeGroupItems(
- duration: .init(
- startsAt: Timetable.companion.fake().contents.first!.timetableItem.startsAt,
- endsAt: Timetable.companion.fake().contents.first!.timetableItem.endsAt
- ),
+ startsTimeString: Timetable.companion.fake().contents.first!.timetableItem.startsTimeString,
+ endsTimeString: Timetable.companion.fake().contents.first!.timetableItem.endsTimeString,
items: [Timetable.companion.fake().contents.first!]
),
]
diff --git a/app-ios/Modules/Sources/Timetable/TimetableViewModel.swift b/app-ios/Modules/Sources/Timetable/TimetableViewModel.swift
index 7bd10c720..133a2be42 100644
--- a/app-ios/Modules/Sources/Timetable/TimetableViewModel.swift
+++ b/app-ios/Modules/Sources/Timetable/TimetableViewModel.swift
@@ -41,19 +41,17 @@ final class TimetableViewModel: ObservableObject {
}
let timetableTimeGroupItems = cachedTimetable.dayTimetable(droidKaigi2023Day: state.selectedDay)
.timetableItems
- .map {
- TimetableTimeGroupItems.Duration(startsAt: $0.startsAt, endsAt: $0.endsAt)
- }
- .map { duration in
+ .map { item in
let items = cachedTimetable.contents
.filter { itemWithFavorite in
- itemWithFavorite.timetableItem.startsAt == duration.startsAt && itemWithFavorite.timetableItem.endsAt == duration.endsAt
+ itemWithFavorite.timetableItem.startsTimeString == item.startsTimeString && itemWithFavorite.timetableItem.endsTimeString == item.endsTimeString
}
.sorted {
$0.timetableItem.room.name.currentLangTitle < $1.timetableItem.room.name.currentLangTitle
}
return TimetableTimeGroupItems(
- duration: duration,
+ startsTimeString: item.startsTimeString,
+ endsTimeString: item.endsTimeString,
items: items
)
}
diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched2023/primitive/AndroidApplicationPlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched2023/primitive/AndroidApplicationPlugin.kt
index 1a9e28a7a..883ad5c6e 100644
--- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched2023/primitive/AndroidApplicationPlugin.kt
+++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched2023/primitive/AndroidApplicationPlugin.kt
@@ -1,10 +1,7 @@
package io.github.droidkaigi.confsched2023.primitive
-import org.gradle.api.JavaVersion
import org.gradle.api.Plugin
import org.gradle.api.Project
-import org.gradle.jvm.toolchain.JavaLanguageVersion
-import org.gradle.kotlin.dsl.dependencies
@Suppress("unused")
class AndroidApplicationPlugin : Plugin {
diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched2023/primitive/AndroidGradleDsl.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched2023/primitive/AndroidGradleDsl.kt
index 986a480fa..b1d793fa3 100644
--- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched2023/primitive/AndroidGradleDsl.kt
+++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched2023/primitive/AndroidGradleDsl.kt
@@ -73,7 +73,7 @@ fun Project.setupDetekt(extension: DetektExtension) {
// parallel processing
parallel = true
// detekt configuration file
- config = files("${project.rootDir}/config/detekt/detekt.yml")
+ config.setFrom("${project.rootDir}/config/detekt/detekt.yml")
// baseline configuration file
baseline = file("${project.rootDir}/config/detekt/baseline.xml")
// apply your own configuration file on top of the default settings
diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched2023/primitive/KoverPlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched2023/primitive/KoverPlugin.kt
index bce450344..1acf05b93 100644
--- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched2023/primitive/KoverPlugin.kt
+++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched2023/primitive/KoverPlugin.kt
@@ -1,12 +1,35 @@
package io.github.droidkaigi.confsched2023.primitive
+import kotlinx.kover.gradle.plugin.dsl.KoverReportExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
+import org.gradle.kotlin.dsl.configure
class KoverPlugin : Plugin {
override fun apply(target: Project) {
- with (target) {
+ with(target) {
pluginManager.apply("org.jetbrains.kotlinx.kover")
+
+ configure {
+ filters {
+ excludes {
+ packages(
+ "com.airbnb.android.showkase",
+ "dagger.hilt.*",
+ "hilt_aggregated_deps",
+ )
+ classes(
+ // classes generated by Hilt
+ "Hilt_*",
+ "*_Factory",
+ "*_HiltModules*",
+ "*Module_Provide*Factory",
+ // compose previews
+ "*Preview*Kt",
+ )
+ }
+ }
+ }
}
}
-}
\ No newline at end of file
+}
diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml
index 619601f49..1af8c5358 100644
--- a/config/detekt/detekt.yml
+++ b/config/detekt/detekt.yml
@@ -61,7 +61,7 @@ style:
ClassOrdering:
active: true
# Multi-line if statements force curly braces
- MandatoryBracesIfStatements:
+ BracesOnIfStatements:
active: true
# Multiline for, while... forces curly braces
MandatoryBracesLoops:
diff --git a/core/designsystem/src/androidMain/kotlin/io/github/droidkaigi/confsched2023/designsystem/preview/Annotations.kt b/core/designsystem/src/androidMain/kotlin/io/github/droidkaigi/confsched2023/designsystem/preview/Annotations.kt
index 3993485ea..44acb7abb 100644
--- a/core/designsystem/src/androidMain/kotlin/io/github/droidkaigi/confsched2023/designsystem/preview/Annotations.kt
+++ b/core/designsystem/src/androidMain/kotlin/io/github/droidkaigi/confsched2023/designsystem/preview/Annotations.kt
@@ -60,25 +60,29 @@ object MultiLanguagePreviewDefinition {
object Japanese {
const val Name = "Japanese"
const val Locale = "ja_JP"
+ const val Language = "ja"
}
object English {
const val Name = "English"
const val Locale = "en_US"
+ const val Language = "en"
}
}
/**
* Annotation for previewing multiple languages.
+ *
+ * Note: locale param need to follow [locale qualifier](https://developer.android.com/guide/topics/resources/providing-resources#LocaleQualifier).
*/
@Preview(
name = MultiLanguagePreviewDefinition.Japanese.Name,
group = MultiLanguagePreviewDefinition.Group,
- locale = MultiLanguagePreviewDefinition.Japanese.Locale,
+ locale = MultiLanguagePreviewDefinition.Japanese.Language,
)
@Preview(
name = MultiLanguagePreviewDefinition.English.Name,
group = MultiLanguagePreviewDefinition.Group,
- locale = MultiLanguagePreviewDefinition.English.Locale,
+ locale = MultiLanguagePreviewDefinition.English.Language,
)
annotation class MultiLanguagePreviews
diff --git a/core/testing/build.gradle.kts b/core/testing/build.gradle.kts
index 3cb39d1b8..e4d7ceee3 100644
--- a/core/testing/build.gradle.kts
+++ b/core/testing/build.gradle.kts
@@ -10,16 +10,16 @@ plugins {
android.namespace = "io.github.droidkaigi.confsched2023.core.testing"
dependencies {
- implementation(project(":core:model"))
- implementation(project(":core:designsystem"))
- implementation(project(":core:data"))
- implementation(project(":feature:main"))
- implementation(project(":feature:sessions"))
- implementation(project(":feature:about"))
- implementation(project(":feature:sponsors"))
- implementation(project(":feature:floor-map"))
- implementation(project(":feature:stamps"))
- implementation(project(":feature:staff"))
+ implementation(projects.core.model)
+ implementation(projects.core.designsystem)
+ implementation(projects.core.data)
+ implementation(projects.feature.main)
+ implementation(projects.feature.sessions)
+ implementation(projects.feature.about)
+ implementation(projects.feature.sponsors)
+ implementation(projects.feature.floorMap)
+ implementation(projects.feature.stamps)
+ implementation(projects.feature.staff)
implementation(libs.daggerHiltAndroidTesting)
implementation(libs.roborazzi)
implementation(libs.kermit)
diff --git a/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/RobotTestRule.kt b/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/RobotTestRule.kt
index 94e7352b9..9ef4ca353 100644
--- a/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/RobotTestRule.kt
+++ b/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/RobotTestRule.kt
@@ -60,6 +60,7 @@ class RobotTestRule(
return RuleChain
.outerRule(HiltAndroidAutoInjectRule(testInstance))
.around(CoroutinesTestRule())
+ .around(TimeZoneTestRule())
.around(object : TestWatcher() {
override fun starting(description: Description) {
// To see logs in the console
diff --git a/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/TimeZoneTestRule.kt b/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/TimeZoneTestRule.kt
new file mode 100644
index 000000000..1510bf47d
--- /dev/null
+++ b/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/TimeZoneTestRule.kt
@@ -0,0 +1,18 @@
+package io.github.droidkaigi.confsched2023.testing
+
+import org.junit.rules.TestWatcher
+import org.junit.runner.Description
+import java.util.TimeZone
+
+class TimeZoneTestRule : TestWatcher() {
+ private lateinit var evacuatedTimeZone: TimeZone
+
+ override fun starting(description: Description) {
+ evacuatedTimeZone = TimeZone.getDefault()
+ TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo"))
+ }
+
+ override fun finished(description: Description) {
+ TimeZone.setDefault(evacuatedTimeZone)
+ }
+}
diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts
index 64e2c98a8..b39cafcb9 100644
--- a/core/ui/build.gradle.kts
+++ b/core/ui/build.gradle.kts
@@ -14,10 +14,10 @@ kotlin {
sourceSets {
commonMain {
dependencies {
- implementation(project(":core:designsystem"))
- implementation(project(":core:data"))
+ implementation(projects.core.designsystem)
+ implementation(projects.core.data)
implementation(libs.kermit)
- api(project(":core:common"))
+ api(projects.core.common)
api(libs.composeImageLoader)
}
}
diff --git a/core/ui/src/androidMain/kotlin/io/github/droidkaigi/confsched2023/ui/TestEnviroment.kt b/core/ui/src/androidMain/kotlin/io/github/droidkaigi/confsched2023/ui/TestEnviroment.kt
index 74b3d9629..f262a8fd5 100644
--- a/core/ui/src/androidMain/kotlin/io/github/droidkaigi/confsched2023/ui/TestEnviroment.kt
+++ b/core/ui/src/androidMain/kotlin/io/github/droidkaigi/confsched2023/ui/TestEnviroment.kt
@@ -3,6 +3,5 @@ package io.github.droidkaigi.confsched2023.ui
import android.os.Build
actual fun isTest(): Boolean {
- if ("robolectric" == Build.FINGERPRINT) return true
- return false
+ return "robolectric" == Build.FINGERPRINT
}
diff --git a/feature/about/src/main/java/io/github/droidkaigi/confsched2023/about/AboutStrings.kt b/feature/about/src/main/java/io/github/droidkaigi/confsched2023/about/AboutStrings.kt
index 73b19076b..f1e01528b 100644
--- a/feature/about/src/main/java/io/github/droidkaigi/confsched2023/about/AboutStrings.kt
+++ b/feature/about/src/main/java/io/github/droidkaigi/confsched2023/about/AboutStrings.kt
@@ -5,25 +5,25 @@ import io.github.droidkaigi.confsched2023.designsystem.strings.Strings
import io.github.droidkaigi.confsched2023.designsystem.strings.StringsBindings
sealed class AboutStrings : Strings(Bindings) {
- object Title : AboutStrings()
- object Description : AboutStrings()
- object DateTitle : AboutStrings()
- object DateDescription : AboutStrings()
- object PlaceTitle : AboutStrings()
- object PlaceDescription : AboutStrings()
+ data object Title : AboutStrings()
+ data object Description : AboutStrings()
+ data object DateTitle : AboutStrings()
+ data object DateDescription : AboutStrings()
+ data object PlaceTitle : AboutStrings()
+ data object PlaceDescription : AboutStrings()
class PlaceLink(
val url: String = "https://goo.gl/maps/vv9sE19JvRjYKtSP9",
) : AboutStrings()
- object CreditsTitle : AboutStrings()
- object Staff : AboutStrings()
- object Contributor : AboutStrings()
- object Sponsor : AboutStrings()
- object OthersTitle : AboutStrings()
- object CodeOfConduct : AboutStrings()
- object License : AboutStrings()
- object PrivacyPolicy : AboutStrings()
- object AppVersion : AboutStrings()
- object LicenceDescription : AboutStrings()
+ data object CreditsTitle : AboutStrings()
+ data object Staff : AboutStrings()
+ data object Contributor : AboutStrings()
+ data object Sponsor : AboutStrings()
+ data object OthersTitle : AboutStrings()
+ data object CodeOfConduct : AboutStrings()
+ data object License : AboutStrings()
+ data object PrivacyPolicy : AboutStrings()
+ data object AppVersion : AboutStrings()
+ data object LicenceDescription : AboutStrings()
private object Bindings : StringsBindings(
Lang.Japanese to { item, _ ->
diff --git a/feature/about/src/main/java/io/github/droidkaigi/confsched2023/about/component/AboutOthers.kt b/feature/about/src/main/java/io/github/droidkaigi/confsched2023/about/component/AboutOthers.kt
index 1f86bd95c..431696ddb 100644
--- a/feature/about/src/main/java/io/github/droidkaigi/confsched2023/about/component/AboutOthers.kt
+++ b/feature/about/src/main/java/io/github/droidkaigi/confsched2023/about/component/AboutOthers.kt
@@ -1,16 +1,22 @@
package io.github.droidkaigi.confsched2023.about.component
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.FileCopy
import androidx.compose.material.icons.outlined.Gavel
import androidx.compose.material.icons.outlined.PrivacyTip
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import io.github.droidkaigi.confsched2023.about.AboutStrings
+import io.github.droidkaigi.confsched2023.designsystem.preview.MultiLanguagePreviews
+import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews
+import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme
const val AboutOthersCodeOfConductItemTestTag = "AboutOthersCodeOfConductItem"
const val AboutOthersLicenseItemTestTag = "AboutOthersLicenseItem"
@@ -70,3 +76,20 @@ fun LazyListScope.aboutOthers(
)
}
}
+
+@MultiThemePreviews
+@MultiLanguagePreviews
+@Composable
+internal fun AboutOthersPreview() {
+ KaigiTheme {
+ Surface {
+ LazyColumn {
+ aboutOthers(
+ onCodeOfConductItemClick = {},
+ onLicenseItemClick = {},
+ onPrivacyPolicyItemClick = {},
+ )
+ }
+ }
+ }
+}
diff --git a/feature/contributors/build.gradle.kts b/feature/contributors/build.gradle.kts
index 507fda518..cb53be2d3 100644
--- a/feature/contributors/build.gradle.kts
+++ b/feature/contributors/build.gradle.kts
@@ -4,6 +4,7 @@ plugins {
id("droidkaigi.primitive.kmp.android.hilt")
id("droidkaigi.primitive.kmp.ios")
id("droidkaigi.primitive.kmp.compose")
+ id("droidkaigi.primitive.kover")
}
android.namespace = "io.github.droidkaigi.confsched2023.feature.contributors"
diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableGridItem.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableGridItem.kt
index be7beb8c3..382cf9a5f 100644
--- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableGridItem.kt
+++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableGridItem.kt
@@ -13,9 +13,11 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Error
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Schedule
import androidx.compose.material3.Icon
@@ -44,6 +46,7 @@ import io.github.droidkaigi.confsched2023.model.TimetableItem
import io.github.droidkaigi.confsched2023.model.TimetableItem.Session
import io.github.droidkaigi.confsched2023.model.TimetableSpeaker
import io.github.droidkaigi.confsched2023.model.fake
+import io.github.droidkaigi.confsched2023.sessions.SessionsStrings
import io.github.droidkaigi.confsched2023.sessions.SessionsStrings.ScheduleIcon
import io.github.droidkaigi.confsched2023.sessions.SessionsStrings.UserIcon
import io.github.droidkaigi.confsched2023.sessions.section.TimetableSizes
@@ -132,6 +135,27 @@ fun TimetableGridItem(
)
}
+ if (timetableItem is Session) {
+ timetableItem.message?.let {
+ Spacer(modifier = Modifier.height(TimetableGridItemSizes.titleToScheduleSpaceHeight))
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Icon(
+ imageVector = Icons.Default.Error,
+ contentDescription = SessionsStrings.ErrorIcon.asString(),
+ tint = MaterialTheme.colorScheme.errorContainer,
+ modifier = Modifier.size(TimetableGridItemSizes.scheduleHeight),
+ )
+ Spacer(modifier = Modifier.width(4.dp))
+ Text(
+ text = it.currentLangTitle,
+ color = MaterialTheme.colorScheme.errorContainer,
+ fontSize = TimetableGridItemSizes.minTitleFontSize,
+ lineHeight = TimetableGridItemSizes.minTitleLineHeight,
+ )
+ }
+ }
+ }
+
Spacer(
modifier = Modifier
.weight(1f)
diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableItemDetailSummaryCard.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableItemDetailSummaryCard.kt
index 191a601bb..715dfccc4 100644
--- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableItemDetailSummaryCard.kt
+++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableItemDetailSummaryCard.kt
@@ -2,20 +2,28 @@ package io.github.droidkaigi.confsched2023.sessions.component
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Error
import androidx.compose.material.icons.outlined.Category
import androidx.compose.material.icons.outlined.Language
import androidx.compose.material.icons.outlined.Place
import androidx.compose.material.icons.outlined.Schedule
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import io.github.droidkaigi.confsched2023.designsystem.preview.MultiLanguagePreviews
@@ -26,6 +34,7 @@ import io.github.droidkaigi.confsched2023.model.TimetableItem.Session
import io.github.droidkaigi.confsched2023.model.fake
import io.github.droidkaigi.confsched2023.model.nameAndFloor
import io.github.droidkaigi.confsched2023.sessions.SessionsStrings
+import io.github.droidkaigi.confsched2023.sessions.SessionsStrings.ErrorIcon
import java.util.Locale
@Composable
@@ -33,43 +42,64 @@ fun TimetableItemDetailSummaryCard(
timetableItem: TimetableItem,
modifier: Modifier = Modifier,
) {
- val isJapaneseLocale = Locale.getDefault().language == Locale("ja").language
+ Column(modifier = modifier) {
+ if (timetableItem is Session) {
+ timetableItem.message?.let {
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Icon(
+ imageVector = Icons.Default.Error,
+ contentDescription = ErrorIcon.asString(),
+ tint = MaterialTheme.colorScheme.error,
+ modifier = Modifier.size(16.dp),
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ Text(
+ text = it.currentLangTitle,
+ color = MaterialTheme.colorScheme.error,
+ style = MaterialTheme.typography.labelLarge,
+ )
+ }
+ Spacer(modifier = Modifier.size(20.dp))
+ }
+ }
+
+ val isJapaneseLocale = Locale.getDefault().language == Locale("ja").language
- Card(
- shape = RoundedCornerShape(12.dp),
- modifier = modifier,
- colors = CardDefaults.cardColors(
- containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
- 1.dp,
+ Card(
+ shape = RoundedCornerShape(12.dp),
+ colors = CardDefaults.cardColors(
+ containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
+ 1.dp,
+ ),
),
- ),
- ) {
- Column(
- verticalArrangement = Arrangement.spacedBy(16.dp),
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp, vertical = 24.dp),
) {
- TimetableItemDetailSummaryCardRow(
- leadingIcon = Icons.Outlined.Schedule,
- label = SessionsStrings.Date.asString(),
- content = timetableItem.formattedDateTimeString,
- )
- TimetableItemDetailSummaryCardRow(
- leadingIcon = Icons.Outlined.Place,
- label = SessionsStrings.Place.asString(),
- content = timetableItem.room.nameAndFloor,
- )
- TimetableItemDetailSummaryCardRow(
- leadingIcon = Icons.Outlined.Language,
- label = SessionsStrings.SupportedLanguages.asString(),
- content = timetableItem.getSupportedLangString(isJapaneseLocale),
- )
- TimetableItemDetailSummaryCardRow(
- leadingIcon = Icons.Outlined.Category,
- label = SessionsStrings.Category.asString(),
- content = timetableItem.category.title.currentLangTitle,
- )
+ Column(
+ verticalArrangement = Arrangement.spacedBy(16.dp),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp, vertical = 24.dp),
+ ) {
+ TimetableItemDetailSummaryCardRow(
+ leadingIcon = Icons.Outlined.Schedule,
+ label = SessionsStrings.Date.asString(),
+ content = timetableItem.formattedDateTimeString,
+ )
+ TimetableItemDetailSummaryCardRow(
+ leadingIcon = Icons.Outlined.Place,
+ label = SessionsStrings.Place.asString(),
+ content = timetableItem.room.nameAndFloor,
+ )
+ TimetableItemDetailSummaryCardRow(
+ leadingIcon = Icons.Outlined.Language,
+ label = SessionsStrings.SupportedLanguages.asString(),
+ content = timetableItem.getSupportedLangString(isJapaneseLocale),
+ )
+ TimetableItemDetailSummaryCardRow(
+ leadingIcon = Icons.Outlined.Category,
+ label = SessionsStrings.Category.asString(),
+ content = timetableItem.category.title.currentLangTitle,
+ )
+ }
}
}
}
diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableTab.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableTab.kt
index 9910d5258..1234e6a21 100644
--- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableTab.kt
+++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableTab.kt
@@ -30,7 +30,6 @@ import androidx.compose.ui.layout.layout
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
-import io.github.droidkaigi.confsched2023.designsystem.preview.MultiLanguagePreviews
import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews
import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme
import io.github.droidkaigi.confsched2023.model.DroidKaigi2023Day
@@ -78,7 +77,7 @@ fun TimetableTab(
},
selectedContentColor = MaterialTheme.colorScheme.onPrimary,
unselectedContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
- modifier = modifier.heightIn(min = minTabHeight),
+ modifier = modifier.heightIn(min = tabMinHeight, max = tabMaxHeight),
)
}
@@ -89,7 +88,7 @@ fun TimetableTabIndicator(
Box(
modifier
.zIndex(-1f)
- .padding(horizontal = tabIndicatorHorizontalSpacing)
+ .padding(horizontal = tabIndicatorHorizontalGap / 2)
.fillMaxSize()
.background(
color = MaterialTheme.colorScheme.primary,
@@ -115,8 +114,13 @@ fun TimetableTabRow(
TabRow(
selectedTabIndex = selectedTabIndex,
modifier = modifier
- .height(maxTabRowHeight - ((maxTabRowHeight - minTabRowHeight) * tabState.tabCollapseProgress))
- .padding(horizontal = tabRowHorizontalSpacing),
+ .height(tabRowMaxHeight - ((tabRowMaxHeight - tabRowMinHeight) * tabState.tabCollapseProgress))
+ .padding(
+ start = tabRowHorizontalSpacing,
+ top = tabRowTopSpacing,
+ end = tabRowHorizontalSpacing,
+ bottom = tabRowBottomSpacing,
+ ),
divider = {},
indicator = indicator,
tabs = tabs,
@@ -124,12 +128,13 @@ fun TimetableTabRow(
}
@Composable
-fun rememberTimetableTabState(): TimetableTabState {
+fun rememberTimetableTabState(initialScrollOffset: Float = 0.0f): TimetableTabState {
val offsetLimit = LocalDensity.current.run {
- (maxTabRowHeight - minTabRowHeight).toPx()
+ (tabRowMaxHeight - tabRowMinHeight).toPx()
}
return rememberSaveable(saver = TimetableTabState.Saver) {
TimetableTabState(
+ initialScrollOffset = initialScrollOffset,
initialOffsetLimit = -offsetLimit,
)
}
@@ -180,15 +185,19 @@ class TimetableTabState(
}
}
-private val minTabHeight = 32.dp
-private val baselineTabHeight = 56.dp
-private val maxTabRowHeight = 84.dp
-private val minTabRowHeight = 56.dp
-private val tabIndicatorHorizontalSpacing = 8.dp
-private val tabRowHorizontalSpacing = (maxTabRowHeight - baselineTabHeight) / 2 - tabIndicatorHorizontalSpacing
+private val tabMinHeight = 32.dp
+private val tabMaxHeight = 56.dp
+
+private val tabIndicatorHorizontalGap = 8.dp
+
+private val tabRowHorizontalSpacing = 16.dp - (tabIndicatorHorizontalGap / 2)
+private val tabRowTopSpacing = 16.dp
+private val tabRowBottomSpacing = 12.dp
+private val tabRowMinHeight = tabMinHeight + tabRowTopSpacing + tabRowBottomSpacing
+private val tabRowMaxHeight = tabMaxHeight + tabRowTopSpacing + tabRowBottomSpacing
@MultiThemePreviews
-@MultiLanguagePreviews
+// @MultiLanguagePreviews
@Composable
fun TimetableTabPreview() {
KaigiTheme {
@@ -202,3 +211,52 @@ fun TimetableTabPreview() {
}
}
}
+
+@MultiThemePreviews
+// @MultiLanguagePreviews
+@Composable
+fun TimetableTabRowPreview() {
+ val scrollState = rememberTimetableTabState()
+ val selectedTabIndex = 0
+ KaigiTheme {
+ Surface {
+ TimetableTabRow(tabState = scrollState, selectedTabIndex = selectedTabIndex) {
+ DroidKaigi2023Day.entries.forEachIndexed { index, day ->
+ TimetableTab(
+ day = day,
+ selected = selectedTabIndex == index,
+ onClick = {},
+ scrollState = scrollState,
+ )
+ }
+ }
+ }
+ }
+}
+
+@MultiThemePreviews
+// @MultiLanguagePreviews
+@Composable
+fun CollapsedTimetableTabRowPreview() {
+ val scrollState = rememberSaveable(saver = TimetableTabState.Saver) {
+ TimetableTabState(
+ initialScrollOffset = -1.0f,
+ initialOffsetLimit = -1.0f,
+ )
+ }
+ val selectedTabIndex = 0
+ KaigiTheme {
+ Surface {
+ TimetableTabRow(tabState = scrollState, selectedTabIndex = selectedTabIndex) {
+ DroidKaigi2023Day.entries.forEachIndexed { index, day ->
+ TimetableTab(
+ day = day,
+ selected = index == selectedTabIndex,
+ onClick = {},
+ scrollState = scrollState,
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 50d8fc88e..f9894e9b6 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
[versions]
-androidGradlePlugin = "8.1.0"
+androidGradlePlugin = "8.1.1"
# For updating Kotlin and Compose Compiler version, see:
# https://github.com/JetBrains/compose-multiplatform/blob/master/VERSIONING.md#kotlin-compatibility
# https://developer.android.com/jetpack/androidx/releases/compose-kotlin?#pre-release_kotlin_compatibility
@@ -36,6 +36,7 @@ ossLicensesPlugin = "0.10.6"
ossLicenses = "17.0.1"
detekt = "1.23.1"
twitterComposeRule = "0.0.26"
+kover = "0.7.3"
[libraries]
androidGradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
@@ -52,6 +53,7 @@ detektGradlePlugin = { group = "io.gitlab.arturbosch.detekt", name = "detekt-gra
detektFormatting = { group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version.ref = "detekt" }
twitterComposeRule = { module = "com.twitter.compose.rules:detekt", version.ref = "twitterComposeRule" }
ossLicensesPlugin = { group = "com.google.android.gms", name = "oss-licenses-plugin", version.ref = "ossLicensesPlugin" }
+koverPlugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" }
androidDesugarJdkLibs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "androidDesugarJdkLibs" }
@@ -145,7 +147,7 @@ kotlinGradlePlugin = { id = "org.jetbrains.kotlin.android", version.ref = "kotli
hiltGradlePlugin = { id = "com.google.dagger.hilt.android", version.ref = "dagger" }
roborazziGradlePlugin = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" }
kspGradlePlugin = { id = "com.google.devtools.ksp", version.ref = "ksp" }
-kotlinxKover = { id = "org.jetbrains.kotlinx.kover", version = "0.7.3" }
+kotlinxKover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
detektGradlePlugin = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
ossLicensesPlugin = { id = "com.google.android.gms.oss-licenses-plugin", version.ref = "ossLicensesPlugin" }
@@ -162,5 +164,6 @@ plugins = [
"kotlinxSerializationGradlePlugin",
"completeKotlinPlugin",
"detektGradlePlugin",
- "ossLicensesPlugin"
+ "ossLicensesPlugin",
+ "koverPlugin"
]
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 1da645c14..cfb3ff2d0 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,9 +1,9 @@
pluginManagement {
includeBuild("build-logic")
repositories {
- gradlePluginPortal()
google()
mavenCentral()
+ gradlePluginPortal()
}
}
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
@@ -12,7 +12,6 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
-
}
}
rootProject.name = "conference-app-2023"