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