Implemented Thumbnail activity.

Change-Id: I I6de8651f3ed36798ca1cc69765c448b821a819a2
This commit is contained in:
Andrzej J. R. Hunt 2012-07-20 17:50:10 +02:00 committed by Michael Meeks
parent 0aa7aa85a4
commit 6794e94bfd
14 changed files with 554 additions and 37 deletions

View file

@ -22,6 +22,7 @@
<service android:name=".communication.CommunicationService" >
</service>
<activity android:name=".ThumbnailActivity"></activity>
</application>
</manifest>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/thumbnail_grid"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:columnWidth="90dp"
android:gravity="center"
android:horizontalSpacing="10dp"
android:numColumns="auto_fit"
android:stretchMode="columnWidth"
android:verticalSpacing="10dp" >
</GridView>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView
android:id="@+id/sub_thumbnail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@drawable/ic_launcher" />
<TextView
android:id="@+id/sub_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="0" />
</LinearLayout>

View file

@ -25,6 +25,13 @@
android:layout_gravity="center_horizontal"
android:text="Previous" />
<Button
android:id="@+id/button_thumbnail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Thumbnail View" />
<ImageView
android:id="@+id/image_preview"
android:layout_width="wrap_content"

View file

@ -52,6 +52,13 @@ public class TestClient extends Activity {
protected void onPause() {
super.onPause();
doUnbindService();
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
// mCommunicationService.disconnect();
stopService(new Intent(this, CommunicationService.class));
}
@ -75,13 +82,14 @@ public class TestClient extends Activity {
};
void doBindService() {
bindService(new Intent(this, CommunicationService.class), mConnection,
Context.BIND_AUTO_CREATE);
Intent aIntent = new Intent(this, CommunicationService.class);
startService(aIntent);
bindService(aIntent, mConnection,
Context.BIND_IMPORTANT);
mIsBound = true;
}
void doUnbindService() {
mCommunicationService.disconnect();
mCommunicationService.setActivityMessenger(null);
if (mIsBound) {
unbindService(mConnection);
@ -125,6 +133,16 @@ public class TestClient extends Activity {
});
Button mThumbnailButton = (Button) findViewById(R.id.button_thumbnail);
mThumbnailButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent aIntent = new Intent(TestClient.this, ThumbnailActivity.class);
startActivity( aIntent);
}
});
}
private void enableButtons(boolean aEnabled) {
@ -146,7 +164,7 @@ public class TestClient extends Activity {
int aSlideNumber = aData.getInt("slide_number");
if ( mCurrentPreviewImageMissing ) {
Bitmap aImage = mCommunicationService
.getPreviewImage(aSlideNumber);
.getSlideShow().getImage(aSlideNumber);
if (aImage != null) {
mImageView.setImageBitmap(aImage);
mCurrentPreviewImageMissing = false;

View file

@ -0,0 +1,185 @@
package org.libreoffice.impressremote;
import org.libreoffice.impressremote.TestClient.MessageHandler;
import org.libreoffice.impressremote.communication.CommunicationService;
import org.libreoffice.impressremote.communication.SlideShow;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
public class ThumbnailActivity extends Activity {
private CommunicationService mCommunicationService;
private boolean mIsBound = false;
private GridView mGrid;
private ImageView mCurrentImage;
private TextView mCurrentText;
private SlideShow mSlideShow;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thumbnail);
bindService(new Intent(this, CommunicationService.class), mConnection,
Context.BIND_ADJUST_WITH_ACTIVITY);
mIsBound = true;
mGrid = (GridView) findViewById(R.id.thumbnail_grid);
mGrid.setOnItemClickListener(new ClickListener());
}
@Override
protected void onPause() {
super.onPause();
mCommunicationService.setActivityMessenger(null);
if (mIsBound) {
unbindService(mConnection);
mIsBound = false;
}
}
private void setSelected(int position) {
if (mCurrentImage != null) {
mCurrentImage.setPadding(0, 0, 0, 0);
}
if (mCurrentText != null) {
mCurrentText.setTypeface(Typeface.create(
mCurrentText.getTypeface(), Typeface.NORMAL));
}
View aV = mGrid.getChildAt(position);
if (aV != null) {
mCurrentImage = (ImageView) aV.findViewById(R.id.sub_thumbnail);
mCurrentText = (TextView) aV.findViewById(R.id.sub_number);
mCurrentImage.setBackgroundColor(Color.RED);
mCurrentImage.setPadding(2, 2, 2, 2);
mCurrentText.setTypeface(Typeface.create(mCurrentText.getTypeface(),
Typeface.BOLD));
}
}
// ------------------------------------------------- SERVICE CONNECTION ----
final Messenger mMessenger = new Messenger(new MessageHandler());
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName aClassName,
IBinder aService) {
mCommunicationService = ((CommunicationService.CBinder) aService)
.getService();
mCommunicationService.setActivityMessenger(mMessenger);
mSlideShow = mCommunicationService.getSlideShow();
mGrid.setAdapter(new ThumbnailAdapter(ThumbnailActivity.this,
mSlideShow));
}
@Override
public void onServiceDisconnected(ComponentName aClassName) {
mCommunicationService = null;
}
};
// ----------------------------------------------------- CLICK LISTENER ----
protected class ClickListener implements AdapterView.OnItemClickListener {
public void onItemClick(AdapterView<?> parent, View v, int position,
long id) {
mCommunicationService.getTransmitter().gotoSlide(position);
}
}
// ---------------------------------------------------- MESSAGE HANDLER ----
protected class MessageHandler extends Handler {
@Override
public void handleMessage(Message aMessage) {
Bundle aData = aMessage.getData();
switch (aMessage.what) {
case CommunicationService.MSG_SLIDE_CHANGED:
int aSlide = aData.getInt("slide_number");
setSelected(aSlide);
break;
case CommunicationService.MSG_SLIDE_PREVIEW:
// int aNSlide = aData.getInt("slide_number");
mGrid.invalidateViews();
break;
}
}
}
// ------------------------------------------------- THUMBNAIL ADAPTER ----
protected class ThumbnailAdapter extends BaseAdapter {
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 Object getItem(int arg0) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@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);
Bitmap aBitmap = mSlideShow.getImage(position);
if (aBitmap != null) {
aImage.setImageBitmap(aBitmap);
}
aText.setText(String.valueOf(position + 1));
// if ((mSlideShow != null)
// && (position == mSlideShow.getCurrentSlide())) {
// setSelected(position);
// }
return v;
}
}
}

