Home | History | Annotate | Download | only in app
      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 
     17 package com.android.gallery3d.app;
     18 
     19 import android.app.Activity;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.graphics.Rect;
     23 import android.os.Bundle;
     24 import android.os.Handler;
     25 import android.os.Message;
     26 import android.os.Vibrator;
     27 import android.provider.MediaStore;
     28 import android.view.ActionMode;
     29 import android.view.Menu;
     30 import android.view.MenuInflater;
     31 import android.view.MenuItem;
     32 import android.widget.Toast;
     33 
     34 import com.android.gallery3d.R;
     35 import com.android.gallery3d.common.Utils;
     36 import com.android.gallery3d.data.DataManager;
     37 import com.android.gallery3d.data.MediaDetails;
     38 import com.android.gallery3d.data.MediaObject;
     39 import com.android.gallery3d.data.MediaSet;
     40 import com.android.gallery3d.data.Path;
     41 import com.android.gallery3d.picasasource.PicasaSource;
     42 import com.android.gallery3d.settings.GallerySettings;
     43 import com.android.gallery3d.ui.ActionModeHandler;
     44 import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener;
     45 import com.android.gallery3d.ui.AlbumSetSlotRenderer;
     46 import com.android.gallery3d.ui.DetailsHelper;
     47 import com.android.gallery3d.ui.DetailsHelper.CloseListener;
     48 import com.android.gallery3d.ui.FadeTexture;
     49 import com.android.gallery3d.ui.GLCanvas;
     50 import com.android.gallery3d.ui.GLRoot;
     51 import com.android.gallery3d.ui.GLView;
     52 import com.android.gallery3d.ui.SelectionManager;
     53 import com.android.gallery3d.ui.SlotView;
     54 import com.android.gallery3d.ui.SynchronizedHandler;
     55 import com.android.gallery3d.util.Future;
     56 import com.android.gallery3d.util.GalleryUtils;
     57 import com.android.gallery3d.util.HelpUtils;
     58 
     59 public 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