Skip to content

Commit

Permalink
Sync: Part 10 sync to upstream (openedx#556)
Browse files Browse the repository at this point in the history
* chore: add video and discussion analytics (#93)

* fix: fixes after merge, deleted IAP parts

* chore: changed dependency version, deleted unused analytics part

* chore: regenerate mocks

* chore: In app ratings modal and profile picture picture sheet UI improvements (#94)

* chore: show subtitles for videos in full-screen mode (#97)

* fix: after merge

* fix: iOS 18 Toggle tap gesture (#98)

* fix: added priority for toggle tap gesture

* fix: fixed tap and drag actions for Toggle

---------

Co-authored-by: Anton Yarmolenko <[email protected]>

* fix: gesture for ios 18.2

* chore: quick fix with performance for subtitles view

---------

Co-authored-by: Saeed Bashir <[email protected]>
Co-authored-by: Anton Yarmolenko <[email protected]>
Co-authored-by: Shafqat Muneer <[email protected]>
  • Loading branch information
4 people authored Dec 30, 2024
1 parent dc28e34 commit 61c6f6b
Show file tree
Hide file tree
Showing 45 changed files with 1,427 additions and 126 deletions.
2 changes: 1 addition & 1 deletion Authorization/Authorization.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1605,7 +1605,7 @@
repositoryURL = "https://github.com/openedx/openedx-app-foundation-ios/";
requirement = {
kind = exactVersion;
version = 1.0.1;
version = 1.0.2;
};
};
/* End XCRemoteSwiftPackageReference section */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public struct StartupView: View {
searchQuery: searchQuery,
sourceScreen: .startup
)
viewModel.logAnalytics(searchQuery: searchQuery)
})
.autocapitalization(.none)
.autocorrectionDisabled()
Expand Down Expand Up @@ -89,6 +90,7 @@ public struct StartupView: View {
searchQuery: searchQuery,
sourceScreen: .startup
)
viewModel.logAnalytics()
} label: {
Text(AuthLocalization.Startup.exploreAllCourses)
.underline()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class StartupViewModel: ObservableObject {
self.config = config
}

func logAnalytics(searchQuery: String?) {
func logAnalytics(searchQuery: String? = nil) {
if let searchQuery {
analytics.trackEvent(
.logistrationCoursesSearch,
Expand Down
2 changes: 1 addition & 1 deletion Core/Core.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2331,7 +2331,7 @@
repositoryURL = "https://github.com/openedx/openedx-app-foundation-ios/";
requirement = {
kind = exactVersion;
version = 1.0.1;
version = 1.0.2;
};
};
/* End XCRemoteSwiftPackageReference section */
Expand Down
41 changes: 41 additions & 0 deletions Core/Core/Analytics/CoreAnalytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ public enum AnalyticsEvent: String {
case bulkDownloadVideosToggle = "Video:Bulk Download Toggle"
case bulkDownloadVideosSubsection = "Video:Bulk Download Subsection"
case bulkDeleteVideosSubsection = "Videos:Delete Subsection Videos"
case videoLoaded = "Video:Loaded"
case videoSpeedChange = "Video:Change Speed"
case videoPlayed = "Video:Played"
case videoPaused = "Video:Paused"
case videoCompleted = "Video:Completed"
case discussionAllPostsClicked = "Discussion:All Posts Clicked"
case discussionFollowingClicked = "Discussion:Following Posts Clicked"
case discussionTopicClicked = "Discussion:Topic Clicked"
Expand All @@ -143,6 +148,12 @@ public enum AnalyticsEvent: String {
case logistrationSignIn = "Logistration:Sign In"
case logistrationRegister = "Logistration:Register"
case profileEdit = "Profile:Edit Profile"
case discussionPostCreated = "Discussion:Post Created"
case discussionResponseAdded = "Discussion:Response Added"
case discussionCommentAdded = "Discussion:Comment Added"
case discussionFollowToggle = "Dicussion:Post Follow Toggle"
case discussionLikeToggle = "Discussion:Like Toggle"
case discussionReportToggle = "Discussion:Report Toggle"
}

public enum EventBIValue: String {
Expand Down Expand Up @@ -192,6 +203,11 @@ public enum EventBIValue: String {
case bulkDownloadVideosToggle = "edx.bi.app.videos.download.toggle"
case bulkDownloadVideosSubsection = "edx.bi.video.subsection.bulkdownload"
case bulkDeleteVideosSubsection = "edx.bi.app.video.delete.subsection"
case videoLoaded = "edx.bi.app.videos.loaded"
case videoSpeedChange = "edx.bi.app.videos.speed.changed"
case videoPlayed = "edx.bi.app.videos.played"
case videoPaused = "edx.bi.app.videos.paused"
case videoCompleted = "edx.bi.app.videos.completed"
case dashboardCourseClicked = "edx.bi.app.course.dashboard"
case courseOutlineVideosTabClicked = "edx.bi.app.course.video_tab"
case courseOutlineOfflineTabClicked = "edx.bi.app.course.offline_tab"
Expand Down Expand Up @@ -231,6 +247,12 @@ public enum EventBIValue: String {
case logistrationSignIn = "edx.bi.app.logistration.signin"
case logistrationRegister = "edx.bi.app.logistration.register"
case profileEdit = "edx.bi.app.profile.edit"
case discussionPostCreated = "edx.bi.app.discussion.post_created"
case discussionResponseAdded = "edx.bi.app.discussion.response_added"
case discussionCommentAdded = "edx.bi.app.discussion.comment_added"
case discussionFollowToggle = "edx.bi.app.discussion.follow_toggle"
case discussionLikeToggle = "edx.bi.app.discussion.like_toggle"
case discussionReportToggle = "edx.bi.app.discussion.report_toggle"
}

public struct EventParamKey {
Expand Down Expand Up @@ -267,6 +289,25 @@ public struct EventParamKey {
public static let pacing = "pacing"
public static let dialog = "dialog"
public static let snackbar = "snackbar"
public static let error = "error"
public static let errorAction = "error_action"
public static let flowType = "flow_type"
public static let alertType = "alert_type"
public static let videoURL = "video_url"
public static let oldSpeed = "old_speed"
public static let newSpeed = "new_speed"
public static let currentTime = "current_time"
public static let duration = "duration"
public static let postType = "post_type"
public static let followPost = "follow_post"
public static let threadID = "thread_id"
public static let responseID = "response_id"
public static let commentID = "comment_id"
public static let author = "author"
public static let follow = "follow"
public static let like = "like"
public static let report = "report"
public static let discussionType = "discussion_type"
}

public struct EventCategory {
Expand Down
10 changes: 6 additions & 4 deletions Core/Core/View/Base/AppReview/Elements/AppReviewButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,19 @@ struct AppReviewButton: View {
: CoreLocalization.Review.Button.rateUs
)
)
.foregroundColor(isActive ? Color.white : Color.black.opacity(0.6))
.foregroundColor(isActive ? Theme.Colors.primaryButtonTextColor : Color.black.opacity(0.6))
.font(Theme.Fonts.labelLarge)
.padding(3)

}.padding(.horizontal, 20)
.padding(.vertical, 9)
}.fixedSize()
.background(
isActive
? Theme.Colors.accentColor
: Theme.Colors.cardViewStroke
Theme.Shapes.buttonShape
.fill(
isActive ? Theme.Colors.accentColor
: Theme.Colors.cardViewStroke
)
)
.accessibilityElement(children: .ignore)
.accessibilityLabel(
Expand Down
2 changes: 1 addition & 1 deletion Course/Course.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2086,7 +2086,7 @@
repositoryURL = "https://github.com/openedx/openedx-app-foundation-ios/";
requirement = {
kind = exactVersion;
version = 1.0.1;
version = 1.0.2;
};
};
CEBCA4322CC13CDE00076589 /* XCRemoteSwiftPackageReference "YouTubePlayerKit" */ = {
Expand Down
60 changes: 60 additions & 0 deletions Course/Course/Presentation/CourseAnalytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,36 @@ public protocol CourseAnalytics {
subSectionID: String,
videos: Int
)

func videoLoaded(courseID: String, blockID: String, videoURL: String)

func videoPlayed(courseID: String, blockID: String, videoURL: String)

func videoSpeedChange(
courseID: String,
blockID: String,
videoURL: String,
oldSpeed: Float,
newSpeed: Float,
currentTime: Double,
duration: Double
)

func videoPaused(
courseID: String,
blockID: String,
videoURL: String,
currentTime: Double,
duration: Double
)

func videoCompleted(
courseID: String,
blockID: String,
videoURL: String,
currentTime: Double,
duration: Double
)
}

#if DEBUG
Expand Down Expand Up @@ -199,5 +229,35 @@ class CourseAnalyticsMock: CourseAnalytics {
subSectionID: String,
videos: Int
) {}

public func videoLoaded(courseID: String, blockID: String, videoURL: String) {}

public func videoPlayed(courseID: String, blockID: String, videoURL: String) {}

public func videoSpeedChange(
courseID: String,
blockID: String,
videoURL: String,
oldSpeed: Float,
newSpeed: Float,
currentTime: Double,
duration: Double
) {}

public func videoPaused(
courseID: String,
blockID: String,
videoURL: String,
currentTime: Double,
duration: Double
) {}

public func videoCompleted(
courseID: String,
blockID: String,
videoURL: String,
currentTime: Double,
duration: Double
) {}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,24 @@ struct CourseVideoDownloadBarView: View {
Toggle("", isOn: .constant(viewModel.isOn))
.toggleStyle(SwitchToggleStyle(tint: Theme.Colors.toggleSwitchColor))
.padding(.trailing, 15)
.onTapGesture {
if !viewModel.isInternetAvaliable {
onNotInternetAvaliable?()
return
.simultaneousGesture(
DragGesture(minimumDistance: 20, coordinateSpace: .local).onEnded { _ in
toggleAction()
}
Task { await viewModel.onToggle() }
}
)
.simultaneousGesture(
TapGesture().onEnded {
toggleAction()
}
)
.accessibilityIdentifier("download_toggle")
}

private func toggleAction() {
if !viewModel.isInternetAvaliable {
onNotInternetAvaliable?()
return
}
Task { await viewModel.onToggle() }
}
}
14 changes: 12 additions & 2 deletions Course/Course/Presentation/Video/EncodedVideoPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public struct EncodedVideoPlayer: View {
@State private var isLoading: Bool = true
@State private var isAnimating: Bool = false
@State private var isOrientationChanged: Bool = false
@State private var subtitleText: String = ""

@State var showAlert = false
@State var alertMessage: String? {
Expand All @@ -54,7 +55,10 @@ public struct EncodedVideoPlayer: View {
VStack(spacing: 10) {
HStack {
VStack {
PlayerViewController(playerController: viewModel.controller)
PlayerViewController(
playerController: viewModel.controller,
subtitleText: $subtitleText
)
.aspectRatio(16 / 9, contentMode: .fit)
.frame(minWidth: playerWidth(for: reader.size))
.cornerRadius(12)
Expand Down Expand Up @@ -115,6 +119,10 @@ public struct EncodedVideoPlayer: View {
viewModel.controller.player?.allowsExternalPlayback = true
viewModel.controller.setNeedsStatusBarAppearanceUpdate()
}
.onReceive(viewModel.$currentTime) { currentTime in
let subtitle = viewModel.findSubtitle(at: Date(milliseconds: currentTime))
subtitleText = subtitle?.text ?? ""
}
}

private func playerWidth(for size: CGSize) -> CGFloat {
Expand All @@ -138,7 +146,9 @@ struct EncodedVideoPlayer_Previews: PreviewProvider {
languages: [],
playerStateSubject: CurrentValueSubject<VideoPlayerState?, Never>(nil),
connectivity: Connectivity(),
playerHolder: PlayerViewControllerHolder.mock
playerHolder: PlayerViewControllerHolder.mock,
appStorage: CoreStorageMock(),
analytics: CourseAnalyticsMock()
),
isOnScreen: true
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Core
import Combine

public class EncodedVideoPlayerViewModel: VideoPlayerViewModel {
var controller: AVPlayerViewController {
(playerHolder.playerController as? AVPlayerViewController) ?? AVPlayerViewController()
var controller: CustomAVPlayerViewController {
(playerHolder.playerController as? CustomAVPlayerViewController) ?? CustomAVPlayerViewController()
}
}
68 changes: 65 additions & 3 deletions Course/Course/Presentation/Video/PlayerViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import SwiftUI
import _AVKit_SwiftUI

struct PlayerViewController: UIViewControllerRepresentable {
var playerController: AVPlayerViewController
var playerController: CustomAVPlayerViewController
@Binding var subtitleText: String

func makeUIViewController(context: Context) -> AVPlayerViewController {
func makeUIViewController(context: Context) -> CustomAVPlayerViewController {
do {
try AVAudioSession.sharedInstance().setCategory(.playback)
} catch {
Expand All @@ -23,5 +24,66 @@ struct PlayerViewController: UIViewControllerRepresentable {
return playerController
}

func updateUIViewController(_ playerController: AVPlayerViewController, context: Context) {}
func updateUIViewController(_ playerController: CustomAVPlayerViewController, context: Context) {
playerController.subtitleText = subtitleText
}
}

class CustomAVPlayerViewController: AVPlayerViewController {
private let subtitleLabel = UILabel()

var subtitleText: String = "" {
didSet {
subtitleLabel.text = subtitleText
}
}

var hideSubtitle: Bool = false {
didSet {
subtitleLabel.isHidden = hideSubtitle
}
}

override func viewDidLoad() {
super.viewDidLoad()

// Configure the subtitle label
subtitleLabel.textColor = .white
subtitleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
subtitleLabel.backgroundColor = UIColor.black.withAlphaComponent(0.6)
subtitleLabel.textAlignment = .center
subtitleLabel.numberOfLines = 0
subtitleLabel.layer.cornerRadius = 8
subtitleLabel.layer.masksToBounds = true
subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
subtitleLabel.isHidden = true

self.delegate = self

// Add subtitle label to the content overlay view of AVPlayerViewController
contentOverlayView?.addSubview(subtitleLabel)

// Set constraints for the subtitle label
NSLayoutConstraint.activate([
subtitleLabel.centerXAnchor.constraint(equalTo: contentOverlayView!.centerXAnchor),
subtitleLabel.bottomAnchor.constraint(equalTo: contentOverlayView!.bottomAnchor, constant: -20),
subtitleLabel.widthAnchor.constraint(lessThanOrEqualTo: contentOverlayView!.widthAnchor, multiplier: 0.9)
])
}
}

extension CustomAVPlayerViewController: @preconcurrency AVPlayerViewControllerDelegate {
func playerViewController(
_ playerViewController: AVPlayerViewController,
willBeginFullScreenPresentationWithAnimationCoordinator coordinator: any UIViewControllerTransitionCoordinator
) {
hideSubtitle = false
}

func playerViewController(
_ playerViewController: AVPlayerViewController,
willEndFullScreenPresentationWithAnimationCoordinator coordinator: any UIViewControllerTransitionCoordinator
) {
hideSubtitle = true
}
}
Loading

0 comments on commit 61c6f6b

Please sign in to comment.