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.res.Configuration;
     21 import android.os.Bundle;
     22 import android.os.Handler;
     23 import android.os.Message;
     24 import android.text.format.Formatter;
     25 import android.view.LayoutInflater;
     26 import android.view.View;
     27 import android.view.View.OnClickListener;
     28 import android.widget.FrameLayout;
     29 import android.widget.ProgressBar;
     30 import android.widget.TextView;
     31 import android.widget.Toast;
     32 
     33 import com.android.gallery3d.R;
     34 import com.android.gallery3d.common.Utils;
     35 import com.android.gallery3d.data.MediaObject;
     36 import com.android.gallery3d.data.MediaSet;
     37 import com.android.gallery3d.data.Path;
     38 import com.android.gallery3d.glrenderer.GLCanvas;
     39 import com.android.gallery3d.ui.CacheStorageUsageInfo;
     40 import com.android.gallery3d.ui.GLRoot;
     41 import com.android.gallery3d.ui.GLView;
     42 import com.android.gallery3d.ui.ManageCacheDrawer;
     43 import com.android.gallery3d.ui.MenuExecutor;
     44 import com.android.gallery3d.ui.SelectionManager;
     45 import com.android.gallery3d.ui.SlotView;
     46 import com.android.gallery3d.ui.SynchronizedHandler;
     47 import com.android.gallery3d.util.Future;
     48 import com.android.gallery3d.util.GalleryUtils;
     49 import com.android.gallery3d.util.ThreadPool.Job;
     50 import com.android.gallery3d.util.ThreadPool.JobContext;
     51 
     52 import java.util.ArrayList;
     53 
     54 public class ManageCachePage extends ActivityState implements
     55         SelectionManager.SelectionListener, MenuExecutor.ProgressListener,
     56         EyePosition.EyePositionListener, OnClickListener {
     57     public static final String KEY_MEDIA_PATH = "media-path";
     58 
     59     @SuppressWarnings("unused")
     60     private static final String TAG = "ManageCachePage";
     61 
     62     private static final int DATA_CACHE_SIZE = 256;
     63     private static final int MSG_REFRESH_STORAGE_INFO = 1;
     64     private static final int MSG_REQUEST_LAYOUT = 2;
     65     private static final int PROGRESS_BAR_MAX = 10000;
     66 
     67     private SlotView mSlotView;
     68     private MediaSet mMediaSet;
     69 
     70     protected SelectionManager mSelectionManager;
     71     protected ManageCacheDrawer mSelectionDrawer;
     72     private AlbumSetDataLoader mAlbumSetDataAdapter;
     73 
     74     private EyePosition mEyePosition;
     75 
     76     // The eyes' position of the user, the origin is at the center of the
     77     // device and the unit is in pixels.
     78     private float mX;
     79     private float mY;
     80     private float mZ;
     81 
     82     private int mAlbumCountToMakeAvailableOffline;
     83     private View mFooterContent;
     84     private CacheStorageUsageInfo mCacheStorageInfo;
     85     private Future<Void> mUpdateStorageInfo;
     86     private Handler mHandler;
     87     private boolean mLayoutReady = false;
     88 
     89     @Override
     90     protected int getBackgroundColorId() {
     91         return R.color.cache_background;
     92     }
     93 
     94     private GLView mRootPane = new GLView() {
     95         private float mMatrix[] = new float[16];
     96 
     97         @Override
     98         protected void renderBackground(GLCanvas view) {
     99             view.clearBuffer(getBackgroundColor());
    100         }
    101 
    102         @Override
    103         protected void onLayout(
    104                 boolean changed, int left, int top, int right, int bottom) {
    105             // Hack: our layout depends on other components on the screen.
    106             // We assume the other components will complete before we get a change
    107             // to run a message in main thread.
    108             if (!mLayoutReady) {
    109                 mHandler.sendEmptyMessage(MSG_REQUEST_LAYOUT);
    110                 return;
    111             }
    112             mLayoutReady = false;
    113 
    114             mEyePosition.resetPosition();
    115             int slotViewTop = mActivity.getGalleryActionBar().getHeight();
    116             int slotViewBottom = bottom - top;
    117 
    118             View footer = mActivity.findViewById(R.id.footer);
    119             if (footer != null) {
    120                 int location[] = {0, 0};
    121                 footer.getLocationOnScreen(location);
    122                 slotViewBottom = location[1];
    123             }
    124 
    125             mSlotView.layout(0, slotViewTop, right - left, slotViewBottom);
    126         }
    127 
    128         @Override
    129         protected void render(GLCanvas canvas) {
    130             canvas.save(GLCanvas.SAVE_FLAG_MATRIX);
    131             GalleryUtils.setViewPointMatrix(mMatrix,
    132                         getWidth() / 2 + mX, getHeight() / 2 + mY, mZ);
    133             canvas.multiplyMatrix(mMatrix, 0);
    134             super.render(canvas);
    135             canvas.restore();
    136         }
    137     };
    138 
    139     @Override
    140     public void onEyePositionChanged(float x, float y, float z) {
    141         mRootPane.lockRendering();
    142         mX = x;
    143         mY = y;
    144         mZ = z;
    145         mRootPane.unlockRendering();
    146         mRootPane.invalidate();
    147     }
    148 
    149     private void onDown(int index) {
    150         mSelectionDrawer.setPressedIndex(index);
    151     }
    152 
    153     private void onUp() {
    154         mSelectionDrawer.setPressedIndex(-1);
    155     }
    156 
    157     public void onSingleTapUp(int slotIndex) {
    158         MediaSet targetSet = mAlbumSetDataAdapter.getMediaSet(slotIndex);
    159         if (targetSet == null) return; // Content is dirty, we shall reload soon
    160 
    161         // ignore selection action if the target set does not support cache
    162         // operation (like a local album).
    163         if ((targetSet.getSupportedOperations()
    164                 & MediaSet.SUPPORT_CACHE) == 0) {
    165             showToastForLocalAlbum();
    166             return;
    167         }
    168 
    169         Path path = targetSet.getPath();
    170         boolean isFullyCached =
    171                 (targetSet.getCacheFlag() == MediaObject.CACHE_FLAG_FULL);
    172         boolean isSelected = mSelectionManager.isItemSelected(path);
    173 
    174         if (!isFullyCached) {
    175             // We only count the media sets that will be made available offline
    176             // in this session.
    177             if (isSelected) {
    178                 --mAlbumCountToMakeAvailableOffline;
    179             } else {
    180                 ++mAlbumCountToMakeAvailableOffline;
    181             }
    182         }
    183 
    184         long sizeOfTarget = targetSet.getCacheSize();
    185         mCacheStorageInfo.increaseTargetCacheSize(
    186                 (isFullyCached ^ isSelected) ? -sizeOfTarget : sizeOfTarget);
    187         refreshCacheStorageInfo();
    188 
    189         mSelectionManager.toggle(path);
    190         mSlotView.invalidate();
    191     }
    192 
    193     @Override
    194     public void onCreate(Bundle data, Bundle restoreState) {
    195         super.onCreate(data, restoreState);
    196         mCacheStorageInfo = new CacheStorageUsageInfo(mActivity);
    197         initializeViews();
    198         initializeData(data);
    199         mEyePosition = new EyePosition(mActivity.getAndroidContext(), this);
    200         mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
    201             @Override
    202             public void handleMessage(Message message) {
    203                 switch (message.what) {
    204                     case MSG_REFRESH_STORAGE_INFO:
    205                         refreshCacheStorageInfo();
    206                         break;
    207                     case MSG_REQUEST_LAYOUT: {
    208                         mLayoutReady = true;
    209                         removeMessages(MSG_REQUEST_LAYOUT);
    210                         mRootPane.requestLayout();
    211                         break;
    212                     }
    213                 }
    214             }
    215         };
    216     }
    217 
    218     @Override
    219     public void onConfigurationChanged(Configuration config) {
    220         // We use different layout resources for different configs
    221         initializeFooterViews();
    222         FrameLayout layout = (FrameLayout) ((Activity) mActivity).findViewById(R.id.footer);
    223         if (layout.getVisibility() == View.VISIBLE) {
    224             layout.removeAllViews();
    225             layout.addView(mFooterContent);
    226         }
    227     }
    228 
    229     @Override
    230     public void onPause() {
    231         super.onPause();
    232         mAlbumSetDataAdapter.pause();
    233         mSelectionDrawer.pause();
    234         mEyePosition.pause();
    235 
    236         if (mUpdateStorageInfo != null) {
    237             mUpdateStorageInfo.cancel();
    238             mUpdateStorageInfo = null;
    239         }
    240         mHandler.removeMessages(MSG_REFRESH_STORAGE_INFO);
    241 
    242         FrameLayout layout = (FrameLayout) ((Activity) mActivity).findViewById(R.id.footer);
    243         layout.removeAllViews();
    244         layout.setVisibility(View.INVISIBLE);
    245     }
    246 
    247     private Job<Void> mUpdateStorageInfoJob = new Job<Void>() {
    248         @Override
    249         public Void run(JobContext jc) {
    250             mCacheStorageInfo.loadStorageInfo(jc);
    251             if (!jc.isCancelled()) {
    252                 mHandler.sendEmptyMessage(MSG_REFRESH_STORAGE_INFO);
    253             }
    254             return null;
    255         }
    256     };
    257 
    258     @Override
    259     public void onResume() {
    260         super.onResume();
    261         setContentPane(mRootPane);
    262         mAlbumSetDataAdapter.resume();
    263         mSelectionDrawer.resume();
    264         mEyePosition.resume();
    265         mUpdateStorageInfo = mActivity.getThreadPool().submit(mUpdateStorageInfoJob);
    266         FrameLayout layout = (FrameLayout) ((Activity) mActivity).findViewById(R.id.footer);
    267         layout.addView(mFooterContent);
    268         layout.setVisibility(View.VISIBLE);
    269     }
    270 
    271     private void initializeData(Bundle data) {
    272         String mediaPath = data.getString(ManageCachePage.KEY_MEDIA_PATH);
    273         mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath);
    274         mSelectionManager.setSourceMediaSet(mMediaSet);
    275 
    276         // We will always be in selection mode in this page.
    277         mSelectionManager.setAutoLeaveSelectionMode(false);
    278         mSelectionManager.enterSelectionMode();
    279 
    280         mAlbumSetDataAdapter = new AlbumSetDataLoader(
    281                 mActivity, mMediaSet, DATA_CACHE_SIZE);
    282         mSelectionDrawer.setModel(mAlbumSetDataAdapter);
    283     }
    284 
    285     private void initializeViews() {
    286         Activity activity = mActivity;
    287 
    288         mSelectionManager = new SelectionManager(mActivity, true);
    289         mSelectionManager.setSelectionListener(this);
    290 
    291         Config.ManageCachePage config = Config.ManageCachePage.get(activity);
    292         mSlotView = new SlotView(mActivity, config.slotViewSpec);
    293         mSelectionDrawer = new ManageCacheDrawer(mActivity, mSelectionManager, mSlotView,
    294                 config.labelSpec, config.cachePinSize, config.cachePinMargin);
    295         mSlotView.setSlotRenderer(mSelectionDrawer);
    296         mSlotView.setListener(new SlotView.SimpleListener() {
    297             @Override
    298             public void onDown(int index) {
    299                 ManageCachePage.this.onDown(index);
    300             }
    301 
    302             @Override
    303             public void onUp(boolean followedByLongPress) {
    304                 ManageCachePage.this.onUp();
    305             }
    306 
    307             @Override
    308             public void onSingleTapUp(int slotIndex) {
    309                 ManageCachePage.this.onSingleTapUp(slotIndex);
    310             }
    311         });
    312         mRootPane.addComponent(mSlotView);
    313         initializeFooterViews();
    314     }
    315 
    316     private void initializeFooterViews() {
    317         Activity activity = mActivity;
    318 
    319         LayoutInflater inflater = activity.getLayoutInflater();
    320         mFooterContent = inflater.inflate(R.layout.manage_offline_bar, null);
    321 
    322         mFooterContent.findViewById(R.id.done).setOnClickListener(this);
    323         refreshCacheStorageInfo();
    324     }
    325 
    326     @Override
    327     public void onClick(View view) {
    328         Utils.assertTrue(view.getId() == R.id.done);
    329         GLRoot root = mActivity.getGLRoot();
    330         root.lockRenderThread();
    331         try {
    332             ArrayList<Path> ids = mSelectionManager.getSelected(false);
    333             if (ids.size() == 0) {
    334                 onBackPressed();
    335                 return;
    336             }
    337             showToast();
    338 
    339             MenuExecutor menuExecutor = new MenuExecutor(mActivity, mSelectionManager);
    340             menuExecutor.startAction(R.id.action_toggle_full_caching,
    341                     R.string.process_caching_requests, this);
    342         } finally {
    343             root.unlockRenderThread();
    344         }
    345     }
    346 
    347     private void showToast() {
    348         if (mAlbumCountToMakeAvailableOffline > 0) {
    349             Activity activity = mActivity;
    350             Toast.makeText(activity, activity.getResources().getQuantityString(
    351                     R.plurals.make_albums_available_offline,
    352                     mAlbumCountToMakeAvailableOffline),
    353                     Toast.LENGTH_SHORT).show();
    354         }
    355     }
    356 
    357     private void showToastForLocalAlbum() {
    358         Activity activity = mActivity;
    359         Toast.makeText(activity, activity.getResources().getString(
    360             R.string.try_to_set_local_album_available_offline),
    361             Toast.LENGTH_SHORT).show();
    362     }
    363 
    364     private void refreshCacheStorageInfo() {
    365         ProgressBar progressBar = (ProgressBar) mFooterContent.findViewById(R.id.progress);
    366         TextView status = (TextView) mFooterContent.findViewById(R.id.status);
    367         progressBar.setMax(PROGRESS_BAR_MAX);
    368         long totalBytes = mCacheStorageInfo.getTotalBytes();
    369         long usedBytes = mCacheStorageInfo.getUsedBytes();
    370         long expectedBytes = mCacheStorageInfo.getExpectedUsedBytes();
    371         long freeBytes = mCacheStorageInfo.getFreeBytes();
    372 
    373         Activity activity = mActivity;
    374         if (totalBytes == 0) {
    375             progressBar.setProgress(0);
    376             progressBar.setSecondaryProgress(0);
    377 
    378             // TODO: get the string translated
    379             String label = activity.getString(R.string.free_space_format, "-");
    380             status.setText(label);
    381         } else {
    382             progressBar.setProgress((int) (usedBytes * PROGRESS_BAR_MAX / totalBytes));
    383             progressBar.setSecondaryProgress(
    384                     (int) (expectedBytes * PROGRESS_BAR_MAX / totalBytes));
    385             String label = activity.getString(R.string.free_space_format,
    386                     Formatter.formatFileSize(activity, freeBytes));
    387             status.setText(label);
    388         }
    389     }
    390 
    391     @Override
    392     public void onProgressComplete(int result) {
    393         onBackPressed();
    394     }
    395 
    396     @Override
    397     public void onProgressUpdate(int index) {
    398     }
    399 
    400     @Override
    401     public void onSelectionModeChange(int mode) {
    402     }
    403 
    404     @Override
    405     public void onSelectionChange(Path path, boolean selected) {
    406     }
    407 
    408     @Override
    409     public void onConfirmDialogDismissed(boolean confirmed) {
    410     }
    411 
    412     @Override
    413     public void onConfirmDialogShown() {
    414     }
    415 
    416     @Override
    417     public void onProgressStart() {
    418     }
    419 }
    420