• R/O
  • HTTP
  • SSH
  • HTTPS

提交

标签
No Tags

Frequently used words (click to add to your profile)

javaandroidc++linuxc#windowsobjective-ccocoaqtpython誰得phprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

packages/apps/Gallery2


Commit MetaInfo

修订版4105e22d72d7a44e67cba4a58ad88018ed664d10 (tree)
时间2011-08-26 21:20:48
作者Ray Chen <raychen@goog...>
CommiterRay Chen

Log Message

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

更改概述

差异

Binary files /dev/null and b/res/drawable-hdpi/ic_lockscreen_chevron_up.png differ
Binary files /dev/null and b/res/drawable-mdpi/ic_lockscreen_chevron_up.png differ
--- /dev/null
+++ b/res/layout/details.xml
@@ -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+/>
--- /dev/null
+++ b/res/values-xlarge/bool.xml
@@ -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
--- /dev/null
+++ b/res/values/bool.xml
@@ -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
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -97,6 +97,8 @@
9797 <string name="slideshow">Slideshow</string>
9898
9999 <string name="details">Details</string>
100+ <string name="details_title">%1$d of %2$d items:</string>
101+ <string name="close">Close</string>
100102
101103 <!-- Title of a menu item to switch from Gallery to Camera app [CHAR LIMIT=30] -->
102104 <string name="switch_to_camera">Switch to Camera</string>
--- a/src/com/android/gallery3d/app/AlbumPage.java
+++ b/src/com/android/gallery3d/app/AlbumPage.java
@@ -28,8 +28,8 @@ import com.android.gallery3d.data.Path;
2828 import com.android.gallery3d.ui.ActionModeHandler;
2929 import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener;
3030 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;
3333 import com.android.gallery3d.ui.GLCanvas;
3434 import com.android.gallery3d.ui.GLView;
3535 import com.android.gallery3d.ui.GridDrawer;
@@ -92,7 +92,8 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
9292 private ActionMode mActionMode;
9393 private ActionModeHandler mActionModeHandler;
9494 private int mFocusIndex = 0;
95- private DetailsWindow mDetailsWindow;
95+ private DetailsHelper mDetailsHelper;
96+ private MyDetailsSource mDetailsSource;
9697 private MediaSet mMediaSet;
9798 private boolean mShowDetails;
9899 private float mUserDistance; // in pixel
@@ -115,13 +116,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
115116 int slotViewRight = right - left;
116117
117118 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);
125120 } else {
126121 mAlbumView.setSelectionDrawer(mGridDrawer);
127122 }
@@ -162,7 +157,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
162157 }
163158 if (mShowDetails) {
164159 mHighlightDrawer.setHighlightItem(item.getPath());
165- mDetailsWindow.reloadDetails(slotIndex);
160+ mDetailsHelper.reloadDetails(slotIndex);
166161 } else if (!mSelectionManager.inSelectionMode()) {
167162 if (mGetContent) {
168163 onGetContent(item);
@@ -221,6 +216,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
221216 if (item == null) return;
222217 mSelectionManager.setAutoLeaveSelectionMode(true);
223218 mSelectionManager.toggle(item.getPath());
219+ mDetailsSource.findIndex(slotIndex);
224220 mAlbumView.invalidate();
225221 }
226222 }
@@ -263,6 +259,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
263259 initializeData(data);
264260 mGetContent = data.getBoolean(Gallery.KEY_GET_CONTENT, false);
265261 mShowClusterMenu = data.getBoolean(KEY_SHOW_CLUSTER_MENU, false);
262+ mDetailsSource = new MyDetailsSource();
266263
267264 startTransition(data);
268265
@@ -328,9 +325,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
328325 mIsActive = false;
329326 mAlbumDataAdapter.pause();
330327 mAlbumView.pause();
331- if (mDetailsWindow != null) {
332- mDetailsWindow.pause();
333- }
328+ DetailsHelper.pause();
334329 Future<?> task = mPendingTask;
335330 if (task != null) {
336331 // cancel on going task
@@ -400,24 +395,23 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
400395
401396 private void showDetails() {
402397 mShowDetails = true;
403- if (mDetailsWindow == null) {
398+ if (mDetailsHelper == null) {
404399 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() {
407402 public void onClose() {
408403 hideDetails();
409404 }
410405 });
411- mRootPane.addComponent(mDetailsWindow);
412406 }
413407 mAlbumView.setSelectionDrawer(mHighlightDrawer);
414- mDetailsWindow.show();
408+ mDetailsHelper.show();
415409 }
416410
417411 private void hideDetails() {
418412 mShowDetails = false;
419413 mAlbumView.setSelectionDrawer(mGridDrawer);
420- mDetailsWindow.hide();
414+ mDetailsHelper.hide();
421415 }
422416
423417 @Override
@@ -572,12 +566,16 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
572566 }
573567 }
574568
575- private class MyDetailsSource implements DetailsWindow.DetailsSource {
569+ private class MyDetailsSource implements DetailsHelper.DetailsSource {
576570 private int mIndex;
577571 public int size() {
578572 return mAlbumDataAdapter.size();
579573 }
580574
575+ public int getIndex() {
576+ return mIndex;
577+ }
578+
581579 // If requested index is out of active window, suggest a valid index.
582580 // If there is no valid index available, return -1.
583581 public int findIndex(int indexHint) {
--- a/src/com/android/gallery3d/app/AlbumSetPage.java
+++ b/src/com/android/gallery3d/app/AlbumSetPage.java
@@ -28,8 +28,8 @@ import com.android.gallery3d.settings.GallerySettings;
2828 import com.android.gallery3d.ui.ActionModeHandler;
2929 import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener;
3030 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;
3333 import com.android.gallery3d.ui.GLCanvas;
3434 import com.android.gallery3d.ui.GLView;
3535 import com.android.gallery3d.ui.GridDrawer;
@@ -88,7 +88,8 @@ public class AlbumSetPage extends ActivityState implements
8888 private boolean mGetAlbum;
8989 private ActionMode mActionMode;
9090 private ActionModeHandler mActionModeHandler;
91- private DetailsWindow mDetailsWindow;
91+ private DetailsHelper mDetailsHelper;
92+ private MyDetailsSource mDetailsSource;
9293 private boolean mShowDetails;
9394 private EyePosition mEyePosition;
9495
@@ -114,13 +115,7 @@ public class AlbumSetPage extends ActivityState implements
114115 int slotViewRight = right - left;
115116
116117 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);
124119 } else {
125120 mAlbumSetView.setSelectionDrawer(mGridDrawer);
126121 }
@@ -182,7 +177,7 @@ public class AlbumSetPage extends ActivityState implements
182177 if (mShowDetails) {
183178 Path path = targetSet.getPath();
184179 mHighlightDrawer.setHighlightItem(path);
185- mDetailsWindow.reloadDetails(slotIndex);
180+ mDetailsHelper.reloadDetails(slotIndex);
186181 } else if (!mSelectionManager.inSelectionMode()) {
187182 Bundle data = new Bundle(getData());
188183 String mediaPath = targetSet.getPath().toString();
@@ -226,6 +221,7 @@ public class AlbumSetPage extends ActivityState implements
226221 if (set == null) return;
227222 mSelectionManager.setAutoLeaveSelectionMode(true);
228223 mSelectionManager.toggle(set.getPath());
224+ mDetailsSource.findIndex(slotIndex);
229225 mAlbumSetView.invalidate();
230226 }
231227 }
@@ -274,6 +270,7 @@ public class AlbumSetPage extends ActivityState implements
274270 mTitle = data.getString(AlbumSetPage.KEY_SET_TITLE);
275271 mSubtitle = data.getString(AlbumSetPage.KEY_SET_SUBTITLE);
276272 mEyePosition = new EyePosition(mActivity.getAndroidContext(), this);
273+ mDetailsSource = new MyDetailsSource();
277274
278275 startTransition();
279276 }
@@ -286,9 +283,7 @@ public class AlbumSetPage extends ActivityState implements
286283 mAlbumSetDataAdapter.pause();
287284 mAlbumSetView.pause();
288285 mEyePosition.pause();
289- if (mDetailsWindow != null) {
290- mDetailsWindow.pause();
291- }
286+ DetailsHelper.pause();
292287 GalleryActionBar actionBar = mActivity.getGalleryActionBar();
293288 if (actionBar != null) actionBar.hideClusterTabs();
294289 }
@@ -520,23 +515,22 @@ public class AlbumSetPage extends ActivityState implements
520515 private void hideDetails() {
521516 mShowDetails = false;
522517 mAlbumSetView.setSelectionDrawer(mGridDrawer);
523- mDetailsWindow.hide();
518+ mDetailsHelper.hide();
524519 }
525520
526521 private void showDetails() {
527522 mShowDetails = true;
528- if (mDetailsWindow == null) {
523+ if (mDetailsHelper == null) {
529524 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() {
532527 public void onClose() {
533528 hideDetails();
534529 }
535530 });
536- mRootPane.addComponent(mDetailsWindow);
537531 }
538532 mAlbumSetView.setSelectionDrawer(mHighlightDrawer);
539- mDetailsWindow.show();
533+ mDetailsHelper.show();
540534 }
541535
542536 private class MyLoadingListener implements LoadingListener {
@@ -557,12 +551,16 @@ public class AlbumSetPage extends ActivityState implements
557551 }
558552 }
559553
560- private class MyDetailsSource implements DetailsWindow.DetailsSource {
554+ private class MyDetailsSource implements DetailsHelper.DetailsSource {
561555 private int mIndex;
562556 public int size() {
563557 return mAlbumSetDataAdapter.size();
564558 }
565559
560+ public int getIndex() {
561+ return mIndex;
562+ }
563+
566564 // If requested index is out of active window, suggest a valid index.
567565 // If there is no valid index available, return -1.
568566 public int findIndex(int indexHint) {
--- a/src/com/android/gallery3d/app/PhotoPage.java
+++ b/src/com/android/gallery3d/app/PhotoPage.java
@@ -25,9 +25,9 @@ import com.android.gallery3d.data.MediaSet;
2525 import com.android.gallery3d.data.MtpDevice;
2626 import com.android.gallery3d.data.Path;
2727 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;
3131 import com.android.gallery3d.ui.FilmStripView;
3232 import com.android.gallery3d.ui.GLCanvas;
3333 import com.android.gallery3d.ui.GLView;
@@ -82,7 +82,7 @@ public class PhotoPage extends ActivityState
8282 private PhotoView mPhotoView;
8383 private PhotoPage.Model mModel;
8484 private FilmStripView mFilmStripView;
85- private DetailsWindow mDetailsWindow;
85+ private DetailsHelper mDetailsHelper;
8686 private boolean mShowDetails;
8787
8888 // mMediaSet could be null if there is no KEY_MEDIA_SET_PATH supplied.
@@ -141,12 +141,8 @@ public class PhotoPage extends ActivityState
141141 right - left, bottom - top);
142142 }
143143 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);
150146 }
151147 }
152148 };
@@ -257,7 +253,7 @@ public class PhotoPage extends ActivityState
257253 if (mCurrentPhoto == null) return;
258254 updateMenuOperations();
259255 if (mShowDetails) {
260- mDetailsWindow.reloadDetails(mModel.getCurrentIndex());
256+ mDetailsHelper.reloadDetails(mModel.getCurrentIndex());
261257 }
262258 String title = photo.getName();
263259 if (title != null) mActionBar.setTitle(title);
@@ -441,22 +437,21 @@ public class PhotoPage extends ActivityState
441437
442438 private void hideDetails() {
443439 mShowDetails = false;
444- mDetailsWindow.hide();
440+ mDetailsHelper.hide();
445441 }
446442
447443 private void showDetails(int index) {
448444 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() {
452448 public void onClose() {
453449 hideDetails();
454450 }
455451 });
456- mRootPane.addComponent(mDetailsWindow);
457452 }
458- mDetailsWindow.reloadDetails(index);
459- mDetailsWindow.show();
453+ mDetailsHelper.reloadDetails(index);
454+ mDetailsHelper.show();
460455 }
461456
462457 public void onSingleTapUp(int x, int y) {
@@ -541,9 +536,7 @@ public class PhotoPage extends ActivityState
541536 if (mFilmStripView != null) {
542537 mFilmStripView.pause();
543538 }
544- if (mDetailsWindow != null) {
545- mDetailsWindow.pause();
546- }
539+ DetailsHelper.pause();
547540 mPhotoView.pause();
548541 mModel.pause();
549542 mHandler.removeMessages(MSG_HIDE_BARS);
@@ -568,6 +561,7 @@ public class PhotoPage extends ActivityState
568561 }
569562
570563 private class MyDetailsSource implements DetailsSource {
564+ private int mIndex;
571565 public MediaDetails getDetails() {
572566 return mModel.getCurrentMediaItem().getDetails();
573567 }
@@ -575,7 +569,12 @@ public class PhotoPage extends ActivityState
575569 return mMediaSet != null ? mMediaSet.getMediaItemCount() : 1;
576570 }
577571 public int findIndex(int indexHint) {
572+ mIndex = indexHint;
578573 return indexHint;
579574 }
575+
576+ public int getIndex() {
577+ return mIndex;
578+ }
580579 }
581580 }
--- /dev/null
+++ b/src/com/android/gallery3d/ui/DetailsAddressResolver.java
@@ -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+}
--- /dev/null
+++ b/src/com/android/gallery3d/ui/DetailsHelper.java
@@ -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+
--- /dev/null
+++ b/src/com/android/gallery3d/ui/DialogDetailsView.java
@@ -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+}
--- a/src/com/android/gallery3d/ui/DetailsWindow.java
+++ b/src/com/android/gallery3d/ui/GLDetailsView.java
@@ -26,19 +26,14 @@ import com.android.gallery3d.R;
2626 import com.android.gallery3d.app.GalleryActivity;
2727 import com.android.gallery3d.common.Utils;
2828 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;
3533
3634 import android.content.Context;
3735 import android.graphics.Color;
3836 import android.graphics.Rect;
39-import android.location.Address;
40-import android.os.Handler;
41-import android.os.Message;
4237 import android.text.format.Formatter;
4338 import android.view.MotionEvent;
4439 import android.view.View.MeasureSpec;
@@ -46,11 +41,9 @@ import android.view.View.MeasureSpec;
4641 import java.util.ArrayList;
4742 import java.util.Map.Entry;
4843
49-// TODO: Add scroll bar to this window.
50-public class DetailsWindow extends GLView {
44+public class GLDetailsView extends GLView implements DetailsViewContainer {
5145 @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";
5447 private static final int FONT_COLOR = Color.WHITE;
5548 private static final int CLOSE_BUTTON_SIZE = 32;
5649
@@ -62,8 +55,6 @@ public class DetailsWindow extends GLView {
6255 private DetailsSource mSource;
6356 private int mIndex;
6457 private int mLocationIndex;
65- private Future<Address> mAddressLookupJob;
66- private Handler mHandler;
6758 private Icon mCloseButton;
6859 private int mMaxDetailLength;
6960 private CloseListener mListener;
@@ -71,32 +62,12 @@ public class DetailsWindow extends GLView {
7162 private ScrollView mScrollView;
7263 private DetailsPanel mDetailPanel = new DetailsPanel();
7364
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) {
8566 mContext = activity;
8667 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+
9869 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);
10071 setBackground(new NinePatchTexture(context, R.drawable.popup_full_dark));
10172
10273 mCloseButton = new Icon(context, icon, CLOSE_BUTTON_SIZE, CLOSE_BUTTON_SIZE) {
@@ -114,8 +85,6 @@ public class DetailsWindow extends GLView {
11485
11586 super.addComponent(mScrollView);
11687 super.addComponent(mCloseButton);
117-
118- reloadDetails(0);
11988 }
12089
12190 public void setCloseListener(CloseListener listener) {
@@ -162,15 +131,16 @@ public class DetailsWindow extends GLView {
162131
163132 @Override
164133 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();
166137 MeasureHelper.getInstance(this)
167- .setPreferredContentSize(PREFERRED_WIDTH, height)
138+ .setPreferredContentSize(mScrollView.getMeasuredWidth(), height)
168139 .measure(widthSpec, heightSpec);
169140 }
170141
171142 @Override
172143 protected void onLayout(boolean sizeChange, int l, int t, int r, int b) {
173- mCloseButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
174144 int bWidth = mCloseButton.getMeasuredWidth();
175145 int bHeight = mCloseButton.getMeasuredHeight();
176146 int width = getWidth();
@@ -184,6 +154,7 @@ public class DetailsWindow extends GLView {
184154 }
185155
186156 public void show() {
157+ reloadDetails(mSource.getIndex());
187158 setVisibility(GLView.VISIBLE);
188159 requestLayout();
189160 }
@@ -193,14 +164,6 @@ public class DetailsWindow extends GLView {
193164 requestLayout();
194165 }
195166
196- public void pause() {
197- Future<Address> lookupJob = mAddressLookupJob;
198- if (lookupJob != null) {
199- lookupJob.cancel();
200- lookupJob.waitDone();
201- }
202- }
203-
204167 public void reloadDetails(int indexHint) {
205168 int index = mSource.findIndex(indexHint);
206169 if (index == -1) return;
@@ -219,19 +182,7 @@ public class DetailsWindow extends GLView {
219182 invalidate();
220183 }
221184
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 {
235186 ArrayList<Texture> mItems;
236187
237188 public MyDataModel(MediaDetails details) {
@@ -248,7 +199,9 @@ public class DetailsWindow extends GLView {
248199 String value;
249200 switch (detail.getKey()) {
250201 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);
252205 break;
253206 }
254207 case MediaDetails.INDEX_SIZE: {
@@ -293,16 +246,17 @@ public class DetailsWindow extends GLView {
293246 Object valueObj = detail.getValue();
294247 // This shouldn't happen, log its key to help us diagnose the problem.
295248 Utils.assertTrue(valueObj != null, "%s's value is Null",
296- getName(context, detail.getKey()));
249+ DetailsHelper.getDetailsName(context, detail.getKey()));
297250 value = valueObj.toString();
298251 }
299252 }
300253 int key = detail.getKey();
301254 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)));
304257 } else {
305- value = String.format("%s : %s", getName(context, key), value);
258+ value = String.format("%s : %s", DetailsHelper.getDetailsName(
259+ context, key), value);
306260 }
307261 Texture label = MultiLineTexture.newInstance(
308262 value, mMaxDetailLength, FONT_SIZE, FONT_COLOR);
@@ -310,54 +264,6 @@ public class DetailsWindow extends GLView {
310264 }
311265 }
312266
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-
361267 public Texture getView(int index) {
362268 return mItems.get(index);
363269 }
@@ -365,50 +271,11 @@ public class DetailsWindow extends GLView {
365271 public int size() {
366272 return mItems.size();
367273 }
368- }
369274
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();
412279 }
413280 }
414281
@@ -423,10 +290,12 @@ public class DetailsWindow extends GLView {
423290 return;
424291 }
425292
426- int h = getPaddings().top + LINE_SPACING;
293+ Rect p = getPaddings();
294+ int h = p.top + LINE_SPACING;
427295 for (int i = 0, n = mModel.size(); i < n; ++i) {
428296 h += mModel.getView(i).getHeight() + LINE_SPACING;
429297 }
298+ h += p.bottom;
430299
431300 MeasureHelper.getInstance(this)
432301 .setPreferredContentSize(PREFERRED_WIDTH, h)
@@ -449,4 +318,8 @@ public class DetailsWindow extends GLView {
449318 }
450319 }
451320 }
321+
322+ public GLView getGLView() {
323+ return this;
324+ }
452325 }
--- a/src/com/android/gallery3d/ui/ScrollView.java
+++ b/src/com/android/gallery3d/ui/ScrollView.java
@@ -44,6 +44,18 @@ public class ScrollView extends GLView {
4444 }
4545
4646 @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
4759 public void onLayout(boolean sizeChange, int l, int t, int r, int b) {
4860 GLView content = getContentView();
4961 int width = getWidth();