diff options
author | Artur Dryomov <artur.dryomov@gmail.com> | 2013-07-27 16:30:17 +0300 |
---|---|---|
committer | Artur Dryomov <artur.dryomov@gmail.com> | 2013-07-29 04:13:10 +0300 |
commit | a92fa984ae24dc25283d4b6805cff01bf00117e5 (patch) | |
tree | 93cd1e8f5768255107cd4c03d7fa49de1570e67b /android | |
parent | ff5c113531ce9819927ecacbbb5853393eff501d (diff) |
Add async slide previews loading.
* The previews storage contains bytes arrays instead of Bitmaps which
should decrease memory footprint.
* All previews processing moved to the background thread.
This change should solve out of memory errors.
Change-Id: Idd4046020c8fe8f977858f1911e04c0ec095380a
Diffstat (limited to 'android')
8 files changed, 235 insertions, 30 deletions
diff --git a/android/sdremote/src/org/libreoffice/impressremote/activity/SlideShowActivity.java b/android/sdremote/src/org/libreoffice/impressremote/activity/SlideShowActivity.java index 7de2ee4049af..4b91a6af8f65 100644 --- a/android/sdremote/src/org/libreoffice/impressremote/activity/SlideShowActivity.java +++ b/android/sdremote/src/org/libreoffice/impressremote/activity/SlideShowActivity.java @@ -245,21 +245,31 @@ public class SlideShowActivity extends SherlockFragmentActivity implements Servi protected void onDestroy() { super.onDestroy(); + disconnectComputer(); + unbindService(); } - private void unbindService() { + private void disconnectComputer() { if (!isServiceBound()) { return; } - unbindService(this); + mCommunicationService.disconnect(); } private boolean isServiceBound() { return mCommunicationService != null; } + private void unbindService() { + if (!isServiceBound()) { + return; + } + + unbindService(this); + } + @Override public void onServiceDisconnected(ComponentName aComponentName) { mCommunicationService = null; diff --git a/android/sdremote/src/org/libreoffice/impressremote/adapter/SlidesGridAdapter.java b/android/sdremote/src/org/libreoffice/impressremote/adapter/SlidesGridAdapter.java index f7f7a010b8ab..846226fa50e0 100644 --- a/android/sdremote/src/org/libreoffice/impressremote/adapter/SlidesGridAdapter.java +++ b/android/sdremote/src/org/libreoffice/impressremote/adapter/SlidesGridAdapter.java @@ -18,14 +18,17 @@ import android.widget.TextView; import org.libreoffice.impressremote.R; import org.libreoffice.impressremote.communication.SlideShow; +import org.libreoffice.impressremote.util.ImageLoader; public class SlidesGridAdapter extends BaseAdapter { private final LayoutInflater mLayoutInflater; + private final ImageLoader mImageLoader; private final SlideShow mSlideShow; public SlidesGridAdapter(Context aContext, SlideShow aSlideShow) { mLayoutInflater = LayoutInflater.from(aContext); + mImageLoader = new ImageLoader(aContext.getResources(), R.drawable.slide_unknown); mSlideShow = aSlideShow; } @@ -37,7 +40,7 @@ public class SlidesGridAdapter extends BaseAdapter { @Override public Object getItem(int aPosition) { - return mSlideShow.getSlidePreview(aPosition); + return mSlideShow.getSlidePreviewBytes(aPosition); } @Override @@ -51,12 +54,12 @@ public class SlidesGridAdapter extends BaseAdapter { ViewHolder aSlideViewHolder = getViewHolder(aSlideView); if (isSlidePreviewAvailable(aPosition)) { - aSlideViewHolder.aSlidePreview.setImageBitmap(mSlideShow.getSlidePreview(aPosition)); + setUpSlidePreview(aSlideViewHolder, aPosition); } else { - aSlideViewHolder.aSlidePreview.setImageResource(R.drawable.slide_unknown); + setUpUnknownSlidePreview(aSlideViewHolder); } - aSlideViewHolder.aSlideIndex.setText(buildSlideIndex(aPosition)); + aSlideViewHolder.mSlideIndex.setText(buildSlideIndex(aPosition)); return aSlideView; } @@ -78,21 +81,31 @@ public class SlidesGridAdapter extends BaseAdapter { } private static final class ViewHolder { - public ImageView aSlidePreview; - public TextView aSlideIndex; + public ImageView mSlidePreview; + public TextView mSlideIndex; } private ViewHolder buildViewHolder(View aView) { ViewHolder aViewHolder = new ViewHolder(); - aViewHolder.aSlidePreview = (ImageView) aView.findViewById(R.id.image_slide_preview); - aViewHolder.aSlideIndex = (TextView) aView.findViewById(R.id.text_slide_index); + aViewHolder.mSlidePreview = (ImageView) aView.findViewById(R.id.image_slide_preview); + aViewHolder.mSlideIndex = (TextView) aView.findViewById(R.id.text_slide_index); return aViewHolder; } private boolean isSlidePreviewAvailable(int aSlideIndex) { - return mSlideShow.getSlidePreview(aSlideIndex) != null; + return mSlideShow.getSlidePreviewBytes(aSlideIndex) != null; + } + + private void setUpSlidePreview(ViewHolder aSlideViewHolder, int aPosition) { + byte[] aSlidePreviewBytes = mSlideShow.getSlidePreviewBytes(aPosition); + + mImageLoader.loadImage(aSlideViewHolder.mSlidePreview, aSlidePreviewBytes); + } + + private void setUpUnknownSlidePreview(ViewHolder aSlideViewHolder) { + aSlideViewHolder.mSlidePreview.setImageResource(R.drawable.slide_unknown); } private String buildSlideIndex(int aPosition) { diff --git a/android/sdremote/src/org/libreoffice/impressremote/adapter/SlidesPagerAdapter.java b/android/sdremote/src/org/libreoffice/impressremote/adapter/SlidesPagerAdapter.java index 352e7a89f849..02097d27a743 100644 --- a/android/sdremote/src/org/libreoffice/impressremote/adapter/SlidesPagerAdapter.java +++ b/android/sdremote/src/org/libreoffice/impressremote/adapter/SlidesPagerAdapter.java @@ -17,14 +17,17 @@ import android.widget.ImageView; import org.libreoffice.impressremote.R; import org.libreoffice.impressremote.communication.SlideShow; +import org.libreoffice.impressremote.util.ImageLoader; public class SlidesPagerAdapter extends PagerAdapter { private final LayoutInflater mLayoutInflater; + private final ImageLoader mImageLoader; private final SlideShow mSlideShow; public SlidesPagerAdapter(Context aContext, SlideShow aSlideShow) { mLayoutInflater = LayoutInflater.from(aContext); + mImageLoader = new ImageLoader(aContext.getResources(), R.drawable.slide_unknown); mSlideShow = aSlideShow; } @@ -39,10 +42,10 @@ public class SlidesPagerAdapter extends PagerAdapter { ImageView aSlideView = (ImageView) getView(aViewGroup); if (isSlidePreviewAvailable(aPosition)) { - aSlideView.setImageBitmap(mSlideShow.getSlidePreview(aPosition)); + setUpSlidePreview(aSlideView, aPosition); } else { - aSlideView.setImageResource(R.drawable.slide_unknown); + setUpUnknownSlidePreview(aSlideView); } aViewGroup.addView(aSlideView); @@ -55,7 +58,17 @@ public class SlidesPagerAdapter extends PagerAdapter { } private boolean isSlidePreviewAvailable(int aSlideIndex) { - return mSlideShow.getSlidePreview(aSlideIndex) != null; + return mSlideShow.getSlidePreviewBytes(aSlideIndex) != null; + } + + private void setUpSlidePreview(ImageView aSlideView, int aPosition) { + byte[] aSlidePreviewBytes = mSlideShow.getSlidePreviewBytes(aPosition); + + mImageLoader.loadImage(aSlideView, aSlidePreviewBytes); + } + + private void setUpUnknownSlidePreview(ImageView aSlideView) { + aSlideView.setImageResource(R.drawable.slide_unknown); } @Override diff --git a/android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java b/android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java index 2e32813fbba0..2977921f25a9 100644 --- a/android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java +++ b/android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java @@ -264,7 +264,7 @@ public class CommunicationService extends Service implements Runnable, MessagesL @Override public void onSlidePreview(int aSlideIndex, byte[] aPreview) { - mSlideShow.setSlidePreview(aSlideIndex, aPreview); + mSlideShow.setSlidePreviewBytes(aSlideIndex, aPreview); Intent aIntent = Intents.buildSlidePreviewIntent(aSlideIndex); LocalBroadcastManager.getInstance(this).sendBroadcast(aIntent); diff --git a/android/sdremote/src/org/libreoffice/impressremote/communication/MessagesReceiver.java b/android/sdremote/src/org/libreoffice/impressremote/communication/MessagesReceiver.java index 4d226d868183..bb6de8fc8742 100644 --- a/android/sdremote/src/org/libreoffice/impressremote/communication/MessagesReceiver.java +++ b/android/sdremote/src/org/libreoffice/impressremote/communication/MessagesReceiver.java @@ -14,10 +14,12 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import android.text.TextUtils; import android.util.Base64; +import android.util.Log; class MessagesReceiver implements Runnable { private final BufferedReader mMessagesReader; diff --git a/android/sdremote/src/org/libreoffice/impressremote/communication/SlideShow.java b/android/sdremote/src/org/libreoffice/impressremote/communication/SlideShow.java index 79852c507b68..a216dfc8f0eb 100644 --- a/android/sdremote/src/org/libreoffice/impressremote/communication/SlideShow.java +++ b/android/sdremote/src/org/libreoffice/impressremote/communication/SlideShow.java @@ -8,8 +8,6 @@ */ package org.libreoffice.impressremote.communication; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.util.SparseArray; @@ -17,7 +15,7 @@ public class SlideShow { private int mSlidesCount; private int mCurrentSlideIndex; - private final SparseArray<Bitmap> mSlidePreviews; + private final SparseArray<byte[]> mSlidePreviewsBytes; private final SparseArray<String> mSlideNotes; private final Timer mTimer; @@ -26,7 +24,7 @@ public class SlideShow { this.mSlidesCount = 0; this.mCurrentSlideIndex = 0; - this.mSlidePreviews = new SparseArray<Bitmap>(); + this.mSlidePreviewsBytes = new SparseArray<byte[]>(); this.mSlideNotes = new SparseArray<String>(); this.mTimer = new Timer(); @@ -52,15 +50,12 @@ public class SlideShow { return getCurrentSlideIndex() + 1; } - public void setSlidePreview(int aSlideIndex, byte[] aSlidePreviewBytes) { - Bitmap aSlidePreview = BitmapFactory - .decodeByteArray(aSlidePreviewBytes, 0, aSlidePreviewBytes.length); - - mSlidePreviews.put(aSlideIndex, aSlidePreview); + public void setSlidePreviewBytes(int aSlideIndex, byte[] aSlidePreviewBytes) { + mSlidePreviewsBytes.put(aSlideIndex, aSlidePreviewBytes); } - public Bitmap getSlidePreview(int aSlideIndex) { - return mSlidePreviews.get(aSlideIndex); + public byte[] getSlidePreviewBytes(int aSlideIndex) { + return mSlidePreviewsBytes.get(aSlideIndex); } public void setSlideNotes(int aSlideIndex, String aSlideNotes) { @@ -85,7 +80,7 @@ public class SlideShow { mSlidesCount = 0; mCurrentSlideIndex = 0; - mSlidePreviews.clear(); + mSlidePreviewsBytes.clear(); mSlideNotes.clear(); } } diff --git a/android/sdremote/src/org/libreoffice/impressremote/fragment/SlidesGridFragment.java b/android/sdremote/src/org/libreoffice/impressremote/fragment/SlidesGridFragment.java index ebc9a24d02ef..25e2819a7c4c 100644 --- a/android/sdremote/src/org/libreoffice/impressremote/fragment/SlidesGridFragment.java +++ b/android/sdremote/src/org/libreoffice/impressremote/fragment/SlidesGridFragment.java @@ -51,14 +51,12 @@ public class SlidesGridFragment extends SherlockFragment implements ServiceConne private void bindService() { Intent aServiceIntent = new Intent(getActivity(), CommunicationService.class); - getActivity().bindService(aServiceIntent, this, Context.BIND_AUTO_CREATE); } @Override public void onServiceConnected(ComponentName aComponentName, IBinder aBinder) { CommunicationService.CBinder aServiceBinder = (CommunicationService.CBinder) aBinder; - mCommunicationService = aServiceBinder.getService(); setUpSlidesGrid(); @@ -109,14 +107,23 @@ public class SlidesGridFragment extends SherlockFragment implements ServiceConne @Override public void onReceive(Context aContext, Intent aIntent) { - if (Intents.Actions.SLIDE_PREVIEW.equals(aIntent.getAction())) { + if (Intents.Actions.SLIDE_SHOW_RUNNING.equals(aIntent.getAction())) { mSlidesGridFragment.refreshSlidesGrid(); + + return; + } + + if (Intents.Actions.SLIDE_PREVIEW.equals(aIntent.getAction())) { + int aSlideIndex = aIntent.getIntExtra(Intents.Extras.SLIDE_INDEX, 0); + + mSlidesGridFragment.refreshSlidePreview(aSlideIndex); } } } private IntentFilter buildIntentsReceiverFilter() { IntentFilter aIntentFilter = new IntentFilter(); + aIntentFilter.addAction(Intents.Actions.SLIDE_SHOW_RUNNING); aIntentFilter.addAction(Intents.Actions.SLIDE_PREVIEW); return aIntentFilter; @@ -132,6 +139,17 @@ public class SlidesGridFragment extends SherlockFragment implements ServiceConne getSlidesGrid().invalidateViews(); } + private void refreshSlidePreview(int aSlideIndex) { + GridView aSlidesGrid = getSlidesGrid(); + View aSlideView = aSlidesGrid.getChildAt(aSlideIndex); + + if (aSlideView == null) { + return; + } + + aSlidesGrid.getAdapter().getView(aSlideIndex, aSlideView, aSlidesGrid); + } + @Override public void onPause() { super.onPause(); diff --git a/android/sdremote/src/org/libreoffice/impressremote/util/ImageLoader.java b/android/sdremote/src/org/libreoffice/impressremote/util/ImageLoader.java new file mode 100644 index 000000000000..1bd2f4f326df --- /dev/null +++ b/android/sdremote/src/org/libreoffice/impressremote/util/ImageLoader.java @@ -0,0 +1,154 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.libreoffice.impressremote.util; + +import java.lang.ref.WeakReference; +import java.util.Arrays; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.widget.ImageView; + +public final class ImageLoader { + private final Resources mResources; + private final Bitmap mLoadingImage; + + public ImageLoader(Resources aResources, int aLoadingImageResourceId) { + mResources = aResources; + mLoadingImage = BitmapFactory.decodeResource(mResources, aLoadingImageResourceId); + } + + public void loadImage(ImageView aImageView, byte[] aImageBytes) { + if (isSameImageLoading(aImageView, aImageBytes)) { + return; + } + + if (isImageLoadingCancellationRequired(aImageView, aImageBytes)) { + cancelImageLoading(aImageView); + } + + startImageLoading(aImageView, aImageBytes); + } + + private boolean isSameImageLoading(ImageView aImageView, byte[] aImageBytes) { + if (!isImageLoading(aImageView)) { + return false; + } + + ImageLoadingTask aImageLoadingTask = getImageLoadingTask(aImageView); + + return Arrays.equals(aImageBytes, aImageLoadingTask.getImageBytes()); + } + + private boolean isImageLoading(ImageView aImageView) { + ImageLoadingTask aImageLoadingTask = getImageLoadingTask(aImageView); + + return aImageLoadingTask != null; + } + + private ImageLoadingTask getImageLoadingTask(ImageView aImageView) { + if (aImageView == null) { + return null; + } + + Drawable aImageDrawable = aImageView.getDrawable(); + + if (!(aImageDrawable instanceof AsyncDrawable)) { + return null; + } + + AsyncDrawable aAsyncImageDrawable = (AsyncDrawable) aImageDrawable; + + return aAsyncImageDrawable.getImageLoadingTask(); + } + + private boolean isImageLoadingCancellationRequired(ImageView aImageView, byte[] aImageBytes) { + return isImageLoading(aImageView) && !isSameImageLoading(aImageView, aImageBytes); + } + + private void cancelImageLoading(ImageView aImageView) { + ImageLoadingTask aImageLoadingTask = getImageLoadingTask(aImageView); + + aImageLoadingTask.cancel(true); + } + + private void startImageLoading(ImageView aImageView, byte[] aImageBytes) { + ImageLoadingTask aImageLoadingTask = new ImageLoadingTask(aImageView, aImageBytes); + AsyncDrawable aAsyncDrawable = new AsyncDrawable(mResources, mLoadingImage, aImageLoadingTask); + + aImageView.setImageDrawable(aAsyncDrawable); + aImageLoadingTask.execute(); + } + + private static class ImageLoadingTask extends AsyncTask<Void, Void, Bitmap> { + private final WeakReference<ImageView> mImageViewReference; + private final byte[] mImageBytes; + + public ImageLoadingTask(ImageView aImageView, byte[] aImageBytes) { + mImageViewReference = new WeakReference<ImageView>(aImageView); + mImageBytes = aImageBytes; + } + + public byte[] getImageBytes() { + return mImageBytes; + } + + @Override + protected Bitmap doInBackground(Void... aParameters) { + return BitmapFactory.decodeByteArray(mImageBytes, 0, mImageBytes.length); + } + + @Override + protected void onPostExecute(Bitmap aBitmap) { + super.onPostExecute(aBitmap); + + if (isCancelled()) { + return; + } + + if (aBitmap == null) { + return; + } + + if (mImageViewReference == null) { + return; + } + + if (getImageView() == null) { + return; + } + + getImageView().setImageBitmap(aBitmap); + } + + private ImageView getImageView() { + return mImageViewReference.get(); + } + } + + private static class AsyncDrawable extends BitmapDrawable { + private final WeakReference<ImageLoadingTask> mImageLoadingTaskReference; + + public AsyncDrawable(Resources aResources, Bitmap aBitmap, ImageLoadingTask aImageLoadingTask) { + super(aResources, aBitmap); + + mImageLoadingTaskReference = new WeakReference<ImageLoadingTask>(aImageLoadingTask); + } + + public ImageLoadingTask getImageLoadingTask() { + return mImageLoadingTaskReference.get(); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |