/*
* 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() {
}
}