1 /* |
|
2 * Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: Store image ja video file processing |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include <mdesession.h> |
|
20 #include <mdeconstants.h> |
|
21 #include "upnpfileutility.h" |
|
22 #include <f32file.h> |
|
23 #include "cmstorelistitem.h" |
|
24 #include "cmsmfilemngr.h" |
|
25 #include "cmsmvideoandimagemngr.h" |
|
26 #include "msdebug.h" |
|
27 |
|
28 |
|
29 // --------------------------------------------------------------------------- |
|
30 // CCmSmVideoAndImageMngr::NewL |
|
31 // --------------------------------------------------------------------------- |
|
32 // |
|
33 CCmSmVideoAndImageMngr* CCmSmVideoAndImageMngr::NewL( |
|
34 MCmSmVideoAndImageMngrObserver& aObserver, CCmSmFileMngr& aFileMngr, |
|
35 CMdESession& aSession, RPointerArray<CCmStoreListItem>& aItems ) |
|
36 { |
|
37 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::NewL() start")); |
|
38 CCmSmVideoAndImageMngr* self = CCmSmVideoAndImageMngr::NewLC( aObserver, |
|
39 aFileMngr, aSession, aItems ); |
|
40 CleanupStack::Pop( self ); |
|
41 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::NewL() end")); |
|
42 return self; |
|
43 } |
|
44 |
|
45 // --------------------------------------------------------------------------- |
|
46 // CCmSmVideoAndImageMngr::NewLC |
|
47 // --------------------------------------------------------------------------- |
|
48 // |
|
49 CCmSmVideoAndImageMngr* CCmSmVideoAndImageMngr::NewLC( |
|
50 MCmSmVideoAndImageMngrObserver& aObserver, CCmSmFileMngr& aFileMngr, |
|
51 CMdESession& aSession, RPointerArray<CCmStoreListItem>& aItems ) |
|
52 { |
|
53 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::NewLC() start")); |
|
54 CCmSmVideoAndImageMngr* self = new ( ELeave ) CCmSmVideoAndImageMngr( |
|
55 aObserver, aFileMngr, aSession, aItems ); |
|
56 CleanupStack::PushL( self ); |
|
57 self->ConstructL(); |
|
58 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::NewLC() end")); |
|
59 return self; |
|
60 } |
|
61 |
|
62 // --------------------------------------------------------------------------- |
|
63 // CCmSmVideoAndImageMngr::~CCmSmVideoAndImageMngr |
|
64 // --------------------------------------------------------------------------- |
|
65 // |
|
66 CCmSmVideoAndImageMngr::~CCmSmVideoAndImageMngr() |
|
67 { |
|
68 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::\ |
|
69 ~CCmSmVideoAndImageMngr() start")); |
|
70 Cancel(); |
|
71 delete iTitleDef; |
|
72 delete iSizeDef; |
|
73 delete iDateDef; |
|
74 delete iQuery; |
|
75 delete iMediaTypeDef; |
|
76 delete iObjectDef; |
|
77 delete iNamespace; |
|
78 iObjects.ResetAndDestroy(); |
|
79 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::\ |
|
80 ~CCmSmVideoAndImageMngr() end")); |
|
81 } |
|
82 |
|
83 // --------------------------------------------------------------------------- |
|
84 // CCmSmVideoAndImageMngr::CCmSmVideoAndImageMngr |
|
85 // --------------------------------------------------------------------------- |
|
86 // |
|
87 CCmSmVideoAndImageMngr::CCmSmVideoAndImageMngr( |
|
88 MCmSmVideoAndImageMngrObserver& aObserver, CCmSmFileMngr& aFileMngr, |
|
89 CMdESession& aSession, RPointerArray<CCmStoreListItem>& aItems ) |
|
90 :CActive( EPriorityStandard ), iObserver( aObserver ), |
|
91 iFileMngr( aFileMngr ), iSession( aSession ), iItems( aItems ) |
|
92 { |
|
93 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::CCmSmVideoAndImageMngr")); |
|
94 CActiveScheduler::Add( this ); |
|
95 } |
|
96 |
|
97 // --------------------------------------------------------------------------- |
|
98 // CCmSmVideoAndImageMngr::ConstructL |
|
99 // --------------------------------------------------------------------------- |
|
100 // |
|
101 void CCmSmVideoAndImageMngr::ConstructL() |
|
102 { |
|
103 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::ConstructL()")); |
|
104 } |
|
105 |
|
106 // --------------------------------------------------------------------------- |
|
107 // CCmSmVideoAndImageMngr::ProcessMedia |
|
108 // --------------------------------------------------------------------------- |
|
109 // |
|
110 void CCmSmVideoAndImageMngr::ProcessMedia( TCmMediaType aType ) |
|
111 { |
|
112 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::ProcessMedia")); |
|
113 |
|
114 TRAPD( err, SetQueryAndStartFindL( aType ) ); |
|
115 iQueriedMedia = aType; |
|
116 if( err ) |
|
117 { |
|
118 TRACE(Print(_L("SetQueryAndStartFindL err = %d"), err )); |
|
119 iObserver.ImagesAndVideosReady( err ); |
|
120 } |
|
121 } |
|
122 |
|
123 // --------------------------------------------------------------------------- |
|
124 // CCmSmVideoAndImageMngr::CancelListRefresh |
|
125 // --------------------------------------------------------------------------- |
|
126 // |
|
127 void CCmSmVideoAndImageMngr::CancelListRefresh() |
|
128 { |
|
129 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::CancelListRefresh()")); |
|
130 ResetQuery(); |
|
131 } |
|
132 |
|
133 // --------------------------------------------------------------------------- |
|
134 // CCmSmVideoAndImageMngr::CancelOperation |
|
135 // --------------------------------------------------------------------------- |
|
136 // |
|
137 void CCmSmVideoAndImageMngr::CancelOperation() |
|
138 { |
|
139 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::CancelOperation")); |
|
140 |
|
141 Cancel(); |
|
142 CancelListRefresh(); |
|
143 iFileMngr.ResetArrays(); |
|
144 } |
|
145 |
|
146 // --------------------------------------------------------------------------- |
|
147 // CCmSmVideoAndImageMngr::HandleQueryNewResults |
|
148 // --------------------------------------------------------------------------- |
|
149 // |
|
150 void CCmSmVideoAndImageMngr::HandleQueryNewResults(CMdEQuery& aQuery, |
|
151 TInt aFirstNewItemIndex, |
|
152 TInt aNewItemCount) |
|
153 { |
|
154 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::HandleQueryNewResults")); |
|
155 |
|
156 CMdEObjectQuery& query = (CMdEObjectQuery &) aQuery; |
|
157 TRACE(Print(_L("[FILL MNGR]\t aNewItemCount %d"), aNewItemCount )); |
|
158 if( &query ) |
|
159 { |
|
160 for ( TInt i=aFirstNewItemIndex; |
|
161 i < aFirstNewItemIndex+aNewItemCount; |
|
162 i++ ) |
|
163 { |
|
164 CMdEObject* object = (CMdEObject*)query.TakeOwnershipOfResult(i); |
|
165 iObjects.AppendL(object); |
|
166 } |
|
167 } |
|
168 |
|
169 } |
|
170 |
|
171 // --------------------------------------------------------------------------- |
|
172 // CCmSmVideoAndImageMngr::HandleQueryCompleted |
|
173 // --------------------------------------------------------------------------- |
|
174 // |
|
175 void CCmSmVideoAndImageMngr::HandleQueryCompleted( CMdEQuery& aQuery, |
|
176 TInt aError ) |
|
177 { |
|
178 TRACE(Print(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::\ |
|
179 HandleQueryCompleted error = %d"), aError )); |
|
180 if (aError == KErrNone) |
|
181 { |
|
182 if( &aQuery ) |
|
183 { |
|
184 CompleteRequest( ECmSmQueryCompleted ); |
|
185 } |
|
186 else |
|
187 { |
|
188 CompleteRequest( ECmSmQueryFailed ); |
|
189 } |
|
190 } |
|
191 else if ( aError == KErrCancel ) |
|
192 { |
|
193 // Query canceled |
|
194 } |
|
195 else |
|
196 { |
|
197 CompleteRequest( ECmSmQueryFailed ); |
|
198 } |
|
199 } |
|
200 |
|
201 // --------------------------------------------------------------------------- |
|
202 // CCmSmVideoAndImageMngr::FilterItems |
|
203 // --------------------------------------------------------------------------- |
|
204 // |
|
205 void CCmSmVideoAndImageMngr::FilterItems() |
|
206 { |
|
207 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::FilterItems")); |
|
208 |
|
209 TInt err( KErrNone ); |
|
210 RArray<TInt> ids; |
|
211 |
|
212 TRAP( err, iFileMngr.MediaTypesL( ids, iQueriedMedia ) ); |
|
213 TRACE(Print(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::FilterItems \ |
|
214 mediatypes err = %d"), err )); |
|
215 TRAP( err, FilterNewItemsL( ids, iQueriedMedia ) ); |
|
216 TRACE(Print(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::FilterItems \ |
|
217 filter new items err = %d"), err )); |
|
218 ids.Reset(); |
|
219 |
|
220 CompleteRequest( ECmSmFilteringReady ); |
|
221 } |
|
222 |
|
223 // --------------------------------------------------------------------------- |
|
224 // CCmSmVideoAndImageMngr::FilterNewItemsL |
|
225 // --------------------------------------------------------------------------- |
|
226 // |
|
227 void CCmSmVideoAndImageMngr::FilterNewItemsL( RArray<TInt>aDeviceIds, |
|
228 TCmMediaType aType ) |
|
229 { |
|
230 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::FilterNewItemsL")); |
|
231 |
|
232 RArray<TInt>storeIds; |
|
233 RArray<TInt>storedIds; |
|
234 |
|
235 |
|
236 // Collecting mde object propertys into CCmFillListItem package |
|
237 for( TInt i = 0; i < iObjects.Count(); i++ ) |
|
238 { |
|
239 // Copy ids into temporary array |
|
240 for( TInt ll = 0; ll < aDeviceIds.Count(); ll++ ) |
|
241 { |
|
242 storeIds.Append(aDeviceIds[ll]); |
|
243 } |
|
244 |
|
245 if ( !UPnPFileUtility::IsFileProtectedL( iObjects[i]->Uri( ) ) ) |
|
246 { |
|
247 if( iFileMngr.CheckIsFilledFileL( iObjects[i]->Uri( ) ) ) |
|
248 { |
|
249 storeIds.Reset(); |
|
250 storeIds.Close(); |
|
251 } |
|
252 iFileMngr.IsAlreadyStoreL( iObjects[i]->Uri(), |
|
253 storedIds, |
|
254 storeIds ); |
|
255 if( KErrNone < storeIds.Count() ) |
|
256 { |
|
257 TCmListItemStatus status; |
|
258 CCmStoreListItem* item = CCmStoreListItem::NewL(); |
|
259 CleanupStack::PushL( item ); |
|
260 item->SetPathL( iObjects[i]->Uri( ) ); |
|
261 item->SetListId( iFileMngr.RetrieveListId( aType ) ); |
|
262 // Ms id values are binded with file status values |
|
263 iFileMngr.RetrieveListStatusValues( aType, status ); |
|
264 CMdEProperty* prop; |
|
265 CMdETextProperty* textprop; |
|
266 TInt propIndex = iObjects[i]->Property( *iTitleDef, prop ); |
|
267 if( propIndex != KErrNotFound ) |
|
268 { |
|
269 textprop = (CMdETextProperty*) prop; |
|
270 item->SetPrimaryTextL( textprop->Value() ); |
|
271 } |
|
272 else |
|
273 { |
|
274 TParsePtrC parser( iObjects[i]->Uri()); |
|
275 item->SetPrimaryTextL( parser.Name() ); |
|
276 LOG(_L("[STORE MNGR]\t Title not found file name used")); |
|
277 } |
|
278 |
|
279 propIndex = iObjects[i]->Property( *iSizeDef, prop ); |
|
280 if( propIndex != KErrNotFound ) |
|
281 { |
|
282 item->SetSize( prop->Uint32ValueL() ); |
|
283 } |
|
284 else |
|
285 { |
|
286 LOG(_L("[STORE MNGR]\t Size not found")); |
|
287 } |
|
288 propIndex = iObjects[i]->Property( *iDateDef, prop ); |
|
289 if( propIndex != KErrNotFound ) |
|
290 { |
|
291 CMdETimeProperty* dateprop = (CMdETimeProperty*)prop; |
|
292 item->SetDate( dateprop->Value() ); |
|
293 } |
|
294 else |
|
295 { |
|
296 LOG(_L("[STORE MNGR]\t Date not found")); |
|
297 } |
|
298 // Where item will be stored |
|
299 for( TInt s = 0 ; s < storeIds.Count(); s++ ) |
|
300 { |
|
301 item->SetDevId( storeIds[s], status ); |
|
302 } |
|
303 // Where item has already been stored |
|
304 for( TInt d = 0; d < storedIds.Count(); d++ ) |
|
305 { |
|
306 item->SetDevId( storedIds[d], ECmStored ); |
|
307 } |
|
308 iItems.AppendL( item ); |
|
309 CleanupStack::Pop( item ); |
|
310 } |
|
311 } |
|
312 storedIds.Reset(); |
|
313 storedIds.Close(); |
|
314 storeIds.Reset(); |
|
315 storeIds.Close(); |
|
316 } |
|
317 } |
|
318 |
|
319 // --------------------------------------------------------------------------- |
|
320 // CCmSmVideoAndImageMngr::SetQueryAndStartFindL |
|
321 // --------------------------------------------------------------------------- |
|
322 // |
|
323 void CCmSmVideoAndImageMngr::SetQueryAndStartFindL( TCmMediaType aType ) |
|
324 { |
|
325 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::SetQueryAndStartFindL")); |
|
326 |
|
327 ResetQuery(); |
|
328 if( !iNamespace ) |
|
329 { |
|
330 iNamespace = &iSession.GetDefaultNamespaceDefL(); |
|
331 } |
|
332 if( iNamespace ) |
|
333 { |
|
334 iObjectDef = |
|
335 &iNamespace->GetObjectDefL( MdeConstants::Object::KBaseObject ); |
|
336 } |
|
337 if( ECmImage == aType || ECmOtherImage == aType ) |
|
338 { |
|
339 TRACE(Print(_L("ECmImage == aType"))); |
|
340 iMediaTypeDef = |
|
341 &iNamespace->GetObjectDefL( MdeConstants::Image::KImageObject ); |
|
342 } |
|
343 else if( ECmVideo == aType || ECmOtherVideo == aType ) |
|
344 { |
|
345 TRACE(Print(_L("ECmVideo == aType"))); |
|
346 iMediaTypeDef = |
|
347 &iNamespace->GetObjectDefL( MdeConstants::Video::KVideoObject ); |
|
348 } |
|
349 else |
|
350 { |
|
351 User::Leave( KErrArgument ); |
|
352 } |
|
353 if( !iObjectDef ) |
|
354 { |
|
355 User::Leave( KErrArgument ); |
|
356 } |
|
357 if( !iMediaTypeDef ) |
|
358 { |
|
359 User::Leave( KErrArgument ); |
|
360 } |
|
361 |
|
362 iQuery = iSession.NewObjectQueryL( *iNamespace, *iMediaTypeDef, this ); |
|
363 if( !iQuery ) |
|
364 { |
|
365 User::Leave( KErrArgument ); |
|
366 } |
|
367 /** Set logic condition */ |
|
368 SetLogicConditionL( aType ); |
|
369 /** Set property filters */ |
|
370 SetPropertyFiltersL(); |
|
371 iQuery->SetResultMode( EQueryResultModeItem ); |
|
372 /** Start query */ |
|
373 iQuery->FindL(); |
|
374 } |
|
375 |
|
376 // --------------------------------------------------------------------------- |
|
377 // CCmSmVideoAndImageMngr::SetPropertyFiltersL |
|
378 // --------------------------------------------------------------------------- |
|
379 // |
|
380 void CCmSmVideoAndImageMngr::SetPropertyFiltersL( ) |
|
381 { |
|
382 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::SetPropertyFiltersL")); |
|
383 |
|
384 iTitleDef = &iObjectDef->GetPropertyDefL( |
|
385 MdeConstants::Object::KTitleProperty ); |
|
386 iSizeDef = &iObjectDef->GetPropertyDefL( |
|
387 MdeConstants::Object::KSizeProperty ); |
|
388 iDateDef = &iObjectDef->GetPropertyDefL( |
|
389 MdeConstants::Object::KCreationDateProperty ); |
|
390 iQuery->AddPropertyFilterL( iTitleDef ); |
|
391 iQuery->AddPropertyFilterL( iSizeDef ); |
|
392 iQuery->AddPropertyFilterL( iDateDef ); |
|
393 } |
|
394 |
|
395 // --------------------------------------------------------------------------- |
|
396 // CCmSmVideoAndImageMngr::SetLogicConditionL |
|
397 // --------------------------------------------------------------------------- |
|
398 // |
|
399 void CCmSmVideoAndImageMngr::SetLogicConditionL( TCmMediaType aType ) |
|
400 { |
|
401 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::SetLogicConditionL")); |
|
402 |
|
403 CMdELogicCondition& rootCondition = iQuery->Conditions(); |
|
404 CMdELogicCondition* contentCondition = &rootCondition; |
|
405 CMdELogicCondition& logicCondition = |
|
406 contentCondition->AddLogicConditionL( ELogicConditionOperatorOr ); |
|
407 logicCondition.AddObjectConditionL( *iMediaTypeDef ); |
|
408 |
|
409 CMdEPropertyDef* originProperty = NULL; |
|
410 if( aType == ECmOtherVideo || aType == ECmOtherImage ) |
|
411 { |
|
412 originProperty = &iObjectDef->GetPropertyDefL( _L("Origin") ); |
|
413 if( originProperty ) |
|
414 { |
|
415 CMdELogicCondition& origLogicCondition = |
|
416 contentCondition->AddLogicConditionL( |
|
417 ELogicConditionOperatorAnd ); |
|
418 origLogicCondition.AddPropertyConditionL( |
|
419 *originProperty, TMdEUintNotEqual( |
|
420 MdeConstants::Object::ECamera )); |
|
421 } |
|
422 } |
|
423 if( aType == ECmVideo || aType == ECmImage ) |
|
424 { |
|
425 originProperty = &iObjectDef->GetPropertyDefL( _L("Origin") ); |
|
426 if( originProperty ) |
|
427 { |
|
428 CMdELogicCondition& origLogicCondition = |
|
429 contentCondition->AddLogicConditionL( |
|
430 ELogicConditionOperatorAnd ); |
|
431 origLogicCondition.AddPropertyConditionL( |
|
432 *originProperty, TMdEUintEqual( |
|
433 MdeConstants::Object::ECamera )); |
|
434 } |
|
435 } |
|
436 |
|
437 } |
|
438 |
|
439 // --------------------------------------------------------------------------- |
|
440 // RCCmSmVideoAndImageMngr::ResetQuery |
|
441 // --------------------------------------------------------------------------- |
|
442 // |
|
443 void CCmSmVideoAndImageMngr::ResetQuery() |
|
444 { |
|
445 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::ResetQuery")); |
|
446 |
|
447 iObjects.ResetAndDestroy(); |
|
448 |
|
449 if ( iQuery ) |
|
450 { |
|
451 iQuery->Cancel(); |
|
452 delete iQuery; |
|
453 iQuery = NULL; |
|
454 } |
|
455 } |
|
456 |
|
457 // --------------------------------------------------------------------------- |
|
458 // CCmSmVideoAndImageMngr::CompleteRequest |
|
459 // --------------------------------------------------------------------------- |
|
460 // |
|
461 void CCmSmVideoAndImageMngr::CompleteRequest( |
|
462 TCmSmFileProcessingStatus aStatus ) |
|
463 { |
|
464 LOG(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::CompleteRequest")); |
|
465 |
|
466 __ASSERT_DEBUG(!IsActive(),\ |
|
467 User::Panic( KCmSmManager, KErrInUse )); |
|
468 SetActive(); |
|
469 TRequestStatus* pStatus = &iStatus; |
|
470 User::RequestComplete( pStatus, aStatus ); |
|
471 } |
|
472 |
|
473 // --------------------------------------------------------------------------- |
|
474 // CCmSmVideoAndImageMngr::RunL |
|
475 // --------------------------------------------------------------------------- |
|
476 // |
|
477 void CCmSmVideoAndImageMngr::RunL() |
|
478 { |
|
479 TRACE(Print(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::RunL\ |
|
480 status = %d"), iStatus.Int() )); |
|
481 switch( iStatus.Int() ) |
|
482 { |
|
483 case ECmSmQueryCompleted: |
|
484 { |
|
485 FilterItems(); |
|
486 break; |
|
487 } |
|
488 case ECmSmFilteringReady: |
|
489 { |
|
490 iObserver.ImagesAndVideosReady( KErrNone ); |
|
491 break; |
|
492 } |
|
493 case ECmSmQueryFailed: |
|
494 { |
|
495 iObserver.ImagesAndVideosReady( KErrGeneral ); |
|
496 break; |
|
497 } |
|
498 default: |
|
499 { |
|
500 LOG(_L("[STORE MNGR]\t Store file processing RunL default")); |
|
501 User::Invariant(); |
|
502 break; |
|
503 } |
|
504 } |
|
505 } |
|
506 |
|
507 // --------------------------------------------------------------------------- |
|
508 // CCmSmVideoAndImageMngr::DoCancel |
|
509 // --------------------------------------------------------------------------- |
|
510 // |
|
511 void CCmSmVideoAndImageMngr::DoCancel() |
|
512 { |
|
513 TRACE(Print(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::DoCancel()"))); |
|
514 } |
|
515 |
|
516 // --------------------------------------------------------------------------- |
|
517 // CCmSmVideoAndImageMngr::RunError |
|
518 // --------------------------------------------------------------------------- |
|
519 // |
|
520 #ifdef _DEBUG |
|
521 TInt CCmSmVideoAndImageMngr::RunError( TInt aError ) |
|
522 { |
|
523 #else |
|
524 TInt CCmSmVideoAndImageMngr::RunError( TInt /*aError*/ ) |
|
525 { |
|
526 #endif |
|
527 TRACE(Print(_L("[STORE MNGR]\t CCmSmVideoAndImageMngr::RunError\ |
|
528 aError = %d"), aError )); |
|
529 iObserver.ImagesAndVideosReady( KErrGeneral ); |
|
530 return KErrNone; |
|
531 } |
|
532 |
|
533 // End of file |
|
534 |
|
535 |
|
536 |
|