src/app/content-details/content-details.page.ts
OnInit
OnDestroy
encapsulation | ViewEncapsulation.None |
selector | app-content-details |
styleUrls | ./content-details.page.scss |
templateUrl | ./content-details.page.html |
constructor(profileService: ProfileService, contentService: ContentService, eventBusService: EventsBusService, storageService: StorageService, downloadService: DownloadService, preferences: SharedPreferences, courseService: CourseService, playerService: PlayerService, zone: NgZone, events: Events, popoverCtrl: PopoverController, platform: Platform, appGlobalService: AppGlobalService, telemetryGeneratorService: TelemetryGeneratorService, commonUtilService: CommonUtilService, courseUtilService: CourseUtilService, utilityService: UtilityService, network: Network, fileSizePipe: FileSizePipe, headerService: AppHeaderService, appVersion: AppVersion, location: Location, router: Router, route: ActivatedRoute, profileSwitchHandler: ProfileSwitchHandler, ratingHandler: RatingHandler, contentPlayerHandler: ContentPlayerHandler, childContentHandler: ChildContentHandler, contentDeleteHandler: ContentDeleteHandler, fileOpener: FileOpener, transfer: FileTransfer, sbProgressLoader: SbProgressLoader, localCourseService: LocalCourseService, formFrameworkUtilService: FormAndFrameworkUtilService, sanitizer: DomSanitizer, screenOrientation: ScreenOrientation)
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Parameters :
|
calculateAvailableUserCount |
calculateAvailableUserCount()
|
Returns :
void
|
cancelDownload |
cancelDownload()
|
Returns :
void
|
checkappAvailability |
checkappAvailability()
|
Returns :
void
|
checkDeviceAPILevel |
checkDeviceAPILevel()
|
Returns :
void
|
checkIsPlayerEnabled | ||||||
checkIsPlayerEnabled(config, playerType)
|
||||||
Parameters :
Returns :
any
|
checkLimitedContentSharingFlag | ||||
checkLimitedContentSharingFlag(content)
|
||||
Parameters :
Returns :
void
|
downloadContent |
downloadContent()
|
Download content
Returns :
void
|
extractApiResponse | ||||||
extractApiResponse(data: Content)
|
||||||
Parameters :
Returns :
void
|
Async fetchCertificateDescription | ||||
fetchCertificateDescription(batchId)
|
||||
Parameters :
Returns :
unknown
|
findHierarchyOfContent |
findHierarchyOfContent()
|
Returns :
void
|
generateDetailsInteractEvent |
generateDetailsInteractEvent()
|
Returns :
void
|
generateEndEvent |
generateEndEvent()
|
Returns :
void
|
generateImpressionEvent | ||||||||||
generateImpressionEvent(download, objectId?, objectType?, objectVersion?)
|
||||||||||
Parameters :
Returns :
void
|
generateQRSessionEndEvent |
generateQRSessionEndEvent(pageId: string, qrData: string)
|
Returns :
void
|
generateStartEvent |
generateStartEvent()
|
Returns :
void
|
generateTelemetry | ||||||
generateTelemetry(forceGenerate?: boolean)
|
||||||
Parameters :
Returns :
void
|
Async getContentState |
getContentState()
|
Returns :
unknown
|
getImageContent |
getImageContent()
|
Returns :
any
|
getImportContentRequestBody | ||||||||||||
getImportContentRequestBody(identifiers: Array
|
||||||||||||
Function to get import content api request params
Parameters :
Returns :
Array<ContentImport>
|
Async getNavParams |
getNavParams()
|
Returns :
any
|
Async getNewPlayerConfiguration |
getNewPlayerConfiguration()
|
Returns :
unknown
|
getNextContent | ||||||
getNextContent(hierarchyInfo, identifier)
|
||||||
Parameters :
Returns :
any
|
Async handleContentPlay | ||||
handleContentPlay(isStreaming)
|
||||
Parameters :
Returns :
any
|
handleDeviceBackButton |
handleDeviceBackButton()
|
Returns :
void
|
handleNavBackButton |
handleNavBackButton()
|
Returns :
boolean
|
Async handlePlayer | ||||
handlePlayer(playerData)
|
||||
Parameters :
Returns :
any
|
importContent | ||||||||||||
importContent(identifiers: Array
|
||||||||||||
Function to get import content api request params
Parameters :
Returns :
void
|
ionViewDidEnter |
ionViewDidEnter()
|
Returns :
void
|
Async ionViewWillEnter |
ionViewWillEnter()
|
Ionic life cycle hook
Returns :
any
|
ionViewWillLeave |
ionViewWillLeave()
|
Ionic life cycle hook
Returns :
void
|
iosCheck |
iosCheck()
|
Returns :
boolean
|
isPlayedFromCourse |
isPlayedFromCourse()
|
Returns :
void
|
markContent |
markContent()
|
Returns :
void
|
mergeProperties | ||||
mergeProperties(mergeProp)
|
||||
Parameters :
Returns :
any
|
ngOnDestroy |
ngOnDestroy()
|
Returns :
void
|
Async ngOnInit |
ngOnInit()
|
Returns :
any
|
Async openConfirmPopUp |
openConfirmPopUp()
|
confirming popUp content
Returns :
any
|
Async openCourseCompletionPopup |
openCourseCompletionPopup()
|
Returns :
any
|
openinBrowser | ||||
openinBrowser(url)
|
||||
Parameters :
Returns :
void
|
Async openPDFPreview | ||||||
openPDFPreview(content: Content)
|
||||||
Parameters :
Returns :
any
|
Async openPlayAsPopup | ||||
openPlayAsPopup(isStreaming)
|
||||
Parameters :
Returns :
any
|
Async openWithVendorApps |
openWithVendorApps()
|
Returns :
any
|
Private playContent |
playContent(isStreaming: boolean, loadPlayer: boolean)
|
Play content
Returns :
void
|
Async playerEvents | ||||
playerEvents(event)
|
||||
Parameters :
Returns :
any
|
playerTelemetryEvents | ||||
playerTelemetryEvents(event)
|
||||
Parameters :
Returns :
void
|
playNextContent |
playNextContent()
|
Returns :
void
|
popToPreviousPage | ||||
popToPreviousPage(isNavBack?)
|
||||
Parameters :
Returns :
void
|
Async promptToLogin |
promptToLogin()
|
Returns :
any
|
rateContent | ||||||
rateContent(popUpType: string)
|
||||||
Parameters :
Returns :
void
|
readLessorReadMore | ||||||||||||
readLessorReadMore(param, objRollup, corRelationList)
|
||||||||||||
method generates telemetry on click Read less or Read more
Parameters :
Returns :
void
|
Async setContentDetails | ||||||||||||||||
setContentDetails(identifier, refreshContentDetails: boolean, showRating: boolean)
|
||||||||||||||||
To set content details in local variable
Parameters :
Returns :
any
|
Async share |
share()
|
Shares content to external devices
Returns :
any
|
showDeletePopup |
showDeletePopup()
|
Returns :
void
|
Async showDownloadTranscript |
showDownloadTranscript()
|
Returns :
any
|
Private Async showPopupDialog |
showPopupDialog()
|
Returns :
any
|
Async showRetiredContentPopup |
showRetiredContentPopup()
|
Returns :
any
|
Async showSwitchUserAlert | ||||||
showSwitchUserAlert(isStreaming: boolean)
|
||||||
alert for playing the content
Parameters :
Returns :
unknown
|
subscribeEvents |
subscribeEvents()
|
Returns :
void
|
subscribePlayEvent |
subscribePlayEvent()
|
Returns :
void
|
subscribeSdkEvent |
subscribeSdkEvent()
|
Subscribe Sunbird-SDK event to get content download progress
Returns :
void
|
viewCredits |
viewCredits()
|
To View Credits popup check if non of these properties exist, then return false else show ViewCreditsComponent
Returns :
boolean
|
apiLevel |
Type : number
|
appAvailability |
Type : string
|
Public appGlobalService |
Type : AppGlobalService
|
appLists |
Type : any
|
appName |
Type : any
|
Private autoPlayQuizContent |
Default value : false
|
backButtonFunc |
Type : Subscription
|
breadCrumbData |
Type : any
|
cancelDownloading |
Default value : false
|
cardData |
Type : any
|
childPaths |
Type : Array<string>
|
Default value : []
|
config |
Type : any
|
content |
Type : Content | any
|
contentDeleteObservable |
Type : any
|
contentDetail |
Type : any
|
contentDetails |
Type : any
|
contentDownloadable |
Type : literal type
|
Default value : {}
|
contentPath |
Type : Array<any>[]
|
Private contentProgressSubscription |
Type : Subscription
|
contentSize |
Type : any
|
Private corRelationList |
Type : Array<CorrelationData>
|
course |
Type : Course
|
courseContext |
Type : any
|
defaultAppIcon |
Type : string
|
defaultLicense |
Type : string
|
depth |
Type : string
|
didViewLoad |
Type : boolean
|
downloadAndPlay |
Type : boolean
|
currently used to identify that its routed from QR code results page Can be sent from any page, where after landing on details page should download or play content automatically |
downloadProgress |
Type : any
|
Private eventSubscription |
Type : Subscription
|
FileSizePipe |
Type : any
|
fileTransfer |
Type : FileTransferObject
|
headerObservable |
Type : any
|
hierarchyInfo |
Type : any
|
identifier |
Type : string
|
isChildContent |
Default value : false
|
isCompatibleWithVendorApps |
Default value : false
|
isContentDownloading$ |
Type : Observable<boolean>
|
isContentPlayed |
Default value : false
|
isCourseCertificateShown |
Type : boolean
|
isDownloadStarted |
Default value : false
|
isIOS |
Default value : false
|
Private isLoginPromptOpen |
Default value : false
|
Public isPlayerLaunched |
Default value : false
|
This flag helps in knowing when the content player is closed and the user is back on content details page. |
isPlayerPlaying |
Default value : false
|
isResumedCourse |
Type : boolean
|
isSingleContent |
Type : boolean
|
isUpdateAvail |
Default value : false
|
Used to handle update content workflow |
isUsrGrpAlrtOpen |
Default value : false
|
launchPlayer |
Type : boolean
|
licenseDetails |
limitedShareContentFlag |
Default value : false
|
loader |
Type : any
|
localImage |
Type : any
|
maxAttemptAssessment |
Type : any
|
nextContentToBePlayed |
Type : any
|
Public objRollup |
Type : Rollup
|
onboarding |
Default value : false
|
pageId |
Default value : PageId.CONTENT_DETAIL
|
Private playerEndEventTriggered |
Type : boolean
|
playerType |
Type : any
|
Default value : null
|
playingContent |
Type : Content
|
playOnlineSpinner |
Type : boolean
|
resultLength |
Type : any
|
resumedCourseCardData |
Type : any
|
shouldGenerateEndTelemetry |
Default value : false
|
shouldGenerateTelemetry |
Default value : true
|
shouldNavigateBack |
Default value : false
|
shouldOpenPlayAsPopup |
Default value : false
|
showChildrenLoader |
Type : any
|
showCourseCompletePopup |
Default value : false
|
showDownload |
Type : boolean
|
showLoading |
Type : any
|
showMessage |
Type : any
|
source |
Type : string
|
Default value : ''
|
Optional streamingUrl |
Type : any
|
telemetryObject |
Type : TelemetryObject
|
userCount |
Type : number
|
Default value : 0
|
userId |
Type : string
|
Default value : ''
|
import { Location } from '@angular/common';
import { Router, ActivatedRoute } from '@angular/router';
import { AppVersion } from '@ionic-native/app-version/ngx';
import { Network } from '@ionic-native/network/ngx';
import { Component, Inject, NgZone, OnInit, ViewEncapsulation, OnDestroy } from '@angular/core';
import {
Platform,
PopoverController
} from '@ionic/angular';
import { Events } from '@app/util/events';
import { Subscription, Observable } from 'rxjs';
import {
Content,
ContentDetailRequest,
ContentEventType,
ContentImport,
ContentImportRequest,
ContentImportResponse,
ContentService,
CorrelationData,
DownloadEventType,
DownloadProgress,
EventsBusEvent,
EventsBusService,
GetAllProfileRequest,
ProfileService,
Rollup,
StorageService,
TelemetryObject,
Course,
DownloadService,
ObjectType,
SharedPreferences,
EventNamespace,
ContentUpdate,
CourseService,
SunbirdSdk,
PlayerService,
ContentAccess,
ContentAccessStatus,
ContentMarkerRequest,
MarkerType,
Profile
} from 'sunbird-sdk';
import { Map } from '@app/app/telemetryutil';
import { ConfirmAlertComponent } from '@app/app/components';
import { AppGlobalService } from '@app/services/app-global-service.service';
import { AppHeaderService } from '@app/services/app-header.service';
import {
ContentConstants, EventTopics, XwalkConstants, RouterLinks, ContentFilterConfig,
ShareItemType, PreferenceKey, MaxAttempt, ProfileConstants
} from '@app/app/app.constant';
import {
CourseUtilService,
LocalCourseService,
UtilityService,
TelemetryGeneratorService,
CommonUtilService, FormAndFrameworkUtilService,
} from '@app/services';
import { ContentInfo } from '@app/services/content/content-info';
import { DialogPopupComponent } from '@app/app/components/popups/dialog-popup/dialog-popup.component';
import {
Environment,
ImpressionType,
InteractSubtype,
InteractType,
Mode,
PageId,
CorReleationDataType,
} from '@app/services/telemetry-constants';
import { FileSizePipe } from '@app/pipes/file-size/file-size';
import { SbGenericPopoverComponent } from '@app/app/components/popups/sb-generic-popover/sb-generic-popover.component';
import { RatingHandler } from '@app/services/rating/rating-handler';
import { ProfileSwitchHandler } from '@app/services/user-groups/profile-switch-handler';
import { ContentPlayerHandler } from '@app/services/content/player/content-player-handler';
import { ChildContentHandler } from '@app/services/content/child-content-handler';
import { ContentDeleteHandler } from '@app/services/content/content-delete-handler';
import { ContentUtil } from '@app/util/content-util';
import { FileTransfer, FileTransferObject } from '@ionic-native/file-transfer/ngx';
import { map, filter, take, tap } from 'rxjs/operators';
import { SbPopoverComponent } from '../components/popups/sb-popover/sb-popover.component';
import { SbSharePopupComponent } from '../components/popups/sb-share-popup/sb-share-popup.component';
import { FileOpener } from '@ionic-native/file-opener/ngx';
import { Components } from '@ionic/core/dist/types/components';
import { SbProgressLoader } from '../../services/sb-progress-loader.service';
import { CourseCompletionPopoverComponent } from '../components/popups/sb-course-completion-popup/sb-course-completion-popup.component';
import { CsPrimaryCategory } from '@project-sunbird/client-services/services/content';
import {ShowVendorAppsComponent} from '@app/app/components/show-vendor-apps/show-vendor-apps.component';
import {FormConstants} from '@app/app/form.constants';
import { TagPrefixConstants } from '@app/services/segmentation-tag/segmentation-tag.service';
import { DomSanitizer } from '@angular/platform-browser';
import { ScreenOrientation } from '@ionic-native/screen-orientation/ngx';
import { DownloadTranscriptPopupComponent } from '../components/popups/download-transcript-popup/download-transcript-popup.component';
declare const cordova;
declare const window;
@Component({
selector: 'app-content-details',
templateUrl: './content-details.page.html',
styleUrls: ['./content-details.page.scss'],
encapsulation: ViewEncapsulation.None
})
export class ContentDetailsPage implements OnInit, OnDestroy {
appName: any;
shouldOpenPlayAsPopup = false;
apiLevel: number;
appAvailability: string;
content: Content | any;
playingContent: Content;
isChildContent = false;
contentDetails: any;
identifier: string;
headerObservable: any;
cardData: any;
depth: string;
isDownloadStarted = false;
downloadProgress: any;
cancelDownloading = false;
loader: any;
userId = '';
public objRollup: Rollup;
isContentPlayed = false;
/**
* Used to handle update content workflow
*/
isUpdateAvail = false;
streamingUrl?: any;
contentDownloadable: {
[contentId: string]: boolean;
} = {};
/**
* currently used to identify that its routed from QR code results page
* Can be sent from any page, where after landing on details page should download or play content automatically
*/
downloadAndPlay: boolean;
/**
* This flag helps in knowing when the content player is closed and the user is back on content details page.
*/
public isPlayerLaunched = false;
launchPlayer: boolean;
isResumedCourse: boolean;
didViewLoad: boolean;
contentDetail: any;
backButtonFunc: Subscription;
shouldGenerateEndTelemetry = false;
source = '';
userCount = 0;
shouldGenerateTelemetry = true;
playOnlineSpinner: boolean;
defaultAppIcon: string;
showMessage: any;
localImage: any;
isUsrGrpAlrtOpen = false;
private corRelationList: Array<CorrelationData>;
private eventSubscription: Subscription;
defaultLicense: string;
showChildrenLoader: any;
showLoading: any;
hierarchyInfo: any;
showDownload: boolean;
contentPath: Array<any>[];
FileSizePipe: any;
childPaths: Array<string> = [];
breadCrumbData: any;
telemetryObject: TelemetryObject;
contentDeleteObservable: any;
isSingleContent: boolean;
resultLength: any;
course: Course;
fileTransfer: FileTransferObject;
contentSize: any;
// Newly Added
licenseDetails;
resumedCourseCardData: any;
limitedShareContentFlag = false;
private isLoginPromptOpen = false;
private autoPlayQuizContent = false;
shouldNavigateBack = false;
isContentDownloading$: Observable<boolean>;
onboarding = false;
showCourseCompletePopup = false;
courseContext: any;
private contentProgressSubscription: Subscription;
private playerEndEventTriggered: boolean;
isCourseCertificateShown: boolean;
pageId = PageId.CONTENT_DETAIL;
maxAttemptAssessment: any;
isCompatibleWithVendorApps = false;
appLists: any;
isIOS = false;
playerType: any = null;
config: any;
nextContentToBePlayed: any;
isPlayerPlaying = false;
constructor(
@Inject('PROFILE_SERVICE') private profileService: ProfileService,
@Inject('CONTENT_SERVICE') private contentService: ContentService,
@Inject('EVENTS_BUS_SERVICE') private eventBusService: EventsBusService,
@Inject('STORAGE_SERVICE') private storageService: StorageService,
@Inject('DOWNLOAD_SERVICE') private downloadService: DownloadService,
@Inject('SHARED_PREFERENCES') private preferences: SharedPreferences,
@Inject('COURSE_SERVICE') private courseService: CourseService,
@Inject('PLAYER_SERVICE') private playerService: PlayerService,
private zone: NgZone,
private events: Events,
private popoverCtrl: PopoverController,
private platform: Platform,
public appGlobalService: AppGlobalService,
private telemetryGeneratorService: TelemetryGeneratorService,
private commonUtilService: CommonUtilService,
private courseUtilService: CourseUtilService,
private utilityService: UtilityService,
private network: Network,
private fileSizePipe: FileSizePipe,
private headerService: AppHeaderService,
private appVersion: AppVersion,
private location: Location,
private router: Router,
private route: ActivatedRoute,
private profileSwitchHandler: ProfileSwitchHandler,
private ratingHandler: RatingHandler,
private contentPlayerHandler: ContentPlayerHandler,
private childContentHandler: ChildContentHandler,
private contentDeleteHandler: ContentDeleteHandler,
private fileOpener: FileOpener,
private transfer: FileTransfer,
private sbProgressLoader: SbProgressLoader,
private localCourseService: LocalCourseService,
private formFrameworkUtilService: FormAndFrameworkUtilService,
private sanitizer: DomSanitizer,
private screenOrientation: ScreenOrientation
) {
this.subscribePlayEvent();
this.checkDeviceAPILevel();
this.checkappAvailability();
this.defaultAppIcon = 'assets/imgs/ic_launcher.png';
this.defaultLicense = ContentConstants.DEFAULT_LICENSE;
this.ratingHandler.resetRating();
this.route.queryParams.subscribe(async(params) => {
await this.getNavParams();
});
}
async getNavParams() {
const extras = this.content || this.router.getCurrentNavigation().extras.state;
if (extras) {
this.course = this.course || extras.course;
this.cardData = this.content || extras.content;
this.isChildContent = extras.isChildContent;
this.cardData.depth = extras.depth === undefined ? '' : extras.depth;
this.corRelationList = extras.corRelation;
this.identifier = this.cardData.contentId || this.cardData.identifier;
this.isResumedCourse = Boolean(extras.isResumedCourse);
this.source = extras.source || this.source;
this.shouldGenerateEndTelemetry = extras.shouldGenerateEndTelemetry;
this.downloadAndPlay = extras.downloadAndPlay;
this.playOnlineSpinner = true;
this.contentPath = extras.paths;
this.breadCrumbData = extras.breadCrumb;
this.launchPlayer = extras.launchplayer;
this.resumedCourseCardData = extras.resumedCourseCardData;
this.isSingleContent = extras.isSingleContent || this.isSingleContent;
this.resultLength = extras.resultsSize;
this.autoPlayQuizContent = extras.autoPlayQuizContent || false;
this.shouldOpenPlayAsPopup = extras.isCourse;
this.shouldNavigateBack = extras.shouldNavigateBack;
this.nextContentToBePlayed = extras.content;
this.playerType = extras.mimeType === 'video/mp4' && !this.content.contentData["interceptionPoints"] ? 'sunbird-video-player' : undefined;
this.checkLimitedContentSharingFlag(extras.content);
if (this.content && this.content.mimeType === 'application/vnd.sunbird.questionset' && !extras.content) {
await this.getContentState();
}
this.onboarding = extras.onboarding || this.onboarding;
this.setContentDetails(this.identifier, false, false);
}
this.isIOS = (this.platform.is('ios'))
this.isContentDownloading$ = this.downloadService.getActiveDownloadRequests().pipe(
map((requests) => !!requests.find((request) => request.identifier === this.identifier))
);
}
iosCheck() {
if (this.platform.is('ios') && this.content.mimeType === 'application/vnd.sunbird.questionset') {
return true;
} else {
return false;
}
}
async ngOnInit() {
this.subscribeEvents();
this.appLists = await this.formFrameworkUtilService.getFormFields(FormConstants.VENDOR_APPS_CONFIG);
this.appLists = this.appLists.filter((appData) => {
if (appData.target.mimeType &&
appData.target.mimeType.indexOf(this.cardData.mimeType) !== -1 &&
appData.target.primaryCategory &&
appData.target.primaryCategory.indexOf(this.cardData.primaryCategory)) {
return true;
}
});
if (this.appLists.length) {
this.isCompatibleWithVendorApps = true;
}
}
getNextContent(hierarchyInfo, identifier) {
return new Promise((resolve) => {
this.contentService.nextContent(hierarchyInfo, identifier).subscribe((res) => {
this.nextContentToBePlayed = res;
resolve(res);
})
})
}
subscribeEvents() {
// DEEPLINK_CONTENT_PAGE_OPEN is used to refresh the contend details on external deeplink clicked
this.events.subscribe(EventTopics.DEEPLINK_CONTENT_PAGE_OPEN, (data) => {
if (data && data.content) {
this.ratingHandler.resetRating();
this.autoPlayQuizContent = data.autoPlayQuizContent || false;
this.checkLimitedContentSharingFlag(data.content);
}
});
this.appVersion.getAppName()
.then((appName: any) => {
this.appName = appName;
});
if (!AppGlobalService.isPlayerLaunched) {
this.calculateAvailableUserCount();
}
this.events.subscribe(EventTopics.PLAYER_CLOSED, (data) => {
if (data.selectedUser) {
if (!data.selectedUser['profileType']) {
console.log('data', !data.selectedUser['profileType']);
this.profileService.getActiveProfileSession().toPromise()
.then((profile) => {
this.profileSwitchHandler.switchUser(profile);
});
} else {
this.profileSwitchHandler.switchUser(data.selectedUser);
}
}
});
this.events.subscribe(EventTopics.NEXT_CONTENT, async (data) => {
this.generateEndEvent();
this.content = data.content;
this.course = data.course;
await this.getNavParams();
setTimeout(() => {
this.contentPlayerHandler.setLastPlayedContentId('');
this.generateTelemetry(true);
}, 1000);
});
}
ngOnDestroy() {
this.events.unsubscribe(EventTopics.PLAYER_CLOSED);
this.events.unsubscribe(EventTopics.NEXT_CONTENT);
this.events.unsubscribe(EventTopics.DEEPLINK_CONTENT_PAGE_OPEN);
if (this.contentProgressSubscription) {
this.contentProgressSubscription.unsubscribe();
}
}
/**
* Ionic life cycle hook
*/
async ionViewWillEnter() {
this.headerService.hideStatusBar();
this.headerService.hideHeader();
if (this.isResumedCourse && !this.contentPlayerHandler.isContentPlayerLaunched()) {
if (this.isUsrGrpAlrtOpen) {
this.isUsrGrpAlrtOpen = false;
}
} else {
this.generateTelemetry();
}
this.isPlayedFromCourse();
if (this.shouldOpenPlayAsPopup) {
await this.getContentState();
}
this.setContentDetails(
this.identifier, true,
this.contentPlayerHandler.getLastPlayedContentId() === this.identifier);
this.subscribeSdkEvent();
this.findHierarchyOfContent();
this.handleDeviceBackButton();
}
ionViewDidEnter() {
this.sbProgressLoader.hide({ id: 'login' });
this.sbProgressLoader.hide({ id: this.identifier });
}
/**
* Ionic life cycle hook
*/
ionViewWillLeave(): void {
if (this.eventSubscription) {
this.eventSubscription.unsubscribe();
}
if (this.contentDeleteObservable) {
this.contentDeleteObservable.unsubscribe();
}
if (this.backButtonFunc) {
this.backButtonFunc.unsubscribe();
}
}
handleNavBackButton() {
if (this.platform.is('ios') && (this.screenOrientation.type === 'landscape-secondary' || this.screenOrientation.type === 'landscape-primary')) {
this.screenOrientation.lock(this.screenOrientation.ORIENTATIONS.PORTRAIT);
return false;
}
this.telemetryGeneratorService.generateBackClickedTelemetry(PageId.CONTENT_DETAIL, Environment.HOME,
true, this.cardData.identifier, this.corRelationList, this.objRollup, this.telemetryObject);
this.didViewLoad = false;
this.generateEndEvent();
if (this.shouldGenerateEndTelemetry) {
this.generateQRSessionEndEvent(this.source, this.cardData.identifier);
}
this.popToPreviousPage(true);
}
handleDeviceBackButton() {
this.backButtonFunc = this.platform.backButton.subscribeWithPriority(10, () => {
if (this.platform.is('ios') && this.screenOrientation.type === 'landscape-secondary') {
this.screenOrientation.lock(this.screenOrientation.ORIENTATIONS.PORTRAIT);
} else {
this.telemetryGeneratorService.generateBackClickedTelemetry(PageId.CONTENT_DETAIL, Environment.HOME,
false, this.cardData.identifier, this.corRelationList, this.objRollup, this.telemetryObject);
this.didViewLoad = false;
this.popToPreviousPage(false);
this.generateEndEvent();
if (this.shouldGenerateEndTelemetry) {
this.generateQRSessionEndEvent(this.source, this.cardData.identifier);
}
}
});
}
subscribePlayEvent() {
this.events.subscribe('playConfig', (config) => {
this.appGlobalService.setSelectedUser(config['selectedUser']);
this.playContent(config.streaming);
});
}
calculateAvailableUserCount() {
const profileRequest: GetAllProfileRequest = {
local: true,
server: false
};
this.profileService.getAllProfiles(profileRequest).pipe(
map((profiles) => profiles.filter((profile) => !!profile.handle))
)
.toPromise()
.then((profiles) => {
if (profiles) {
this.userCount = profiles.length;
}
if (this.appGlobalService.isUserLoggedIn()) {
this.userCount += 1;
}
}).catch((error) => {
console.error('Error occurred= ', error);
});
}
/**
* To set content details in local variable
* @param identifier identifier of content / course
*/
async setContentDetails(identifier, refreshContentDetails: boolean, showRating: boolean) {
let loader;
if (!showRating) {
loader = await this.commonUtilService.getLoader();
await loader.present();
}
const req: ContentDetailRequest = {
contentId: identifier,
objectType: this.cardData.objectType,
attachFeedback: true,
attachContentAccess: true,
emitUpdateIfAny: refreshContentDetails
};
this.contentService.getContentDetails(req).toPromise()
.then(async (data: Content) => {
if (data) {
if (data.contentData.size) {
this.contentSize = data.contentData.size;
}
if(this.cardData && this.cardData.hierachyInfo) {
await this.getNextContent(this.cardData.hierachyInfo, this.cardData.identifier);
}
this.extractApiResponse(data);
if (!showRating) {
await loader.dismiss();
}
if (data.contentData.status === 'Retired') {
this.showRetiredContentPopup();
}
} else {
if (!showRating) {
await loader.dismiss();
}
}
if (showRating) {
this.contentPlayerHandler.setContentPlayerLaunchStatus(false);
this.ratingHandler.showRatingPopup(
this.isContentPlayed,
data,
'automatic',
this.corRelationList,
this.objRollup,
this.shouldNavigateBack,
() => { this.openCourseCompletionPopup(); });
this.contentPlayerHandler.setLastPlayedContentId('');
}
})
.catch(async (error: any) => {
await loader.dismiss();
if (this.isDownloadStarted) {
this.contentDownloadable[this.content.identifier] = false;
this.isDownloadStarted = false;
}
if (error.hasOwnProperty('CONNECTION_ERROR')) {
this.commonUtilService.showToast('ERROR_NO_INTERNET_MESSAGE');
} else if (error.hasOwnProperty('SERVER_ERROR') || error.hasOwnProperty('SERVER_AUTH_ERROR')) {
this.commonUtilService.showToast('ERROR_FETCHING_DATA');
} else {
this.commonUtilService.showToast('ERROR_CONTENT_NOT_AVAILABLE');
}
this.location.back();
});
}
rateContent(popUpType: string) {
this.ratingHandler.showRatingPopup(this.isContentPlayed, this.content, popUpType, this.corRelationList, this.objRollup);
}
extractApiResponse(data: Content) {
this.checkLimitedContentSharingFlag(data);
if (this.isResumedCourse) {
const parentIdentifier = this.resumedCourseCardData && this.resumedCourseCardData.contentId ?
this.resumedCourseCardData.contentId : this.resumedCourseCardData.identifier;
this.childContentHandler.setChildContents(parentIdentifier, 0, this.identifier);
}
this.content = data;
if (data.contentData.licenseDetails && Object.keys(data.contentData.licenseDetails).length) {
this.licenseDetails = data.contentData.licenseDetails;
}
this.contentDownloadable[this.content.identifier] = data.isAvailableLocally;
if (this.content.lastUpdatedTime !== 0) {
this.playOnlineSpinner = false;
}
this.content.contentData.appIcon =
this.commonUtilService.convertFileSrc(ContentUtil.getAppIcon(this.content.contentData.appIcon, data.basePath,
this.commonUtilService.networkInfo.isNetworkAvailable));
this.content.contentAccess = data.contentAccess ? data.contentAccess : [];
this.content.contentMarker = data.contentMarker ? data.contentMarker : [];
if (this.cardData && this.cardData.hierarchyInfo) {
data.hierarchyInfo = this.cardData.hierarchyInfo;
this.isChildContent = true;
}
if (this.content.contentData.streamingUrl &&
(this.content.mimeType !== 'application/vnd.ekstep.h5p-archive')) {
this.streamingUrl = this.content.contentData.streamingUrl;
}
if (this.content.contentData.attributions && this.content.contentData.attributions.length) {
this.content.contentData.attributions = (this.content.contentData.attributions.sort()).join(', ');
}
if (!this.isChildContent && this.content.contentMarker.length
&& this.content.contentMarker[0].extraInfoMap
&& this.content.contentMarker[0].extraInfoMap.hierarchyInfo
&& this.content.contentMarker[0].extraInfoMap.hierarchyInfo.length) {
this.isChildContent = true;
}
this.playingContent = data;
this.telemetryObject = ContentUtil.getTelemetryObject(this.content);
this.markContent();
// Check locally available
if (Boolean(data.isAvailableLocally)) {
this.isUpdateAvail = data.isUpdateAvailable && !this.isUpdateAvail;
} else {
console.log("Data is not available locally.");
}
if (this.content.contentData.me_totalDownloads) {
this.content.contentData.me_totalDownloads = parseInt(this.content.contentData.me_totalDownloads, 10) + '';
}
if (this.isResumedCourse) {
this.cardData.contentData = this.content;
this.cardData.pkgVersion = this.content.contentData.pkgVersion;
this.generateTelemetry();
}
if (this.shouldGenerateTelemetry) {
this.generateDetailsInteractEvent();
this.shouldGenerateEndTelemetry = false;
}
if (this.contentPlayerHandler.isContentPlayerLaunched()) {
this.downloadAndPlay = false;
}
if (this.downloadAndPlay) {
if (!this.contentDownloadable[this.content.identifier] || this.content.isUpdateAvailable) {
/**
* Content is not downloaded then call the following method
* It will download the content and play it
*/
this.downloadContent();
} else {
/**
* If the content is already downloaded then just play it
*/
this.showSwitchUserAlert(false);
}
}
if ( (this.content.mimeType === 'video/mp4' || this.content.mimeType === 'video/webm') &&
!(typeof this.content.contentData['interceptionPoints'] === 'object' && this.content.contentData['interceptionPoints'] != null &&
Object.keys(this.content.contentData['interceptionPoints']).length !== 0) ) {
if (data && data.hierarchyInfo) {
this.getNextContent(data.hierarchyInfo, data.identifier);
}
this.playContent(true, true);
}
}
getImageContent() {
if(this.platform.is('ios')) {
return this.sanitizer.bypassSecurityTrustUrl(this.content.contentData.appIcon);
} else {
return this.content.contentData.appIcon;
}
}
generateTelemetry(forceGenerate?: boolean) {
if (!this.didViewLoad && !this.isContentPlayed || forceGenerate) {
this.objRollup = ContentUtil.generateRollUp(this.cardData.hierarchyInfo, this.identifier);
this.telemetryObject = ContentUtil.getTelemetryObject(this.cardData);
this.generateImpressionEvent(false, this.cardData.identifier, this.telemetryObject.type, this.cardData.pkgVersion);
this.generateStartEvent();
}
this.didViewLoad = true;
}
generateDetailsInteractEvent() {
const values = new Map();
values['isUpdateAvailable'] = this.isUpdateAvail;
values['isDownloaded'] = this.contentDownloadable[this.content.identifier];
values['autoAfterDownload'] = this.downloadAndPlay ? true : false;
this.telemetryGeneratorService.generateInteractTelemetry(InteractType.OTHER,
ImpressionType.DETAIL,
Environment.HOME,
PageId.CONTENT_DETAIL,
this.telemetryObject,
values,
this.objRollup,
this.corRelationList);
}
generateImpressionEvent(download, objectId?, objectType?, objectVersion?) {
if (this.corRelationList && this.corRelationList.length) {
this.corRelationList.push({
id: PageId.CONTENT_DETAIL,
type: CorReleationDataType.CHILD_UI
});
}
if (this.downloadAndPlay || download) {
this.telemetryGeneratorService.generateImpressionTelemetry(
download ? InteractType.DOWNLOAD_COMPLETE : InteractSubtype.DOWNLOAD_REQUEST,
download ? InteractType.DOWNLOAD_COMPLETE : InteractSubtype.DOWNLOAD_REQUEST,
download ? PageId.QR_CONTENT_RESULT : PageId.CONTENT_DETAIL,
this.source === PageId.ONBOARDING_PROFILE_PREFERENCES ? Environment.ONBOARDING : Environment.HOME,
undefined,
undefined,
undefined,
undefined,
this.corRelationList);
}
this.telemetryGeneratorService.generateImpressionTelemetry(
ImpressionType.DETAIL, '',
PageId.CONTENT_DETAIL,
Environment.HOME,
objectId,
objectType,
objectVersion,
this.objRollup,
this.corRelationList);
this.telemetryGeneratorService.generateImpressionTelemetry(
ImpressionType.PAGE_REQUEST, '',
PageId.CONTENT_DETAIL,
this.source === PageId.ONBOARDING_PROFILE_PREFERENCES ? Environment.ONBOARDING : Environment.HOME,
);
this.telemetryGeneratorService.generatePageLoadedTelemetry(
PageId.CONTENT_DETAIL,
this.source === PageId.ONBOARDING_PROFILE_PREFERENCES ? Environment.ONBOARDING : Environment.HOME,
undefined,
undefined,
undefined,
undefined,
this.corRelationList
);
}
generateStartEvent() {
this.telemetryGeneratorService.generateStartTelemetry(
PageId.CONTENT_DETAIL,
this.telemetryObject,
this.objRollup,
this.corRelationList);
}
generateEndEvent() {
this.telemetryGeneratorService.generateEndTelemetry(
this.telemetryObject.type ? this.telemetryObject.type : CsPrimaryCategory.LEARNING_RESOURCE,
Mode.PLAY,
PageId.CONTENT_DETAIL,
Environment.HOME,
this.telemetryObject,
this.objRollup,
this.corRelationList);
}
generateQRSessionEndEvent(pageId: string, qrData: string) {
if (pageId !== undefined) {
const telemetryObject = new TelemetryObject(qrData, 'qr', '');
this.telemetryGeneratorService.generateEndTelemetry(
'qr',
Mode.PLAY,
pageId,
Environment.HOME,
telemetryObject,
undefined,
this.corRelationList);
}
}
popToPreviousPage(isNavBack?) {
this.appGlobalService.showCourseCompletePopup = false;
if (this.screenOrientation.type === 'landscape-primary') {
this.screenOrientation.lock(this.screenOrientation.ORIENTATIONS.PORTRAIT);
}
if (this.isSingleContent) {
!this.onboarding ? this.router.navigate([`/${RouterLinks.TABS}`]) : window.history.go(-3);
} else if (this.source === PageId.ONBOARDING_PROFILE_PREFERENCES) {
if (this.appGlobalService.isOnBoardingCompleted) {
this.router.navigate([`/${RouterLinks.TABS}`]);
} else {
this.router.navigate([`/${RouterLinks.PROFILE_SETTINGS}`], { state: { showFrameworkCategoriesMenu: true }, replaceUrl: true });
}
} else if (this.resultLength === 1) {
window.history.go(-2);
} else {
this.location.back();
}
}
/**
* Function to get import content api request params
*
* @param identifiers contains list of content identifier(s)
*/
getImportContentRequestBody(identifiers: Array<string>, isChild: boolean): Array<ContentImport> {
const requestParams = [];
const folderPath = this.platform.is('ios') ? cordova.file.documentsDirectory : this.storageService.getStorageDestinationDirectoryPath();
identifiers.forEach((value) => {
requestParams.push({
isChildContent: isChild,
destinationFolder: folderPath,
contentId: value,
correlationData: this.corRelationList !== undefined ? this.corRelationList : [],
rollUp: isChild ? this.objRollup : undefined
});
});
return requestParams;
}
/**
* Function to get import content api request params
*
* @param identifiers contains list of content identifier(s)
*/
importContent(identifiers: Array<string>, isChild: boolean) {
const contentImportRequest: ContentImportRequest = {
contentImportArray: this.getImportContentRequestBody(identifiers, isChild),
contentStatusArray: ['Live'],
fields: ['appIcon', 'name', 'subject', 'size', 'gradeLevel']
};
// Call content service
this.contentService.importContent(contentImportRequest).toPromise()
.then((data: ContentImportResponse[]) => {
if (data && data[0].status === -1) {
this.showDownload = false;
this.isDownloadStarted = false;
this.commonUtilService.showToast('ERROR_CONTENT_NOT_AVAILABLE');
}
})
.catch((error) => {
console.log('error while loading content details', error);
if (this.isDownloadStarted) {
this.showDownload = false;
this.contentDownloadable[this.content.identifier] = false;
this.isDownloadStarted = false;
}
this.commonUtilService.showToast('SOMETHING_WENT_WRONG');
});
}
openinBrowser(url) {
this.commonUtilService.openUrlInBrowser(url);
}
/**
* Subscribe Sunbird-SDK event to get content download progress
*/
subscribeSdkEvent() {
this.eventSubscription = this.eventBusService.events().subscribe((event: EventsBusEvent) => {
this.zone.run(() => {
if (event.type === DownloadEventType.PROGRESS) {
const downloadEvent = event as DownloadProgress;
if (downloadEvent.payload.identifier === this.content.identifier) {
this.showDownload = true;
this.isDownloadStarted = true;
this.downloadProgress = downloadEvent.payload.progress === -1 ? '0' : downloadEvent.payload.progress;
this.downloadProgress = Math.round(this.downloadProgress);
if (isNaN(this.downloadProgress)) {
this.downloadProgress = 0;
}
if (this.downloadProgress === 100) {
this.showLoading = false;
this.showDownload = false;
this.content.isAvailableLocally = true;
}
}
}
// Get child content
if (event.type === ContentEventType.IMPORT_COMPLETED) {
if (this.isDownloadStarted) {
this.isDownloadStarted = false;
this.cancelDownloading = false;
this.contentDownloadable[this.content.identifier] = true;
this.generateImpressionEvent(true);
this.setContentDetails(this.identifier, false, false);
this.downloadProgress = '';
this.events.publish('savedResources:update', {
update: true
});
}
}
// For content update available
if (event.payload && event.type === ContentEventType.UPDATE) {
this.zone.run(() => {
this.isUpdateAvail = true;
if (event.payload.size) {
this.content.contentData.size = event.payload.size;
}
});
}
if (event.payload && event.type === ContentEventType.SERVER_CONTENT_DATA) {
this.zone.run(() => {
const eventPayload = event.payload;
if (this.content && eventPayload.contentId === this.content.identifier) {
if (eventPayload.streamingUrl && (this.content.mimeType !== 'application/vnd.ekstep.h5p-archive')) {
this.streamingUrl = eventPayload.streamingUrl;
this.playingContent.contentData.streamingUrl = eventPayload.streamingUrl;
} else {
this.playOnlineSpinner = false;
}
if (eventPayload.licenseDetails && Object.keys(eventPayload.licenseDetails).length) {
this.licenseDetails = eventPayload.licenseDetails;
}
}
});
}
});
}) as any;
}
/**
* confirming popUp content
*/
async openConfirmPopUp() {
if (this.limitedShareContentFlag) {
this.commonUtilService.showToast('DOWNLOAD_NOT_ALLOWED_FOR_QUIZ');
return;
}
if (!this.content.contentData.downloadUrl) {
this.commonUtilService.showToast('DOWNLOAD_NOT_ALLOWED_FOR_QUIZ');
return;
}
this.telemetryGeneratorService.generateInteractTelemetry(InteractType.TOUCH,
this.isUpdateAvail ? InteractSubtype.UPDATE_INITIATE : InteractSubtype.DOWNLOAD_INITIATE,
Environment.HOME,
PageId.CONTENT_DETAIL,
this.telemetryObject,
undefined,
this.objRollup,
this.corRelationList);
if (this.commonUtilService.networkInfo.isNetworkAvailable) {
const popover = await this.popoverCtrl.create({
component: ConfirmAlertComponent,
componentProps: {
sbPopoverMainTitle: this.content.contentData.name,
icon: null,
metaInfo:
'1 item ' + '(' + this.fileSizePipe.transform(this.content.contentData.size || this.contentSize, 2) + ')',
isUpdateAvail: this.contentDownloadable[this.content.identifier] && this.isUpdateAvail,
},
cssClass: 'sb-popover info',
});
await popover.present();
const { data } = await popover.onDidDismiss();
if (data) {
this.downloadContent();
} else {
this.telemetryGeneratorService.generateInteractTelemetry(
InteractType.TOUCH,
InteractSubtype.CLOSE_CLICKED,
Environment.HOME,
PageId.CONTENT_DETAIL,
this.telemetryObject, undefined,
this.objRollup,
this.corRelationList);
}
} else {
this.commonUtilService.showToast('ERROR_NO_INTERNET_MESSAGE');
}
}
/**
* Download content
*/
downloadContent() {
this.zone.run(() => {
if (this.commonUtilService.networkInfo.isNetworkAvailable) {
this.showDownload = true;
this.downloadProgress = '0';
this.isDownloadStarted = true;
const values = new Map();
values['network-type'] = this.network.type;
values['size'] = this.content.contentData.size;
this.importContent([this.identifier], this.isChildContent);
this.telemetryGeneratorService.generateInteractTelemetry(InteractType.TOUCH,
this.isUpdateAvail ? InteractSubtype.UPDATE_INITIATE : InteractSubtype.DOWNLOAD_INITIATE,
Environment.HOME,
PageId.CONTENT_DETAIL,
this.telemetryObject,
values,
this.objRollup,
this.corRelationList);
}
});
}
cancelDownload() {
const ObjectTelemetry = new TelemetryObject(this.cardData.identifier, ObjectType.CONTENT, '');
const corRelationData: CorrelationData[] = [{
id: InteractSubtype.DOWLOAD_POPUP,
type: CorReleationDataType.CHILD_UI
}];
this.telemetryGeneratorService.generateInteractTelemetry(
InteractType.SELECT_CLOSE,
InteractSubtype.CANCEL,
this.source === PageId.ONBOARDING_PROFILE_PREFERENCES ? Environment.ONBOARDING : Environment.HOME,
PageId.CONTENT_DETAIL,
ObjectTelemetry,
undefined,
undefined,
corRelationData
);
this.telemetryGeneratorService.generateInteractTelemetry(InteractType.TOUCH,
InteractSubtype.DOWNLOAD_CANCEL_CLICKED,
Environment.HOME,
PageId.CONTENT_DETAIL,
this.telemetryObject,
undefined,
this.objRollup,
this.corRelationList);
this.contentService.cancelDownload(this.identifier).toPromise()
.then(() => {
this.zone.run(() => {
this.telemetryGeneratorService.generateContentCancelClickedTelemetry(this.content, this.downloadProgress);
this.isDownloadStarted = false;
this.showDownload = false;
this.downloadProgress = '';
if (!this.isUpdateAvail) {
this.contentDownloadable[this.content.identifier] = false;
}
});
}).catch((error: any) => {
this.zone.run(() => {
console.log('Error: download error =>>>>>', error);
});
});
}
async handleContentPlay(isStreaming) {
const maxAttempt: MaxAttempt = await this.commonUtilService.handleAssessmentStatus(this.maxAttemptAssessment);
if (maxAttempt.isCloseButtonClicked || maxAttempt.limitExceeded) {
return;
}
if (this.limitedShareContentFlag) {
if (!this.content || !this.content.contentData || !this.content.contentData.streamingUrl) {
return;
}
if (!this.appGlobalService.isUserLoggedIn()) {
this.promptToLogin();
} else {
this.showSwitchUserAlert(true);
}
} else {
this.showSwitchUserAlert(isStreaming);
}
}
/**
* alert for playing the content
*/
async showSwitchUserAlert(isStreaming: boolean) {
if (isStreaming && !this.commonUtilService.networkInfo.isNetworkAvailable) {
this.commonUtilService.showToast('INTERNET_CONNECTIVITY_NEEDED');
return false;
} else {
const values = new Map();
const subtype: string = isStreaming ? InteractSubtype.PLAY_ONLINE : InteractSubtype.PLAY_FROM_DEVICE;
values['networkType'] = this.network.type;
this.telemetryGeneratorService.generateInteractTelemetry(InteractType.TOUCH,
subtype,
Environment.HOME,
PageId.CONTENT_DETAIL,
this.telemetryObject,
values,
this.objRollup,
this.corRelationList);
}
if (!AppGlobalService.isPlayerLaunched && this.userCount > 2 && this.network.type !== '2g' && !this.shouldOpenPlayAsPopup
&& !this.limitedShareContentFlag) {
this.openPlayAsPopup(isStreaming);
} else if (this.network.type === '2g' && !this.contentDownloadable[this.content.identifier]) {
const popover = await this.popoverCtrl.create({
component: SbGenericPopoverComponent,
componentProps: {
sbPopoverHeading: this.commonUtilService.translateMessage('LOW_BANDWIDTH'),
sbPopoverMainTitle: this.commonUtilService.translateMessage('LOW_BANDWIDTH_DETECTED'),
actionsButtons: [
{
btntext: this.commonUtilService.translateMessage('PLAY_ONLINE'),
btnClass: 'popover-color'
},
{
btntext: this.commonUtilService.translateMessage('DOWNLOAD'),
btnClass: 'sb-btn sb-btn-normal sb-btn-info'
}
],
icon: {
md: 'sad',
ios: 'sad',
className: ''
},
metaInfo: '',
sbPopoverContent: this.commonUtilService.translateMessage('CONSIDER_DOWNLOAD')
},
cssClass: 'sb-popover warning',
});
await popover.present();
const { data } = await popover.onDidDismiss();
if (data == null) {
return;
}
if (data && data.isLeftButtonClicked) {
if (!AppGlobalService.isPlayerLaunched && this.userCount > 2 && !this.shouldOpenPlayAsPopup && !this.limitedShareContentFlag) {
this.openPlayAsPopup(isStreaming);
} else {
this.playContent(isStreaming);
}
} else {
this.downloadContent();
}
} else {
if (this.source === PageId.ONBOARDING_PROFILE_PREFERENCES) {
this.telemetryGeneratorService.generateImpressionTelemetry(
InteractType.PLAY,
InteractSubtype.DOWNLOAD,
PageId.QR_CONTENT_RESULT,
Environment.ONBOARDING
);
}
this.playContent(isStreaming);
}
}
async showRetiredContentPopup() {
const popover = await this.popoverCtrl.create({
component: SbGenericPopoverComponent,
componentProps: {
sbPopoverHeading: this.commonUtilService.translateMessage('CONTENT_NOT_AVAILABLE'),
sbPopoverMainTitle: this.commonUtilService.translateMessage('CONTENT_RETIRED_BY_AUTHOR'),
actionsButtons: [
],
icon: {
md: 'warning',
ios: 'warning',
className: ''
}
},
cssClass: 'sb-popover warning',
});
await popover.present();
popover.onDidDismiss().then(() => {
this.location.back();
});
}
async openPlayAsPopup(isStreaming) {
const profile = this.appGlobalService.getCurrentUser();
this.isUsrGrpAlrtOpen = true;
const confirm = await this.popoverCtrl.create({
component: SbGenericPopoverComponent,
componentProps: {
sbPopoverHeading: this.commonUtilService.translateMessage('PLAY_AS'),
sbPopoverMainTitle: profile.handle,
actionsButtons: [
{
btntext: this.commonUtilService.translateMessage('YES'),
btnClass: 'popover-color'
}
],
icon: null
},
cssClass: 'sb-popover info',
});
await confirm.present();
const { data } = await confirm.onDidDismiss();
if (data == null) {
return;
}
if (data && data.isLeftButtonClicked) {
this.playContent(isStreaming);
// Incase of close button click data.isLeftButtonClicked = null so we have put the false condition check
}
}
/**
* Play content
*/
private playContent(isStreaming: boolean, loadPlayer: boolean = false) {
if (this.apiLevel < 21 && this.appAvailability === 'false' && !this.isIOS) {
this.showPopupDialog();
} else {
const hierachyInfo = this.childContentHandler.contentHierarchyInfo || this.content.hierarchyInfo;
const contentInfo: ContentInfo = {
telemetryObject: this.telemetryObject,
rollUp: this.objRollup,
correlationList: this.corRelationList,
hierachyInfo,
course: this.course
};
if (this.isResumedCourse) {
this.playingContent.hierarchyInfo = hierachyInfo;
}
this.contentPlayerHandler.launchContentPlayer(this.playingContent, isStreaming,
this.downloadAndPlay, contentInfo, this.shouldOpenPlayAsPopup , true , this.isChildContent, this.maxAttemptAssessment,
loadPlayer ? (val) => this.handlePlayer(val) : undefined);
this.downloadAndPlay = false;
}
}
async handlePlayer(playerData) {
this.config = playerData.state.config;
let playerConfig = await this.formFrameworkUtilService.getPdfPlayerConfiguration();
if (["video/mp4", "video/webm"].includes(playerData.state.config['metadata']['mimeType']) && this.checkIsPlayerEnabled(playerConfig , 'videoPlayer').name === "videoPlayer" &&
(!this.content.contentData['interceptionPoints'] || Object.keys(this.content.contentData['interceptionPoints'].length === 0))) {
this.config = await this.getNewPlayerConfiguration();
this.config['config'].sideMenu.showPrint = false;
this.playerType = 'sunbird-video-player';
this.isPlayerPlaying = true;
}
}
async getNewPlayerConfiguration() {
const nextContent = this.config['metadata'].hierarchyInfo && this.nextContentToBePlayed ? { name: this.nextContentToBePlayed.contentData.name, identifier: this.nextContentToBePlayed.contentData.identifier } : undefined;
this.config['context']['pdata']['pid'] = 'sunbird.app.contentplayer';
if (this.config['metadata'].isAvailableLocally) {
this.config['metadata'].contentData.streamingUrl = '/_app_file_' + this.config['metadata'].basePath ?? this.config['metadata'].contentData.streamingUrl;
}
this.config['metadata']['contentData']['basePath'] = '/_app_file_' + this.config['metadata'].basePath;
this.config['metadata']['contentData']['isAvailableLocally'] = this.config['metadata'].isAvailableLocally;
this.config['metadata'] = this.config['metadata'].contentData;
this.config['data'] = {};
this.config['config'] = {
...this.config['config'],
nextContent,
sideMenu: {
showShare: true,
showDownload: true,
showReplay: false,
showExit: true,
showPrint: true
}
};
if(this.config['metadata']['mimeType'] === "application/vnd.sunbird.questionset"){
let questionSet;
try{
questionSet = await this.contentService.getQuestionSetRead(this.content.identifier, {fields:'instructions'}).toPromise();
} catch(e){
console.log(e);
}
this.config['metadata']['instructions'] = questionSet && questionSet.questionset.instructions ? questionSet.questionset.instructions : undefined;
}
const profile = await this.profileService.getActiveSessionProfile({ requiredFields: ProfileConstants.REQUIRED_FIELDS }).toPromise();
this.config['context'].userData = {
firstName: profile && profile.serverProfile && profile.serverProfile.firstName ? profile.serverProfile.firstName : profile.handle,
lastName: ''
};
return this.config;
}
checkIsPlayerEnabled(config , playerType) {
return config.fields.find(ele => ele.name === playerType && ele.values[0].isEnabled);
}
playerTelemetryEvents(event) {
if (event) {
SunbirdSdk.instance.telemetryService.saveTelemetry(JSON.stringify(event)).subscribe(
(res) => console.log('response after telemetry', res),
);
}
}
async playerEvents(event) {
if (event.edata) {
const userId: string = this.appGlobalService.getCurrentUser().uid;
const parentId: string = (this.content.rollup && this.content.rollup.l1) ? this.content.rollup.l1 : this.content.identifier;
const contentId: string = this.content.identifier;
if (event.edata['type'] === 'END') {
const saveState: string = JSON.stringify(event.metaData);
this.playerService.savePlayerState(userId, parentId, contentId, saveState);
this.isPlayerPlaying = false;
}
if (event.edata['type'] === 'EXIT') {
this.playerService.deletePlayerSaveState(userId, parentId, contentId);
if (this.screenOrientation.type === 'landscape-primary') {
this.screenOrientation.lock(this.screenOrientation.ORIENTATIONS.PORTRAIT);
}
} else if(event.edata.type === 'NEXT_CONTENT_PLAY') {
if (this.screenOrientation.type === 'landscape-primary') {
this.screenOrientation.lock(this.screenOrientation.ORIENTATIONS.PORTRAIT);
}
this.playNextContent();
} else if (event.edata.type === 'compatibility-error') {
cordova.plugins.InAppUpdateManager.checkForImmediateUpdate(
() => {},
() => {}
);
} else if (event.edata.type === 'exdata') {
if (event.edata.currentattempt) {
const attemptInfo = {
isContentDisabled: event.edata.maxLimitExceeded,
isLastAttempt: event.edata.isLastAttempt
};
this.commonUtilService.handleAssessmentStatus(attemptInfo);
}
} else if (event.edata['type'] === 'FULLSCREEN') {
if(!this.platform.is('ios') || (this.platform.is('ios') && this.playerType !== "sunbird-video-player")) {
if (this.screenOrientation.type === 'portrait-primary') {
this.screenOrientation.lock(this.screenOrientation.ORIENTATIONS.LANDSCAPE);
} else if (this.screenOrientation.type === 'landscape-primary') {
this.screenOrientation.lock(this.screenOrientation.ORIENTATIONS.PORTRAIT);
}
}
}
} else if (event.type === 'ended') {
this.isContentPlayed = true;
this.rateContent('manual');
} else if (event.type === 'REPLAY') {
this.isPlayerPlaying = true;
}
}
playNextContent() {
const content = this.nextContentToBePlayed;
this.config = undefined;
this.events.publish(EventTopics.NEXT_CONTENT, {
content,
course: this.course
});
}
checkappAvailability() {
this.utilityService.checkAppAvailability(XwalkConstants.APP_ID)
.then((response: any) => {
this.appAvailability = response;
console.log('check App availability', this.appAvailability);
})
.catch((error: any) => {
console.error('Error ', error);
});
}
checkDeviceAPILevel() {
this.utilityService.getDeviceAPILevel()
.then((res: any) => {
this.apiLevel = res;
}).catch((error: any) => {
console.error('Error ', error);
});
}
showDeletePopup() {
this.contentDeleteObservable = this.contentDeleteHandler.contentDeleteCompleted$.subscribe(() => {
this.content.contentData.streamingUrl = this.streamingUrl;
this.contentDownloadable[this.content.identifier] = false;
const playContent = this.playingContent;
playContent.isAvailableLocally = false;
this.isDownloadStarted = false;
});
const contentInfo: ContentInfo = {
telemetryObject: this.telemetryObject,
rollUp: this.objRollup,
correlationList: this.corRelationList,
hierachyInfo: undefined
};
// when content size and sizeOn device is undefined
if (!this.content.contentData.size) {
this.content.contentData.size = this.contentSize;
}
this.contentDeleteHandler.showContentDeletePopup(this.content, this.isChildContent, contentInfo, PageId.CONTENT_DETAIL);
}
/**
* Shares content to external devices
*/
async share() {
// when content size and sizeOn device is undefined
if (!this.content.contentData.size) {
this.content.contentData.size = this.contentSize;
}
const popover = await this.popoverCtrl.create({
component: SbSharePopupComponent,
componentProps: {
content: this.content,
corRelationList: this.corRelationList,
objRollup: this.objRollup,
pageId: PageId.CONTENT_DETAIL,
shareItemType: this.isChildContent ? ShareItemType.LEAF_CONTENT : ShareItemType.ROOT_CONTENT
},
cssClass: 'sb-popover',
});
await popover.present();
}
/**
* To View Credits popup
* check if non of these properties exist, then return false
* else show ViewCreditsComponent
*/
viewCredits() {
if (!this.content.contentData.creator && !this.content.contentData.creators) {
if (!this.content.contentData.contributors && !this.content.contentData.owner) {
if (!this.content.contentData.attributions) {
return false;
}
}
}
this.courseUtilService.showCredits(this.content, PageId.CONTENT_DETAIL, this.objRollup, this.corRelationList);
}
/**
* method generates telemetry on click Read less or Read more
* @param param string as read less or read more
* @param objRollup object roll up
* @param corRelationList correlation List
*/
readLessorReadMore(param, objRollup, corRelationList) {
if(param === 'read-more-clicked'){
this.appGlobalService.setAccessibilityFocus('read-more-content')
} else {
this.appGlobalService.setAccessibilityFocus('read-more-less-btn');
}
param = 'read-more-clicked' === param ? InteractSubtype.READ_MORE_CLICKED : InteractSubtype.READ_LESS_CLICKED;
this.telemetryGeneratorService.generateInteractTelemetry(InteractType.TOUCH,
param,
Environment.HOME,
PageId.CONTENT_DETAIL,
this.telemetryObject,
undefined,
objRollup,
corRelationList
);
}
private async showPopupDialog() {
const popover = await this.popoverCtrl.create({
component: DialogPopupComponent,
componentProps: {
title: this.commonUtilService.translateMessage('ANDROID_NOT_SUPPORTED'),
body: this.commonUtilService.translateMessage('ANDROID_NOT_SUPPORTED_DESC'),
buttonText: this.commonUtilService.translateMessage('INSTALL_CROSSWALK')
},
cssClass: 'popover-alert'
});
await popover.present();
}
mergeProperties(mergeProp) {
return ContentUtil.mergeProperties(this.content.contentData, mergeProp);
}
findHierarchyOfContent() {
if (this.cardData && this.cardData.hierarchyInfo && this.breadCrumbData) {
this.cardData.hierarchyInfo.forEach((element) => {
const contentName = this.breadCrumbData.get(element.identifier);
this.childPaths.push(contentName);
});
this.childPaths.push(this.breadCrumbData.get(this.cardData.identifier));
}
}
isPlayedFromCourse() {
if (this.cardData.hierarchyInfo && this.cardData.hierarchyInfo.length && this.cardData.hierarchyInfo[0].contentType === 'course') {
this.shouldOpenPlayAsPopup = true;
}
}
async promptToLogin() {
if (this.appGlobalService.isUserLoggedIn()) {
if (this.autoPlayQuizContent) {
setTimeout(() => {
this.handleContentPlay(true);
this.autoPlayQuizContent = false;
}, 1000);
}
return;
}
if (this.isLoginPromptOpen) {
return;
}
this.telemetryGeneratorService.generateImpressionTelemetry(ImpressionType.VIEW,
'', PageId.SIGNIN_POPUP,
Environment.HOME,
this.telemetryObject.id,
this.telemetryObject.type,
this.telemetryObject.version,
this.objRollup,
this.corRelationList);
this.isLoginPromptOpen = true;
const confirm = await this.popoverCtrl.create({
component: SbPopoverComponent,
componentProps: {
sbPopoverMainTitle: this.commonUtilService.translateMessage('YOU_MUST_LOGIN_TO_ACCESS_QUIZ_CONTENT'),
metaInfo: this.commonUtilService.translateMessage('QUIZ_CONTENTS_ONLY_REGISTERED_USERS'),
sbPopoverHeading: this.commonUtilService.translateMessage('OVERLAY_SIGN_IN'),
isNotShowCloseIcon: true,
actionsButtons: [
{
btntext: this.commonUtilService.translateMessage('OVERLAY_SIGN_IN'),
btnClass: 'popover-color'
},
]
},
cssClass: 'sb-popover info',
});
await confirm.present();
const { data } = await confirm.onDidDismiss();
if (data && data.canDelete) {
this.telemetryGeneratorService.generateInteractTelemetry(InteractType.TOUCH,
InteractSubtype.LOGIN_CLICKED,
Environment.HOME,
PageId.SIGNIN_POPUP,
this.telemetryObject,
undefined,
this.objRollup,
this.corRelationList
);
this.router.navigate([RouterLinks.SIGN_IN], {state: {navigateToCourse: true}});
}
this.isLoginPromptOpen = false;
}
checkLimitedContentSharingFlag(content) {
this.limitedShareContentFlag = (content && content.contentData &&
content.contentData.status === ContentFilterConfig.CONTENT_STATUS_UNLISTED);
if (this.limitedShareContentFlag) {
this.content = content;
this.playingContent = content;
this.identifier = content.contentId || content.identifier;
this.telemetryObject = ContentUtil.getTelemetryObject(content);
this.promptToLogin();
window['segmentation'].SBTagService.pushTag(
window['segmentation'].SBTagService.getTags(TagPrefixConstants.CONTENT_ID) ? this.identifier : [this.identifier],
TagPrefixConstants.CONTENT_ID,
window['segmentation'].SBTagService.getTags(TagPrefixConstants.CONTENT_ID) ? false : true
);
}
}
async openPDFPreview(content: Content) {
this.telemetryGeneratorService.generateInteractTelemetry(InteractType.TOUCH,
InteractSubtype.PRINT_PDF_CLICKED,
Environment.HOME,
PageId.CONTENT_DETAIL,
this.telemetryObject,
undefined,
this.objRollup,
this.corRelationList
);
let url: string;
const pdf = ContentUtil.resolvePDFPreview(content);
const loader: Components.IonLoading = await this.commonUtilService.getLoader();
await loader.present();
try {
if (!pdf.availableLocally) {
this.fileTransfer = this.transfer.create();
const entry = await this.fileTransfer
.download(pdf.url, cordova.file.cacheDirectory + pdf.url.substring(pdf.url.lastIndexOf('/') + 1));
url = entry.toURL();
} else {
url = 'file://' + pdf.url;
}
await new Promise<boolean>((resolve, reject) => {
window.cordova.plugins.printer.canPrintItem(url, (canPrint: boolean) => {
if (canPrint) {
window.cordova.plugins.printer.print(url);
return resolve();
}
return reject('Could not print item');
});
});
} catch (e) {
this.commonUtilService.showToast('ERROR_COULD_NOT_OPEN_FILE');
} finally {
await loader.dismiss();
}
}
// pass coursecontext to ratinghandler if course is completed
async getContentState() {
return new Promise(async (resolve, reject) => {
this.courseContext = await this.preferences.getString(PreferenceKey.CONTENT_CONTEXT).toPromise();
if (this.courseContext) {
this.courseContext = JSON.parse(this.courseContext);
if (this.courseContext.courseId && this.courseContext.batchId && this.courseContext.leafNodeIds) {
const courseDetails: any = await this.localCourseService.getCourseProgress(this.courseContext);
const progress = courseDetails.progress;
const contentStatusData = courseDetails.contentStatusData || {};
if (progress !== 100) {
this.appGlobalService.showCourseCompletePopup = true;
}
if (this.appGlobalService.showCourseCompletePopup && progress === 100) {
this.appGlobalService.showCourseCompletePopup = false;
this.showCourseCompletePopup = true;
}
this.maxAttemptAssessment = this.localCourseService.fetchAssessmentStatus(contentStatusData, this.cardData);
}
}
resolve();
});
}
async openCourseCompletionPopup() {
if (this.isCourseCertificateShown) {
return;
}
await this.getContentState();
if (!this.showCourseCompletePopup) {
if (this.contentProgressSubscription) {
this.contentProgressSubscription.unsubscribe();
}
this.contentProgressSubscription = this.eventBusService.events(EventNamespace.CONTENT)
.pipe(
filter((event) =>
event.type === ContentEventType.COURSE_STATE_UPDATED && this.course &&
(event as ContentUpdate).payload.contentId === this.course.contentId && this.shouldOpenPlayAsPopup
),
take(1),
tap(() => this.openCourseCompletionPopup())
)
.subscribe();
return;
}
const popUp = await this.popoverCtrl.create({
component: CourseCompletionPopoverComponent,
componentProps: {
isCertified: this.courseContext['isCertified'],
certificateDescription: await this.fetchCertificateDescription(this.courseContext && this.courseContext.batchId),
course: this.course ? this.course.content : undefined
},
cssClass: 'sb-course-completion-popover',
});
this.isCourseCertificateShown = true;
await popUp.present();
const { data } = await popUp.onDidDismiss();
if (data === undefined) {
this.telemetryGeneratorService.generateInteractTelemetry(
InteractType.TOUCH,
InteractSubtype.CLOSE_CLICKED,
PageId.COURSE_COMPLETION_POPUP,
Environment.HOME
);
}
}
async fetchCertificateDescription(batchId) {
if (!batchId) {
return '';
}
try {
const batchDetails = await this.courseService.getBatchDetails({ batchId }).toPromise();
for (let key in batchDetails.cert_templates) {
return (batchDetails && batchDetails.cert_templates[key] &&
batchDetails.cert_templates[key].description) || '';
}
} catch (e) {
console.log(e);
return '';
}
}
async openWithVendorApps() {
this.telemetryGeneratorService.generateInteractTelemetry(
InteractType.TOUCH,
InteractSubtype.OPEN_WITH_PLAYER_CLICKED,
Environment.HOME,
PageId.CONTENT_DETAIL,
);
const popoverElement = await this.popoverCtrl.create({
component: ShowVendorAppsComponent,
componentProps: {
content: this.content,
appLists: this.appLists
},
cssClass: 'sb-popover'
});
await popoverElement.present();
}
async showDownloadTranscript() {
const newThemePopover = await this.popoverCtrl.create({
component: DownloadTranscriptPopupComponent,
componentProps: {
contentData: this.content.contentData
},
backdropDismiss: false,
showBackdrop: true,
cssClass: 'download-transcript-popup'
});
newThemePopover.present();
}
markContent() {
const addContentAccessRequest: ContentAccess = {
status: ContentAccessStatus.PLAYED,
contentId: this.identifier,
contentType: this.content.contentType
};
const profile: Profile = this.appGlobalService.getCurrentUser();
this.profileService.addContentAccess(addContentAccessRequest).toPromise().then((data) => {
if (data) {
this.events.publish(EventTopics.LAST_ACCESS_ON, true);
}
});
const contentMarkerRequest: ContentMarkerRequest = {
uid: profile.uid,
contentId: this.identifier,
data: JSON.stringify(this.content.contentData),
marker: MarkerType.PREVIEWED,
isMarked: true,
extraInfo: {}
};
this.contentService.setContentMarker(contentMarkerRequest).toPromise().then();
}
}
<!-- New content details -->
<ion-content *ngIf="content">
<div class="sb-play-container sb-player-container" *ngIf="commonUtilService.networkInfo.isNetworkAvailable && playerType !== 'sunbird-video-player'"
[ngClass]="{'standalone-player-container': contentDownloadable[content?.identifier] && (content.mimeType === 'video/mp4' || content.mimeType === 'video/webm'), '': !contentDownloadable[content?.identifier]}">
<div *ngIf="contentDownloadable[content?.identifier]">
<img class="sb-player-container" [src]="content | imageContent" alt="">
<div class="sb-overlay-container">
<div class="sb-back-transparent" (click)="handleNavBackButton();$event.stopPropagation()">
<div class="sb-circle">
<img src="assets/imgs/ic_back.svg" alt="back" aria-label="back" class="sb-arrow-back" role="button">
</div>
</div>
<div class="sb-dt-content sb-dt-content-main">
<div class="left">
<div class="sb-dt-title" role="heading" aria-level="1"> {{content?.contentData?.name}}</div>
<div class="sb-dt-source" *ngIf="content?.contentData?.owner || content?.contentData?.creator">
<span class="sb-by-gray">{{ 'CONTENT_CREATED_BY' | translate }} </span>
<span class="sb-user">{{ content?.contentData?.owner || content?.contentData?.creator }}</span>
<span class="sb-rating-view" *ngIf="content?.contentData?.me_averageRating">
<span class="content-vertical-line"></span>
<span class="sb-rating-value">{{ content?.contentData?.me_averageRating }}</span>
<span class="sb-rating-star">
<ion-icon name="star"></ion-icon>
</span>
</span>
</div>
</div>
</div>
<div class="sb-common-player-group sb-play-title-group">
<button fill="clear" icon-only class="sb-common-title" aria-label="Play video" (click)="handleContentPlay(false)">
<img src="assets/imgs/play_circle.svg" role="button" alt="play" class="play-circle">
</button>
<p class="play-text"> {{'PLAY' | translate}} </p>
</div>
</div>
</div>
<div *ngIf="!contentDownloadable[content?.identifier]">
<img class="sb-player-container" [src]="content| imageContent" alt="">
<div class="sb-overlay-container">
<div [ngClass]="{'sb-back-transparent': platform.is('android'), 'sb-back-transparent-ios': platform.is('ios')}"
(click)="handleNavBackButton();$event.stopPropagation()">
<div [ngClass]="{'sb-circle': platform.is('android'), 'sb-circle-ios': platform.is('ios')}">
<img src="assets/imgs/ic_back.svg" alt="back" aria-label="back" class="sb-arrow-back" role="button">
</div>
</div>
<div class="sb-dt-content sb-dt-content-main">
<div class="left">
<div class="sb-dt-title" role="heading" aria-level="1"> {{content?.contentData?.name}}</div>
<div class="sb-dt-source" *ngIf="content?.contentData?.owner || content?.contentData?.creator">
<span class="sb-by-gray">{{ 'CONTENT_CREATED_BY' | translate }} </span>
<span class="sb-user">{{ content?.contentData?.owner || content?.contentData?.creator }}</span>
<span class="sb-rating-view" *ngIf="content?.contentData?.me_averageRating">
<span class="content-vertical-line"></span>
<span class="sb-rating-value">{{ content?.contentData?.me_averageRating }}</span>
<span class="sb-rating-star">
<ion-icon name="star"></ion-icon>
</span>
</span>
</div>
</div>
</div>
<div class="sb-common-player-group sb-play-title-group"
*ngIf="content.mimeType !== 'application/vnd.ekstep.h5p-archive'">
<button fill="clear" icon-only class="sb-common-title" aria-label="Play video" (click)="handleContentPlay(true)">
<img src="assets/imgs/play_circle.svg" role="button" alt="play" class="play-circle">
</button>
<p class="play-text"> {{'PLAY' | translate}} </p>
</div>
<div class="sb-common-player-group sb-download-title-group" (click)="openConfirmPopUp()"
*ngIf="content.mimeType === 'application/vnd.ekstep.h5p-archive'">
<ion-button fill="clear" icon-only class="sb-common-title">
<ion-icon name="cloud-download" role="button" class="sb-common-white-icon"></ion-icon>
</ion-button>
<div class="sb-common-footer-text">{{'PLEASE_DOWNLOAD_TO_PLAY' | translate}}</div>
</div>
</div>
</div>
</div>
<div *ngIf="playerType === 'sunbird-video-player'"
class="sb-play-container sb-player-container standalone-player-container" [ngClass]="{'' : isPlayerPlaying, 'sb-enable-endpage': (!isPlayerPlaying && screenOrientation.type === 'portrait-primary'), 'player-fullscreen': screenOrientation.type === 'landscape-primary'}">
<div class="sb-overlay-container">
<div class="sb-back-transparent" (click)="handleNavBackButton();$event.stopPropagation()">
<div class="sb-circle">
<img src="assets/imgs/ic_back.svg" alt="back" aria-label="back" class="sb-arrow-back" role="button">
</div>
</div>
</div>
<div *ngIf="playerType === 'sunbird-video-player' && config" [ngClass]="{'aspectratio' : isPlayerPlaying, '': !isPlayerPlaying}" data-ratio="16:9" #aspectRatio>
<sunbird-video-player [playerConfig]="config" (playerEvent)="playerEvents($event)"
(telemetryEvent)="playerTelemetryEvents($event)"></sunbird-video-player>
</div>
</div>
<div *ngIf="!commonUtilService.networkInfo.isNetworkAvailable && playerType !== 'sunbird-video-player'">
<div class="sb-play-container sb-player-container" *ngIf="contentDownloadable[content?.identifier] && playerType !== 'sunbird-video-player'">
<img class="sb-player-container" [src]="content | imageContent" alt="">
<div class="sb-overlay-container">
<div class="sb-back-transparent" (click)="handleNavBackButton();$event.stopPropagation()">
<div class="sb-circle">
<img src="assets/imgs/ic_back.svg" alt="back" aria-label="back" class="sb-arrow-back" role="button">
</div>
</div>
<div class="sb-dt-content sb-dt-content-main">
<div class="left">
<div class="sb-dt-title" role="heading" aria-level="1"> {{content?.contentData?.name}}</div>
<div class="sb-dt-source" *ngIf="content?.contentData?.owner || content?.contentData?.creator">
<span class="sb-by-gray">{{ 'CONTENT_CREATED_BY' | translate }} </span>
<span class="sb-user">{{ content?.contentData?.owner || content?.contentData?.creator }}</span>
<span class="sb-rating-view" *ngIf="content?.contentData?.me_averageRating">
<span class="content-vertical-line"></span>
<span class="sb-rating-value">{{ content?.contentData?.me_averageRating }}</span>
<span class="sb-rating-star">
<ion-icon name="star"></ion-icon>
</span>
</span>
</div>
</div>
</div>
<div class="sb-common-player-group sb-play-title-group">
<button fill="clear" icon-only class="sb-common-title" aria-label="Play video" (click)="handleContentPlay(false)">
<img src="assets/imgs/play_circle.svg" role="button" alt="play" class="play-circle">
</button>
<p class="play-text"> {{'PLAY' | translate}} </p>
</div>
</div>
</div>
<div *ngIf="contentDownloadable[content?.identifier] && playerType === 'sunbird-video-player'"
class="sb-play-container sb-player-container standalone-player-container"
[ngClass]="{'' : isPlayerPlaying, 'sb-enable-endpage': (!isPlayerPlaying && screenOrientation.type === 'portrait-primary'), 'player-fullscreen': screenOrientation.type === 'landscape-primary'}">
<div class="sb-overlay-container">
<div class="sb-back-transparent" (click)="handleNavBackButton();$event.stopPropagation()">
<div class="sb-circle">
<img src="assets/imgs/ic_back.svg" alt="back" aria-label="back" class="sb-arrow-back" role="button">
</div>
</div>
</div>
<div *ngIf="playerType === 'sunbird-video-player' && config" [ngClass]="{'aspectratio' : isPlayerPlaying, '': !isPlayerPlaying}"
data-ratio="16:9" #aspectRatio>
<sunbird-video-player [playerConfig]="config" (playerEvent)="playerEvents($event)"
(telemetryEvent)="playerTelemetryEvents($event)"></sunbird-video-player>
</div>
</div>
<div class="sb-player-container sb-offline-container" *ngIf="!contentDownloadable[content?.identifier]">
<div class="sb-back-transparent" (click)="handleNavBackButton();$event.stopPropagation()">
<div class="sb-circle">
<img src="assets/imgs/ic_back.svg" alt="back" aria-label="back" class="sb-arrow-back" role="button">
</div>
</div>
<div class="sb-common-player-group sb-common-title-group">
<div class="sb-offline-title">
<img src="assets/imgs/ic_offline_white_sm.png" alt="offline" />
</div>
<div class="sb-common-footer-text">{{'YOU_ARE_NOT_CONNECTED_TO_THE_INTERNET' | translate}}</div>
<div class="sb-common-footer-text sb-sub-footer">{{'ONLY_DOWNLOADED_CONTENT_CAN_BE_PLAYED_OFFLINE' |
translate}}</div>
</div>
</div>
</div>
<div class="sb-detail-card-info">
<div class="sb-dt-card">
<div class="sb-dt-card-actions">
<div class="sb-btn-tile-group">
<div *ngIf="content && !this.limitedShareContentFlag">
<button *ngIf="!contentDownloadable[content?.identifier]" (click)="openConfirmPopUp()"
[ngClass]="!(content?.contentData?.downloadUrl) ? 'download-disabled' : null"
[disabled]="isDownloadStarted || (isContentDownloading$ | async) || (content.mimeType === 'video/webm' && platform.is('ios'))" fill="clear" icon-only
class="sb-btn-tile">
<img src="assets/imgs/outline_download.svg" alt="download content" class="mb-4">
<span *ngIf="!isDownloadStarted" class="sb-btn-footer-text">{{'DOWNLOAD' | translate}}</span>
<span *ngIf="isDownloadStarted && showDownload" class="btn-popover-progress-container">
<span class="btn-progress-highlight" [ngStyle]="{'width': this.downloadProgress + '%'}"></span>
</span>
</button>
</div>
<button class="sb-btn-tile" *ngIf="isCompatibleWithVendorApps" (click)="openWithVendorApps()">
<img src="assets/imgs/ic_open_with.svg" alt="open-with" class="mb-4">
<span class="sb-btn-footer-text">{{'FRMELEMNTS_LBL_OPEN_WITH' | translate}}</span>
</button>
<button class="sb-btn-tile" *ngIf="contentDownloadable[content?.identifier]" (click)="showDeletePopup()">
<img src="assets/imgs/outline_delete.svg" alt="delete content" class="mb-4">
<span class="sb-btn-footer-text">{{'DELETE' | translate}}</span>
</button>
<button (click)="share()" class="sb-btn-tile" *ngIf="!this.limitedShareContentFlag">
<img src="assets/imgs/outline_share.svg" alt="share content" class="mb-4">
<span class="sb-btn-footer-text">{{'SHARE' | translate}}</span>
</button>
<button class="sb-btn-tile" (click)="rateContent('manual')">
<img src="assets/imgs/outline_rate_star.svg" alt="rate content" class="mb-4">
<span class="sb-btn-footer-text">{{'RATE' | translate}}</span>
</button>
<button class="sb-btn-tile" *ngIf="contentDownloadable[content?.identifier] && this.isUpdateAvail"
(click)="openConfirmPopUp()">
<img src="assets/imgs/outline_update.svg" alt="update content" class="mb-4">
<span class="sb-btn-footer-text">{{'UPDATE' | translate}}</span>
</button>
<button class="sb-btn-tile" (click)="openPDFPreview(content)" *ngIf="content?.contentData?.itemSetPreviewUrl">
<img src="assets/imgs/outline_print.svg" alt="print content" class="mb-4">
<span class="sb-btn-footer-text">{{'PRINT' | translate}}</span>
</button>
</div>
</div>
</div>
</div>
<div class="sb-content-info-container">
<div class="sb-content-info-items"
*ngIf="content?.contentData?.description || content?.contentData?.gradeLevel || content?.contentData?.subject || content?.contentData?.board">
<div class="sb-content-info-title">{{'ABOUT' | translate}}</div>
<div class="audience-info" *ngIf="content?.contentData.audience">
<ion-card-title class="font-12 subtitle-color label-margin-bottom">
<strong>{{'CONTENT_RELEVANT_MSG' | translate }}</strong>
<span class="sb-content-title-data">{{content?.contentData?.audience}}</span>
</ion-card-title>
</div>
<div *ngIf="content?.contentData?.transcripts" (click)="showDownloadTranscript()" class="transcript-download audience-info">
<strong>{{ 'DOWNLOAD_TRANSCRIPT' | translate}}</strong>
<span>
<img src="./assets/imgs/download-icon.svg" alt="download">
</span>
</div>
<ion-card class="sb-content-info-card"
*ngIf="content?.contentData?.description || content?.contentData?.gradeLevel || content?.contentData?.subject || content?.contentData?.board">
<div class="sb-content-info-heading"
*ngIf="content?.contentData?.description && content?.contentData?.description?.length">
{{content?.description}}</div>
<div class="sb-info" *ngIf="content?.contentData?.se_boards; else previousBoard">
<div class="sb-content-title">{{ 'BOARD' | translate }}</div>
<div class="sb-content-title-data">{{content?.contentData?.se_boards | aliased}}</div>
</div>
<ng-template #previousBoard>
<div class="sb-info">
<div class="sb-content-title">{{ 'BOARD' | translate }}</div>
<div class="sb-content-title-data">{{content?.contentData?.board | aliased}}</div>
</div>
</ng-template>
<div *ngIf="showMoreFlag" id="read-more-content" tabindex="0">
<div class="sb-info" *ngIf="content?.contentData?.se_mediums; else previousMedium">
<div class="sb-content-title">{{'MEDIUM' | translate}}</div>
<div class="sb-content-title-data">{{content?.contentData?.se_mediums | csa}}</div>
</div>
<ng-template #previousMedium>
<div class="sb-info">
<div class="sb-content-title">{{'MEDIUM' | translate}}</div>
<div class="sb-content-title-data">{{content?.contentData?.medium | csa}}</div>
</div>
</ng-template>
<div class="sb-info" *ngIf="content?.contentData?.se_gradeLevels && content?.contentData?.se_gradeLevels?.length; else previousGradeLevel">
<div class="sb-content-title">{{'CLASS' | translate}}</div>
<div class="sb-content-title-data">{{content?.contentData?.se_gradeLevels?.join(', ')}}</div>
</div>
<ng-template #previousGradeLevel>
<div class="sb-info">
<div class="sb-content-title">{{'CLASS' | translate}}</div>
<div class="sb-content-title-data">{{content?.contentData?.gradeLevel?.join(', ')}}</div>
</div>
</ng-template>
<div class="sb-info" *ngIf="content?.contentData?.subject">
<div class="sb-content-title">{{'SUBJECT' | translate}}</div>
<div class="sb-content-title-data">{{content?.contentData?.subject | csa}}</div>
</div>
<div class="sb-info" *ngIf="content?.contentData?.license && !licenseDetails">
<div class="sb-content-title">{{'LICENSE' | translate}}</div>
<div class="sb-content-title-data">{{content?.contentData?.license}}</div>
</div>
<div class="sb-info" *ngIf="!content?.contentData?.license && !licenseDetails">
<div class="sb-content-title">{{'LICENSE' | translate}}</div>
<div class="sb-content-title-data">{{defaultLicense}}</div>
</div>
<div class="sb-info" *ngIf="licenseDetails">
<div class="sb-content-title">{{'MULTIPLE_LICENSE' | translate:{'%s': licenseDetails?.name} }}</div>
{{licenseDetails.description}}
<div class="sb-url" (click)="openinBrowser(licenseDetails?.url)">{{ licenseDetails?.url }}</div>
</div>
<div class="sb-info" *ngIf="content?.contentData?.creators || content?.contentData?.creator">
<div class="sb-content-title">{{ 'CREATORS' | translate:{'%s': appName} }}</div>
<div class="sb-content-title-data">{{mergeProperties(['creators', 'creator'])}}</div>
</div>
<div class="sb-info" *ngIf="content?.contentData?.organisation">
<div class="sb-content-title">{{ 'PUBLISHED_BY' | translate:{'%s': appName} }}</div>
<div class="sb-content-title-data">{{content?.contentData?.organisation}}</div>
</div>
<div class="sb-info" *ngIf="content?.contentData?.author">
<div class="sb-content-title">{{'AUTHOR' | translate}}</div>
<div class="sb-content-title-data">{{content?.contentData?.author}}</div>
</div>
<div class="sb-info" *ngIf="content?.contentData?.copyright">
<div class="sb-content-title">{{'COPYRIGHT' | translate}}</div>
<div class="sb-content-title-data">
{{content?.contentData?.copyright}}{{content?.contentData.copyrightYear ? ", " +
content?.contentData.copyrightYear : ""}}
</div>
</div>
<div class="sb-info"
*ngIf="content?.contentData?.attributions && !content?.contentData?.contributors && !content?.contentData?.creators">
<div class="sb-content-title">{{'ATTRIBUTIONS' | translate}}</div>
<div class="sb-content-title-data">{{mergeProperties(['attributions', 'contributors', 'creators'])}}</div>
</div>
<div class="sb-info" *ngIf="content?.contentData?.origin">
<div class="sb-content-title">{{'ORIGINAL_CONTENT_DATA' | translate}}</div>
</div>
<div class="sb-info" *ngIf="content?.contentData?.origin && !content?.contentData?.originData">
<div class="sb-content-title-data">{{'NOT_AVAILABLE' | translate}}</div>
</div>
<div class="sb-info" *ngIf="content?.contentData?.originData?.name">
<div class="sb-content-title">{{'ORIGINAL_CONTENT' | translate}}</div>
<div class="sb-content-title-data">{{content?.contentData?.originData?.name}}</div>
</div>
<div class="sb-info" *ngIf="content?.contentData?.originData?.author">
<div class="sb-content-title">{{'ORIGINAL_AUTHOR' | translate}}</div>
<div class="sb-content-title-data">{{content?.contentData?.originData?.author}}</div>
</div>
<div class="sb-info" *ngIf="content?.contentData?.originData?.license">
<div class="sb-content-title">
{{'MULTIPLE_LICENSE' | translate:{'%s': content?.contentData?.originData?.license} }}</div>
</div>
<div class="sb-info" *ngIf="content?.contentData?.originData?.organisation">
<div class="sb-content-title">{{ 'PUBLISHED_BY' | translate:{'%s': appName} }}</div>
<div class="sb-content-title-data">{{content?.contentData?.originData?.organisation}}</div>
</div>
</div>
<div role="button" class="read-more" id="read-more-less-btn" tabindex="0" *ngIf="content && content.contentData.description &&
content.contentData.description.length > 10 || content.contentData.gradeLevel || content.contentData.subject"
(click)="showMoreFlag = !showMoreFlag; readLessorReadMore(showMoreFlag ? 'read-more-clicked' : 'read-less-clicked')">
<div *ngIf="showMoreFlag">
{{ 'READ_LESS' | translate }}
<ion-icon name="arrow-up" aria-hidden="true"></ion-icon>
</div>
<div *ngIf="!showMoreFlag">
{{ 'READ_MORE' | translate }}
</div>
</div>
</ion-card>
</div>
</div>
</ion-content>
<!-- Page footer -->
<app-sb-download-popup *ngIf="showDownload" (cancelDownloadEmit)="cancelDownload($event)" tabindex="1" role="dialog"
[downloadProgress]="downloadProgress" [contentName]="content?.contentData?.name" [isUpdateAvail]="isUpdateAvail"
[showDownload]="showDownload" [contentAvailableLocally]="contentDownloadable[content?.identifier]"
[contentSize]="content?.contentData?.size">
</app-sb-download-popup>
<div>
<add-activity-to-group [identifier]="identifier" [pageId]="pageId"></add-activity-to-group>
</div>
./content-details.page.scss
@import "src/assets/styles/base/_variables.scss";
@import "src/assets/styles/component/sb-offline-toast";
@import "src/assets/styles/_variables.scss";
.text-font {
.button-inner {
font-size: 1rem !important;
}
}
.cancel-download {
font-size: 3.125rem;
float: right;
padding-top: 3px;
}
.sb-url {
color: var(--app-primary);
font-size: $font-size-base;
}
.scroll-content {
margin-bottom: 0 !important;
}
.btn-progress-highlight {
left: 0;
background: $primary-800;
height: 0.313rem;
position: absolute;
border-radius: inherit;
border: 0;
}
.btn-popover-progress-container {
position: relative;
border-radius: 33px;
display: inline-block;
background: $primary-200;
height: 0.313rem;
width: 3.5rem;
}
.sb-back-transparent {
width: 3.13rem;
min-height: 3.13rem;
background: transparent;
}
.content-btn-padding {
padding: $sb-modal-actions-padding !important;
}
.sb-dt-card {
margin: 0 !important;
box-shadow: none;
background-color: $primary-100;
}
.sb-content-info-container .sb-content-info-items {
background: transparent;
.sb-content-info-title {
color: var(--app-gray);
margin: 0;
}
.sb-content-info-card {
box-shadow: none;
}
}
.sb-dt-title {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
font-weight: bold;
font-size: 1rem;
color: $gray-0;
margin-right: 15px;
}
.sb-rating-value {
font-weight: bold;
color: $gray-0;
}
.play-text {
color: $gray-0;
font-size: $font-size-base;
margin-top: 0;
text-transform: capitalize;
}
.sb-rating-star {
color: $yellow;
}
.sb-dt-source {
margin-top: 10px;
.sb-by-gray {
font-size: ($font-size-base - 0.25);
color: $gray-0;
}
.sb-user {
font-size: ($font-size-base - 0.125);
color: $gray-0;
}
}
.sb-dt-content.sb-dt-content-main {
text-align: left;
margin-left: 1.5rem;
}
.disable-ripple ion-button-effect {
display: none;
}
.disable-ripple {
pointer-events: none;
}
.content-vertical-line {
border-left: 2px solid white;
height: 1.125rem;
position: relative;
margin-left: 8px;
margin-right: 8px;
}
.sb-dt-card .sb-dt-card-actions .sb-btn-tile-group .sb-btn-tile {
background: transparent;
}
.sb-player-container .sb-common-player-group.sb-common-title-group {
top: 40%;
}
.sb-common-player-group{
.play-circle{
width: 4rem;
}
}
.sb-btn-tile-group button[disabled]{
opacity: 0.5 !important;
pointer-events: none !important;
cursor: default;
}
.download-disabled{
opacity: 0.5 !important;
cursor: default;
}
.sb-arrow-back{
padding: 0 !important;
transform: rotateY(180deg);
font-size: 1.5rem;
}
.sb-btn-footer-text{
color: var(--app-medium-gray);
}
.read-more{
color: $blue;
font-weight: bold;
font-size: 0.75rem;
padding-left: 0.5rem;
}
.sb-detail-card-info{
.sb-dt-card-actions{
padding-left: 1rem !important;
}
}
.audience-info {
margin-left: 1.4rem;
margin-top: 1rem;
}
.font-12 {
font-size: 0.75rem !important;
}
.subtitle-color{
color: map-get($colors, granite_gray);
}
// **** iOS
.sb-back-transparent-ios {
width: 3.13rem;
min-height: 6.25rem;
background: transparent;
}
// **** iOS
sb-player-side-menu-icon.sb-player-side-menu-icon.notVisible.ng-star-inserted {
display: none;
}
//aspect ratio
.aspectratio {
position: relative;
height: 0;
width: 100%;
z-index: 99;
&[data-ratio="16:9"] {
padding-top: 56.25%;
}
[data-ratio="4:3"] {
padding-top: 75%;
}
& > * {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
video,iframe {
width: 100%;
height: 100% !important;
}
}
}
.sb-play-container.sb-enable-endpage {
.user-score-card svg {
width: 80%;
}
.user-score-card, .player-endpage__right-panel, .user-options {
padding: 0 0 0 0 !important;
}
}
.sb-play-container .sb-back-transparent .sb-circle{
background-color: var(--app-light-gray);
}
.player-fullscreen {
width: 100%;
height: 100vh!important;
position:fixed !important;
top:0;
left:0;
bottom:0;
right:0;
border:none;
margin:0;
padding:0;
z-index:9999;
.player-endpage,.video-js,.player-for-back-ward-controls {
height: 100vh !important;
}
}
.standalone-player-container {
height: auto !important;
}
.transcript-download{
color: $blue;
font-family: "Noto Sans", sans-serif;
font-size: 1rem;
letter-spacing: 0;
line-height: 1.125rem;
}