Skip to content

Commit

Permalink
Merge pull request openedx#384 from edx/feat/pip-mode
Browse files Browse the repository at this point in the history
Feat/pip mode
  • Loading branch information
rnr authored Apr 8, 2024
2 parents 7765405 + 14be4c3 commit 76f0430
Show file tree
Hide file tree
Showing 21 changed files with 635 additions and 105 deletions.
4 changes: 4 additions & 0 deletions Course/Course.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
02F98A8128F8224200DE94C0 /* Discussion.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02F98A8028F8224200DE94C0 /* Discussion.framework */; };
02FFAD0D29E4347300140E46 /* VideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FFAD0C29E4347300140E46 /* VideoPlayerViewModel.swift */; };
060E8BCA2B5FD68C0080C952 /* UnitStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 060E8BC92B5FD68C0080C952 /* UnitStack.swift */; };
065275352BB1B39C0093BCCA /* PlayerViewControllerHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 065275342BB1B39C0093BCCA /* PlayerViewControllerHolder.swift */; };
068DDA5F2B1E198700FF8CCB /* CourseUnitDropDownList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068DDA5B2B1E198700FF8CCB /* CourseUnitDropDownList.swift */; };
068DDA602B1E198700FF8CCB /* CourseUnitVerticalsDropdownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068DDA5C2B1E198700FF8CCB /* CourseUnitVerticalsDropdownView.swift */; };
068DDA612B1E198700FF8CCB /* CourseUnitDropDownCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068DDA5D2B1E198700FF8CCB /* CourseUnitDropDownCell.swift */; };
Expand Down Expand Up @@ -150,6 +151,7 @@
02F98A8028F8224200DE94C0 /* Discussion.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Discussion.framework; sourceTree = BUILT_PRODUCTS_DIR; };
02FFAD0C29E4347300140E46 /* VideoPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerViewModel.swift; sourceTree = "<group>"; };
060E8BC92B5FD68C0080C952 /* UnitStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitStack.swift; sourceTree = "<group>"; };
065275342BB1B39C0093BCCA /* PlayerViewControllerHolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerViewControllerHolder.swift; sourceTree = "<group>"; };
068DDA5B2B1E198700FF8CCB /* CourseUnitDropDownList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseUnitDropDownList.swift; sourceTree = "<group>"; };
068DDA5C2B1E198700FF8CCB /* CourseUnitVerticalsDropdownView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseUnitVerticalsDropdownView.swift; sourceTree = "<group>"; };
068DDA5D2B1E198700FF8CCB /* CourseUnitDropDownCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseUnitDropDownCell.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -445,6 +447,7 @@
070019AA28F6F79E00D5FC78 /* Video */ = {
isa = PBXGroup;
children = (
065275342BB1B39C0093BCCA /* PlayerViewControllerHolder.swift */,
02F066E729DC71750073E13B /* SubtittlesView.swift */,
0766DFCB299AA7A600EBEF6A /* YouTubeVideoPlayer.swift */,
022F8E152A1DFBC6008EFAB9 /* YouTubeVideoPlayerViewModel.swift */,
Expand Down Expand Up @@ -830,6 +833,7 @@
022C64E029ADEA9B000F532B /* Data_UpdatesResponse.swift in Sources */,
02454CA02A2618E70043052A /* YouTubeView.swift in Sources */,
02454CA22A26190A0043052A /* EncodedVideoView.swift in Sources */,
065275352BB1B39C0093BCCA /* PlayerViewControllerHolder.swift in Sources */,
068DDA612B1E198700FF8CCB /* CourseUnitDropDownCell.swift in Sources */,
BAC0E0DE2B32F0F3006B68A9 /* DownloadsViewModel.swift in Sources */,
02B6B3BC28E1D14F00232911 /* CourseRepository.swift in Sources */,
Expand Down
4 changes: 0 additions & 4 deletions Course/Course/Presentation/CourseRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ public protocol CourseRouter: BaseRouter {
courseName: String,
blockId: String,
courseID: String,
sectionName: String,
verticalIndex: Int,
chapters: [CourseChapter],
chapterIndex: Int,
Expand All @@ -27,7 +26,6 @@ public protocol CourseRouter: BaseRouter {
courseName: String,
blockId: String,
courseID: String,
sectionName: String,
verticalIndex: Int,
chapters: [CourseChapter],
chapterIndex: Int,
Expand Down Expand Up @@ -75,7 +73,6 @@ public class CourseRouterMock: BaseRouterMock, CourseRouter {
courseName: String,
blockId: String,
courseID: String,
sectionName: String,
verticalIndex: Int,
chapters: [CourseChapter],
chapterIndex: Int,
Expand All @@ -86,7 +83,6 @@ public class CourseRouterMock: BaseRouterMock, CourseRouter {
courseName: String,
blockId: String,
courseID: String,
sectionName: String,
verticalIndex: Int,
chapters: [CourseChapter],
chapterIndex: Int,
Expand Down
1 change: 0 additions & 1 deletion Course/Course/Presentation/Outline/CourseOutlineView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ public struct CourseOutlineView: View {
courseName: course.displayName,
blockId: continueBlock?.id ?? "",
courseID: course.id,
sectionName: continueUnit.displayName,
verticalIndex: continueWith.verticalIndex,
chapters: course.childs,
chapterIndex: continueWith.chapterIndex,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,6 @@ struct CourseStructureNestedListView: View {
courseName: viewModel.courseStructure?.displayName ?? "",
blockId: block.id,
courseID: viewModel.courseStructure?.id ?? "",
sectionName: block.displayName,
verticalIndex: 0,
chapters: course.childs,
chapterIndex: chapterIndex,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ public struct CourseVerticalView: View {
courseName: courseName,
blockId: block.id,
courseID: courseID,
sectionName: block.displayName,
verticalIndex: index,
chapters: viewModel.chapters,
chapterIndex: viewModel.chapterIndex,
Expand Down
5 changes: 0 additions & 5 deletions Course/Course/Presentation/Unit/CourseNavigationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,13 @@ struct CourseNavigationView: View {

@ObservedObject
private var viewModel: CourseUnitViewModel
private let sectionName: String
private let playerStateSubject: CurrentValueSubject<VideoPlayerState?, Never>

init(
sectionName: String,
viewModel: CourseUnitViewModel,
playerStateSubject: CurrentValueSubject<VideoPlayerState?, Never>
) {
self.viewModel = viewModel
self.sectionName = sectionName
self.playerStateSubject = playerStateSubject
}

Expand Down Expand Up @@ -128,7 +125,6 @@ struct CourseNavigationView: View {
courseName: viewModel.courseName,
blockId: viewModel.lessonID,
courseID: viewModel.courseID,
sectionName: viewModel.selectedLesson().displayName,
verticalIndex: data.verticalIndex,
chapters: viewModel.chapters,
chapterIndex: data.chapterIndex,
Expand Down Expand Up @@ -170,7 +166,6 @@ struct CourseNavigationView_Previews: PreviewProvider {
)

CourseNavigationView(
sectionName: "Name",
viewModel: viewModel,
playerStateSubject: CurrentValueSubject<VideoPlayerState?, Never>(nil)
)
Expand Down
14 changes: 6 additions & 8 deletions Course/Course/Presentation/Unit/CourseUnitView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import Theme

public struct CourseUnitView: View {

@ObservedObject private var viewModel: CourseUnitViewModel
@ObservedObject public var viewModel: CourseUnitViewModel
@State private var showAlert: Bool = false
@State var alertMessage: String? {
didSet {
Expand All @@ -27,7 +27,6 @@ public struct CourseUnitView: View {
@State var showDiscussion: Bool = false
@Environment(\.isPresented) private var isPresented
@Environment(\.isHorizontal) private var isHorizontal
private let sectionName: String
public let playerStateSubject = CurrentValueSubject<VideoPlayerState?, Never>(nil)

//Dropdown parameters
Expand Down Expand Up @@ -60,11 +59,9 @@ public struct CourseUnitView: View {

public init(
viewModel: CourseUnitViewModel,
sectionName: String,
isDropdownActive: Bool = false
) {
self.viewModel = viewModel
self.sectionName = sectionName
self.isDropdownActive = isDropdownActive
viewModel.loadIndex()
viewModel.nextTitles()
Expand Down Expand Up @@ -122,7 +119,9 @@ public struct CourseUnitView: View {
offsetY: isHorizontal ? landscapeTopSpacing : portraitTopSpacing,
showDropdown: $showDropdown
) { [weak viewModel] vertical in
viewModel?.route(to: vertical)
let data = VerticalData.dataFor(blockId: vertical.childs.first?.id, in: viewModel?.chapters ?? [])
viewModel?.route(to: data)
playerStateSubject.send(VideoPlayerState.kill)
}
}
}
Expand Down Expand Up @@ -173,7 +172,7 @@ public struct CourseUnitView: View {
switch LessonType.from(block, streamingQuality: viewModel.streamingQuality) {
// MARK: YouTube
case let .youtube(url, blockID):
if index >= viewModel.index - 1 && index <= viewModel.index + 1 {
if index == viewModel.index {
if viewModel.connectivity.isInternetAvaliable {
YouTubeView(
name: block.displayName,
Expand Down Expand Up @@ -418,7 +417,6 @@ public struct CourseUnitView: View {
Spacer()
}
CourseNavigationView(
sectionName: sectionName,
viewModel: viewModel,
playerStateSubject: playerStateSubject
)
Expand Down Expand Up @@ -566,7 +564,7 @@ struct CourseUnitView_Previews: PreviewProvider {
connectivity: Connectivity(),
storage: CourseStorageMock(),
manager: DownloadManagerMock()
), sectionName: "")
))
}
}
//swiftlint:enable all
Expand Down
94 changes: 71 additions & 23 deletions Course/Course/Presentation/Unit/CourseUnitViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public enum LessonType: Equatable {
case .discussion:
return .discussion(block.topicId ?? "", block.id, block.displayName)
case .video:
if block.encodedVideo?.youtubeVideoUrl != nil, let encodedVideo = block.encodedVideo?.video(streamingQuality: streamingQuality)?.url {
if block.encodedVideo?.youtubeVideoUrl != nil,
let encodedVideo = block.encodedVideo?.video(streamingQuality: streamingQuality)?.url {
return .video(videoUrl: encodedVideo, blockID: block.id)
} else if let youtubeVideoUrl = block.encodedVideo?.youtubeVideoUrl {
return .youtube(youtubeVideoUrl: youtubeVideoUrl, blockID: block.id)
Expand All @@ -53,19 +54,46 @@ public enum LessonType: Equatable {
}
}

public struct VerticalData: Equatable {
public var chapterIndex: Int
public var sequentialIndex: Int
public var verticalIndex: Int
public var blockIndex: Int

public init(chapterIndex: Int, sequentialIndex: Int, verticalIndex: Int, blockIndex: Int) {
self.chapterIndex = chapterIndex
self.sequentialIndex = sequentialIndex
self.verticalIndex = verticalIndex
self.blockIndex = blockIndex
}

public static func dataFor(blockId: String?, in chapters: [CourseChapter]) -> VerticalData? {
guard let blockId = blockId else { return nil }
for (chapterIndex, chapter) in chapters.enumerated() {
for (sequentialIndex, sequential) in chapter.childs.enumerated() {
for (verticalIndex, vertical) in sequential.childs.enumerated() {
for (blockIndex, block) in vertical.childs.enumerated() where block.id.contains(blockId) {
return VerticalData(
chapterIndex: chapterIndex,
sequentialIndex: sequentialIndex,
verticalIndex: verticalIndex,
blockIndex: blockIndex
)
}
}
}
}
return nil
}
}

public class CourseUnitViewModel: ObservableObject {

enum LessonAction {
case next
case previous
}

struct VerticalData {
var chapterIndex: Int
var sequentialIndex: Int
var verticalIndex: Int
}

var verticals: [CourseVertical]
var verticalIndex: Int
var courseName: String
Expand Down Expand Up @@ -95,7 +123,7 @@ public class CourseUnitViewModel: ObservableObject {
let chapterIndex: Int
let sequentialIndex: Int

var streamingQuality: StreamingQuality {
var streamingQuality: StreamingQuality {
storage.userSettings?.streamingQuality ?? .auto
}

Expand Down Expand Up @@ -142,7 +170,7 @@ public class CourseUnitViewModel: ObservableObject {

private func selectLesson() -> Int {
guard verticals[verticalIndex].childs.count > 0 else { return 0 }
let index = verticals[verticalIndex].childs.firstIndex(where: { $0.id == lessonID }) ?? 0
let index = verticals[verticalIndex].childs.firstIndex(where: { $0.id.contains(lessonID) }) ?? 0
nextTitles()
return index
}
Expand Down Expand Up @@ -218,11 +246,16 @@ public class CourseUnitViewModel: ObservableObject {
// MARK: Navigation to next vertical
var nextData: VerticalData? {
nextData(
from: VerticalData(
chapterIndex: chapterIndex,
sequentialIndex: sequentialIndex,
verticalIndex: verticalIndex
)
from: currentData
)
}

var currentData: VerticalData {
VerticalData(
chapterIndex: chapterIndex,
sequentialIndex: sequentialIndex,
verticalIndex: verticalIndex,
blockIndex: index
)
}

Expand Down Expand Up @@ -271,6 +304,7 @@ public class CourseUnitViewModel: ObservableObject {
}

if let vertical = vertical(for: resultData), vertical.childs.count > 0 {
resultData.blockIndex = 0
return resultData
} else {
return nextData(from: resultData)
Expand All @@ -291,20 +325,34 @@ public class CourseUnitViewModel: ObservableObject {
)
}

func route(to vertical: CourseVertical) {
if let index = verticals.firstIndex(where: { $0.id == vertical.id }),
let block = vertical.childs.first {
func blockFor(index: Int, in vertical: CourseVertical) -> CourseBlock? {
guard index >= 0 && index < vertical.childs.count else { return nil }
return vertical.childs[index]
}

func route(to data: VerticalData?, animated: Bool = false) {
guard let data = data, data != currentData else { return }
if let vertical = vertical(for: data),
let block = blockFor(index: data.blockIndex, in: vertical) {
router.replaceCourseUnit(
courseName: courseName,
blockId: block.id,
blockId: block.blockId,
courseID: courseID,
sectionName: block.displayName,
verticalIndex: index,
verticalIndex: data.verticalIndex,
chapters: chapters,
chapterIndex: chapterIndex,
sequentialIndex: sequentialIndex,
animated: false
chapterIndex: data.chapterIndex,
sequentialIndex: data.sequentialIndex,
animated: animated
)
}
}

public func route(to blockId: String?) {
guard let data = VerticalData.dataFor(blockId: blockId, in: chapters) else { return }
route(to: data, animated: true)
}

public var currentCourseId: String {
courseID
}
}
11 changes: 8 additions & 3 deletions Course/Course/Presentation/Video/EncodedVideoPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public struct EncodedVideoPlayer: View {
VStack {
PlayerViewController(
videoURL: viewModel.url,
controller: viewModel.controller,
playerHolder: viewModel.controllerHolder,
bitrate: viewModel.getVideoResolution(),
progress: { progress in
if progress >= 0.8 {
Expand All @@ -81,7 +81,10 @@ public struct EncodedVideoPlayer: View {
.frame(minWidth: playerWidth(for: reader.size))
.cornerRadius(12)
.onAppear {
viewModel.controller.player?.play()
if !viewModel.controllerHolder.isPlayingInPip,
!viewModel.controllerHolder.isOtherPlayerInPip {
viewModel.controller.player?.play()
}
}
if isHorizontal {
Spacer()
Expand Down Expand Up @@ -168,7 +171,9 @@ struct EncodedVideoPlayer_Previews: PreviewProvider {
interactor: CourseInteractor(repository: CourseRepositoryMock()),
router: CourseRouterMock(),
appStorage: CoreStorageMock(),
connectivity: Connectivity()
connectivity: Connectivity(),
pipManager: PipManagerProtocolMock(),
selectedCourseTab: 0
),
isOnScreen: true
)
Expand Down
Loading

0 comments on commit 76f0430

Please sign in to comment.