View file

@ -25,8 +25,9 @@ public class CommunicationService extends Service {
NETWORK, BLUETOOTH
};
public static final int MSG_SLIDE_CHANGED = 1;
public static final int MSG_SLIDE_PREVIEW = 2;
public static final int MSG_SLIDESHOW_STARTED = 1;
public static final int MSG_SLIDE_CHANGED = 2;
public static final int MSG_SLIDE_PREVIEW = 3;
private Transmitter mTransmitter;
@ -74,8 +75,8 @@ public class CommunicationService extends Service {
mClient.closeConnection();
}
public Bitmap getPreviewImage(int aSlide) {
return mReceiver.getPreviewImage(aSlide);
public SlideShow getSlideShow() {
return mReceiver.getSlideShow();
}

View file

@ -23,54 +23,72 @@ public class Receiver {
private Messenger mActivityMessenger;
private SparseArray<byte[]> mPreviewImages = new SparseArray<byte[]>();
private SlideShow mSlideShow = null;
public SlideShow getSlideShow() {
return mSlideShow;
}
public void setActivityMessenger(Messenger aActivityMessenger) {
mActivityMessenger = aActivityMessenger;
}
public Bitmap getPreviewImage(int aSlide) {
byte[] aImage = mPreviewImages.get(aSlide);
return BitmapFactory.decodeByteArray(aImage, 0, aImage.length);
}
public void parseCommand(ArrayList<String> aCommand) {
System.out.println("parsing " + aCommand.get(0));
if (mActivityMessenger == null) {
return;
}
String aInstruction = aCommand.get(0);
if (aInstruction.equals("slide_updated")) {
int aSlideNumber = Integer.parseInt(aCommand.get(1));
if (aInstruction.equals("slideshow_started")) {
int aSlideShowlength = Integer.parseInt(aCommand.get(1));
mSlideShow = new SlideShow(aSlideShowlength);
Message aMessage = Message.obtain(null,
CommunicationService.MSG_SLIDE_CHANGED);
CommunicationService.MSG_SLIDESHOW_STARTED);
Bundle aData = new Bundle();
aData.putInt("slide_number", aSlideNumber);
aMessage.setData(aData);
try {
mActivityMessenger.send(aMessage);
} catch (RemoteException e) {
// Dead Handler -- i.e. Activity gone.
}
} else if (aInstruction.equals("slide_preview")) {
int aSlideNumber = Integer.parseInt(aCommand.get(1));
String aImageString = aCommand.get(2);
byte[] aImage = Base64.decode(aImageString, Base64.DEFAULT);
} else {
if (mSlideShow == null)
return;
// Store image internally
mPreviewImages.put(aSlideNumber, aImage);
if (aInstruction.equals("slide_updated")) {
// Notify the frontend
Message aMessage = Message.obtain(null,
CommunicationService.MSG_SLIDE_PREVIEW);
Bundle aData = new Bundle();
aData.putInt("slide_number", aSlideNumber);
aMessage.setData(aData);
try {
mActivityMessenger.send(aMessage);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
int aSlideNumber = Integer.parseInt(aCommand.get(1));
Message aMessage = Message.obtain(null,
CommunicationService.MSG_SLIDE_CHANGED);
Bundle aData = new Bundle();
aData.putInt("slide_number", aSlideNumber);
aMessage.setData(aData);
try {
mActivityMessenger.send(aMessage);
} catch (RemoteException e) {
// Dead Handler -- i.e. Activity gone.
}
} else if (aInstruction.equals("slide_preview")) {
int aSlideNumber = Integer.parseInt(aCommand.get(1));
String aImageString = aCommand.get(2);
byte[] aImage = Base64.decode(aImageString, Base64.DEFAULT);
// Store image internally
mSlideShow.putImage(aSlideNumber, aImage);
// Notify the frontend
Message aMessage = Message.obtain(null,
CommunicationService.MSG_SLIDE_PREVIEW);
Bundle aData = new Bundle();
aData.putInt("slide_number", aSlideNumber);
aMessage.setData(aData);
try {
mActivityMessenger.send(aMessage);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,37 @@
package org.libreoffice.impressremote.communication;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.SparseArray;
public class SlideShow {
private SparseArray<byte[]> mPreviewImages = new SparseArray<byte[]>();
private int mSize = 0;
private int mCurrentSlide = 0;
protected SlideShow(int aSize) {
mSize = aSize;
}
public int getSize() {
return mSize;
}
public int getCurrentSlide() {
return mCurrentSlide;
}
protected void setCurrentSlide(int aSlide) {
mCurrentSlide = aSlide;
}
protected void putImage(int aSlide, byte[] aImage) {
mPreviewImages.put(aSlide, aImage);
}
public Bitmap getImage(int aSlide) {
byte[] aImage = mPreviewImages.get(aSlide);
return BitmapFactory.decodeByteArray(aImage, 0, aImage.length);
}
}

View file

@ -0,0 +1,154 @@
/* -*- Mode: C++; 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/.
*/
#include "ImagePreparer.hxx"
#include <comphelper/processfactory.hxx>
#include <osl/file.hxx>
#include <xmlsec/base64.h>
#include <rtl/ustrbuf.hxx>
#include <sax/tools/converter.hxx>
#include <rtl/strbuf.hxx>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/document/XFilter.hpp>
#include <com/sun/star/document/XExporter.hpp>
using namespace sd;
using namespace rtl;
using namespace osl;
using namespace ::com::sun::star;
ImagePreparer::ImagePreparer(
const uno::Reference<presentation::XSlideShowController>& rxController,
Transmitter *aTransmitter, rtl::Reference<ImagePreparer>& rRef )
: Thread( "ImagePreparer Thread" ),
xController( rxController ),
pTransmitter( aTransmitter ),
mRef( rRef )
{
}
ImagePreparer::~ImagePreparer()
{
}
void ImagePreparer::execute()
{
fprintf( stderr, "ImagePreparer running\n" );
sal_uInt32 aSlides = xController->getSlideCount();
for ( sal_uInt32 i = 0; i < aSlides; i++ )
{
if ( !xController->isRunning() ) // stopped/disposed of.
{
break;
}
sendPreview( i );
}
fprintf( stderr, "ImagePreparer done\n" );
mRef.clear();
}
void ImagePreparer::sendPreview( sal_uInt32 aSlideNumber )
{
sal_uInt64 aSize;
uno::Sequence<sal_Int8> aImageData = preparePreview( aSlideNumber, 160, 120,
aSize );
if ( !xController->isRunning() )
return;
OUStringBuffer aStrBuffer;
::sax::Converter::encodeBase64( aStrBuffer, aImageData );
OString aEncodedShortString = OUStringToOString(
aStrBuffer.makeStringAndClear(), RTL_TEXTENCODING_UTF8 );
// Start the writing
OStringBuffer aBuffer;
aBuffer.append( "slide_preview\n" );
aBuffer.append( OString::valueOf( sal_Int32( aSlideNumber ) ).getStr() );
aBuffer.append( "\n" );
aBuffer.append( aEncodedShortString.getStr() );
aBuffer.append( "\n\n" );
pTransmitter->addMessage( aBuffer.makeStringAndClear(),
Transmitter::Priority::LOW );
}
uno::Sequence<sal_Int8> ImagePreparer::preparePreview(
sal_uInt32 aSlideNumber, sal_uInt32 aWidth, sal_uInt32 aHeight,
sal_uInt64 &rSize )
{
OUString aFileURL;
FileBase::createTempFile( 0, 0, &aFileURL );
uno::Reference< lang::XMultiServiceFactory > xServiceManager(
::comphelper::getProcessServiceFactory(),
uno::UNO_QUERY_THROW );
uno::Reference< document::XFilter > xFilter(
xServiceManager->createInstance(
"com.sun.star.drawing.GraphicExportFilter" ) ,
uno::UNO_QUERY_THROW );
uno::Reference< document::XExporter > xExporter( xFilter,
uno::UNO_QUERY_THROW );
if ( !xController->isRunning() )
return uno::Sequence<sal_Int8>();
uno::Reference< lang::XComponent > xSourceDoc(
xController->getSlideByIndex( aSlideNumber ),
uno::UNO_QUERY_THROW );
xExporter->setSourceDocument( xSourceDoc );
uno::Sequence< beans::PropertyValue > aFilterData(3);
aFilterData[0].Name = "PixelWidth";
aFilterData[0].Value <<= aWidth;
aFilterData[1].Name = "PixelHeight";
aFilterData[1].Value <<= aHeight;
aFilterData[2].Name = "ColorMode";
aFilterData[2].Value <<= sal_Int32(0); // 0: Color, 1: B&W
uno::Sequence< beans::PropertyValue > aProps(3);
aProps[0].Name = "MediaType";
aProps[0].Value <<= OUString( "image/png" );
aProps[1].Name = "URL";
aProps[1].Value <<= aFileURL;
aProps[2].Name = "FilterData";
aProps[2].Value <<= aFilterData;
xFilter->filter( aProps );
// FIXME: error handling.
File aFile( aFileURL );
aFile.open(0);
sal_uInt64 aRead;
rSize = 0;
aFile.getSize( rSize );
uno::Sequence<sal_Int8> aContents( rSize );
aFile.read( aContents.getArray(), rSize, aRead );
aFile.close();
File::remove( aFileURL );
return aContents;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View file

@ -0,0 +1,51 @@
/* -*- Mode: C++; 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/.
*/
#ifndef _SD_IMPRESSREMOTE_IMAGEPREPARER_HXX
#define _SD_IMPRESSREMOTE_IMAGEPREPARER_HXX
#include <salhelper/thread.hxx>
#include <rtl/ref.hxx>
#include <com/sun/star/presentation/XSlideShowController.hpp>
#include "Transmitter.hxx"
namespace css = ::com::sun::star;
namespace sd
{
class ImagePreparer:
public salhelper::Thread
{
public:
ImagePreparer( const
css::uno::Reference<css::presentation::XSlideShowController>&
rxController, sd::Transmitter *aTransmitter,
rtl::Reference<ImagePreparer>& rRef );
~ImagePreparer();
private:
css::uno::Reference<css::presentation::XSlideShowController> xController;
Transmitter *pTransmitter;
rtl::Reference<ImagePreparer>& mRef;
// Thread method
void execute();
void sendPreview( sal_uInt32 aSlideNumber );
css::uno::Sequence<sal_Int8> preparePreview( sal_uInt32 aSlideNumber,
sal_uInt32 aWidth, sal_uInt32 aHeight, sal_uInt64 &rSize );
};
}
#endif // _SD_IMPRESSREMOTE_IMAGEPREPARER_HXX
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View file

@ -48,6 +48,14 @@ void Listener::init( const css::uno::Reference< css::presentation::XSlideShowCon
aController->addSlideShowListener( this );
fprintf( stderr, "Registered listener.\n" );
sal_Int32 aSlides = aController->getSlideCount();
OStringBuffer aBuffer;
aBuffer.append( "slideshow_started\n" )
.append( OString::valueOf( aSlides ) ).append( "\n\n" );
pTransmitter->addMessage( aBuffer.makeStringAndClear(),
Transmitter::Priority::HIGH );
mPreparer.set( new ImagePreparer( aController, pTransmitter, mPreparer ) );
mPreparer->launch();
}

View file

@ -26,6 +26,10 @@
namespace css = ::com::sun::star;
namespace sd {
/**
* Slide show listener. This class can also be used for anything else that is
* specific to the lifetime of one slideshow while a client is connected.
*/
class Listener
: protected ::cppu::BaseMutex,
public ::cppu::WeakComponentImplHelper1< css::presentation::XSlideShowListener >

View file

@ -94,8 +94,7 @@ void Server::listenThread()
}
// TODO: deal with transmision errors gracefully.
fprintf( stderr, "done with transmitting\n" );
mListener->disposing();
mListener = NULL;
presentationStopped();
delete pTransmitter;
pTransmitter = NULL;