37 #include <AiwGenericParam.h> |
37 #include <AiwGenericParam.h> |
38 #include <mpxcollectionuihelper.h> |
38 #include <mpxcollectionuihelper.h> |
39 #include <mpxcollectionhelperfactory.h> |
39 #include <mpxcollectionhelperfactory.h> |
40 #include <mpxcollectionplugin.hrh> |
40 #include <mpxcollectionplugin.hrh> |
41 #include <mpxmediageneralextdefs.h> |
41 #include <mpxmediageneralextdefs.h> |
42 |
|
43 #include <streaminglinkmodel.h> |
42 #include <streaminglinkmodel.h> |
44 #include <mpsettingsmodel.h> |
|
45 #include <coeutils.h> |
43 #include <coeutils.h> |
46 #include <videoplaylistutility.h> |
44 #include <videoplaylistutility.h> |
47 #include <cmmanagerext.h> |
45 #include <mmf/common/mmfcontrollerframeworkbase.h> |
48 #include <cmpluginwlandef.h> |
|
49 #include <commdb.h> // CMDBSession |
|
50 #include <commsdattypesv1_1.h> // CCDWAPIPBearerRecord |
|
51 #include <es_sock.h> |
|
52 #include <commdbconnpref.h> // TCommDbConnPref |
|
53 #include <extendedconnpref.h> |
|
54 |
46 |
55 #include "mpxvideoplayerappuiengine.h" |
47 #include "mpxvideoplayerappuiengine.h" |
56 #include "mpxvideoplayerlogger.h" |
48 #include "mpxvideoplayerlogger.h" |
57 #include "mpxvideoplayerconstants.h" |
49 #include "mpxvideoplayerconstants.h" |
58 #include "mpxvideoembeddedpdlhandler.h" |
50 #include "mpxvideoembeddedpdlhandler.h" |
59 #include <mpxvideoplaybackdefs.h> |
51 #include <mpxvideoplaybackdefs.h> |
60 #include "mpxvideo_debug.h" |
52 #include "mpxvideo_debug.h" |
61 #include "mpxvideoplayercustomviewmsgconsts.h" |
53 #include "mpxvideoplayercustomviewmsgconsts.h" |
62 |
54 |
63 _LIT( KMpxVideoPlayerQueryIap, "IAP\\Id" ); |
|
64 |
|
65 const TInt KMpxPlaybackPluginTypeUid = 0x101FFCA0; |
55 const TInt KMpxPlaybackPluginTypeUid = 0x101FFCA0; |
66 |
56 |
67 // ----------------------------------------------------------------------------- |
57 // ----------------------------------------------------------------------------- |
68 // CMpxVideoPlayerAppUiEngine::CMpxVideoPlayerAppUiEngine |
58 // CMpxVideoPlayerAppUiEngine::CMpxVideoPlayerAppUiEngine |
69 // ----------------------------------------------------------------------------- |
59 // ----------------------------------------------------------------------------- |
799 |
767 |
800 CleanupStack::PopAndDestroy( playlist ); |
768 CleanupStack::PopAndDestroy( playlist ); |
801 } |
769 } |
802 } |
770 } |
803 |
771 |
804 // ------------------------------------------------------------------------------------------------- |
|
805 // CMpxVideoPlayerAppUiEngine::SetAccessPointL |
|
806 // ------------------------------------------------------------------------------------------------- |
|
807 // |
|
808 void CMpxVideoPlayerAppUiEngine::SetAccessPointL() |
|
809 { |
|
810 MPX_ENTER_EXIT(_L("CMpxVideoPlayerAppUiEngine::SetAccessPointL()")); |
|
811 |
|
812 // |
|
813 // a value was passed in for argument aAPId |
|
814 // |
|
815 if ( ( iExtAccessPointId != KErrUnknown ) && IsWLANAccessPointL( iExtAccessPointId ) ) |
|
816 { |
|
817 // |
|
818 // An external WLAN access point was passed in by an embedding app, use it |
|
819 // |
|
820 iAccessPointId = iExtAccessPointId; |
|
821 } |
|
822 else |
|
823 { |
|
824 // |
|
825 // No access point was passed in or it's not WLAN, retrieve the default access point |
|
826 // |
|
827 TInt defaultAPId(0); |
|
828 |
|
829 MPX_TRAPD( err, defaultAPId = GetDefaultAccessPointL() ); |
|
830 |
|
831 if ( ( err == KErrNone ) && ( defaultAPId != 0 )) |
|
832 { |
|
833 // |
|
834 // Use the default access point |
|
835 // |
|
836 iAccessPointId = defaultAPId; |
|
837 } |
|
838 else |
|
839 { |
|
840 // |
|
841 // Valid default access point was not found |
|
842 // |
|
843 if ( iExtAccessPointId != KErrUnknown ) |
|
844 { |
|
845 // |
|
846 // Use the AP passed in by embedding app, regardless of bearer type |
|
847 // |
|
848 iAccessPointId = iExtAccessPointId; |
|
849 } |
|
850 else |
|
851 { |
|
852 // |
|
853 // Try to fetch AP ID and open connection selection dialog if required |
|
854 // |
|
855 TUint32 apUid = TryToGetAccessPointL(); |
|
856 |
|
857 // |
|
858 // Use selected access point |
|
859 // |
|
860 if ( apUid != 0 ) |
|
861 { |
|
862 // |
|
863 // convert the AccessPoint-Uid to AccessPoint-Id |
|
864 // |
|
865 iAccessPointId = GetAccessPointIdForUIDL( apUid ); |
|
866 } |
|
867 } |
|
868 } |
|
869 } |
|
870 } |
|
871 |
|
872 // ------------------------------------------------------------------------------------------------- |
|
873 // CMpxVideoPlayerAppUiEngine::IsWLANAccessPointL |
|
874 // ------------------------------------------------------------------------------------------------- |
|
875 // |
|
876 TBool CMpxVideoPlayerAppUiEngine::IsWLANAccessPointL( TInt aAPId ) |
|
877 { |
|
878 MPX_DEBUG(_L("CMpxVideoPlayerAppUiEngine::IsWLANAccessPointL(%d)"), aAPId); |
|
879 |
|
880 TBool wlanAP(EFalse); |
|
881 TUint32 bearer( 0 ); |
|
882 |
|
883 RCmManagerExt cmManager; |
|
884 CleanupClosePushL( cmManager ); |
|
885 cmManager.OpenL(); |
|
886 |
|
887 bearer = cmManager.ConnectionMethodL( aAPId ).GetIntAttributeL( CMManager::ECmBearerType ); |
|
888 CleanupStack::PopAndDestroy( &cmManager ); |
|
889 |
|
890 if ( bearer == KUidWlanBearerType ) |
|
891 { |
|
892 wlanAP = ETrue; |
|
893 } |
|
894 |
|
895 MPX_DEBUG(_L("CMpxVideoPlayerAppUiEngine::IsWLANAccessPointL() return %d"), wlanAP); |
|
896 |
|
897 return wlanAP; |
|
898 } |
|
899 |
|
900 // ----------------------------------------------------------------------------- |
|
901 // CMpxVideoPlayerAppUiEngine::GetDefaultAccessPointL |
|
902 // ----------------------------------------------------------------------------- |
|
903 // |
|
904 TInt CMpxVideoPlayerAppUiEngine::GetDefaultAccessPointL() |
|
905 { |
|
906 TInt defaultAP(0); |
|
907 TUint32 iap; |
|
908 |
|
909 CMPSettingsModel* ropSettings = CMPSettingsModel::NewL( KSettingsModelForROPUid ); |
|
910 |
|
911 CleanupStack::PushL( ropSettings ); |
|
912 |
|
913 ropSettings->LoadSettingsL( EConfigDefault ); |
|
914 |
|
915 User::LeaveIfError( ropSettings->GetDefaultAp( iap ) ); |
|
916 |
|
917 CleanupStack::PopAndDestroy(); // ropSettings |
|
918 |
|
919 defaultAP = GetAccessPointIdForUIDL( iap ); |
|
920 |
|
921 MPX_DEBUG(_L("CMpxVideoPlayerAppUiEngine::GetDefaultAccessPointL(%d)"), defaultAP); |
|
922 |
|
923 return defaultAP; |
|
924 } |
|
925 |
|
926 // ----------------------------------------------------------------------------- |
|
927 // CMpxVideoPlayerAppUiEngine::GetAccessPointIdForUIDL |
|
928 // ----------------------------------------------------------------------------- |
|
929 // |
|
930 TInt CMpxVideoPlayerAppUiEngine::GetAccessPointIdForUIDL( TUint32 aAPUid ) |
|
931 { |
|
932 MPX_ENTER_EXIT(_L("CMpxVideoPlayerAppUiEngine::GetAccessPointIdForUIDL()"), |
|
933 _L(" aAPUid = %d"), aAPUid); |
|
934 |
|
935 TInt apId(0); |
|
936 |
|
937 CMDBSession* db = CMDBSession::NewL( CMDBSession::LatestVersion() ); |
|
938 CleanupStack::PushL( db ); |
|
939 |
|
940 // |
|
941 // WapIpBearer table contains the mapping between wap and iap id's. |
|
942 // |
|
943 CCDWAPIPBearerRecord* wapBearerRecord = |
|
944 static_cast<CCDWAPIPBearerRecord*>(CCDRecordBase::RecordFactoryL(KCDTIdWAPIPBearerRecord)); |
|
945 |
|
946 CleanupStack::PushL( wapBearerRecord ); |
|
947 |
|
948 wapBearerRecord->iWAPAccessPointId = aAPUid; |
|
949 |
|
950 TBool found = wapBearerRecord->FindL( *db ); |
|
951 |
|
952 if ( ! found ) |
|
953 { |
|
954 User::Leave( KErrNotFound ); |
|
955 } |
|
956 |
|
957 apId = static_cast<TUint32>( wapBearerRecord->iWAPIAP ); |
|
958 |
|
959 CleanupStack::PopAndDestroy( wapBearerRecord ); |
|
960 CleanupStack::PopAndDestroy( db ); |
|
961 |
|
962 MPX_DEBUG(_L("CMpxVideoPlayerAppUiEngine::GetAccessPointIdForUIDL(%d)"), apId); |
|
963 |
|
964 return apId; |
|
965 } |
|
966 |
|
967 // ----------------------------------------------------------------------------- |
|
968 // CMpxVideoPlayerAppUiEngine::TryToGetAccessPointL |
|
969 // ----------------------------------------------------------------------------- |
|
970 // |
|
971 TUint32 CMpxVideoPlayerAppUiEngine::TryToGetAccessPointL() |
|
972 { |
|
973 MPX_ENTER_EXIT(_L("CMpxVideoPlayerAppUiEngine::TryToGetAccessPointL()")); |
|
974 |
|
975 TUint32 returnVal(0); |
|
976 |
|
977 RSocketServ ss; |
|
978 CleanupClosePushL( ss ); |
|
979 |
|
980 User::LeaveIfError( ss.Connect() ); |
|
981 |
|
982 RConnection conn; |
|
983 CleanupClosePushL( conn ); |
|
984 |
|
985 User::LeaveIfError( conn.Open( ss ) ); |
|
986 |
|
987 TConnPrefList prefList; |
|
988 TExtendedConnPref prefs; |
|
989 prefs.SetSnapPurpose( CMManager::ESnapPurposeInternet ); |
|
990 prefs.SetNoteBehaviour( TExtendedConnPref::ENoteBehaviourConnSilent ); |
|
991 prefList.AppendL( &prefs ); |
|
992 |
|
993 TInt err = conn.Start( prefList ); |
|
994 |
|
995 if ( err == KErrNone ) |
|
996 { |
|
997 returnVal = QueryIap( conn ); |
|
998 } |
|
999 else if ( err == KErrNotFound ) |
|
1000 { |
|
1001 // |
|
1002 // SNAP is empty or no WLAN was available. |
|
1003 // Try again with connection selection dialog. |
|
1004 // |
|
1005 MPX_DEBUG(_L(" connection start returned KErrNotFound")); |
|
1006 |
|
1007 TConnPrefList prefList2; |
|
1008 TExtendedConnPref prefs2; |
|
1009 |
|
1010 prefs2.SetConnSelectionDialog( ETrue ); |
|
1011 prefList2.AppendL( &prefs2 ); |
|
1012 |
|
1013 err = conn.Start( prefList2 ); |
|
1014 |
|
1015 if ( err == KErrNone ) |
|
1016 { |
|
1017 returnVal = QueryIap( conn ); |
|
1018 } |
|
1019 else |
|
1020 { |
|
1021 User::Leave( err ); |
|
1022 } |
|
1023 } |
|
1024 else |
|
1025 { |
|
1026 User::Leave( err ); |
|
1027 } |
|
1028 |
|
1029 CleanupStack::PopAndDestroy( &conn ); |
|
1030 |
|
1031 CleanupStack::PopAndDestroy( &ss ); |
|
1032 |
|
1033 MPX_DEBUG(_L("CMpxVideoPlayerAppUiEngine::TryToGetAccessPointL() AP ID: %d"), returnVal); |
|
1034 |
|
1035 return returnVal; |
|
1036 } |
|
1037 |
|
1038 // ------------------------------------------------------------------------------------------------- |
|
1039 // CMpxVideoPlayerAppUiEngine::QueryIap() |
|
1040 // ------------------------------------------------------------------------------------------------- |
|
1041 // |
|
1042 TUint32 CMpxVideoPlayerAppUiEngine::QueryIap( RConnection& aConn ) |
|
1043 { |
|
1044 TUint32 iap( 0 ); |
|
1045 aConn.GetIntSetting( KMpxVideoPlayerQueryIap, iap ); |
|
1046 MPX_DEBUG(_L("CMpxVideoPlayerAppUiEngine::QueryIap(%u)"), iap); |
|
1047 return iap; |
|
1048 } |
|
1049 |
|
1050 // ----------------------------------------------------------------------------- |
|
1051 // CMpxVideoPlayerAppUiEngine::ProcessActivationMessageL |
|
1052 // ----------------------------------------------------------------------------- |
|
1053 // |
|
1054 void CMpxVideoPlayerAppUiEngine::ProcessActivationMessageL( const TDesC8 &aMsg ) |
|
1055 { |
|
1056 MPX_ENTER_EXIT(_L("CMpxVideoPlayerAppUiEngine::ProcessActivationMessageL()")); |
|
1057 |
|
1058 if ( aMsg.Length() < KVideoPlayerActivationMessageMinLength ) |
|
1059 { |
|
1060 User::Leave( KErrNotSupported ); |
|
1061 } |
|
1062 |
|
1063 TVideoPlayerActivationMessage msgHandler; |
|
1064 TPckg<TVideoPlayerActivationMessage> paramsPckg( msgHandler ); |
|
1065 paramsPckg.Copy( aMsg ); |
|
1066 |
|
1067 MPX_DEBUG(_L("CMpxVideoPlayerAppUiEngine::ProcessActivationMessageL() sender = %d, type = %d"), |
|
1068 msgHandler.iMsgSender, msgHandler.iMsgType ); |
|
1069 |
|
1070 if ( msgHandler.iMsgSender == TVideoPlayerActivationMessage::EMatrixMenu ) |
|
1071 { |
|
1072 // Clear the view history, so app exits back to matrix. |
|
1073 iViewUtility->PushDefaultHistoryL(); |
|
1074 } |
|
1075 |
|
1076 if ( msgHandler.iMsgType == TVideoPlayerActivationMessage::EOpenInternetVideos ) |
|
1077 { |
|
1078 iViewUtility->ActivateViewL( TUid::Uid( KMpxVideoPlayerVodViewPluginTypeId ) ); |
|
1079 } |
|
1080 else if ( msgHandler.iMsgType == TVideoPlayerActivationMessage::EOpenVideoStorage ) |
|
1081 { |
|
1082 iViewUtility->ActivateViewL( TUid::Uid( KUidMyVideosViewTypeId ) ); |
|
1083 } |
|
1084 else if ( msgHandler.iMsgType == TVideoPlayerActivationMessage::ELaunchVideoToPlayer ) |
|
1085 { |
|
1086 // |
|
1087 // Launch video to player, it can be either local video or stream. |
|
1088 // If there's path and mpx id, clip can be played directly. |
|
1089 // |
|
1090 if ( msgHandler.iServiceId && msgHandler.iFullPath.Length() > 0 ) |
|
1091 { |
|
1092 if ( iRecognizer->IdentifyMediaTypeL( msgHandler.iFullPath ) |
|
1093 == CMediaRecognizer::ELocalVideoFile ) |
|
1094 { |
|
1095 TMPXItemId id; |
|
1096 id.iId1 = msgHandler.iServiceId; |
|
1097 CMPXMedia* media = CMPXMedia::NewL(); |
|
1098 CleanupStack::PushL( media ); |
|
1099 media->SetTObjectValueL<TMPXItemId>( KMPXMediaGeneralId, id ); |
|
1100 OpenMediaL( *media ); |
|
1101 CleanupStack::PopAndDestroy( media ); |
|
1102 } |
|
1103 else |
|
1104 { |
|
1105 // Most likely the clip has been deleted |
|
1106 User::Leave( KErrNotFound ); |
|
1107 } |
|
1108 } |
|
1109 else |
|
1110 { |
|
1111 TBool vodUiRunning = ( iViewUtility->ActiveViewType() == |
|
1112 TUid::Uid( KMpxVideoPlayerVodViewPluginTypeId ) ); |
|
1113 |
|
1114 if ( vodUiRunning ) |
|
1115 { |
|
1116 // VOD is active, must use the utility to pass the message. |
|
1117 SendCustomCommandToActiveViewL( aMsg ); |
|
1118 } |
|
1119 else |
|
1120 { |
|
1121 // VOD is not running, activate with custom message. |
|
1122 HBufC* customMsg = HBufC::NewLC( paramsPckg.Length() ); |
|
1123 customMsg->Des().Copy( paramsPckg ); |
|
1124 iViewUtility->ActivateViewL( TUid::Uid( KMpxVideoPlayerVodViewPluginTypeId ), customMsg ); |
|
1125 CleanupStack::PopAndDestroy( customMsg ); |
|
1126 |
|
1127 // Clear the view history, so playback returns where it was started |
|
1128 iViewUtility->PushDefaultHistoryL(); |
|
1129 } |
|
1130 } |
|
1131 } |
|
1132 else if ( msgHandler.iMsgType == TVideoPlayerActivationMessage::ELaunchServiceById ) |
|
1133 { |
|
1134 if ( iViewUtility->ActiveViewType() == TUid::Uid( KMpxVideoPlayerVodViewPluginTypeId ) ) |
|
1135 { |
|
1136 // VOD is active, must use the utility to pass the message. |
|
1137 SendCustomCommandToActiveViewL( aMsg ); |
|
1138 } |
|
1139 else |
|
1140 { |
|
1141 // VOD is not running, activate with custom message. |
|
1142 HBufC* customMsg = HBufC::NewLC( paramsPckg.Length() ); |
|
1143 customMsg->Des().Copy( paramsPckg ); |
|
1144 iViewUtility->ActivateViewL( TUid::Uid( KMpxVideoPlayerVodViewPluginTypeId ), customMsg ); |
|
1145 CleanupStack::PopAndDestroy( customMsg ); |
|
1146 } |
|
1147 } |
|
1148 } |
|
1149 |
772 |
1150 // ----------------------------------------------------------------------------- |
773 // ----------------------------------------------------------------------------- |
1151 // CMpxVideoPlayerAppUiEngine::HandleMessageL() |
774 // CMpxVideoPlayerAppUiEngine::HandleMessageL() |
1152 // ----------------------------------------------------------------------------- |
775 // ----------------------------------------------------------------------------- |
1153 // |
776 // |
1303 // back command exits the app. otherwise activate |
921 // back command exits the app. otherwise activate |
1304 // the previous view in our local view stack |
922 // the previous view in our local view stack |
1305 // |
923 // |
1306 if ( iViewUtility->ViewHistoryDepth() <= 1 ) |
924 if ( iViewUtility->ViewHistoryDepth() <= 1 ) |
1307 { |
925 { |
1308 iExitToMatrixMenu = EFalse; |
|
1309 ActivateExitActiveObject(); |
926 ActivateExitActiveObject(); |
1310 } |
927 } |
1311 else |
928 else |
1312 { |
929 { |
1313 StepBackCollectionPathL(); |
930 StepBackCollectionPathL(); |
1314 } |
931 } |
1315 } |
932 } |
1316 |
933 |
|
934 // ----------------------------------------------------------------------------- |
|
935 // |
|
936 // ----------------------------------------------------------------------------- |
|
937 // |
1317 TBool CMpxVideoPlayerAppUiEngine::ProcessCommandParametersL( TApaCommand aCommand, |
938 TBool CMpxVideoPlayerAppUiEngine::ProcessCommandParametersL( TApaCommand aCommand, |
1318 TFileName& aDocumentName, |
939 TFileName& aDocumentName, |
1319 const TDesC8& aTail ) |
940 const TDesC8& aTail ) |
1320 { |
941 { |
1321 MPX_ENTER_EXIT(_L("CMpxVideoPlayerAppUiEngine::ProcessCommandParametersL()")); |
942 MPX_ENTER_EXIT(_L("CMpxVideoPlayerAppUiEngine::ProcessCommandParametersL()")); |
1322 |
943 |
1323 TBool retVal = EFalse; |
944 TBool retVal = EFalse; |
1324 |
945 |
1325 // |
946 // |
1326 // If command line has tail, this is probably a message from Matrix, Live TV |
947 // If we are embedded or the command is to open a document |
1327 // reminder, Video Center soft notification, or other VC component. |
948 // |
1328 // |
949 if ( iAppUi->IsEmbedded() || aCommand != EApaCommandRun ) |
1329 if ( aTail.Length() > 0 ) |
950 { |
1330 { |
951 if ( iRecognizer->IsValidStreamingPrefix( aDocumentName ) ) |
1331 ProcessActivationMessageL( aTail ); |
952 { |
|
953 retVal = ETrue; |
|
954 } |
|
955 else |
|
956 { |
|
957 retVal = ConeUtils::FileExists( aDocumentName ); |
|
958 } |
1332 } |
959 } |
1333 else |
960 else |
1334 { |
961 { |
1335 // |
962 StartStandAloneL(); |
1336 // If we are embedded or the command is to open a document |
|
1337 // |
|
1338 if ( iAppUi->IsEmbedded() || aCommand != EApaCommandRun ) |
|
1339 { |
|
1340 if ( iRecognizer->IsValidStreamingPrefix( aDocumentName ) ) |
|
1341 { |
|
1342 retVal = ETrue; |
|
1343 } |
|
1344 else |
|
1345 { |
|
1346 retVal = ConeUtils::FileExists( aDocumentName ); |
|
1347 } |
|
1348 } |
|
1349 else |
|
1350 { |
|
1351 StartStandAloneL(); |
|
1352 } |
|
1353 } |
963 } |
1354 |
964 |
1355 return retVal; |
965 return retVal; |
1356 } |
966 } |
1357 |
967 |
|
968 // ----------------------------------------------------------------------------- |
|
969 // |
|
970 // ----------------------------------------------------------------------------- |
|
971 // |
1358 void CMpxVideoPlayerAppUiEngine::ActivateExitActiveObject() |
972 void CMpxVideoPlayerAppUiEngine::ActivateExitActiveObject() |
1359 { |
973 { |
1360 MPX_ENTER_EXIT(_L("CMpxVideoPlayerAppUiEngine::ActivateExitActiveObject()")); |
974 MPX_ENTER_EXIT(_L("CMpxVideoPlayerAppUiEngine::ActivateExitActiveObject()")); |
1361 |
975 |
1362 if ( ! iExitAo->IsActive() ) |
976 if ( ! iExitAo->IsActive() ) |
1363 { |
977 { |
1364 iExitAo->Start( TCallBack( CMpxVideoPlayerAppUiEngine::ExitApplicationL, this ) ); |
978 iExitAo->Start( TCallBack( CMpxVideoPlayerAppUiEngine::ExitApplicationL, this ) ); |
1365 } |
979 } |
1366 } |
980 } |
1367 |
981 |
1368 // ----------------------------------------------------------------------------- |
982 // ------------------------------------------------------------------------------------------------- |
1369 // CMpxVideoPlayerAppUiEngine::ExitApplicationL |
983 // CMpxVideoPlayerAppUiEngine::ExitApplicationL |
1370 // ----------------------------------------------------------------------------- |
984 // ------------------------------------------------------------------------------------------------- |
1371 // |
985 // |
1372 TInt CMpxVideoPlayerAppUiEngine::ExitApplicationL( TAny* aPtr ) |
986 TInt CMpxVideoPlayerAppUiEngine::ExitApplicationL( TAny* aPtr ) |
1373 { |
987 { |
1374 MPX_DEBUG(_L("CMpxVideoPlayerAppUiEngine::ExitApplicationL()")); |
988 MPX_DEBUG(_L("CMpxVideoPlayerAppUiEngine::ExitApplicationL()")); |
1375 |
989 |