summaryrefslogtreecommitdiff
path: root/android
diff options
context:
space:
mode:
authorAndrzej J.R. Hunt <andrzej@ahunt.org>2012-07-24 11:46:48 +0200
committerMichael Meeks <michael.meeks@suse.com>2012-08-06 10:23:00 +0100
commitfa189184fd1f139986aab32531ab19f03223d312 (patch)
tree1f985d62c60cdb76f38caf2acd9600d66f2ae964 /android
parent612a2064986643ba69de4b59d2baab9068987ba3 (diff)
Added cover flow.
Change-Id: I7ffc47269a2e0b1fea4ec4dbcf6c03cdc37d4721
Diffstat (limited to 'android')
-rw-r--r--android/sdremote/res/layout/fragment_presentation.xml23
-rw-r--r--android/sdremote/res/values/attrs.xml10
-rw-r--r--android/sdremote/src/org/libreoffice/impressremote/PresentationActivity.java5
-rw-r--r--android/sdremote/src/org/libreoffice/impressremote/PresentationFragment.java112
-rw-r--r--android/sdremote/src/org/libreoffice/impressremote/ThumbnailFragment.java2
-rw-r--r--android/sdremote/src/org/libreoffice/impressremote/communication/Client.java1
-rw-r--r--android/sdremote/src/pl/polidea/coverflow/AbstractCoverFlowImageAdapter.java125
-rw-r--r--android/sdremote/src/pl/polidea/coverflow/CoverFlow.java388
-rw-r--r--android/sdremote/src/pl/polidea/coverflow/ReflectingImageAdapter.java129
-rw-r--r--android/sdremote/src/pl/polidea/coverflow/ResourceImageAdapter.java90
10 files changed, 867 insertions, 18 deletions
diff --git a/android/sdremote/res/layout/fragment_presentation.xml b/android/sdremote/res/layout/fragment_presentation.xml
index 3da7e0997cf6..fb501489cb47 100644
--- a/android/sdremote/res/layout/fragment_presentation.xml
+++ b/android/sdremote/res/layout/fragment_presentation.xml
@@ -1,16 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/presentation_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- android:id="@+id/presentation_layout">
+ android:orientation="vertical" >
- <ImageView
- android:id="@+id/imageView1"
- android:layout_width="wrap_content"
+ <pl.polidea.coverflow.CoverFlow
+ xmlns:coverflow="http://schemas.android.com/apk/res/org.libreoffice.impressremote"
+ android:id="@+id/presentation_coverflow"
+ android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:contentDescription="TODO"
- android:src="@drawable/testimage" />
+ android:layout_marginTop="5dip"
+ coverflow:imageHeight="150dip"
+ coverflow:imageReflectionRatio="0.2"
+ coverflow:imageWidth="100dip"
+ coverflow:reflectionGap="2dip"
+ coverflow:withReflection="true" />
<ImageView
android:id="@+id/presentation_handle"
@@ -25,12 +30,12 @@
android:src="@drawable/handle" />
<ScrollView
- android:id="@+id/scrollView1"
+ android:id="@+id/presentation_scrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<WebView
- android:id="@+id/textView1"
+ android:id="@+id/presentation_notes"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</ScrollView>
diff --git a/android/sdremote/res/values/attrs.xml b/android/sdremote/res/values/attrs.xml
new file mode 100644
index 000000000000..43de3bc8e95f
--- /dev/null
+++ b/android/sdremote/res/values/attrs.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <declare-styleable name="CoverFlow">
+ <attr name="imageWidth" format="dimension" />
+ <attr name="imageHeight" format="dimension" />
+ <attr name="withReflection" format="boolean" />
+ <attr name="reflectionGap" format="dimension" />
+ <attr name="imageReflectionRatio" format="float" />
+ </declare-styleable>
+</resources> \ No newline at end of file
diff --git a/android/sdremote/src/org/libreoffice/impressremote/PresentationActivity.java b/android/sdremote/src/org/libreoffice/impressremote/PresentationActivity.java
index e06d2b5a3e1b..88066101a588 100644
--- a/android/sdremote/src/org/libreoffice/impressremote/PresentationActivity.java
+++ b/android/sdremote/src/org/libreoffice/impressremote/PresentationActivity.java
@@ -69,8 +69,10 @@ public class PresentationActivity extends Activity {
mCommunicationService = ((CommunicationService.CBinder) aService)
.getService();
mCommunicationService.setActivityMessenger(mMessenger);
+
+ mPresentationFragment
+ .setCommunicationService(mCommunicationService);
mThumbnailFragment.setCommunicationService(mCommunicationService);
- // TODO: add mCommunicationSercie to all fragments.
}
@@ -85,6 +87,7 @@ public class PresentationActivity extends Activity {
protected class MessageHandler extends Handler {
@Override
public void handleMessage(Message aMessage) {
+ mPresentationFragment.handleMessage(aMessage);
mThumbnailFragment.handleMessage(aMessage);
// Bundle aData = aMessage.getData();
// TODO: pass to fragments
diff --git a/android/sdremote/src/org/libreoffice/impressremote/PresentationFragment.java b/android/sdremote/src/org/libreoffice/impressremote/PresentationFragment.java
index c031986d25ee..28ba8d0fb031 100644
--- a/android/sdremote/src/org/libreoffice/impressremote/PresentationFragment.java
+++ b/android/sdremote/src/org/libreoffice/impressremote/PresentationFragment.java
@@ -1,8 +1,15 @@
package org.libreoffice.impressremote;
+import org.libreoffice.impressremote.communication.CommunicationService;
+import org.libreoffice.impressremote.communication.SlideShow;
+
+import pl.polidea.coverflow.AbstractCoverFlowImageAdapter;
+import pl.polidea.coverflow.CoverFlow;
import android.app.Fragment;
import android.content.Context;
+import android.graphics.Bitmap;
import android.os.Bundle;
+import android.os.Message;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -10,16 +17,20 @@ import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.webkit.WebView;
+import android.widget.AdapterView;
import android.widget.ImageView;
public class PresentationFragment extends Fragment {
- private View mTopView;
+ private CoverFlow mTopView;
private ImageView mHandle;
private View mLayout;
-
+ private WebView mNotes;
private Context mContext;
+ private CommunicationService mCommunicationService;
+ private SlideShow mSlideShow;
+
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mContext = container.getContext();
@@ -27,20 +38,25 @@ public class PresentationFragment extends Fragment {
View v = inflater.inflate(R.layout.fragment_presentation, container,
false);
- WebView mWebView = (WebView) v.findViewById(R.id.textView1);
+ mNotes = (WebView) v.findViewById(R.id.presentation_notes);
String summary = "<html><body>This is just a test<br/><ul><li>And item</li><li>And again</li></ul>More text<br/>Blabla<br/>Blabla<br/>blabla<br/>Blabla</body></html>";
- mWebView.loadData(summary, "text/html", null);
+ mNotes.loadData(summary, "text/html", null);
- mTopView = v.findViewById(R.id.imageView1);
+ mTopView = (CoverFlow) v.findViewById(R.id.presentation_coverflow);
mLayout = v.findViewById(R.id.presentation_layout);
mHandle = (ImageView) v.findViewById(R.id.presentation_handle);
mHandle.setOnTouchListener(new SizeListener());
+ if (mCommunicationService != null && mSlideShow != null) {
+ mTopView.setAdapter(new ThumbnailAdapter(mContext, mSlideShow));
+ }
+
return v;
}
+ // -------------------------------------------------- RESIZING LISTENER ----
private class SizeListener implements OnTouchListener {
@Override
@@ -75,4 +91,90 @@ public class PresentationFragment extends Fragment {
return true;
}
}
+
+ // ----------------------------------------------------- CLICK LISTENER ----
+ protected class ClickListener implements AdapterView.OnItemClickListener {
+ public void onItemClick(AdapterView<?> parent, View v, int position,
+ long id) {
+ if (mCommunicationService != null)
+ mCommunicationService.getTransmitter().gotoSlide(position);
+ }
+ }
+
+ // ---------------------------------------------------- MESSAGE HANDLER ----
+ public void setCommunicationService(
+ CommunicationService aCommunicationService) {
+ mCommunicationService = aCommunicationService;
+ mSlideShow = mCommunicationService.getSlideShow();
+ if (mTopView != null) {
+ mTopView.setAdapter(new ThumbnailAdapter(mContext, mSlideShow));
+ }
+ }
+
+ public void handleMessage(Message aMessage) {
+ Bundle aData = aMessage.getData();
+ switch (aMessage.what) {
+ case CommunicationService.MSG_SLIDE_CHANGED:
+ int aSlide = aData.getInt("slide_number");
+ mTopView.setSelection(aSlide, true);
+ break;
+ case CommunicationService.MSG_SLIDE_PREVIEW:
+ int aNSlide = aData.getInt("slide_number");
+ if (mTopView.getSelectedItemPosition() == aNSlide) {
+ // mTopView. // TODO: update the current item
+ }
+ break;
+
+ }
+ }
+
+ // ------------------------------------------------- THUMBNAIL ADAPTER ----
+ protected class ThumbnailAdapter extends AbstractCoverFlowImageAdapter {
+
+ private Context mContext;
+
+ private SlideShow mSlideShow;
+
+ public ThumbnailAdapter(Context aContext, SlideShow aSlideShow) {
+ mContext = aContext;
+ mSlideShow = aSlideShow;
+ }
+
+ @Override
+ public int getCount() {
+ return mSlideShow.getSize();
+ }
+
+ // @Override
+ // public View getView(int position, View convertView, ViewGroup parent)
+ // {
+ // LayoutInflater aInflater = (LayoutInflater) mContext
+ // .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ // View v = aInflater.inflate(R.layout.slide_thumbnail, null);
+ //
+ // ImageView aImage = (ImageView) v.findViewById(R.id.sub_thumbnail);
+ // TextView aText = (TextView) v.findViewById(R.id.sub_number);
+ //
+ // // Do the image & number styling
+ // int aBorderWidth = getResources().getInteger(
+ // R.integer.thumbnail_border_width);
+ // aImage.setPadding(aBorderWidth, aBorderWidth, aBorderWidth,
+ // aBorderWidth);
+ //
+ // Bitmap aBitmap = mSlideShow.getImage(position);
+ // if (aBitmap != null) {
+ // aImage.setImageBitmap(aBitmap);
+ // }
+ //
+ // aText.setText(String.valueOf(position + 1));
+ //
+ // return v;
+ // }
+
+ @Override
+ protected Bitmap createBitmap(int position) {
+ Bitmap aBitmap = mSlideShow.getImage(position);
+ return aBitmap;
+ }
+ }
}
diff --git a/android/sdremote/src/org/libreoffice/impressremote/ThumbnailFragment.java b/android/sdremote/src/org/libreoffice/impressremote/ThumbnailFragment.java
index b1ed882a4493..de07603a2fec 100644
--- a/android/sdremote/src/org/libreoffice/impressremote/ThumbnailFragment.java
+++ b/android/sdremote/src/org/libreoffice/impressremote/ThumbnailFragment.java
@@ -101,8 +101,6 @@ public class ThumbnailFragment extends Fragment {
}
}
- // ------------------------------------------------- SERVICE CONNECTION ----
-
// ----------------------------------------------------- CLICK LISTENER ----
protected class ClickListener implements AdapterView.OnItemClickListener {
public void onItemClick(AdapterView<?> parent, View v, int position,
diff --git a/android/sdremote/src/org/libreoffice/impressremote/communication/Client.java b/android/sdremote/src/org/libreoffice/impressremote/communication/Client.java
index b3209b802972..bb76df9aef6b 100644
--- a/android/sdremote/src/org/libreoffice/impressremote/communication/Client.java
+++ b/android/sdremote/src/org/libreoffice/impressremote/communication/Client.java
@@ -60,7 +60,6 @@ public abstract class Client {
String aTemp;
// read until empty line
while ((aTemp = aReader.readLine()).length() != 0) {
- System.out.println("Read:" + aTemp);
aList.add(aTemp);
}
mReceiver.parseCommand(aList);
diff --git a/android/sdremote/src/pl/polidea/coverflow/AbstractCoverFlowImageAdapter.java b/android/sdremote/src/pl/polidea/coverflow/AbstractCoverFlowImageAdapter.java
new file mode 100644
index 000000000000..f60975fa156a
--- /dev/null
+++ b/android/sdremote/src/pl/polidea/coverflow/AbstractCoverFlowImageAdapter.java
@@ -0,0 +1,125 @@
+package pl.polidea.coverflow;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+
+/**
+ * This class is an adapter that provides base, abstract class for images
+ * adapter.
+ *
+ */
+public abstract class AbstractCoverFlowImageAdapter extends BaseAdapter {
+
+ /** The Constant TAG. */
+ private static final String TAG = AbstractCoverFlowImageAdapter.class
+ .getSimpleName();
+
+ /** The width. */
+ private float width = 0;
+
+ /** The height. */
+ private float height = 0;
+
+ /** The bitmap map. */
+ private final Map<Integer, WeakReference<Bitmap>> bitmapMap = new HashMap<Integer, WeakReference<Bitmap>>();
+
+ public AbstractCoverFlowImageAdapter() {
+ super();
+ }
+
+ /**
+ * Set width for all pictures.
+ *
+ * @param width
+ * picture height
+ */
+ public synchronized void setWidth(final float width) {
+ this.width = width;
+ }
+
+ /**
+ * Set height for all pictures.
+ *
+ * @param height
+ * picture height
+ */
+ public synchronized void setHeight(final float height) {
+ this.height = height;
+ }
+
+ @Override
+ public final Bitmap getItem(final int position) {
+ final WeakReference<Bitmap> weakBitmapReference = bitmapMap
+ .get(position);
+ if (weakBitmapReference != null) {
+ final Bitmap bitmap = weakBitmapReference.get();
+ if (bitmap == null) {
+ Log.v(TAG, "Empty bitmap reference at position: " + position
+ + ":" + this);
+ } else {
+ Log.v(TAG, "Reusing bitmap item at position: " + position + ":"
+ + this);
+ return bitmap;
+ }
+ }
+ Log.v(TAG, "Creating item at position: " + position + ":" + this);
+ final Bitmap bitmap = createBitmap(position);
+ bitmapMap.put(position, new WeakReference<Bitmap>(bitmap));
+ Log.v(TAG, "Created item at position: " + position + ":" + this);
+ return bitmap;
+ }
+
+ /**
+ * Creates new bitmap for the position specified.
+ *
+ * @param position
+ * position
+ * @return Bitmap created
+ */
+ protected abstract Bitmap createBitmap(int position);
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.widget.Adapter#getItemId(int)
+ */
+ @Override
+ public final synchronized long getItemId(final int position) {
+ return position;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.widget.Adapter#getView(int, android.view.View,
+ * android.view.ViewGroup)
+ */
+ @Override
+ public final synchronized ImageView getView(final int position,
+ final View convertView, final ViewGroup parent) {
+ ImageView imageView;
+ if (convertView == null) {
+ final Context context = parent.getContext();
+ Log.v(TAG, "Creating Image view at position: " + position + ":"
+ + this);
+ imageView = new ImageView(context);
+ imageView.setLayoutParams(new CoverFlow.LayoutParams((int) width,
+ (int) height));
+ } else {
+ Log.v(TAG, "Reusing view at position: " + position + ":" + this);
+ imageView = (ImageView) convertView;
+ }
+ imageView.setImageBitmap(getItem(position));
+ return imageView;
+ }
+
+}
diff --git a/android/sdremote/src/pl/polidea/coverflow/CoverFlow.java b/android/sdremote/src/pl/polidea/coverflow/CoverFlow.java
new file mode 100644
index 000000000000..09da2a09f3f0
--- /dev/null
+++ b/android/sdremote/src/pl/polidea/coverflow/CoverFlow.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2010 Neil Davies
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This code is base on the Android Gallery widget and was Created
+ * by Neil Davies neild001 'at' gmail dot com to be a Coverflow widget
+ *
+ * @author Neil Davies
+ */
+package pl.polidea.coverflow;
+
+import org.libreoffice.impressremote.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Camera;
+import android.graphics.Matrix;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.Transformation;
+import android.widget.Gallery;
+import android.widget.ImageView;
+import android.widget.SpinnerAdapter;
+
+/**
+ * Cover Flow implementation.
+ *
+ */
+public class CoverFlow extends Gallery {
+
+ /**
+ * Graphics Camera used for transforming the matrix of ImageViews.
+ */
+ private final Camera mCamera = new Camera();
+
+ /**
+ * The maximum angle the Child ImageView will be rotated by.
+ */
+ private int mMaxRotationAngle = 60;
+
+ /**
+ * The maximum zoom on the centre Child.
+ */
+ private int mMaxZoom = -120;
+
+ /**
+ * The Centre of the Coverflow.
+ */
+ private int mCoveflowCenter;
+
+ /** The image height. */
+ private float imageHeight;
+
+ /** The image width. */
+ private float imageWidth;
+
+ /** The reflection gap. */
+ private float reflectionGap;
+
+ /** The with reflection. */
+ private boolean withReflection;
+
+ /** The image reflection ratio. */
+ private float imageReflectionRatio;
+
+ /**
+ * Gets the image height.
+ *
+ * @return the image height
+ */
+ public float getImageHeight() {
+ return imageHeight;
+ }
+
+ /**
+ * Sets the image height.
+ *
+ * @param imageHeight
+ * the new image height
+ */
+ public void setImageHeight(final float imageHeight) {
+ this.imageHeight = imageHeight;
+ }
+
+ /**
+ * Gets the image width.
+ *
+ * @return the image width
+ */
+ public float getImageWidth() {
+ return imageWidth;
+ }
+
+ /**
+ * Sets the image width.
+ *
+ * @param imageWidth
+ * the new image width
+ */
+ public void setImageWidth(final float imageWidth) {
+ this.imageWidth = imageWidth;
+ }
+
+ /**
+ * Gets the reflection gap.
+ *
+ * @return the reflection gap
+ */
+ public float getReflectionGap() {
+ return reflectionGap;
+ }
+
+ /**
+ * Sets the reflection gap.
+ *
+ * @param reflectionGap
+ * the new reflection gap
+ */
+ public void setReflectionGap(final float reflectionGap) {
+ this.reflectionGap = reflectionGap;
+ }
+
+ /**
+ * Checks if is with reflection.
+ *
+ * @return true, if is with reflection
+ */
+ public boolean isWithReflection() {
+ return withReflection;
+ }
+
+ /**
+ * Sets the with reflection.
+ *
+ * @param withReflection
+ * the new with reflection
+ */
+ public void setWithReflection(final boolean withReflection) {
+ this.withReflection = withReflection;
+ }
+
+ /**
+ * Sets the image reflection ratio.
+ *
+ * @param imageReflectionRatio
+ * the new image reflection ratio
+ */
+ public void setImageReflectionRatio(final float imageReflectionRatio) {
+ this.imageReflectionRatio = imageReflectionRatio;
+ }
+
+ /**
+ * Gets the image reflection ratio.
+ *
+ * @return the image reflection ratio
+ */
+ public float getImageReflectionRatio() {
+ return imageReflectionRatio;
+ }
+
+ public CoverFlow(final Context context) {
+ super(context);
+ this.setStaticTransformationsEnabled(true);
+ }
+
+ public CoverFlow(final Context context, final AttributeSet attrs) {
+ this(context, attrs, android.R.attr.galleryStyle);
+ }
+
+ public CoverFlow(final Context context, final AttributeSet attrs,
+ final int defStyle) {
+ super(context, attrs, defStyle);
+ parseAttributes(context, attrs);
+ this.setStaticTransformationsEnabled(true);
+ }
+
+ /**
+ * Get the max rotational angle of the image.
+ *
+ * @return the mMaxRotationAngle
+ */
+ public int getMaxRotationAngle() {
+ return mMaxRotationAngle;
+ }
+
+ /**
+ * Sets the.
+ *
+ * @param adapter
+ * the new adapter
+ */
+ @Override
+ public void setAdapter(final SpinnerAdapter adapter) {
+ if (!(adapter instanceof AbstractCoverFlowImageAdapter)) {
+ throw new IllegalArgumentException(
+ "The adapter should derive from "
+ + AbstractCoverFlowImageAdapter.class
+ .getName());
+ }
+ final AbstractCoverFlowImageAdapter coverAdapter = (AbstractCoverFlowImageAdapter) adapter;
+ coverAdapter.setWidth(imageWidth);
+ coverAdapter.setHeight(imageHeight);
+ if (withReflection) {
+ final ReflectingImageAdapter reflectAdapter = new ReflectingImageAdapter(
+ coverAdapter);
+ reflectAdapter.setReflectionGap(reflectionGap);
+ reflectAdapter.setWidthRatio(imageReflectionRatio);
+ reflectAdapter.setWidth(imageWidth);
+ reflectAdapter.setHeight(imageHeight * (1 + imageReflectionRatio));
+ super.setAdapter(reflectAdapter);
+ } else {
+ super.setAdapter(adapter);
+ }
+ }
+
+ /**
+ * Set the max rotational angle of each image.
+ *
+ * @param maxRotationAngle
+ * the mMaxRotationAngle to set
+ */
+ public void setMaxRotationAngle(final int maxRotationAngle) {
+ mMaxRotationAngle = maxRotationAngle;
+ }
+
+ /**
+ * Get the Max zoom of the centre image.
+ *
+ * @return the mMaxZoom
+ */
+ public int getMaxZoom() {
+ return mMaxZoom;
+ }
+
+ /**
+ * Set the max zoom of the centre image.
+ *
+ * @param maxZoom
+ * the mMaxZoom to set
+ */
+ public void setMaxZoom(final int maxZoom) {
+ mMaxZoom = maxZoom;
+ }
+
+ /**
+ * Get the Centre of the Coverflow.
+ *
+ * @return The centre of this Coverflow.
+ */
+ private int getCenterOfCoverflow() {
+ return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2
+ + getPaddingLeft();
+ }
+
+ /**
+ * Get the Centre of the View.
+ *
+ * @return The centre of the given view.
+ */
+ private static int getCenterOfView(final View view) {
+ return view.getLeft() + view.getWidth() / 2;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see #setStaticTransformationsEnabled(boolean)
+ */
+ @Override
+ protected boolean getChildStaticTransformation(final View child,
+ final Transformation t) {
+
+ final int childCenter = getCenterOfView(child);
+ final int childWidth = child.getWidth();
+ int rotationAngle = 0;
+
+ t.clear();
+ t.setTransformationType(Transformation.TYPE_MATRIX);
+
+ if (childCenter == mCoveflowCenter) {
+ transformImageBitmap((ImageView) child, t, 0);
+ } else {
+ rotationAngle = (int) ((float) (mCoveflowCenter - childCenter)
+ / childWidth * mMaxRotationAngle);
+ if (Math.abs(rotationAngle) > mMaxRotationAngle) {
+ rotationAngle = rotationAngle < 0 ? -mMaxRotationAngle
+ : mMaxRotationAngle;
+ }
+ transformImageBitmap((ImageView) child, t, rotationAngle);
+ }
+
+ return true;
+ }
+
+ /**
+ * This is called during layout when the size of this view has changed. If
+ * you were just added to the view hierarchy, you're called with the old
+ * values of 0.
+ *
+ * @param w
+ * Current width of this view.
+ * @param h
+ * Current height of this view.
+ * @param oldw
+ * Old width of this view.
+ * @param oldh
+ * Old height of this view.
+ */
+ @Override
+ protected void onSizeChanged(final int w, final int h, final int oldw,
+ final int oldh) {
+ mCoveflowCenter = getCenterOfCoverflow();
+ super.onSizeChanged(w, h, oldw, oldh);
+ }
+
+ /**
+ * Transform the Image Bitmap by the Angle passed.
+ *
+ * @param imageView
+ * ImageView the ImageView whose bitmap we want to rotate
+ * @param t
+ * transformation
+ * @param rotationAngle
+ * the Angle by which to rotate the Bitmap
+ */
+ private void transformImageBitmap(final ImageView child,
+ final Transformation t, final int rotationAngle) {
+ mCamera.save();
+ final Matrix imageMatrix = t.getMatrix();
+
+ final int height = child.getLayoutParams().height;
+
+ final int width = child.getLayoutParams().width;
+ final int rotation = Math.abs(rotationAngle);
+
+ mCamera.translate(0.0f, 0.0f, 100.0f);
+
+ // As the angle of the view gets less, zoom in
+ if (rotation < mMaxRotationAngle) {
+ final float zoomAmount = (float) (mMaxZoom + rotation * 1.5);
+ mCamera.translate(0.0f, 0.0f, zoomAmount);
+ }
+
+ mCamera.rotateY(rotationAngle);
+ mCamera.getMatrix(imageMatrix);
+ imageMatrix.preTranslate(-(width / 2.0f), -(height / 2.0f));
+ imageMatrix.postTranslate((width / 2.0f), (height / 2.0f));
+ mCamera.restore();
+ }
+
+ /**
+ * Parses the attributes.
+ *
+ * @param context
+ * the context
+ * @param attrs
+ * the attrs
+ */
+ private void parseAttributes(final Context context, final AttributeSet attrs) {
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.CoverFlow);
+ try {
+ imageWidth = a.getDimension(R.styleable.CoverFlow_imageWidth, 480);
+ imageHeight = a.getDimension(R.styleable.CoverFlow_imageHeight, 320);
+ withReflection = a.getBoolean(R.styleable.CoverFlow_withReflection,
+ false);
+ imageReflectionRatio = a.getFloat(
+ R.styleable.CoverFlow_imageReflectionRatio, 0.2f);
+ reflectionGap = a.getDimension(R.styleable.CoverFlow_reflectionGap,
+ 4);
+ setSpacing(-15);
+ } finally {
+ a.recycle();
+ }
+ }
+
+} \ No newline at end of file
diff --git a/android/sdremote/src/pl/polidea/coverflow/ReflectingImageAdapter.java b/android/sdremote/src/pl/polidea/coverflow/ReflectingImageAdapter.java
new file mode 100644
index 000000000000..ff044753039f
--- /dev/null
+++ b/android/sdremote/src/pl/polidea/coverflow/ReflectingImageAdapter.java
@@ -0,0 +1,129 @@
+package pl.polidea.coverflow;
+
+import android.R.color;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Shader.TileMode;
+
+/**
+ * This adapter provides reflected images from linked adapter.
+ *
+ * @author potiuk
+ *
+ */
+public class ReflectingImageAdapter extends AbstractCoverFlowImageAdapter {
+
+ /** The linked adapter. */
+ private final AbstractCoverFlowImageAdapter linkedAdapter;
+ /**
+ * Gap between the image and its reflection.
+ */
+ private float reflectionGap;
+
+ /** The image reflection ratio. */
+ private float imageReflectionRatio;
+
+ /**
+ * Sets the width ratio.
+ *
+ * @param imageReflectionRatio
+ * the new width ratio
+ */
+ public void setWidthRatio(final float imageReflectionRatio) {
+ this.imageReflectionRatio = imageReflectionRatio;
+ }
+
+ /**
+ * Creates reflecting adapter.
+ *
+ * @param linkedAdapter
+ * adapter that provides images to get reflections
+ */
+ public ReflectingImageAdapter(
+ final AbstractCoverFlowImageAdapter linkedAdapter) {
+ super();
+ this.linkedAdapter = linkedAdapter;
+ }
+
+ /**
+ * Sets the reflection gap.
+ *
+ * @param reflectionGap
+ * the new reflection gap
+ */
+ public void setReflectionGap(final float reflectionGap) {
+ this.reflectionGap = reflectionGap;
+ }
+
+ /**
+ * Gets the reflection gap.
+ *
+ * @return the reflection gap
+ */
+ public float getReflectionGap() {
+ return reflectionGap;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see pl.polidea.coverflow.AbstractCoverFlowImageAdapter#createBitmap(int)
+ */
+ @Override
+ protected Bitmap createBitmap(final int position) {
+ return createReflectedImages(linkedAdapter.getItem(position));
+ }
+
+ /**
+ * Creates the reflected images.
+ *
+ * @param originalImage
+ * the original image
+ * @return true, if successful
+ */
+ public Bitmap createReflectedImages(final Bitmap originalImage) {
+ final int width = originalImage.getWidth();
+ final int height = originalImage.getHeight();
+ final Matrix matrix = new Matrix();
+ matrix.preScale(1, -1);
+ final Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0,
+ (int) (height * imageReflectionRatio), width,
+ (int) (height - height * imageReflectionRatio), matrix,
+ false);
+ final Bitmap bitmapWithReflection = Bitmap.createBitmap(width,
+ (int) (height + height * imageReflectionRatio),
+ Config.ARGB_8888);
+ final Canvas canvas = new Canvas(bitmapWithReflection);
+ canvas.drawBitmap(originalImage, 0, 0, null);
+ final Paint deafaultPaint = new Paint();
+ deafaultPaint.setColor(color.transparent);
+ canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null);
+ final Paint paint = new Paint();
+ final LinearGradient shader = new LinearGradient(0,
+ originalImage.getHeight(), 0,
+ bitmapWithReflection.getHeight() + reflectionGap,
+ 0x70ffffff, 0x00ffffff, TileMode.CLAMP);
+ paint.setShader(shader);
+ paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
+ canvas.drawRect(0, height, width, bitmapWithReflection.getHeight()
+ + reflectionGap, paint);
+ return bitmapWithReflection;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.widget.Adapter#getCount()
+ */
+ @Override
+ public int getCount() {
+ return linkedAdapter.getCount();
+ }
+
+}
diff --git a/android/sdremote/src/pl/polidea/coverflow/ResourceImageAdapter.java b/android/sdremote/src/pl/polidea/coverflow/ResourceImageAdapter.java
new file mode 100644
index 000000000000..469d0d165b0b
--- /dev/null
+++ b/android/sdremote/src/pl/polidea/coverflow/ResourceImageAdapter.java
@@ -0,0 +1,90 @@
+package pl.polidea.coverflow;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.util.Log;
+
+/**
+ * This class is an adapter that provides images from a fixed set of resource
+ * ids. Bitmaps and ImageViews are kept as weak references so that they can be
+ * cleared by garbage collection when not needed.
+ *
+ */
+public class ResourceImageAdapter extends AbstractCoverFlowImageAdapter {
+
+ /** The Constant TAG. */
+ private static final String TAG = ResourceImageAdapter.class
+ .getSimpleName();
+
+ /** The Constant DEFAULT_LIST_SIZE. */
+ private static final int DEFAULT_LIST_SIZE = 20;
+
+ /** The Constant IMAGE_RESOURCE_IDS. */
+ private static final List<Integer> IMAGE_RESOURCE_IDS = new ArrayList<Integer>(
+ DEFAULT_LIST_SIZE);
+
+ /** The Constant DEFAULT_RESOURCE_LIST. */
+ private static final int[] DEFAULT_RESOURCE_LIST = {};
+ /** The bitmap map. */
+ private final Map<Integer, WeakReference<Bitmap>> bitmapMap = new HashMap<Integer, WeakReference<Bitmap>>();
+
+ private final Context context;
+
+ /**
+ * Creates the adapter with default set of resource images.
+ *
+ * @param context
+ * context
+ */
+ public ResourceImageAdapter(final Context context) {
+ super();
+ this.context = context;
+ setResources(DEFAULT_RESOURCE_LIST);
+ }
+
+ /**
+ * Replaces resources with those specified.
+ *
+ * @param resourceIds
+ * array of ids of resources.
+ */
+ public final synchronized void setResources(final int[] resourceIds) {
+ IMAGE_RESOURCE_IDS.clear();
+ for (final int resourceId : resourceIds) {
+ IMAGE_RESOURCE_IDS.add(resourceId);
+ }
+ notifyDataSetChanged();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.widget.Adapter#getCount()
+ */
+ @Override
+ public synchronized int getCount() {
+ return IMAGE_RESOURCE_IDS.size();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see pl.polidea.coverflow.AbstractCoverFlowImageAdapter#createBitmap(int)
+ */
+ @Override
+ protected Bitmap createBitmap(final int position) {
+ Log.v(TAG, "creating item " + position);
+ final Bitmap bitmap = ((BitmapDrawable) context.getResources()
+ .getDrawable(IMAGE_RESOURCE_IDS.get(position)))
+ .getBitmap();
+ bitmapMap.put(position, new WeakReference<Bitmap>(bitmap));
+ return bitmap;
+ }
+} \ No newline at end of file