• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.gallery3d.app;
18 
19 import android.app.Activity;
20 import android.content.res.Configuration;
21 import android.os.Bundle;
22 import android.os.Handler;
23 import android.os.Message;
24 import android.text.format.Formatter;
25 import android.view.LayoutInflater;
26 import android.view.View;
27 import android.view.View.OnClickListener;
28 import android.widget.FrameLayout;
29 import android.widget.ProgressBar;
30 import android.widget.TextView;
31 import android.widget.Toast;
32 
33 import com.android.gallery3d.R;
34 import com.android.gallery3d.common.Utils;
35 import com.android.gallery3d.data.MediaObject;
36 import com.android.gallery3d.data.MediaSet;
37 import com.android.gallery3d.data.Path;
38 import com.android.gallery3d.ui.CacheStorageUsageInfo;
39 import com.android.gallery3d.ui.GLCanvas;
40 import com.android.gallery3d.ui.GLRoot;
41 import com.android.gallery3d.ui.GLView;
42 import com.android.gallery3d.ui.ManageCacheDrawer;
43 import com.android.gallery3d.ui.MenuExecutor;
44 import com.android.gallery3d.ui.SelectionManager;
45 import com.android.gallery3d.ui.SlotView;
46 import com.android.gallery3d.ui.SynchronizedHandler;
47 import com.android.gallery3d.util.Future;
48 import com.android.gallery3d.util.GalleryUtils;
49 import com.android.gallery3d.util.ThreadPool.Job;
50 import com.android.gallery3d.util.ThreadPool.JobContext;
51 
52 import java.util.ArrayList;
53 
54 public 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
onEyePositionChanged(float x, float y, float z)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 
onDown(int index)144     private void onDown(int index) {
145         mSelectionDrawer.setPressedIndex(index);
146     }
147 
onUp()148     private void onUp() {
149         mSelectionDrawer.setPressedIndex(-1);
150     }
151 
onSingleTapUp(int slotIndex)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
onCreate(Bundle data, Bundle restoreState)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
onConfigurationChanged(Configuration config)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
onPause()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
onResume()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 
initializeData(Bundle data)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 
initializeViews()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 
initializeFooterViews()310     private void initializeFooterViews() {
311         Activity activity = (Activity) mActivity;
312 
313         LayoutInflater inflater = activity.getLayoutInflater();
314         mFooterContent = inflater.inflate(R.layout.manage_offline_bar, null);
315 
316         mFooterContent.findViewById(R.id.done).setOnClickListener(this);
317         refreshCacheStorageInfo();
318     }
319 
320     @Override
onClick(View view)321     public void onClick(View view) {
322         Utils.assertTrue(view.getId() == R.id.done);
323         GLRoot root = mActivity.getGLRoot();
324         root.lockRenderThread();
325         try {
326             ArrayList<Path> ids = mSelectionManager.getSelected(false);
327             if (ids.size() == 0) {
328                 onBackPressed();
329                 return;
330             }
331             showToast();
332 
333             MenuExecutor menuExecutor = new MenuExecutor(mActivity, mSelectionManager);
334             menuExecutor.startAction(R.id.action_toggle_full_caching,
335                     R.string.process_caching_requests, this);
336         } finally {
337             root.unlockRenderThread();
338         }
339     }
340 
showToast()341     private void showToast() {
342         if (mAlbumCountToMakeAvailableOffline > 0) {
343             Activity activity = (Activity) mActivity;
344             Toast.makeText(activity, activity.getResources().getQuantityString(
345                     R.plurals.make_albums_available_offline,
346                     mAlbumCountToMakeAvailableOffline),
347                     Toast.LENGTH_SHORT).show();
348         }
349     }
350 
showToastForLocalAlbum()351     private void showToastForLocalAlbum() {
352         Activity activity = (Activity) mActivity;
353         Toast.makeText(activity, activity.getResources().getString(
354             R.string.try_to_set_local_album_available_offline),
355             Toast.LENGTH_SHORT).show();
356     }
357 
refreshCacheStorageInfo()358     private void refreshCacheStorageInfo() {
359         ProgressBar progressBar = (ProgressBar) mFooterContent.findViewById(R.id.progress);
360         TextView status = (TextView) mFooterContent.findViewById(R.id.status);
361         progressBar.setMax(PROGRESS_BAR_MAX);
362         long totalBytes = mCacheStorageInfo.getTotalBytes();
363         long usedBytes = mCacheStorageInfo.getUsedBytes();
364         long expectedBytes = mCacheStorageInfo.getExpectedUsedBytes();
365         long freeBytes = mCacheStorageInfo.getFreeBytes();
366 
367         Activity activity = (Activity) mActivity;
368         if (totalBytes == 0) {
369             progressBar.setProgress(0);
370             progressBar.setSecondaryProgress(0);
371 
372             // TODO: get the string translated
373             String label = activity.getString(R.string.free_space_format, "-");
374             status.setText(label);
375         } else {
376             progressBar.setProgress((int) (usedBytes * PROGRESS_BAR_MAX / totalBytes));
377             progressBar.setSecondaryProgress(
378                     (int) (expectedBytes * PROGRESS_BAR_MAX / totalBytes));
379             String label = activity.getString(R.string.free_space_format,
380                     Formatter.formatFileSize(activity, freeBytes));
381             status.setText(label);
382         }
383     }
384 
385     @Override
onProgressComplete(int result)386     public void onProgressComplete(int result) {
387         onBackPressed();
388     }
389 
390     @Override
onProgressUpdate(int index)391     public void onProgressUpdate(int index) {
392     }
393 
394     @Override
onSelectionModeChange(int mode)395     public void onSelectionModeChange(int mode) {
396     }
397 
398     @Override
onSelectionChange(Path path, boolean selected)399     public void onSelectionChange(Path path, boolean selected) {
400     }
401 
402     @Override
onConfirmDialogDismissed(boolean confirmed)403     public void onConfirmDialogDismissed(boolean confirmed) {
404     }
405 
406     @Override
onConfirmDialogShown()407     public void onConfirmDialogShown() {
408     }
409 }
410