packages/apps/Gallery2
修订版 | 4105e22d72d7a44e67cba4a58ad88018ed664d10 (tree) |
---|---|
时间 | 2011-08-26 21:20:48 |
作者 | Ray Chen <raychen@goog...> |
Commiter | Ray Chen |
Fix 5133608 [UI] Details should be displayed as a system dialog in phone UI
Fix 5132798 [UI] Details popup on tablet is incorrectly placed and should not have x icon to close
Fix 5199822 Long press and select "detail" shows details of another item
Change-Id: I0e992ded8a154edb1c7a81b75d0461d5bf309f31
@@ -0,0 +1,23 @@ | ||
1 | +<?xml version="1.0" encoding="utf-8"?> | |
2 | +<!-- Copyright (C) 2006 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 | +<TextView xmlns:android="http://schemas.android.com/apk/res/android" | |
18 | + android:id="@android:id/text1" | |
19 | + android:layout_width="match_parent" | |
20 | + android:layout_height="wrap_content" | |
21 | + android:textAppearance="?android:attr/textAppearanceMedium" | |
22 | + android:gravity="left" | |
23 | +/> |
@@ -0,0 +1,4 @@ | ||
1 | +<?xml version="1.0" encoding="utf-8"?> | |
2 | +<resources> | |
3 | + <bool name="dialog_details_view">false</bool> | |
4 | +</resources> | |
\ No newline at end of file |
@@ -0,0 +1,4 @@ | ||
1 | +<?xml version="1.0" encoding="utf-8"?> | |
2 | +<resources> | |
3 | + <bool name="dialog_details_view">true</bool> | |
4 | +</resources> | |
\ No newline at end of file |
@@ -97,6 +97,8 @@ | ||
97 | 97 | <string name="slideshow">Slideshow</string> |
98 | 98 | |
99 | 99 | <string name="details">Details</string> |
100 | + <string name="details_title">%1$d of %2$d items:</string> | |
101 | + <string name="close">Close</string> | |
100 | 102 | |
101 | 103 | <!-- Title of a menu item to switch from Gallery to Camera app [CHAR LIMIT=30] --> |
102 | 104 | <string name="switch_to_camera">Switch to Camera</string> |
@@ -28,8 +28,8 @@ import com.android.gallery3d.data.Path; | ||
28 | 28 | import com.android.gallery3d.ui.ActionModeHandler; |
29 | 29 | import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener; |
30 | 30 | import com.android.gallery3d.ui.AlbumView; |
31 | -import com.android.gallery3d.ui.DetailsWindow; | |
32 | -import com.android.gallery3d.ui.DetailsWindow.CloseListener; | |
31 | +import com.android.gallery3d.ui.DetailsHelper; | |
32 | +import com.android.gallery3d.ui.DetailsHelper.CloseListener; | |
33 | 33 | import com.android.gallery3d.ui.GLCanvas; |
34 | 34 | import com.android.gallery3d.ui.GLView; |
35 | 35 | import com.android.gallery3d.ui.GridDrawer; |
@@ -92,7 +92,8 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster | ||
92 | 92 | private ActionMode mActionMode; |
93 | 93 | private ActionModeHandler mActionModeHandler; |
94 | 94 | private int mFocusIndex = 0; |
95 | - private DetailsWindow mDetailsWindow; | |
95 | + private DetailsHelper mDetailsHelper; | |
96 | + private MyDetailsSource mDetailsSource; | |
96 | 97 | private MediaSet mMediaSet; |
97 | 98 | private boolean mShowDetails; |
98 | 99 | private float mUserDistance; // in pixel |
@@ -115,13 +116,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster | ||
115 | 116 | int slotViewRight = right - left; |
116 | 117 | |
117 | 118 | if (mShowDetails) { |
118 | - mDetailsWindow.measure( | |
119 | - MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); | |
120 | - int width = mDetailsWindow.getMeasuredWidth(); | |
121 | - int detailLeft = right - left - width; | |
122 | - slotViewRight = detailLeft; | |
123 | - mDetailsWindow.layout(detailLeft, slotViewTop, detailLeft + width, | |
124 | - bottom - top); | |
119 | + mDetailsHelper.layout(left, slotViewTop, right, bottom); | |
125 | 120 | } else { |
126 | 121 | mAlbumView.setSelectionDrawer(mGridDrawer); |
127 | 122 | } |
@@ -162,7 +157,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster | ||
162 | 157 | } |
163 | 158 | if (mShowDetails) { |
164 | 159 | mHighlightDrawer.setHighlightItem(item.getPath()); |
165 | - mDetailsWindow.reloadDetails(slotIndex); | |
160 | + mDetailsHelper.reloadDetails(slotIndex); | |
166 | 161 | } else if (!mSelectionManager.inSelectionMode()) { |
167 | 162 | if (mGetContent) { |
168 | 163 | onGetContent(item); |
@@ -221,6 +216,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster | ||
221 | 216 | if (item == null) return; |
222 | 217 | mSelectionManager.setAutoLeaveSelectionMode(true); |
223 | 218 | mSelectionManager.toggle(item.getPath()); |
219 | + mDetailsSource.findIndex(slotIndex); | |
224 | 220 | mAlbumView.invalidate(); |
225 | 221 | } |
226 | 222 | } |
@@ -263,6 +259,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster | ||
263 | 259 | initializeData(data); |
264 | 260 | mGetContent = data.getBoolean(Gallery.KEY_GET_CONTENT, false); |
265 | 261 | mShowClusterMenu = data.getBoolean(KEY_SHOW_CLUSTER_MENU, false); |
262 | + mDetailsSource = new MyDetailsSource(); | |
266 | 263 | |
267 | 264 | startTransition(data); |
268 | 265 |
@@ -328,9 +325,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster | ||
328 | 325 | mIsActive = false; |
329 | 326 | mAlbumDataAdapter.pause(); |
330 | 327 | mAlbumView.pause(); |
331 | - if (mDetailsWindow != null) { | |
332 | - mDetailsWindow.pause(); | |
333 | - } | |
328 | + DetailsHelper.pause(); | |
334 | 329 | Future<?> task = mPendingTask; |
335 | 330 | if (task != null) { |
336 | 331 | // cancel on going task |
@@ -400,24 +395,23 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster | ||
400 | 395 | |
401 | 396 | private void showDetails() { |
402 | 397 | mShowDetails = true; |
403 | - if (mDetailsWindow == null) { | |
398 | + if (mDetailsHelper == null) { | |
404 | 399 | mHighlightDrawer = new HighlightDrawer(mActivity.getAndroidContext()); |
405 | - mDetailsWindow = new DetailsWindow(mActivity, new MyDetailsSource()); | |
406 | - mDetailsWindow.setCloseListener(new CloseListener() { | |
400 | + mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource); | |
401 | + mDetailsHelper.setCloseListener(new CloseListener() { | |
407 | 402 | public void onClose() { |
408 | 403 | hideDetails(); |
409 | 404 | } |
410 | 405 | }); |
411 | - mRootPane.addComponent(mDetailsWindow); | |
412 | 406 | } |
413 | 407 | mAlbumView.setSelectionDrawer(mHighlightDrawer); |
414 | - mDetailsWindow.show(); | |
408 | + mDetailsHelper.show(); | |
415 | 409 | } |
416 | 410 | |
417 | 411 | private void hideDetails() { |
418 | 412 | mShowDetails = false; |
419 | 413 | mAlbumView.setSelectionDrawer(mGridDrawer); |
420 | - mDetailsWindow.hide(); | |
414 | + mDetailsHelper.hide(); | |
421 | 415 | } |
422 | 416 | |
423 | 417 | @Override |
@@ -572,12 +566,16 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster | ||
572 | 566 | } |
573 | 567 | } |
574 | 568 | |
575 | - private class MyDetailsSource implements DetailsWindow.DetailsSource { | |
569 | + private class MyDetailsSource implements DetailsHelper.DetailsSource { | |
576 | 570 | private int mIndex; |
577 | 571 | public int size() { |
578 | 572 | return mAlbumDataAdapter.size(); |
579 | 573 | } |
580 | 574 | |
575 | + public int getIndex() { | |
576 | + return mIndex; | |
577 | + } | |
578 | + | |
581 | 579 | // If requested index is out of active window, suggest a valid index. |
582 | 580 | // If there is no valid index available, return -1. |
583 | 581 | public int findIndex(int indexHint) { |
@@ -28,8 +28,8 @@ import com.android.gallery3d.settings.GallerySettings; | ||
28 | 28 | import com.android.gallery3d.ui.ActionModeHandler; |
29 | 29 | import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener; |
30 | 30 | import com.android.gallery3d.ui.AlbumSetView; |
31 | -import com.android.gallery3d.ui.DetailsWindow; | |
32 | -import com.android.gallery3d.ui.DetailsWindow.CloseListener; | |
31 | +import com.android.gallery3d.ui.DetailsHelper; | |
32 | +import com.android.gallery3d.ui.DetailsHelper.CloseListener; | |
33 | 33 | import com.android.gallery3d.ui.GLCanvas; |
34 | 34 | import com.android.gallery3d.ui.GLView; |
35 | 35 | import com.android.gallery3d.ui.GridDrawer; |
@@ -88,7 +88,8 @@ public class AlbumSetPage extends ActivityState implements | ||
88 | 88 | private boolean mGetAlbum; |
89 | 89 | private ActionMode mActionMode; |
90 | 90 | private ActionModeHandler mActionModeHandler; |
91 | - private DetailsWindow mDetailsWindow; | |
91 | + private DetailsHelper mDetailsHelper; | |
92 | + private MyDetailsSource mDetailsSource; | |
92 | 93 | private boolean mShowDetails; |
93 | 94 | private EyePosition mEyePosition; |
94 | 95 |
@@ -114,13 +115,7 @@ public class AlbumSetPage extends ActivityState implements | ||
114 | 115 | int slotViewRight = right - left; |
115 | 116 | |
116 | 117 | if (mShowDetails) { |
117 | - mDetailsWindow.measure( | |
118 | - MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); | |
119 | - int width = mDetailsWindow.getMeasuredWidth(); | |
120 | - int detailLeft = right - left - width; | |
121 | - slotViewRight = detailLeft; | |
122 | - mDetailsWindow.layout(detailLeft, slotViewTop, detailLeft + width, | |
123 | - bottom - top); | |
118 | + mDetailsHelper.layout(left, slotViewTop, right, bottom); | |
124 | 119 | } else { |
125 | 120 | mAlbumSetView.setSelectionDrawer(mGridDrawer); |
126 | 121 | } |
@@ -182,7 +177,7 @@ public class AlbumSetPage extends ActivityState implements | ||
182 | 177 | if (mShowDetails) { |
183 | 178 | Path path = targetSet.getPath(); |
184 | 179 | mHighlightDrawer.setHighlightItem(path); |
185 | - mDetailsWindow.reloadDetails(slotIndex); | |
180 | + mDetailsHelper.reloadDetails(slotIndex); | |
186 | 181 | } else if (!mSelectionManager.inSelectionMode()) { |
187 | 182 | Bundle data = new Bundle(getData()); |
188 | 183 | String mediaPath = targetSet.getPath().toString(); |
@@ -226,6 +221,7 @@ public class AlbumSetPage extends ActivityState implements | ||
226 | 221 | if (set == null) return; |
227 | 222 | mSelectionManager.setAutoLeaveSelectionMode(true); |
228 | 223 | mSelectionManager.toggle(set.getPath()); |
224 | + mDetailsSource.findIndex(slotIndex); | |
229 | 225 | mAlbumSetView.invalidate(); |
230 | 226 | } |
231 | 227 | } |
@@ -274,6 +270,7 @@ public class AlbumSetPage extends ActivityState implements | ||
274 | 270 | mTitle = data.getString(AlbumSetPage.KEY_SET_TITLE); |
275 | 271 | mSubtitle = data.getString(AlbumSetPage.KEY_SET_SUBTITLE); |
276 | 272 | mEyePosition = new EyePosition(mActivity.getAndroidContext(), this); |
273 | + mDetailsSource = new MyDetailsSource(); | |
277 | 274 | |
278 | 275 | startTransition(); |
279 | 276 | } |
@@ -286,9 +283,7 @@ public class AlbumSetPage extends ActivityState implements | ||
286 | 283 | mAlbumSetDataAdapter.pause(); |
287 | 284 | mAlbumSetView.pause(); |
288 | 285 | mEyePosition.pause(); |
289 | - if (mDetailsWindow != null) { | |
290 | - mDetailsWindow.pause(); | |
291 | - } | |
286 | + DetailsHelper.pause(); | |
292 | 287 | GalleryActionBar actionBar = mActivity.getGalleryActionBar(); |
293 | 288 | if (actionBar != null) actionBar.hideClusterTabs(); |
294 | 289 | } |
@@ -520,23 +515,22 @@ public class AlbumSetPage extends ActivityState implements | ||
520 | 515 | private void hideDetails() { |
521 | 516 | mShowDetails = false; |
522 | 517 | mAlbumSetView.setSelectionDrawer(mGridDrawer); |
523 | - mDetailsWindow.hide(); | |
518 | + mDetailsHelper.hide(); | |
524 | 519 | } |
525 | 520 | |
526 | 521 | private void showDetails() { |
527 | 522 | mShowDetails = true; |
528 | - if (mDetailsWindow == null) { | |
523 | + if (mDetailsHelper == null) { | |
529 | 524 | mHighlightDrawer = new HighlightDrawer(mActivity.getAndroidContext()); |
530 | - mDetailsWindow = new DetailsWindow(mActivity, new MyDetailsSource()); | |
531 | - mDetailsWindow.setCloseListener(new CloseListener() { | |
525 | + mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource); | |
526 | + mDetailsHelper.setCloseListener(new CloseListener() { | |
532 | 527 | public void onClose() { |
533 | 528 | hideDetails(); |
534 | 529 | } |
535 | 530 | }); |
536 | - mRootPane.addComponent(mDetailsWindow); | |
537 | 531 | } |
538 | 532 | mAlbumSetView.setSelectionDrawer(mHighlightDrawer); |
539 | - mDetailsWindow.show(); | |
533 | + mDetailsHelper.show(); | |
540 | 534 | } |
541 | 535 | |
542 | 536 | private class MyLoadingListener implements LoadingListener { |
@@ -557,12 +551,16 @@ public class AlbumSetPage extends ActivityState implements | ||
557 | 551 | } |
558 | 552 | } |
559 | 553 | |
560 | - private class MyDetailsSource implements DetailsWindow.DetailsSource { | |
554 | + private class MyDetailsSource implements DetailsHelper.DetailsSource { | |
561 | 555 | private int mIndex; |
562 | 556 | public int size() { |
563 | 557 | return mAlbumSetDataAdapter.size(); |
564 | 558 | } |
565 | 559 | |
560 | + public int getIndex() { | |
561 | + return mIndex; | |
562 | + } | |
563 | + | |
566 | 564 | // If requested index is out of active window, suggest a valid index. |
567 | 565 | // If there is no valid index available, return -1. |
568 | 566 | public int findIndex(int indexHint) { |
@@ -25,9 +25,9 @@ import com.android.gallery3d.data.MediaSet; | ||
25 | 25 | import com.android.gallery3d.data.MtpDevice; |
26 | 26 | import com.android.gallery3d.data.Path; |
27 | 27 | import com.android.gallery3d.picasasource.PicasaSource; |
28 | -import com.android.gallery3d.ui.DetailsWindow; | |
29 | -import com.android.gallery3d.ui.DetailsWindow.CloseListener; | |
30 | -import com.android.gallery3d.ui.DetailsWindow.DetailsSource; | |
28 | +import com.android.gallery3d.ui.DetailsHelper; | |
29 | +import com.android.gallery3d.ui.DetailsHelper.CloseListener; | |
30 | +import com.android.gallery3d.ui.DetailsHelper.DetailsSource; | |
31 | 31 | import com.android.gallery3d.ui.FilmStripView; |
32 | 32 | import com.android.gallery3d.ui.GLCanvas; |
33 | 33 | import com.android.gallery3d.ui.GLView; |
@@ -82,7 +82,7 @@ public class PhotoPage extends ActivityState | ||
82 | 82 | private PhotoView mPhotoView; |
83 | 83 | private PhotoPage.Model mModel; |
84 | 84 | private FilmStripView mFilmStripView; |
85 | - private DetailsWindow mDetailsWindow; | |
85 | + private DetailsHelper mDetailsHelper; | |
86 | 86 | private boolean mShowDetails; |
87 | 87 | |
88 | 88 | // mMediaSet could be null if there is no KEY_MEDIA_SET_PATH supplied. |
@@ -141,12 +141,8 @@ public class PhotoPage extends ActivityState | ||
141 | 141 | right - left, bottom - top); |
142 | 142 | } |
143 | 143 | if (mShowDetails) { |
144 | - mDetailsWindow.measure( | |
145 | - MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); | |
146 | - int width = mDetailsWindow.getMeasuredWidth(); | |
147 | - int viewTop = GalleryActionBar.getHeight((Activity) mActivity); | |
148 | - mDetailsWindow.layout( | |
149 | - 0, viewTop, width, bottom - top - filmStripHeight); | |
144 | + mDetailsHelper.layout(left, GalleryActionBar.getHeight((Activity) mActivity), | |
145 | + right, bottom); | |
150 | 146 | } |
151 | 147 | } |
152 | 148 | }; |
@@ -257,7 +253,7 @@ public class PhotoPage extends ActivityState | ||
257 | 253 | if (mCurrentPhoto == null) return; |
258 | 254 | updateMenuOperations(); |
259 | 255 | if (mShowDetails) { |
260 | - mDetailsWindow.reloadDetails(mModel.getCurrentIndex()); | |
256 | + mDetailsHelper.reloadDetails(mModel.getCurrentIndex()); | |
261 | 257 | } |
262 | 258 | String title = photo.getName(); |
263 | 259 | if (title != null) mActionBar.setTitle(title); |
@@ -441,22 +437,21 @@ public class PhotoPage extends ActivityState | ||
441 | 437 | |
442 | 438 | private void hideDetails() { |
443 | 439 | mShowDetails = false; |
444 | - mDetailsWindow.hide(); | |
440 | + mDetailsHelper.hide(); | |
445 | 441 | } |
446 | 442 | |
447 | 443 | private void showDetails(int index) { |
448 | 444 | mShowDetails = true; |
449 | - if (mDetailsWindow == null) { | |
450 | - mDetailsWindow = new DetailsWindow(mActivity, new MyDetailsSource()); | |
451 | - mDetailsWindow.setCloseListener(new CloseListener() { | |
445 | + if (mDetailsHelper == null) { | |
446 | + mDetailsHelper = new DetailsHelper(mActivity, mRootPane, new MyDetailsSource()); | |
447 | + mDetailsHelper.setCloseListener(new CloseListener() { | |
452 | 448 | public void onClose() { |
453 | 449 | hideDetails(); |
454 | 450 | } |
455 | 451 | }); |
456 | - mRootPane.addComponent(mDetailsWindow); | |
457 | 452 | } |
458 | - mDetailsWindow.reloadDetails(index); | |
459 | - mDetailsWindow.show(); | |
453 | + mDetailsHelper.reloadDetails(index); | |
454 | + mDetailsHelper.show(); | |
460 | 455 | } |
461 | 456 | |
462 | 457 | public void onSingleTapUp(int x, int y) { |
@@ -541,9 +536,7 @@ public class PhotoPage extends ActivityState | ||
541 | 536 | if (mFilmStripView != null) { |
542 | 537 | mFilmStripView.pause(); |
543 | 538 | } |
544 | - if (mDetailsWindow != null) { | |
545 | - mDetailsWindow.pause(); | |
546 | - } | |
539 | + DetailsHelper.pause(); | |
547 | 540 | mPhotoView.pause(); |
548 | 541 | mModel.pause(); |
549 | 542 | mHandler.removeMessages(MSG_HIDE_BARS); |
@@ -568,6 +561,7 @@ public class PhotoPage extends ActivityState | ||
568 | 561 | } |
569 | 562 | |
570 | 563 | private class MyDetailsSource implements DetailsSource { |
564 | + private int mIndex; | |
571 | 565 | public MediaDetails getDetails() { |
572 | 566 | return mModel.getCurrentMediaItem().getDetails(); |
573 | 567 | } |
@@ -575,7 +569,12 @@ public class PhotoPage extends ActivityState | ||
575 | 569 | return mMediaSet != null ? mMediaSet.getMediaItemCount() : 1; |
576 | 570 | } |
577 | 571 | public int findIndex(int indexHint) { |
572 | + mIndex = indexHint; | |
578 | 573 | return indexHint; |
579 | 574 | } |
575 | + | |
576 | + public int getIndex() { | |
577 | + return mIndex; | |
578 | + } | |
580 | 579 | } |
581 | 580 | } |
@@ -0,0 +1,116 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2011 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.ui; | |
18 | + | |
19 | +import com.android.gallery3d.app.GalleryActivity; | |
20 | +import com.android.gallery3d.data.MediaDetails; | |
21 | +import com.android.gallery3d.util.Future; | |
22 | +import com.android.gallery3d.util.FutureListener; | |
23 | +import com.android.gallery3d.util.GalleryUtils; | |
24 | +import com.android.gallery3d.util.ReverseGeocoder; | |
25 | +import com.android.gallery3d.util.ThreadPool.Job; | |
26 | +import com.android.gallery3d.util.ThreadPool.JobContext; | |
27 | + | |
28 | +import android.content.Context; | |
29 | +import android.location.Address; | |
30 | +import android.os.Handler; | |
31 | +import android.os.Looper; | |
32 | + | |
33 | +public class DetailsAddressResolver { | |
34 | + private AddressResolvingListener mListener; | |
35 | + private double[] mLatlng; | |
36 | + private GalleryActivity mContext; | |
37 | + private Future<Address> mAddressLookupJob; | |
38 | + private Handler mHandler; | |
39 | + | |
40 | + private class AddressLookupJob implements Job<Address> { | |
41 | + | |
42 | + protected AddressLookupJob(double[] latlng) { | |
43 | + mLatlng = latlng; | |
44 | + } | |
45 | + | |
46 | + public Address run(JobContext jc) { | |
47 | + ReverseGeocoder geocoder = new ReverseGeocoder(mContext.getAndroidContext()); | |
48 | + return geocoder.lookupAddress(mLatlng[0], mLatlng[1], true); | |
49 | + } | |
50 | + } | |
51 | + | |
52 | + public interface AddressResolvingListener { | |
53 | + public void onAddressAvailable(String address); | |
54 | + } | |
55 | + | |
56 | + public DetailsAddressResolver(GalleryActivity context) { | |
57 | + mContext = context; | |
58 | + mHandler = new Handler(Looper.getMainLooper()); | |
59 | + } | |
60 | + | |
61 | + public String resolveAddress(double[] latlng, AddressResolvingListener listener) { | |
62 | + mLatlng = latlng; | |
63 | + mListener = listener; | |
64 | + mAddressLookupJob = mContext.getThreadPool().submit( | |
65 | + new AddressLookupJob(latlng), | |
66 | + new FutureListener<Address>() { | |
67 | + public void onFutureDone(final Future<Address> future) { | |
68 | + mAddressLookupJob = null; | |
69 | + if (!future.isCancelled()) { | |
70 | + mHandler.post(new Runnable() { | |
71 | + public void run() { | |
72 | + updateLocation(future.get()); | |
73 | + } | |
74 | + }); | |
75 | + } | |
76 | + } | |
77 | + }); | |
78 | + return GalleryUtils.formatLatitudeLongitude("(%f,%f)", mLatlng[0], mLatlng[1]); | |
79 | + } | |
80 | + | |
81 | + private void updateLocation(Address address) { | |
82 | + if (address != null) { | |
83 | + Context context = mContext.getAndroidContext(); | |
84 | + String parts[] = { | |
85 | + address.getAdminArea(), | |
86 | + address.getSubAdminArea(), | |
87 | + address.getLocality(), | |
88 | + address.getSubLocality(), | |
89 | + address.getThoroughfare(), | |
90 | + address.getSubThoroughfare(), | |
91 | + address.getPremises(), | |
92 | + address.getPostalCode(), | |
93 | + address.getCountryName() | |
94 | + }; | |
95 | + | |
96 | + String addressText = ""; | |
97 | + for (int i = 0; i < parts.length; i++) { | |
98 | + if (parts[i] == null || parts[i].isEmpty()) continue; | |
99 | + if (!addressText.isEmpty()) { | |
100 | + addressText += ", "; | |
101 | + } | |
102 | + addressText += parts[i]; | |
103 | + } | |
104 | + String text = String.format("%s : %s", DetailsHelper.getDetailsName( | |
105 | + context, MediaDetails.INDEX_LOCATION), addressText); | |
106 | + mListener.onAddressAvailable(text); | |
107 | + } | |
108 | + } | |
109 | + | |
110 | + public void cancel() { | |
111 | + if (mAddressLookupJob != null) { | |
112 | + mAddressLookupJob.cancel(); | |
113 | + mAddressLookupJob = null; | |
114 | + } | |
115 | + } | |
116 | +} |
@@ -0,0 +1,144 @@ | ||
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 | +package com.android.gallery3d.ui; | |
17 | + | |
18 | +import com.android.gallery3d.R; | |
19 | +import com.android.gallery3d.app.GalleryActivity; | |
20 | +import com.android.gallery3d.data.MediaDetails; | |
21 | +import com.android.gallery3d.ui.DetailsAddressResolver.AddressResolvingListener; | |
22 | + | |
23 | +import android.content.Context; | |
24 | +import android.view.View.MeasureSpec; | |
25 | + | |
26 | +public class DetailsHelper { | |
27 | + private static DetailsAddressResolver sAddressResolver; | |
28 | + private DetailsViewContainer mContainer; | |
29 | + | |
30 | + public interface DetailsSource { | |
31 | + public int size(); | |
32 | + public int getIndex(); | |
33 | + public int findIndex(int indexHint); | |
34 | + public MediaDetails getDetails(); | |
35 | + } | |
36 | + | |
37 | + public interface CloseListener { | |
38 | + public void onClose(); | |
39 | + } | |
40 | + | |
41 | + public interface DetailsViewContainer { | |
42 | + public void reloadDetails(int indexHint); | |
43 | + public void setCloseListener(CloseListener listener); | |
44 | + public void show(); | |
45 | + public void hide(); | |
46 | + } | |
47 | + | |
48 | + public DetailsHelper(GalleryActivity activity, GLView rootPane, DetailsSource source) { | |
49 | + boolean useDialog = activity.getAndroidContext().getResources().getBoolean( | |
50 | + R.bool.dialog_details_view); | |
51 | + if (useDialog) { | |
52 | + mContainer = new DialogDetailsView(activity, source); | |
53 | + } else { | |
54 | + mContainer = new GLDetailsView(activity, source); | |
55 | + rootPane.addComponent((GLView) mContainer); | |
56 | + } | |
57 | + } | |
58 | + | |
59 | + public void layout(int left, int top, int right, int bottom) { | |
60 | + if (mContainer instanceof GLView) { | |
61 | + GLView view = (GLView) mContainer; | |
62 | + view.measure(MeasureSpec.UNSPECIFIED, | |
63 | + MeasureSpec.makeMeasureSpec(bottom - top, MeasureSpec.AT_MOST)); | |
64 | + view.layout(0, top, view.getMeasuredWidth(), top + view.getMeasuredHeight()); | |
65 | + } | |
66 | + } | |
67 | + | |
68 | + public void reloadDetails(int indexHint) { | |
69 | + mContainer.reloadDetails(indexHint); | |
70 | + } | |
71 | + | |
72 | + public void setCloseListener(CloseListener listener) { | |
73 | + mContainer.setCloseListener(listener); | |
74 | + } | |
75 | + | |
76 | + public static String resolveAddress(GalleryActivity activity, double[] latlng, | |
77 | + AddressResolvingListener listener) { | |
78 | + if (sAddressResolver == null) { | |
79 | + sAddressResolver = new DetailsAddressResolver(activity); | |
80 | + } else { | |
81 | + sAddressResolver.cancel(); | |
82 | + } | |
83 | + return sAddressResolver.resolveAddress(latlng, listener); | |
84 | + } | |
85 | + | |
86 | + public static void pause() { | |
87 | + if (sAddressResolver != null) sAddressResolver.cancel(); | |
88 | + } | |
89 | + | |
90 | + public void show() { | |
91 | + mContainer.show(); | |
92 | + } | |
93 | + | |
94 | + public void hide() { | |
95 | + mContainer.hide(); | |
96 | + } | |
97 | + | |
98 | + public static String getDetailsName(Context context, int key) { | |
99 | + switch (key) { | |
100 | + case MediaDetails.INDEX_TITLE: | |
101 | + return context.getString(R.string.title); | |
102 | + case MediaDetails.INDEX_DESCRIPTION: | |
103 | + return context.getString(R.string.description); | |
104 | + case MediaDetails.INDEX_DATETIME: | |
105 | + return context.getString(R.string.time); | |
106 | + case MediaDetails.INDEX_LOCATION: | |
107 | + return context.getString(R.string.location); | |
108 | + case MediaDetails.INDEX_PATH: | |
109 | + return context.getString(R.string.path); | |
110 | + case MediaDetails.INDEX_WIDTH: | |
111 | + return context.getString(R.string.width); | |
112 | + case MediaDetails.INDEX_HEIGHT: | |
113 | + return context.getString(R.string.height); | |
114 | + case MediaDetails.INDEX_ORIENTATION: | |
115 | + return context.getString(R.string.orientation); | |
116 | + case MediaDetails.INDEX_DURATION: | |
117 | + return context.getString(R.string.duration); | |
118 | + case MediaDetails.INDEX_MIMETYPE: | |
119 | + return context.getString(R.string.mimetype); | |
120 | + case MediaDetails.INDEX_SIZE: | |
121 | + return context.getString(R.string.file_size); | |
122 | + case MediaDetails.INDEX_MAKE: | |
123 | + return context.getString(R.string.maker); | |
124 | + case MediaDetails.INDEX_MODEL: | |
125 | + return context.getString(R.string.model); | |
126 | + case MediaDetails.INDEX_FLASH: | |
127 | + return context.getString(R.string.flash); | |
128 | + case MediaDetails.INDEX_APERTURE: | |
129 | + return context.getString(R.string.aperture); | |
130 | + case MediaDetails.INDEX_FOCAL_LENGTH: | |
131 | + return context.getString(R.string.focal_length); | |
132 | + case MediaDetails.INDEX_WHITE_BALANCE: | |
133 | + return context.getString(R.string.white_balance); | |
134 | + case MediaDetails.INDEX_EXPOSURE_TIME: | |
135 | + return context.getString(R.string.exposure_time); | |
136 | + case MediaDetails.INDEX_ISO: | |
137 | + return context.getString(R.string.iso); | |
138 | + default: | |
139 | + return "Unknown key" + key; | |
140 | + } | |
141 | + } | |
142 | +} | |
143 | + | |
144 | + |
@@ -0,0 +1,239 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2011 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.ui; | |
18 | + | |
19 | +import static com.android.gallery3d.ui.DetailsWindowConfig.FONT_SIZE; | |
20 | +import static com.android.gallery3d.ui.DetailsWindowConfig.LEFT_RIGHT_EXTRA_PADDING; | |
21 | +import static com.android.gallery3d.ui.DetailsWindowConfig.LINE_SPACING; | |
22 | +import static com.android.gallery3d.ui.DetailsWindowConfig.PREFERRED_WIDTH; | |
23 | +import static com.android.gallery3d.ui.DetailsWindowConfig.TOP_BOTTOM_EXTRA_PADDING; | |
24 | + | |
25 | +import com.android.gallery3d.R; | |
26 | +import com.android.gallery3d.app.GalleryActivity; | |
27 | +import com.android.gallery3d.common.Utils; | |
28 | +import com.android.gallery3d.data.MediaDetails; | |
29 | +import com.android.gallery3d.ui.DetailsAddressResolver.AddressResolvingListener; | |
30 | +import com.android.gallery3d.ui.DetailsHelper.CloseListener; | |
31 | +import com.android.gallery3d.ui.DetailsHelper.DetailsViewContainer; | |
32 | +import com.android.gallery3d.ui.DetailsHelper.DetailsSource; | |
33 | +import com.android.gallery3d.util.Future; | |
34 | +import com.android.gallery3d.util.FutureListener; | |
35 | +import com.android.gallery3d.util.GalleryUtils; | |
36 | +import com.android.gallery3d.util.ReverseGeocoder; | |
37 | +import com.android.gallery3d.util.ThreadPool.Job; | |
38 | +import com.android.gallery3d.util.ThreadPool.JobContext; | |
39 | + | |
40 | +import android.app.Activity; | |
41 | +import android.app.AlertDialog; | |
42 | +import android.app.Dialog; | |
43 | +import android.content.Context; | |
44 | +import android.content.DialogInterface; | |
45 | +import android.database.DataSetObserver; | |
46 | +import android.graphics.Color; | |
47 | +import android.graphics.Rect; | |
48 | +import android.location.Address; | |
49 | +import android.os.Handler; | |
50 | +import android.os.Message; | |
51 | +import android.text.format.Formatter; | |
52 | +import android.view.ContextThemeWrapper; | |
53 | +import android.view.LayoutInflater; | |
54 | +import android.view.MotionEvent; | |
55 | +import android.view.View; | |
56 | +import android.view.ViewGroup; | |
57 | +import android.view.View.MeasureSpec; | |
58 | +import android.widget.BaseAdapter; | |
59 | +import android.widget.TextView; | |
60 | + | |
61 | +import java.util.ArrayList; | |
62 | +import java.util.Map.Entry; | |
63 | + | |
64 | +public class DialogDetailsView implements DetailsViewContainer { | |
65 | + @SuppressWarnings("unused") | |
66 | + private static final String TAG = "DialogDetailsView"; | |
67 | + | |
68 | + private GalleryActivity mContext; | |
69 | + private DetailsAdapter mAdapter; | |
70 | + private MediaDetails mDetails; | |
71 | + private DetailsSource mSource; | |
72 | + private int mIndex; | |
73 | + private Dialog mDialog; | |
74 | + private int mLocationIndex; | |
75 | + | |
76 | + public DialogDetailsView(GalleryActivity activity, DetailsSource source) { | |
77 | + mContext = activity; | |
78 | + mSource = source; | |
79 | + } | |
80 | + | |
81 | + public void show() { | |
82 | + reloadDetails(mSource.getIndex()); | |
83 | + mDialog.show(); | |
84 | + } | |
85 | + | |
86 | + public void hide() { | |
87 | + mDialog.hide(); | |
88 | + } | |
89 | + | |
90 | + public void reloadDetails(int indexHint) { | |
91 | + int index = mSource.findIndex(indexHint); | |
92 | + if (index == -1) return; | |
93 | + MediaDetails details = mSource.getDetails(); | |
94 | + if (details != null) { | |
95 | + if (mIndex == index && mDetails == details) return; | |
96 | + mIndex = index; | |
97 | + mDetails = details; | |
98 | + setDetails(details); | |
99 | + } | |
100 | + } | |
101 | + | |
102 | + public boolean isVisible() { | |
103 | + return mDialog.isShowing(); | |
104 | + } | |
105 | + | |
106 | + private void setDetails(MediaDetails details) { | |
107 | + mAdapter = new DetailsAdapter(details); | |
108 | + String title = String.format( | |
109 | + mContext.getAndroidContext().getString(R.string.details_title), | |
110 | + mIndex + 1, mSource.size()); | |
111 | + mDialog = new AlertDialog.Builder((Activity) mContext) | |
112 | + .setAdapter(mAdapter, null) | |
113 | + .setTitle(title) | |
114 | + .setPositiveButton(R.string.close, new DialogInterface.OnClickListener() { | |
115 | + public void onClick(DialogInterface dialog, int whichButton) { | |
116 | + mDialog.dismiss(); | |
117 | + } | |
118 | + }) | |
119 | + .create(); | |
120 | + } | |
121 | + | |
122 | + private class DetailsAdapter extends BaseAdapter implements AddressResolvingListener { | |
123 | + private ArrayList<String> mItems; | |
124 | + | |
125 | + public DetailsAdapter(MediaDetails details) { | |
126 | + Context context = mContext.getAndroidContext(); | |
127 | + mItems = new ArrayList<String>(details.size()); | |
128 | + mLocationIndex = -1; | |
129 | + setDetails(context, details); | |
130 | + } | |
131 | + | |
132 | + private void setDetails(Context context, MediaDetails details) { | |
133 | + for (Entry<Integer, Object> detail : details) { | |
134 | + String value; | |
135 | + switch (detail.getKey()) { | |
136 | + case MediaDetails.INDEX_LOCATION: { | |
137 | + double[] latlng = (double[]) detail.getValue(); | |
138 | + mLocationIndex = mItems.size(); | |
139 | + value = DetailsHelper.resolveAddress(mContext, latlng, mAdapter); | |
140 | + break; | |
141 | + } | |
142 | + case MediaDetails.INDEX_SIZE: { | |
143 | + value = Formatter.formatFileSize( | |
144 | + context, (Long) detail.getValue()); | |
145 | + break; | |
146 | + } | |
147 | + case MediaDetails.INDEX_WHITE_BALANCE: { | |
148 | + value = "1".equals(detail.getValue()) | |
149 | + ? context.getString(R.string.manual) | |
150 | + : context.getString(R.string.auto); | |
151 | + break; | |
152 | + } | |
153 | + case MediaDetails.INDEX_FLASH: { | |
154 | + MediaDetails.FlashState flash = | |
155 | + (MediaDetails.FlashState) detail.getValue(); | |
156 | + // TODO: camera doesn't fill in the complete values, show more information | |
157 | + // when it is fixed. | |
158 | + if (flash.isFlashFired()) { | |
159 | + value = context.getString(R.string.flash_on); | |
160 | + } else { | |
161 | + value = context.getString(R.string.flash_off); | |
162 | + } | |
163 | + break; | |
164 | + } | |
165 | + case MediaDetails.INDEX_EXPOSURE_TIME: { | |
166 | + value = (String) detail.getValue(); | |
167 | + double time = Double.valueOf(value); | |
168 | + if (time < 1.0f) { | |
169 | + value = String.format("1/%d", (int) (0.5f + 1 / time)); | |
170 | + } else { | |
171 | + int integer = (int) time; | |
172 | + time -= integer; | |
173 | + value = String.valueOf(integer) + "''"; | |
174 | + if (time > 0.0001) { | |
175 | + value += String.format(" 1/%d", (int) (0.5f + 1 / time)); | |
176 | + } | |
177 | + } | |
178 | + break; | |
179 | + } | |
180 | + default: { | |
181 | + Object valueObj = detail.getValue(); | |
182 | + // This shouldn't happen, log its key to help us diagnose the problem. | |
183 | + Utils.assertTrue(valueObj != null, "%s's value is Null", | |
184 | + DetailsHelper.getDetailsName(context, detail.getKey())); | |
185 | + value = valueObj.toString(); | |
186 | + } | |
187 | + } | |
188 | + int key = detail.getKey(); | |
189 | + if (details.hasUnit(key)) { | |
190 | + value = String.format("%s : %s %s", DetailsHelper.getDetailsName( | |
191 | + context, key), value, context.getString(details.getUnit(key))); | |
192 | + } else { | |
193 | + value = String.format("%s : %s", DetailsHelper.getDetailsName( | |
194 | + context, key), value); | |
195 | + } | |
196 | + mItems.add(value); | |
197 | + } | |
198 | + } | |
199 | + | |
200 | + public boolean areAllItemsEnabled() { | |
201 | + return false; | |
202 | + } | |
203 | + | |
204 | + public boolean isEnabled(int position) { | |
205 | + return false; | |
206 | + } | |
207 | + | |
208 | + public int getCount() { | |
209 | + return mItems.size(); | |
210 | + } | |
211 | + | |
212 | + public Object getItem(int position) { | |
213 | + return mDetails.getDetail(position); | |
214 | + } | |
215 | + | |
216 | + public long getItemId(int position) { | |
217 | + return position; | |
218 | + } | |
219 | + | |
220 | + public View getView(int position, View convertView, ViewGroup parent) { | |
221 | + TextView tv; | |
222 | + if (convertView == null) { | |
223 | + tv = (TextView) LayoutInflater.from(mContext.getAndroidContext()).inflate( | |
224 | + R.layout.details, parent, false); | |
225 | + } else { | |
226 | + tv = (TextView) convertView; | |
227 | + } | |
228 | + tv.setText(mItems.get(position)); | |
229 | + return tv; | |
230 | + } | |
231 | + | |
232 | + public void onAddressAvailable(String address) { | |
233 | + mItems.set(mLocationIndex, address); | |
234 | + } | |
235 | + } | |
236 | + | |
237 | + public void setCloseListener(CloseListener listener) { | |
238 | + } | |
239 | +} |
@@ -26,19 +26,14 @@ import com.android.gallery3d.R; | ||
26 | 26 | import com.android.gallery3d.app.GalleryActivity; |
27 | 27 | import com.android.gallery3d.common.Utils; |
28 | 28 | import com.android.gallery3d.data.MediaDetails; |
29 | -import com.android.gallery3d.util.Future; | |
30 | -import com.android.gallery3d.util.FutureListener; | |
31 | -import com.android.gallery3d.util.GalleryUtils; | |
32 | -import com.android.gallery3d.util.ReverseGeocoder; | |
33 | -import com.android.gallery3d.util.ThreadPool.Job; | |
34 | -import com.android.gallery3d.util.ThreadPool.JobContext; | |
29 | +import com.android.gallery3d.ui.DetailsAddressResolver.AddressResolvingListener; | |
30 | +import com.android.gallery3d.ui.DetailsHelper.DetailsSource; | |
31 | +import com.android.gallery3d.ui.DetailsHelper.DetailsViewContainer; | |
32 | +import com.android.gallery3d.ui.DetailsHelper.CloseListener; | |
35 | 33 | |
36 | 34 | import android.content.Context; |
37 | 35 | import android.graphics.Color; |
38 | 36 | import android.graphics.Rect; |
39 | -import android.location.Address; | |
40 | -import android.os.Handler; | |
41 | -import android.os.Message; | |
42 | 37 | import android.text.format.Formatter; |
43 | 38 | import android.view.MotionEvent; |
44 | 39 | import android.view.View.MeasureSpec; |
@@ -46,11 +41,9 @@ import android.view.View.MeasureSpec; | ||
46 | 41 | import java.util.ArrayList; |
47 | 42 | import java.util.Map.Entry; |
48 | 43 | |
49 | -// TODO: Add scroll bar to this window. | |
50 | -public class DetailsWindow extends GLView { | |
44 | +public class GLDetailsView extends GLView implements DetailsViewContainer { | |
51 | 45 | @SuppressWarnings("unused") |
52 | - private static final String TAG = "DetailsWindow"; | |
53 | - private static final int MSG_REFRESH_LOCATION = 1; | |
46 | + private static final String TAG = "GLDetailsView"; | |
54 | 47 | private static final int FONT_COLOR = Color.WHITE; |
55 | 48 | private static final int CLOSE_BUTTON_SIZE = 32; |
56 | 49 |
@@ -62,8 +55,6 @@ public class DetailsWindow extends GLView { | ||
62 | 55 | private DetailsSource mSource; |
63 | 56 | private int mIndex; |
64 | 57 | private int mLocationIndex; |
65 | - private Future<Address> mAddressLookupJob; | |
66 | - private Handler mHandler; | |
67 | 58 | private Icon mCloseButton; |
68 | 59 | private int mMaxDetailLength; |
69 | 60 | private CloseListener mListener; |
@@ -71,32 +62,12 @@ public class DetailsWindow extends GLView { | ||
71 | 62 | private ScrollView mScrollView; |
72 | 63 | private DetailsPanel mDetailPanel = new DetailsPanel(); |
73 | 64 | |
74 | - public interface DetailsSource { | |
75 | - public int size(); | |
76 | - public int findIndex(int indexHint); | |
77 | - public MediaDetails getDetails(); | |
78 | - } | |
79 | - | |
80 | - public interface CloseListener { | |
81 | - public void onClose(); | |
82 | - } | |
83 | - | |
84 | - public DetailsWindow(GalleryActivity activity, DetailsSource source) { | |
65 | + public GLDetailsView(GalleryActivity activity, DetailsSource source) { | |
85 | 66 | mContext = activity; |
86 | 67 | mSource = source; |
87 | - mHandler = new SynchronizedHandler(activity.getGLRoot()) { | |
88 | - @Override | |
89 | - public void handleMessage(Message msg) { | |
90 | - switch(msg.what) { | |
91 | - case MSG_REFRESH_LOCATION: | |
92 | - mModel.updateLocation((Address) msg.obj); | |
93 | - invalidate(); | |
94 | - break; | |
95 | - } | |
96 | - } | |
97 | - }; | |
68 | + | |
98 | 69 | Context context = activity.getAndroidContext(); |
99 | - ResourceTexture icon = new ResourceTexture(context, R.drawable.ic_menu_cancel_holo_light); | |
70 | + ResourceTexture icon = new ResourceTexture(context, R.drawable.ic_lockscreen_chevron_up); | |
100 | 71 | setBackground(new NinePatchTexture(context, R.drawable.popup_full_dark)); |
101 | 72 | |
102 | 73 | mCloseButton = new Icon(context, icon, CLOSE_BUTTON_SIZE, CLOSE_BUTTON_SIZE) { |
@@ -114,8 +85,6 @@ public class DetailsWindow extends GLView { | ||
114 | 85 | |
115 | 86 | super.addComponent(mScrollView); |
116 | 87 | super.addComponent(mCloseButton); |
117 | - | |
118 | - reloadDetails(0); | |
119 | 88 | } |
120 | 89 | |
121 | 90 | public void setCloseListener(CloseListener listener) { |
@@ -162,15 +131,16 @@ public class DetailsWindow extends GLView { | ||
162 | 131 | |
163 | 132 | @Override |
164 | 133 | protected void onMeasure(int widthSpec, int heightSpec) { |
165 | - int height = MeasureSpec.getSize(heightSpec); | |
134 | + mScrollView.measure(widthSpec, heightSpec); | |
135 | + mCloseButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); | |
136 | + int height = mScrollView.getMeasuredHeight() + mCloseButton.getMeasuredHeight(); | |
166 | 137 | MeasureHelper.getInstance(this) |
167 | - .setPreferredContentSize(PREFERRED_WIDTH, height) | |
138 | + .setPreferredContentSize(mScrollView.getMeasuredWidth(), height) | |
168 | 139 | .measure(widthSpec, heightSpec); |
169 | 140 | } |
170 | 141 | |
171 | 142 | @Override |
172 | 143 | protected void onLayout(boolean sizeChange, int l, int t, int r, int b) { |
173 | - mCloseButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); | |
174 | 144 | int bWidth = mCloseButton.getMeasuredWidth(); |
175 | 145 | int bHeight = mCloseButton.getMeasuredHeight(); |
176 | 146 | int width = getWidth(); |
@@ -184,6 +154,7 @@ public class DetailsWindow extends GLView { | ||
184 | 154 | } |
185 | 155 | |
186 | 156 | public void show() { |
157 | + reloadDetails(mSource.getIndex()); | |
187 | 158 | setVisibility(GLView.VISIBLE); |
188 | 159 | requestLayout(); |
189 | 160 | } |
@@ -193,14 +164,6 @@ public class DetailsWindow extends GLView { | ||
193 | 164 | requestLayout(); |
194 | 165 | } |
195 | 166 | |
196 | - public void pause() { | |
197 | - Future<Address> lookupJob = mAddressLookupJob; | |
198 | - if (lookupJob != null) { | |
199 | - lookupJob.cancel(); | |
200 | - lookupJob.waitDone(); | |
201 | - } | |
202 | - } | |
203 | - | |
204 | 167 | public void reloadDetails(int indexHint) { |
205 | 168 | int index = mSource.findIndex(indexHint); |
206 | 169 | if (index == -1) return; |
@@ -219,19 +182,7 @@ public class DetailsWindow extends GLView { | ||
219 | 182 | invalidate(); |
220 | 183 | } |
221 | 184 | |
222 | - private class AddressLookupJob implements Job<Address> { | |
223 | - double[] mLatlng; | |
224 | - protected AddressLookupJob(double[] latlng) { | |
225 | - mLatlng = latlng; | |
226 | - } | |
227 | - | |
228 | - public Address run(JobContext jc) { | |
229 | - ReverseGeocoder geocoder = new ReverseGeocoder(mContext.getAndroidContext()); | |
230 | - return geocoder.lookupAddress(mLatlng[0], mLatlng[1], true); | |
231 | - } | |
232 | - } | |
233 | - | |
234 | - private class MyDataModel { | |
185 | + private class MyDataModel implements AddressResolvingListener { | |
235 | 186 | ArrayList<Texture> mItems; |
236 | 187 | |
237 | 188 | public MyDataModel(MediaDetails details) { |
@@ -248,7 +199,9 @@ public class DetailsWindow extends GLView { | ||
248 | 199 | String value; |
249 | 200 | switch (detail.getKey()) { |
250 | 201 | case MediaDetails.INDEX_LOCATION: { |
251 | - value = getLocationText((double[]) detail.getValue()); | |
202 | + double[] latlng = (double[]) detail.getValue(); | |
203 | + mLocationIndex = mItems.size(); | |
204 | + value = DetailsHelper.resolveAddress(mContext, latlng, this); | |
252 | 205 | break; |
253 | 206 | } |
254 | 207 | case MediaDetails.INDEX_SIZE: { |
@@ -293,16 +246,17 @@ public class DetailsWindow extends GLView { | ||
293 | 246 | Object valueObj = detail.getValue(); |
294 | 247 | // This shouldn't happen, log its key to help us diagnose the problem. |
295 | 248 | Utils.assertTrue(valueObj != null, "%s's value is Null", |
296 | - getName(context, detail.getKey())); | |
249 | + DetailsHelper.getDetailsName(context, detail.getKey())); | |
297 | 250 | value = valueObj.toString(); |
298 | 251 | } |
299 | 252 | } |
300 | 253 | int key = detail.getKey(); |
301 | 254 | if (details.hasUnit(key)) { |
302 | - value = String.format("%s : %s %s", getName(context, key), value, | |
303 | - context.getString(details.getUnit(key))); | |
255 | + value = String.format("%s : %s %s", DetailsHelper.getDetailsName( | |
256 | + context, key), value, context.getString(details.getUnit(key))); | |
304 | 257 | } else { |
305 | - value = String.format("%s : %s", getName(context, key), value); | |
258 | + value = String.format("%s : %s", DetailsHelper.getDetailsName( | |
259 | + context, key), value); | |
306 | 260 | } |
307 | 261 | Texture label = MultiLineTexture.newInstance( |
308 | 262 | value, mMaxDetailLength, FONT_SIZE, FONT_COLOR); |
@@ -310,54 +264,6 @@ public class DetailsWindow extends GLView { | ||
310 | 264 | } |
311 | 265 | } |
312 | 266 | |
313 | - private String getLocationText(double[] latlng) { | |
314 | - String text = GalleryUtils.formatLatitudeLongitude("(%f,%f)", latlng[0], latlng[1]); | |
315 | - mAddressLookupJob = mContext.getThreadPool().submit( | |
316 | - new AddressLookupJob(latlng), | |
317 | - new FutureListener<Address>() { | |
318 | - public void onFutureDone(Future<Address> future) { | |
319 | - mAddressLookupJob = null; | |
320 | - if (!future.isCancelled()) { | |
321 | - mHandler.sendMessage(mHandler.obtainMessage( | |
322 | - MSG_REFRESH_LOCATION, future.get())); | |
323 | - } | |
324 | - } | |
325 | - }); | |
326 | - mLocationIndex = mItems.size(); | |
327 | - return text; | |
328 | - } | |
329 | - | |
330 | - public void updateLocation(Address address) { | |
331 | - int index = mLocationIndex; | |
332 | - if (address != null && index >=0 && index < mItems.size()) { | |
333 | - Context context = mContext.getAndroidContext(); | |
334 | - String parts[] = { | |
335 | - address.getAdminArea(), | |
336 | - address.getSubAdminArea(), | |
337 | - address.getLocality(), | |
338 | - address.getSubLocality(), | |
339 | - address.getThoroughfare(), | |
340 | - address.getSubThoroughfare(), | |
341 | - address.getPremises(), | |
342 | - address.getPostalCode(), | |
343 | - address.getCountryName() | |
344 | - }; | |
345 | - | |
346 | - String addressText = ""; | |
347 | - for (int i = 0; i < parts.length; i++) { | |
348 | - if (parts[i] == null || parts[i].isEmpty()) continue; | |
349 | - if (!addressText.isEmpty()) { | |
350 | - addressText += ", "; | |
351 | - } | |
352 | - addressText += parts[i]; | |
353 | - } | |
354 | - String text = String.format("%s : %s", getName(context, | |
355 | - MediaDetails.INDEX_LOCATION), addressText); | |
356 | - mItems.set(index, MultiLineTexture.newInstance( | |
357 | - text, mMaxDetailLength, FONT_SIZE, FONT_COLOR)); | |
358 | - } | |
359 | - } | |
360 | - | |
361 | 267 | public Texture getView(int index) { |
362 | 268 | return mItems.get(index); |
363 | 269 | } |
@@ -365,50 +271,11 @@ public class DetailsWindow extends GLView { | ||
365 | 271 | public int size() { |
366 | 272 | return mItems.size(); |
367 | 273 | } |
368 | - } | |
369 | 274 | |
370 | - private static String getName(Context context, int key) { | |
371 | - switch (key) { | |
372 | - case MediaDetails.INDEX_TITLE: | |
373 | - return context.getString(R.string.title); | |
374 | - case MediaDetails.INDEX_DESCRIPTION: | |
375 | - return context.getString(R.string.description); | |
376 | - case MediaDetails.INDEX_DATETIME: | |
377 | - return context.getString(R.string.time); | |
378 | - case MediaDetails.INDEX_LOCATION: | |
379 | - return context.getString(R.string.location); | |
380 | - case MediaDetails.INDEX_PATH: | |
381 | - return context.getString(R.string.path); | |
382 | - case MediaDetails.INDEX_WIDTH: | |
383 | - return context.getString(R.string.width); | |
384 | - case MediaDetails.INDEX_HEIGHT: | |
385 | - return context.getString(R.string.height); | |
386 | - case MediaDetails.INDEX_ORIENTATION: | |
387 | - return context.getString(R.string.orientation); | |
388 | - case MediaDetails.INDEX_DURATION: | |
389 | - return context.getString(R.string.duration); | |
390 | - case MediaDetails.INDEX_MIMETYPE: | |
391 | - return context.getString(R.string.mimetype); | |
392 | - case MediaDetails.INDEX_SIZE: | |
393 | - return context.getString(R.string.file_size); | |
394 | - case MediaDetails.INDEX_MAKE: | |
395 | - return context.getString(R.string.maker); | |
396 | - case MediaDetails.INDEX_MODEL: | |
397 | - return context.getString(R.string.model); | |
398 | - case MediaDetails.INDEX_FLASH: | |
399 | - return context.getString(R.string.flash); | |
400 | - case MediaDetails.INDEX_APERTURE: | |
401 | - return context.getString(R.string.aperture); | |
402 | - case MediaDetails.INDEX_FOCAL_LENGTH: | |
403 | - return context.getString(R.string.focal_length); | |
404 | - case MediaDetails.INDEX_WHITE_BALANCE: | |
405 | - return context.getString(R.string.white_balance); | |
406 | - case MediaDetails.INDEX_EXPOSURE_TIME: | |
407 | - return context.getString(R.string.exposure_time); | |
408 | - case MediaDetails.INDEX_ISO: | |
409 | - return context.getString(R.string.iso); | |
410 | - default: | |
411 | - return "Unknown key" + key; | |
275 | + public void onAddressAvailable(String address) { | |
276 | + mItems.set(mLocationIndex, MultiLineTexture.newInstance( | |
277 | + address, mMaxDetailLength, FONT_SIZE, FONT_COLOR)); | |
278 | + GLDetailsView.this.invalidate(); | |
412 | 279 | } |
413 | 280 | } |
414 | 281 |
@@ -423,10 +290,12 @@ public class DetailsWindow extends GLView { | ||
423 | 290 | return; |
424 | 291 | } |
425 | 292 | |
426 | - int h = getPaddings().top + LINE_SPACING; | |
293 | + Rect p = getPaddings(); | |
294 | + int h = p.top + LINE_SPACING; | |
427 | 295 | for (int i = 0, n = mModel.size(); i < n; ++i) { |
428 | 296 | h += mModel.getView(i).getHeight() + LINE_SPACING; |
429 | 297 | } |
298 | + h += p.bottom; | |
430 | 299 | |
431 | 300 | MeasureHelper.getInstance(this) |
432 | 301 | .setPreferredContentSize(PREFERRED_WIDTH, h) |
@@ -449,4 +318,8 @@ public class DetailsWindow extends GLView { | ||
449 | 318 | } |
450 | 319 | } |
451 | 320 | } |
321 | + | |
322 | + public GLView getGLView() { | |
323 | + return this; | |
324 | + } | |
452 | 325 | } |
@@ -44,6 +44,18 @@ public class ScrollView extends GLView { | ||
44 | 44 | } |
45 | 45 | |
46 | 46 | @Override |
47 | + protected void onMeasure(int widthSpec, int heightSpec) { | |
48 | + GLView view = getContentView(); | |
49 | + if (view != null) { | |
50 | + view.measure(widthSpec, heightSpec); | |
51 | + MeasureHelper.getInstance(this) | |
52 | + .setPreferredContentSize(view.getMeasuredWidth(), | |
53 | + view.getMeasuredHeight()) | |
54 | + .measure(widthSpec, heightSpec); | |
55 | + } | |
56 | + } | |
57 | + | |
58 | + @Override | |
47 | 59 | public void onLayout(boolean sizeChange, int l, int t, int r, int b) { |
48 | 60 | GLView content = getContentView(); |
49 | 61 | int width = getWidth(); |