AlbumSetPage.java revision e8c1e69f85efb8673d0606f3aca729a366038753
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.gallery3d.app; 18 19import android.app.Activity; 20import android.content.Context; 21import android.content.Intent; 22import android.graphics.Rect; 23import android.os.Bundle; 24import android.os.Handler; 25import android.os.Message; 26import android.os.Vibrator; 27import android.provider.MediaStore; 28import android.view.ActionMode; 29import android.view.Menu; 30import android.view.MenuInflater; 31import android.view.MenuItem; 32import android.widget.Toast; 33 34import com.android.gallery3d.R; 35import com.android.gallery3d.common.Utils; 36import com.android.gallery3d.data.DataManager; 37import com.android.gallery3d.data.MediaDetails; 38import com.android.gallery3d.data.MediaObject; 39import com.android.gallery3d.data.MediaSet; 40import com.android.gallery3d.data.Path; 41import com.android.gallery3d.picasasource.PicasaSource; 42import com.android.gallery3d.settings.GallerySettings; 43import com.android.gallery3d.ui.ActionModeHandler; 44import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener; 45import com.android.gallery3d.ui.AlbumSetSlotRenderer; 46import com.android.gallery3d.ui.DetailsHelper; 47import com.android.gallery3d.ui.DetailsHelper.CloseListener; 48import com.android.gallery3d.ui.FadeTexture; 49import com.android.gallery3d.ui.GLCanvas; 50import com.android.gallery3d.ui.GLRoot; 51import com.android.gallery3d.ui.GLView; 52import com.android.gallery3d.ui.SelectionManager; 53import com.android.gallery3d.ui.SlotView; 54import com.android.gallery3d.ui.SynchronizedHandler; 55import com.android.gallery3d.util.Future; 56import com.android.gallery3d.util.GalleryUtils; 57import com.android.gallery3d.util.HelpUtils; 58 59public class AlbumSetPage extends ActivityState implements 60 SelectionManager.SelectionListener, GalleryActionBar.ClusterRunner, 61 EyePosition.EyePositionListener, MediaSet.SyncListener { 62 @SuppressWarnings("unused") 63 private static final String TAG = "AlbumSetPage"; 64 65 private static final int MSG_PICK_ALBUM = 1; 66 67 public static final String KEY_MEDIA_PATH = "media-path"; 68 public static final String KEY_SET_TITLE = "set-title"; 69 public static final String KEY_SET_SUBTITLE = "set-subtitle"; 70 public static final String KEY_SELECTED_CLUSTER_TYPE = "selected-cluster"; 71 72 private static final int DATA_CACHE_SIZE = 256; 73 private static final int REQUEST_DO_ANIMATION = 1; 74 75 private static final int BIT_LOADING_RELOAD = 1; 76 private static final int BIT_LOADING_SYNC = 2; 77 78 private boolean mIsActive = false; 79 private SlotView mSlotView; 80 private AlbumSetSlotRenderer mAlbumSetView; 81 82 private MediaSet mMediaSet; 83 private String mTitle; 84 private String mSubtitle; 85 private boolean mShowClusterMenu; 86 private GalleryActionBar mActionBar; 87 private int mSelectedAction; 88 private Vibrator mVibrator; 89 90 protected SelectionManager mSelectionManager; 91 private AlbumSetDataLoader mAlbumSetDataAdapter; 92 93 private boolean mGetContent; 94 private boolean mGetAlbum; 95 private ActionMode mActionMode; 96 private ActionModeHandler mActionModeHandler; 97 private DetailsHelper mDetailsHelper; 98 private MyDetailsSource mDetailsSource; 99 private boolean mShowDetails; 100 private EyePosition mEyePosition; 101 private Handler mHandler; 102 103 // The eyes' position of the user, the origin is at the center of the 104 // device and the unit is in pixels. 105 private float mX; 106 private float mY; 107 private float mZ; 108 109 private Future<Integer> mSyncTask = null; 110 111 private int mLoadingBits = 0; 112 private boolean mInitialSynced = false; 113 114 private final GLView mRootPane = new GLView() { 115 private final float mMatrix[] = new float[16]; 116 117 @Override 118 protected void renderBackground(GLCanvas view) { 119 view.clearBuffer(); 120 } 121 122 @Override 123 protected void onLayout( 124 boolean changed, int left, int top, int right, int bottom) { 125 mEyePosition.resetPosition(); 126 127 int slotViewTop = mActionBar.getHeight(); 128 int slotViewBottom = bottom - top; 129 int slotViewRight = right - left; 130 131 if (mShowDetails) { 132 mDetailsHelper.layout(left, slotViewTop, right, bottom); 133 } else { 134 mAlbumSetView.setHighlightItemPath(null); 135 } 136 137 mSlotView.layout(0, slotViewTop, slotViewRight, slotViewBottom); 138 } 139 140 @Override 141 protected void render(GLCanvas canvas) { 142 canvas.save(GLCanvas.SAVE_FLAG_MATRIX); 143 GalleryUtils.setViewPointMatrix(mMatrix, 144 getWidth() / 2 + mX, getHeight() / 2 + mY, mZ); 145 canvas.multiplyMatrix(mMatrix, 0); 146 super.render(canvas); 147 canvas.restore(); 148 } 149 }; 150 151 @Override 152 public void onEyePositionChanged(float x, float y, float z) { 153 mRootPane.lockRendering(); 154 mX = x; 155 mY = y; 156 mZ = z; 157 mRootPane.unlockRendering(); 158 mRootPane.invalidate(); 159 } 160 161 @Override 162 public void onBackPressed() { 163 if (mShowDetails) { 164 hideDetails(); 165 } else if (mSelectionManager.inSelectionMode()) { 166 mSelectionManager.leaveSelectionMode(); 167 } else { 168 super.onBackPressed(); 169 } 170 } 171 172 private void getSlotCenter(int slotIndex, int center[]) { 173 Rect offset = new Rect(); 174 mRootPane.getBoundsOf(mSlotView, offset); 175 Rect r = mSlotView.getSlotRect(slotIndex); 176 int scrollX = mSlotView.getScrollX(); 177 int scrollY = mSlotView.getScrollY(); 178 center[0] = offset.left + (r.left + r.right) / 2 - scrollX; 179 center[1] = offset.top + (r.top + r.bottom) / 2 - scrollY; 180 } 181 182 public void onSingleTapUp(int slotIndex) { 183 if (!mIsActive) return; 184 185 if (mSelectionManager.inSelectionMode()) { 186 MediaSet targetSet = mAlbumSetDataAdapter.getMediaSet(slotIndex); 187 if (targetSet == null) return; // Content is dirty, we shall reload soon 188 mSelectionManager.toggle(targetSet.getPath()); 189 mSlotView.invalidate(); 190 } else { 191 // Show pressed-up animation for the single-tap. 192 mAlbumSetView.setPressedIndex(slotIndex); 193 mAlbumSetView.setPressedUp(); 194 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PICK_ALBUM, slotIndex, 0), 195 FadeTexture.DURATION); 196 } 197 } 198 199 private void pickAlbum(int slotIndex) { 200 if (!mIsActive) return; 201 202 MediaSet targetSet = mAlbumSetDataAdapter.getMediaSet(slotIndex); 203 if (targetSet == null) return; // Content is dirty, we shall reload soon 204 String mediaPath = targetSet.getPath().toString(); 205 206 Bundle data = new Bundle(getData()); 207 int[] center = new int[2]; 208 getSlotCenter(slotIndex, center); 209 data.putIntArray(AlbumPage.KEY_SET_CENTER, center); 210 if (mGetAlbum && targetSet.isLeafAlbum()) { 211 Activity activity = (Activity) mActivity; 212 Intent result = new Intent() 213 .putExtra(AlbumPicker.KEY_ALBUM_PATH, targetSet.getPath().toString()); 214 activity.setResult(Activity.RESULT_OK, result); 215 activity.finish(); 216 } else if (targetSet.getSubMediaSetCount() > 0) { 217 data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath); 218 mActivity.getStateManager().startStateForResult( 219 AlbumSetPage.class, REQUEST_DO_ANIMATION, data); 220 } else { 221 if (!mGetContent && (targetSet.getSupportedOperations() 222 & MediaObject.SUPPORT_IMPORT) != 0) { 223 data.putBoolean(AlbumPage.KEY_AUTO_SELECT_ALL, true); 224 } 225 data.putString(AlbumPage.KEY_MEDIA_PATH, mediaPath); 226 boolean inAlbum = mActivity.getStateManager().hasStateClass(AlbumPage.class); 227 // We only show cluster menu in the first AlbumPage in stack 228 data.putBoolean(AlbumPage.KEY_SHOW_CLUSTER_MENU, !inAlbum); 229 mActivity.getStateManager().startStateForResult( 230 AlbumPage.class, REQUEST_DO_ANIMATION, data); 231 } 232 } 233 234 private void onDown(int index) { 235 mAlbumSetView.setPressedIndex(index); 236 } 237 238 private void onUp(boolean followedByLongPress) { 239 if (followedByLongPress) { 240 // Avoid showing press-up animations for long-press. 241 mAlbumSetView.setPressedIndex(-1); 242 } else { 243 mAlbumSetView.setPressedUp(); 244 } 245 } 246 247 public void onLongTap(int slotIndex) { 248 if (mGetContent || mGetAlbum) return; 249 MediaSet set = mAlbumSetDataAdapter.getMediaSet(slotIndex); 250 if (set == null) return; 251 mSelectionManager.setAutoLeaveSelectionMode(true); 252 mSelectionManager.toggle(set.getPath()); 253 mDetailsSource.findIndex(slotIndex); 254 mSlotView.invalidate(); 255 } 256 257 @Override 258 public void doCluster(int clusterType) { 259 String basePath = mMediaSet.getPath().toString(); 260 String newPath = FilterUtils.switchClusterPath(basePath, clusterType); 261 Bundle data = new Bundle(getData()); 262 data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath); 263 data.putInt(KEY_SELECTED_CLUSTER_TYPE, clusterType); 264 mActivity.getStateManager().switchState(this, AlbumSetPage.class, data); 265 } 266 267 @Override 268 public void onCreate(Bundle data, Bundle restoreState) { 269 initializeViews(); 270 initializeData(data); 271 Context context = mActivity.getAndroidContext(); 272 mGetContent = data.getBoolean(Gallery.KEY_GET_CONTENT, false); 273 mGetAlbum = data.getBoolean(Gallery.KEY_GET_ALBUM, false); 274 mTitle = data.getString(AlbumSetPage.KEY_SET_TITLE); 275 mSubtitle = data.getString(AlbumSetPage.KEY_SET_SUBTITLE); 276 mEyePosition = new EyePosition(context, this); 277 mDetailsSource = new MyDetailsSource(); 278 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); 279 mActionBar = mActivity.getGalleryActionBar(); 280 mSelectedAction = data.getInt(AlbumSetPage.KEY_SELECTED_CLUSTER_TYPE, 281 FilterUtils.CLUSTER_BY_ALBUM); 282 283 mHandler = new SynchronizedHandler(mActivity.getGLRoot()) { 284 @Override 285 public void handleMessage(Message message) { 286 switch (message.what) { 287 case MSG_PICK_ALBUM: { 288 pickAlbum(message.arg1); 289 break; 290 } 291 default: throw new AssertionError(message.what); 292 } 293 } 294 }; 295 } 296 297 private void clearLoadingBit(int loadingBit) { 298 mLoadingBits &= ~loadingBit; 299 if (mLoadingBits == 0 && mIsActive) { 300 // Only show toast when there's no album and we are going to finish 301 // the page. Toast is redundant if we are going to stay on this page. 302 if ((mAlbumSetDataAdapter.size() == 0)) { 303 if (mActivity.getStateManager().getStateCount() > 1) { 304 Toast.makeText((Context) mActivity, 305 R.string.empty_album, Toast.LENGTH_LONG).show(); 306 mActivity.getStateManager().finishState(this); 307 } 308 } 309 } 310 } 311 312 private void setLoadingBit(int loadingBit) { 313 mLoadingBits |= loadingBit; 314 } 315 316 @Override 317 public void onPause() { 318 super.onPause(); 319 mIsActive = false; 320 mActionModeHandler.pause(); 321 mAlbumSetDataAdapter.pause(); 322 mAlbumSetView.pause(); 323 mEyePosition.pause(); 324 DetailsHelper.pause(); 325 // Call disableClusterMenu to avoid receiving callback after paused. 326 // Don't hide menu here otherwise the list menu will disappear earlier than 327 // the action bar, which is janky and unwanted behavior. 328 mActionBar.disableClusterMenu(false); 329 if (mSyncTask != null) { 330 mSyncTask.cancel(); 331 mSyncTask = null; 332 clearLoadingBit(BIT_LOADING_SYNC); 333 } 334 } 335 336 @Override 337 public void onResume() { 338 super.onResume(); 339 mIsActive = true; 340 setContentPane(mRootPane); 341 342 // Set the reload bit here to prevent it exit this page in clearLoadingBit(). 343 setLoadingBit(BIT_LOADING_RELOAD); 344 mAlbumSetDataAdapter.resume(); 345 346 mAlbumSetView.resume(); 347 mEyePosition.resume(); 348 mActionModeHandler.resume(); 349 if (mShowClusterMenu) { 350 mActionBar.enableClusterMenu(mSelectedAction, this); 351 } 352 if (!mInitialSynced) { 353 setLoadingBit(BIT_LOADING_SYNC); 354 mSyncTask = mMediaSet.requestSync(AlbumSetPage.this); 355 } 356 } 357 358 private void initializeData(Bundle data) { 359 String mediaPath = data.getString(AlbumSetPage.KEY_MEDIA_PATH); 360 mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath); 361 mSelectionManager.setSourceMediaSet(mMediaSet); 362 mAlbumSetDataAdapter = new AlbumSetDataLoader( 363 mActivity, mMediaSet, DATA_CACHE_SIZE); 364 mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener()); 365 mAlbumSetView.setModel(mAlbumSetDataAdapter); 366 } 367 368 private void initializeViews() { 369 mSelectionManager = new SelectionManager(mActivity, true); 370 mSelectionManager.setSelectionListener(this); 371 372 Config.AlbumSetPage config = Config.AlbumSetPage.get((Context) mActivity); 373 mSlotView = new SlotView(mActivity, config.slotViewSpec); 374 mAlbumSetView = new AlbumSetSlotRenderer( 375 mActivity, mSelectionManager, mSlotView, config.labelSpec); 376 mSlotView.setSlotRenderer(mAlbumSetView); 377 mSlotView.setListener(new SlotView.SimpleListener() { 378 @Override 379 public void onDown(int index) { 380 AlbumSetPage.this.onDown(index); 381 } 382 383 @Override 384 public void onUp(boolean followedByLongPress) { 385 AlbumSetPage.this.onUp(followedByLongPress); 386 } 387 388 @Override 389 public void onSingleTapUp(int slotIndex) { 390 AlbumSetPage.this.onSingleTapUp(slotIndex); 391 } 392 393 @Override 394 public void onLongTap(int slotIndex) { 395 AlbumSetPage.this.onLongTap(slotIndex); 396 } 397 }); 398 399 mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager); 400 mActionModeHandler.setActionModeListener(new ActionModeListener() { 401 @Override 402 public boolean onActionItemClicked(MenuItem item) { 403 return onItemSelected(item); 404 } 405 }); 406 mRootPane.addComponent(mSlotView); 407 } 408 409 @Override 410 protected boolean onCreateActionBar(Menu menu) { 411 Activity activity = (Activity) mActivity; 412 MenuInflater inflater = activity.getMenuInflater(); 413 414 final boolean inAlbum = mActivity.getStateManager().hasStateClass( 415 AlbumPage.class); 416 417 if (mGetContent) { 418 inflater.inflate(R.menu.pickup, menu); 419 int typeBits = mData.getInt( 420 Gallery.KEY_TYPE_BITS, DataManager.INCLUDE_IMAGE); 421 int id = R.string.select_image; 422 if ((typeBits & DataManager.INCLUDE_VIDEO) != 0) { 423 id = (typeBits & DataManager.INCLUDE_IMAGE) == 0 424 ? R.string.select_video 425 : R.string.select_item; 426 } 427 mActionBar.setTitle(id); 428 } else if (mGetAlbum) { 429 inflater.inflate(R.menu.pickup, menu); 430 mActionBar.setTitle(R.string.select_album); 431 } else { 432 mShowClusterMenu = !inAlbum; 433 inflater.inflate(R.menu.albumset, menu); 434 MenuItem selectItem = menu.findItem(R.id.action_select); 435 436 if (selectItem != null) { 437 boolean selectAlbums = !inAlbum && 438 mActionBar.getClusterTypeAction() == FilterUtils.CLUSTER_BY_ALBUM; 439 if (selectAlbums) { 440 selectItem.setTitle(R.string.select_album); 441 } else { 442 selectItem.setTitle(R.string.select_group); 443 } 444 } 445 446 FilterUtils.setupMenuItems(mActionBar, mMediaSet.getPath(), false); 447 MenuItem switchCamera = menu.findItem(R.id.action_camera); 448 if (switchCamera != null) { 449 switchCamera.setVisible(GalleryUtils.isCameraAvailable(activity)); 450 } 451 final MenuItem helpMenu = menu.findItem(R.id.action_general_help); 452 HelpUtils.prepareHelpMenuItem(mActivity.getAndroidContext(), 453 helpMenu, R.string.help_url_gallery_main); 454 455 mActionBar.setTitle(mTitle); 456 mActionBar.setSubtitle(mSubtitle); 457 } 458 return true; 459 } 460 461 @Override 462 protected boolean onItemSelected(MenuItem item) { 463 Activity activity = (Activity) mActivity; 464 switch (item.getItemId()) { 465 case R.id.action_cancel: 466 activity.setResult(Activity.RESULT_CANCELED); 467 activity.finish(); 468 return true; 469 case R.id.action_select: 470 mSelectionManager.setAutoLeaveSelectionMode(false); 471 mSelectionManager.enterSelectionMode(); 472 return true; 473 case R.id.action_details: 474 if (mAlbumSetDataAdapter.size() != 0) { 475 if (mShowDetails) { 476 hideDetails(); 477 } else { 478 showDetails(); 479 } 480 } else { 481 Toast.makeText(activity, 482 activity.getText(R.string.no_albums_alert), 483 Toast.LENGTH_SHORT).show(); 484 } 485 return true; 486 case R.id.action_camera: { 487 GalleryUtils.startCameraActivity(activity); 488 return true; 489 } 490 case R.id.action_manage_offline: { 491 Bundle data = new Bundle(); 492 String mediaPath = mActivity.getDataManager().getTopSetPath( 493 DataManager.INCLUDE_ALL); 494 data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath); 495 mActivity.getStateManager().startState(ManageCachePage.class, data); 496 return true; 497 } 498 case R.id.action_sync_picasa_albums: { 499 PicasaSource.requestSync(activity); 500 return true; 501 } 502 case R.id.action_settings: { 503 activity.startActivity(new Intent(activity, GallerySettings.class)); 504 return true; 505 } 506 default: 507 return false; 508 } 509 } 510 511 @Override 512 protected void onStateResult(int requestCode, int resultCode, Intent data) { 513 switch (requestCode) { 514 case REQUEST_DO_ANIMATION: { 515 mSlotView.startRisingAnimation(); 516 } 517 } 518 } 519 520 private String getSelectedString() { 521 int count = mSelectionManager.getSelectedCount(); 522 int action = mActionBar.getClusterTypeAction(); 523 int string = action == FilterUtils.CLUSTER_BY_ALBUM 524 ? R.plurals.number_of_albums_selected 525 : R.plurals.number_of_groups_selected; 526 String format = mActivity.getResources().getQuantityString(string, count); 527 return String.format(format, count); 528 } 529 530 @Override 531 public void onSelectionModeChange(int mode) { 532 switch (mode) { 533 case SelectionManager.ENTER_SELECTION_MODE: { 534 mActionBar.disableClusterMenu(true); 535 mActionMode = mActionModeHandler.startActionMode(); 536 mVibrator.vibrate(100); 537 break; 538 } 539 case SelectionManager.LEAVE_SELECTION_MODE: { 540 mActionMode.finish(); 541 if (mShowClusterMenu) { 542 mActionBar.enableClusterMenu(mSelectedAction, this); 543 } 544 mRootPane.invalidate(); 545 break; 546 } 547 case SelectionManager.SELECT_ALL_MODE: { 548 mActionModeHandler.updateSupportedOperation(); 549 mRootPane.invalidate(); 550 break; 551 } 552 } 553 } 554 555 @Override 556 public void onSelectionChange(Path path, boolean selected) { 557 Utils.assertTrue(mActionMode != null); 558 mActionModeHandler.setTitle(getSelectedString()); 559 mActionModeHandler.updateSupportedOperation(path, selected); 560 } 561 562 private void hideDetails() { 563 mShowDetails = false; 564 mDetailsHelper.hide(); 565 mAlbumSetView.setHighlightItemPath(null); 566 mSlotView.invalidate(); 567 } 568 569 private void showDetails() { 570 mShowDetails = true; 571 if (mDetailsHelper == null) { 572 mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource); 573 mDetailsHelper.setCloseListener(new CloseListener() { 574 @Override 575 public void onClose() { 576 hideDetails(); 577 } 578 }); 579 } 580 mDetailsHelper.show(); 581 } 582 583 @Override 584 public void onSyncDone(final MediaSet mediaSet, final int resultCode) { 585 if (resultCode == MediaSet.SYNC_RESULT_ERROR) { 586 Log.d(TAG, "onSyncDone: " + Utils.maskDebugInfo(mediaSet.getName()) + " result=" 587 + resultCode); 588 } 589 ((Activity) mActivity).runOnUiThread(new Runnable() { 590 @Override 591 public void run() { 592 GLRoot root = mActivity.getGLRoot(); 593 root.lockRenderThread(); 594 try { 595 if (resultCode == MediaSet.SYNC_RESULT_SUCCESS) { 596 mInitialSynced = true; 597 } 598 clearLoadingBit(BIT_LOADING_SYNC); 599 if (resultCode == MediaSet.SYNC_RESULT_ERROR && mIsActive) { 600 Log.w(TAG, "failed to load album set"); 601 } 602 } finally { 603 root.unlockRenderThread(); 604 } 605 } 606 }); 607 } 608 609 private class MyLoadingListener implements LoadingListener { 610 @Override 611 public void onLoadingStarted() { 612 setLoadingBit(BIT_LOADING_RELOAD); 613 } 614 615 @Override 616 public void onLoadingFinished() { 617 clearLoadingBit(BIT_LOADING_RELOAD); 618 } 619 } 620 621 private class MyDetailsSource implements DetailsHelper.DetailsSource { 622 private int mIndex; 623 624 @Override 625 public int size() { 626 return mAlbumSetDataAdapter.size(); 627 } 628 629 @Override 630 public int getIndex() { 631 return mIndex; 632 } 633 634 // If requested index is out of active window, suggest a valid index. 635 // If there is no valid index available, return -1. 636 @Override 637 public int findIndex(int indexHint) { 638 if (mAlbumSetDataAdapter.isActive(indexHint)) { 639 mIndex = indexHint; 640 } else { 641 mIndex = mAlbumSetDataAdapter.getActiveStart(); 642 if (!mAlbumSetDataAdapter.isActive(mIndex)) { 643 return -1; 644 } 645 } 646 return mIndex; 647 } 648 649 @Override 650 public MediaDetails getDetails() { 651 MediaObject item = mAlbumSetDataAdapter.getMediaSet(mIndex); 652 if (item != null) { 653 mAlbumSetView.setHighlightItemPath(item.getPath()); 654 return item.getDetails(); 655 } else { 656 return null; 657 } 658 } 659 } 660} 661