/* * 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 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 mUpdateStorageInfoJob = new Job() { @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 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() { } }