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