| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.gallery3d.app; |
| |
| import android.app.Activity; |
| import android.content.res.Configuration; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.text.format.Formatter; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.View.OnClickListener; |
| import android.widget.FrameLayout; |
| import android.widget.ProgressBar; |
| import android.widget.TextView; |
| import android.widget.Toast; |
| |
| import com.android.gallery3d.R; |
| import com.android.gallery3d.common.Utils; |
| import com.android.gallery3d.data.MediaObject; |
| import com.android.gallery3d.data.MediaSet; |
| import com.android.gallery3d.data.Path; |
| import com.android.gallery3d.glrenderer.GLCanvas; |
| import com.android.gallery3d.ui.CacheStorageUsageInfo; |
| import com.android.gallery3d.ui.GLRoot; |
| import com.android.gallery3d.ui.GLView; |
| import com.android.gallery3d.ui.ManageCacheDrawer; |
| import com.android.gallery3d.ui.MenuExecutor; |
| import com.android.gallery3d.ui.SelectionManager; |
| import com.android.gallery3d.ui.SlotView; |
| import com.android.gallery3d.ui.SynchronizedHandler; |
| import com.android.gallery3d.util.Future; |
| import com.android.gallery3d.util.GalleryUtils; |
| import com.android.gallery3d.util.ThreadPool.Job; |
| import com.android.gallery3d.util.ThreadPool.JobContext; |
| |
| import java.util.ArrayList; |
| |
| public class ManageCachePage extends ActivityState implements |
| SelectionManager.SelectionListener, MenuExecutor.ProgressListener, |
| EyePosition.EyePositionListener, OnClickListener { |
| public static final String KEY_MEDIA_PATH = "media-path"; |
| |
| @SuppressWarnings("unused") |
| private static final String TAG = "ManageCachePage"; |
| |
| private static final int DATA_CACHE_SIZE = 256; |
| private static final int MSG_REFRESH_STORAGE_INFO = 1; |
| private static final int MSG_REQUEST_LAYOUT = 2; |
| private static final int PROGRESS_BAR_MAX = 10000; |
| |
| private SlotView mSlotView; |
| private MediaSet mMediaSet; |
| |
| protected SelectionManager mSelectionManager; |
| protected ManageCacheDrawer mSelectionDrawer; |
| private AlbumSetDataLoader mAlbumSetDataAdapter; |
| |
| private EyePosition mEyePosition; |
| |
| // The eyes' position of the user, the origin is at the center of the |
| // device and the unit is in pixels. |
| private float mX; |
| private float mY; |
| private float mZ; |
| |
| private int mAlbumCountToMakeAvailableOffline; |
| private View mFooterContent; |
| private CacheStorageUsageInfo mCacheStorageInfo; |
| private Future<Void> mUpdateStorageInfo; |
| private Handler mHandler; |
| private boolean mLayoutReady = false; |
| |
| @Override |
| protected int getBackgroundColorId() { |
| return R.color.cache_background; |
| } |
| |
| private GLView mRootPane = new GLView() { |
| private float mMatrix[] = new float[16]; |
| |
| @Override |
| protected void renderBackground(GLCanvas view) { |
| view.clearBuffer(getBackgroundColor()); |
| } |
| |
| @Override |
| protected void onLayout( |
| boolean changed, int left, int top, int right, int bottom) { |
| // Hack: our layout depends on other components on the screen. |
| // We assume the other components will complete before we get a change |
| // to run a message in main thread. |
| if (!mLayoutReady) { |
| mHandler.sendEmptyMessage(MSG_REQUEST_LAYOUT); |
| return; |
| } |
| mLayoutReady = false; |
| |
| mEyePosition.resetPosition(); |
| int slotViewTop = mActivity.getGalleryActionBar().getHeight(); |
| int slotViewBottom = bottom - top; |
| |
| View footer = mActivity.findViewById(R.id.footer); |
| if (footer != null) { |
| int location[] = {0, 0}; |
| footer.getLocationOnScreen(location); |
| slotViewBottom = location[1]; |
| } |
| |
| mSlotView.layout(0, slotViewTop, right - left, slotViewBottom); |
| } |
| |
| @Override |
| protected void render(GLCanvas canvas) { |
| canvas.save(GLCanvas.SAVE_FLAG_MATRIX); |
| GalleryUtils.setViewPointMatrix(mMatrix, |
| getWidth() / 2 + mX, getHeight() / 2 + mY, mZ); |
| canvas.multiplyMatrix(mMatrix, 0); |
| super.render(canvas); |
| canvas.restore(); |
| } |
| }; |
| |
| @Override |
| public void onEyePositionChanged(float x, float y, float z) { |
| mRootPane.lockRendering(); |
| mX = x; |
| mY = y; |
| mZ = z; |
| mRootPane.unlockRendering(); |
| mRootPane.invalidate(); |
| } |
| |
| private void onDown(int index) { |
| mSelectionDrawer.setPressedIndex(index); |
| } |
| |
| private void onUp() { |
| mSelectionDrawer.setPressedIndex(-1); |
| } |
| |
| public void onSingleTapUp(int slotIndex) { |
| MediaSet targetSet = mAlbumSetDataAdapter.getMediaSet(slotIndex); |
| if (targetSet == null) return; // Content is dirty, we shall reload soon |
| |
| // ignore selection action if the target set does not support cache |
| // operation (like a local album). |
| if ((targetSet.getSupportedOperations() |
| & MediaSet.SUPPORT_CACHE) == 0) { |
| showToastForLocalAlbum(); |
| return; |
| } |
| |
| Path path = targetSet.getPath(); |
| boolean isFullyCached = |
| (targetSet.getCacheFlag() == MediaObject.CACHE_FLAG_FULL); |
| boolean isSelected = mSelectionManager.isItemSelected(path); |
| |
| if (!isFullyCached) { |
| // We only count the media sets that will be made available offline |
| // in this session. |
| if (isSelected) { |
| --mAlbumCountToMakeAvailableOffline; |
| } else { |
| ++mAlbumCountToMakeAvailableOffline; |
| } |
| } |
| |
| long sizeOfTarget = targetSet.getCacheSize(); |
| mCacheStorageInfo.increaseTargetCacheSize( |
| (isFullyCached ^ isSelected) ? -sizeOfTarget : sizeOfTarget); |
| refreshCacheStorageInfo(); |
| |
| mSelectionManager.toggle(path); |
| mSlotView.invalidate(); |
| } |
| |
| @Override |
| public void onCreate(Bundle data, Bundle restoreState) { |
| super.onCreate(data, restoreState); |
| mCacheStorageInfo = new CacheStorageUsageInfo(mActivity); |
| initializeViews(); |
| initializeData(data); |
| mEyePosition = new EyePosition(mActivity.getAndroidContext(), this); |
| mHandler = new SynchronizedHandler(mActivity.getGLRoot()) { |
| @Override |
| public void handleMessage(Message message) { |
| switch (message.what) { |
| case MSG_REFRESH_STORAGE_INFO: |
| refreshCacheStorageInfo(); |
| break; |
| case MSG_REQUEST_LAYOUT: { |
| mLayoutReady = true; |
| removeMessages(MSG_REQUEST_LAYOUT); |
| mRootPane.requestLayout(); |
| break; |
| } |
| } |
| } |
| }; |
| } |
| |
| @Override |
| public void onConfigurationChanged(Configuration config) { |
| // We use different layout resources for different configs |
| initializeFooterViews(); |
| FrameLayout layout = (FrameLayout) ((Activity) mActivity).findViewById(R.id.footer); |
| if (layout.getVisibility() == View.VISIBLE) { |
| layout.removeAllViews(); |
| layout.addView(mFooterContent); |
| } |
| } |
| |
| @Override |
| public void onPause() { |
| super.onPause(); |
| mAlbumSetDataAdapter.pause(); |
| mSelectionDrawer.pause(); |
| mEyePosition.pause(); |
| |
| if (mUpdateStorageInfo != null) { |
| mUpdateStorageInfo.cancel(); |
| mUpdateStorageInfo = null; |
| } |
| mHandler.removeMessages(MSG_REFRESH_STORAGE_INFO); |
| |
| FrameLayout layout = (FrameLayout) ((Activity) mActivity).findViewById(R.id.footer); |
| layout.removeAllViews(); |
| layout.setVisibility(View.INVISIBLE); |
| } |
| |
| private Job<Void> mUpdateStorageInfoJob = new Job<Void>() { |
| @Override |
| public Void run(JobContext jc) { |
| mCacheStorageInfo.loadStorageInfo(jc); |
| if (!jc.isCancelled()) { |
| mHandler.sendEmptyMessage(MSG_REFRESH_STORAGE_INFO); |
| } |
| return null; |
| } |
| }; |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| setContentPane(mRootPane); |
| mAlbumSetDataAdapter.resume(); |
| mSelectionDrawer.resume(); |
| mEyePosition.resume(); |
| mUpdateStorageInfo = mActivity.getThreadPool().submit(mUpdateStorageInfoJob); |
| FrameLayout layout = (FrameLayout) ((Activity) mActivity).findViewById(R.id.footer); |
| layout.addView(mFooterContent); |
| layout.setVisibility(View.VISIBLE); |
| } |
| |
| private void initializeData(Bundle data) { |
| String mediaPath = data.getString(ManageCachePage.KEY_MEDIA_PATH); |
| mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath); |
| mSelectionManager.setSourceMediaSet(mMediaSet); |
| |
| // We will always be in selection mode in this page. |
| mSelectionManager.setAutoLeaveSelectionMode(false); |
| mSelectionManager.enterSelectionMode(); |
| |
| mAlbumSetDataAdapter = new AlbumSetDataLoader( |
| mActivity, mMediaSet, DATA_CACHE_SIZE); |
| mSelectionDrawer.setModel(mAlbumSetDataAdapter); |
| } |
| |
| private void initializeViews() { |
| Activity activity = mActivity; |
| |
| mSelectionManager = new SelectionManager(mActivity, true); |
| mSelectionManager.setSelectionListener(this); |
| |
| Config.ManageCachePage config = Config.ManageCachePage.get(activity); |
| mSlotView = new SlotView(mActivity, config.slotViewSpec); |
| mSelectionDrawer = new ManageCacheDrawer(mActivity, mSelectionManager, mSlotView, |
| config.labelSpec, config.cachePinSize, config.cachePinMargin); |
| mSlotView.setSlotRenderer(mSelectionDrawer); |
| mSlotView.setListener(new SlotView.SimpleListener() { |
| @Override |
| public void onDown(int index) { |
| ManageCachePage.this.onDown(index); |
| } |
| |
| @Override |
| public void onUp(boolean followedByLongPress) { |
| ManageCachePage.this.onUp(); |
| } |
| |
| @Override |
| public void onSingleTapUp(int slotIndex) { |
| ManageCachePage.this.onSingleTapUp(slotIndex); |
| } |
| }); |
| mRootPane.addComponent(mSlotView); |
| initializeFooterViews(); |
| } |
| |
| private void initializeFooterViews() { |
| Activity activity = mActivity; |
| |
| LayoutInflater inflater = activity.getLayoutInflater(); |
| mFooterContent = inflater.inflate(R.layout.manage_offline_bar, null); |
| |
| mFooterContent.findViewById(R.id.done).setOnClickListener(this); |
| refreshCacheStorageInfo(); |
| } |
| |
| @Override |
| public void onClick(View view) { |
| Utils.assertTrue(view.getId() == R.id.done); |
| GLRoot root = mActivity.getGLRoot(); |
| root.lockRenderThread(); |
| try { |
| ArrayList<Path> ids = mSelectionManager.getSelected(false); |
| if (ids.size() == 0) { |
| onBackPressed(); |
| return; |
| } |
| showToast(); |
| |
| MenuExecutor menuExecutor = new MenuExecutor(mActivity, mSelectionManager); |
| menuExecutor.startAction(R.id.action_toggle_full_caching, |
| R.string.process_caching_requests, this); |
| } finally { |
| root.unlockRenderThread(); |
| } |
| } |
| |
| private void showToast() { |
| if (mAlbumCountToMakeAvailableOffline > 0) { |
| Activity activity = mActivity; |
| Toast.makeText(activity, activity.getResources().getQuantityString( |
| R.plurals.make_albums_available_offline, |
| mAlbumCountToMakeAvailableOffline), |
| Toast.LENGTH_SHORT).show(); |
| } |
| } |
| |
| private void showToastForLocalAlbum() { |
| Activity activity = mActivity; |
| Toast.makeText(activity, activity.getResources().getString( |
| R.string.try_to_set_local_album_available_offline), |
| Toast.LENGTH_SHORT).show(); |
| } |
| |
| private void refreshCacheStorageInfo() { |
| ProgressBar progressBar = (ProgressBar) mFooterContent.findViewById(R.id.progress); |
| TextView status = (TextView) mFooterContent.findViewById(R.id.status); |
| progressBar.setMax(PROGRESS_BAR_MAX); |
| long totalBytes = mCacheStorageInfo.getTotalBytes(); |
| long usedBytes = mCacheStorageInfo.getUsedBytes(); |
| long expectedBytes = mCacheStorageInfo.getExpectedUsedBytes(); |
| long freeBytes = mCacheStorageInfo.getFreeBytes(); |
| |
| Activity activity = mActivity; |
| if (totalBytes == 0) { |
| progressBar.setProgress(0); |
| progressBar.setSecondaryProgress(0); |
| |
| // TODO: get the string translated |
| String label = activity.getString(R.string.free_space_format, "-"); |
| status.setText(label); |
| } else { |
| progressBar.setProgress((int) (usedBytes * PROGRESS_BAR_MAX / totalBytes)); |
| progressBar.setSecondaryProgress( |
| (int) (expectedBytes * PROGRESS_BAR_MAX / totalBytes)); |
| String label = activity.getString(R.string.free_space_format, |
| Formatter.formatFileSize(activity, freeBytes)); |
| status.setText(label); |
| } |
| } |
| |
| @Override |
| public void onProgressComplete(int result) { |
| onBackPressed(); |
| } |
| |
| @Override |
| public void onProgressUpdate(int index) { |
| } |
| |
| @Override |
| public void onSelectionModeChange(int mode) { |
| } |
| |
| @Override |
| public void onSelectionChange(Path path, boolean selected) { |
| } |
| |
| @Override |
| public void onConfirmDialogDismissed(boolean confirmed) { |
| } |
| |
| @Override |
| public void onConfirmDialogShown() { |
| } |
| |
| @Override |
| public void onProgressStart() { |
| } |
| } |