Blackberry JAVA DEVELOPMENT ENVIRONMENT User Manual

BlackBerry Java Development Environment
Multimedia Development Guide
BlackBerry Java Development Environment Multimedia Development Guide
Last modified: 14 July 2008
Part number: 13697641
At the time of publication, this documentation is based on the BlackBerry Java Development Environment version 4.3.1.
Send us your comments on product documentation: https://www.blackberry.com/DocsFeedback.
Bluetooth is a trademark of Bluetooth SIG. Java is a trademarks of Sun Microsystems, Inc. Plazmic is a trademark of Plazmic Inc. All other brands, product names, company names, trademarks and service marks are the properties of their respective owners. names, company names, trademarks and service marks are the properties of their respective owners.
The BlackBerry smartphone and other devices and/or associated software are protected by copyright, international treaties, and various patents, includin g one or more of the following U.S. pat ents: 6,278,442; 6,271,605; 6,219,694; 6,075,470; 6,073,318; D445,428; D433,460; D416,256. Other patents are registered or pending in the U.S. and in various countries around the world. Visit of RIM (as hereinafter defined) patents.
This documentation including all documentation incorporated by reference herein such as documentation provided or made available at
www.blackberry.com/go/docs is provided or made accessible "AS IS" and "AS AVAILABLE" and without condition, endorsement, guarantee,
representation, or warranty of any kind by Research In Motion Limited and its affiliated companies ("RIM") and RIM assumes no responsibility for any typographical, technical, or other inaccuracies, errors, or omissions in this documentation. In order to protect RIM proprietary and confidential information and/or trade secrets, this documentation may describe some aspects of RIM technology in generalized terms. RIM reserves the right to periodically change information that is contained in this documentation; however, RIM makes no commitment to provide any such changes, updates, enhancements, or other additions to this documentation to you in a timely manner or at all.
All other brands, product
www.rim.com/patents for a list
This documentation might contain references to third-party sources of information, hardware or software, products or services including components and content such as content protected by copyright and/or third-party web sites (collectively the "Third Party Products and Services"). RIM does not control, and is not responsible for, any Third Party Products and Services including, without limitation the content, accuracy, copyright compliance, compatibility, performance, trustworthiness, legality, decency, links, or any other aspect of Third Party Products and Services. The inclusion of a reference to Third Party Products and Services in this documentation does not imply endorsement by RIM of the Third Party Products and Services or the third party in any way.
EXCEPT TO THE EXTENT SPECIFICALLY PROHIBITED BY APPLICABLE LAW IN YOUR JURISDICTION, ALL CONDITIONS, ENDORSEMENTS, GUARANTEES, REPRESENTATIONS, OR WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY CONDITIONS, ENDORSEMENTS, GUARANTEES, REPRESENTATIONS OR WARRANTIES OF DURABILITY, FITNESS FOR A PARTICULAR PURPOSE OR USE, MERCHANTABILITY, MERCHANTABLE QUALITY, NON-INFRINGEMENT, SATISFACTORY QUALITY, OR TITLE, OR ARISING FROM A STATUTE OR CUSTOM OR A COURSE OF DEALING OR USAGE OF TRADE, OR RELATED TO THE DOCUMENTATION OR ITS USE, OR PERFORMANCE OR NON-PERFORMANCE OF ANY SOFTWARE, HARDWARE, SERVICE, OR ANY THIRD PARTY PRODUCTS AND SERVICES REFERENCED HEREIN, ARE HEREBY EXCLUDED. YOU MAY ALSO HAVE OTHER RIGHTS THAT VARY BY STATE OR PROVINCE. SOME JURISDICTIONS MAY NOT ALLOW THE EXCLUSION OR LIMITATION OF IMPLIED WARRANTIES AND CONDITIONS. TO THE EXTENT PERMITTED BY LAW, ANY IMPLIED WARRANTIES OR CONDITIONS RELATING TO THE DOCUMENTATION TO THE EXTENT THEY CANNOT BE EXCLUDED AS SET OUT ABOVE, BUT CAN BE LIMITED, ARE HEREBY LIMITED TO NINETY (90) DAYS FROM THE DATE YOU FIRST ACQUIRED THE DOCUMENTATION OR THE ITEM THAT IS THE SUBJECT OF THE CLAIM.
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW IN YOUR JURISDICTION, IN NO EVENT SHALL RIM BE LIABLE FOR ANY TYPE OF DAMAGES RELATED TO THIS DOCUMENTATION OR ITS USE, OR PERFORMANCE OR NON-PERFORMANCE OF ANY SOFTWARE, HARDWARE, SERVICE, OR ANY THIRD PARTY PRODUCTS AND SERVICES REFERENCED HEREIN INCLUDING WITHOUT LIMITATION ANY OF THE FOLLOWING DAMAGES: DIRECT, CONSEQUENTIAL, EXEMPLARY, INCIDENTAL, INDIRECT, SPECIAL, PUNITIVE, OR AGGRAVATED DAMAGES, DAMAGES FOR LOSS OF PROFITS OR REVENUES, FAILURE TO REALIZE ANY EXPECTED SAVINGS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, LOSS OF BUSINESS OPPORTUNITY, OR CORRUPTION OR LOSS OF DATA, FAILURES TO TRANSMIT OR RECEIVE ANY DATA, PROBLEMS ASSOCIATED WITH ANY APPLICATIONS USED IN CONJUNCTION WITH RIM PRODUCTS OR SERVICES, DOWNTIME COSTS, LOSS OF THE USE OF RIM PRODUCTS OR SERVICES OR ANY PORTION THEREOF OR OF ANY AIRTIME SERVICES, COST OF SUBSTITUTE GOODS, COSTS OF COVER, FACILITIES OR SERVICES, COST OF CAPITAL, OR OTHER SIMILAR PECUNIARY LOSSES, WHETHER OR NOT SUCH DAMAGES WERE FORESEEN OR UNFORESEEN, AND EVEN IF RIM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW IN YOUR JURISDICTION, RIM SHALL HAVE NO OTHER OBLIGATION, DUTY, OR LIABILITY WHATSOEVER IN CONTRACT, TORT, OR OTHERWISE TO YOU INCLUDING ANY LIABILITY FOR NEGLIGENCE OR STRICT LIABILITY.
THE LIMITATIONS, EXCLUSIONS, AND DISCLAIMERS HEREIN SHALL APPLY: (A) IRRESPECTIVE OF THE NATURE OF THE CAUSE OF ACTION, DEMAND, OR ACTION BY YOU INCLUDING BUT NOT LIMITED TO BREACH OF CONTRACT, NEGLIGENCE, TORT, STRICT LIABILITY OR ANY OTHER LEGAL THEORY AND SHALL SURVIVE A FUNDAMENTAL BREACH OR BREACHES OR THE FAILURE OF THE ESSENTIAL PURPOSE OF THIS AGREEMENT OR OF ANY REMEDY CONTAINED HEREIN; AND (B) TO RIM AND ITS AFFILIATED COMPANIES, THEIR SUCCESSORS, ASSIGNS, AGENTS, SUPPLIERS (INCLUDING AIRTIME SERVICE PROVIDERS), AUTHORIZED RIM DISTRIBUTORS (ALSO INCLUDING AIRTIME SERVICE PROVIDERS) AND THEIR RESPECTIVE DIRECTORS, EMPLOYEES, AND INDEPENDENT CONTRACTORS.
IN ADDITION TO THE LIMITATIONS AND EXCLUSIONS SET OUT ABOVE, IN NO EVENT SHALL ANY DIRECTOR, EMPLOYEE, AGENT, DISTRIBUTOR, SUPPLIER, INDEPENDENT CONTRACTOR OF RIM OR ANY AFFILIATES OF RIM HAVE ANY LIABILITY ARISING FROM OR RELATED TO THE DOCUMENTATION.
Prior to subscribing for, installing, or using any Third Party Products and Services, it is your responsibility to ensure that your airtime service provider has agreed to support all of their features. Installation or use of Third Party Products and Services with RIM's products and services may require one or more patent, trademark, copyright, or other licenses in order to avoid infringement or violation of third party rights. You are solely responsible for determining whether to use Third Party Products and Services and if any third party licenses are required to do so. If required you are responsible for acquiring them. You should not install or use Third Party Products and Services until all necessary licenses have been acquired. Any Third Party Products and Services that are provided with RIM's products and services are provided as a convenience to you and are provided "AS IS" with no express or implied conditions, endorsements, guarantees, representations, or warranties of any kind by RIM and RIM assumes no liability whatsoever, in relation thereto. Your use of Third Party Products and Services shall be governed by and subject to you agreeing to the terms of separate licenses and other agreements applicable thereto with third parties, except to the extent expressly covered by a license or other agreement with RIM.
The terms of use of any RIM product or service are set out in a separate license or other agreement with RIM applicable thereto. NOTHING IN THIS DOCUMENTATION IS INTENDED TO SUPERSEDE ANY EXPRESS WRITTEN AGREEMENTS OR WARRANTIES PROVIDED BY RIM FOR PORTIONS OF ANY RIM PRODUCT OR SERVICE OTHER THAN THIS DOCUMENTATION.
Research In Motion Limited 295 Phillip Street Waterloo, ON N2L 3W8 Canada
Published in Canada
Research In Motion UK Limited 200 Bath Road Slough, Berkshire SL1 3XE United Kingdom

Contents

1 Working with multimedia in a BlackBerry Application.................................................................................. 7
BlackBerry Applications and multimedia.......................................................................................................7
2 Using audio and video in a BlackBerry Application .......................................................................................9
Accessing stored media ...................................................................................................................................9
Access media on a BlackBerry smartphone........................................................................................... 9
Access media on a microSD card ............................................................................................................ 9
Playing media in the BlackBerry Browser .....................................................................................................9
Play audio in the BlackBerry Browser ....................................................................................................9
Play a video in the BlackBerry Browser.................................................................................................10
Playing media in the media application.......................................................................................................10
Start the media application with or without content..........................................................................10
Streaming data from a remote source to the media application .......................................................11
Creating a BlackBerry Application that plays media files.........................................................................15
Change the state of a Player ..................................................................................................................17
Play media in a BlackBerry Application ................................................................................................17
Receive and send media application events ........................................................................................19
Create different kinds of players...........................................................................................................20
Record audio on a BlackBerry smartphone.................................................................................................24
Send audio to a Bluetooth enabled headset...............................................................................................26
3 Using SVG content in a BlackBerry Application...........................................................................................29
Using SVG content .........................................................................................................................................29
Download SVG content..................................................................................................................................29
Display SVG content from the Plazmic Media Engine version 4.2 ..................................................29
Play SVG content.....................................................................................................................................30
Listen for events related to SVG content..............................................................................................31
Code sample: Manage events when an application downloads SVG content............................... 32
Create a custom connection to SVG content......................................................................................33
4 Working with images from the camera...........................................................................................................35
Creating an image with the camera.............................................................................................................35
Start the camera from a BlackBerry Application................................................................................35
Receive notification of file system events...................................................................................................35
Detect when an image is added or removed in the file system........................................................35
Retrieving an image from the file system ................................................................................................... 37
Retrieve an image from memory on a BlackBerry device.................................................................. 37
Retrieve an image from a microSD card ..............................................................................................37
Moving an image in the file system ............................................................................................................. 37
Move an image within the same directory of the file system ........................................................... 37
Move an image to a different directory of the file system ................................................................ 37
Deleting an image from the file system ......................................................................................................38
Delete an image from memory on a BlackBerry device.....................................................................38
Delete an image from a microSD card .................................................................................................39
5 Working with images ..........................................................................................................................................41
Drawing and positioning images...................................................................................................................41
Using images...................................................................................................................................................43
Use unprocessed images........................................................................................................................43
Use encoded images...............................................................................................................................43
6 Code samples ......................................................................................................................................................45
Code sample: Retrieving and displaying SVG content .............................................................................45
Code sample: Implementing a listener to download SVG content .........................................................46
Code sample: Implementing a custom connection to SVG content .......................................................48
Code sample: Using an unprocessed image to recreate an encoded image.........................................50
Code sample: Drawing a new bitmap using an existing bitmap ..............................................................51
1

Working with multimedia in a BlackBerry Application

BlackBerry Applications and multimedia

BlackBerry Applications and multimedia
You can create a BlackBerry® Application that plays media in the BlackBerry® Browser, or plays media in the media application on the BlackBerry smartphone. You can also create a BlackBerry Application that is a media application. You can create a BlackBerry Application that is a media application and plays binary SVG content. You can create an application that records audio or sends audio to a Bluetooth® enabled headset.
For more information about media types that the BlackBerry smartphone supports, visit www.blackberry.com/ btsc/support to read knowledge base article KB05482.
BlackBerry Java Development Environment Multimedia Development Guide
8
2

Using audio and video in a BlackBerry Application

Accessing stored media Playing media in the BlackBerry Browser Playing media in the media application Creating a BlackBerry Application that plays media files Record audio on a BlackBerry smartphone Send audio to a Bluetooth enabled headset

Accessing stored media

You can create a BlackBerry® Application that uses the Connector class and FileConnection interface to access media stored on the BlackBerry smartphone or a microSD card.

Access media on a BlackBerry smartphone

>Invoke Connector.open(). Pass the following parameters: the file protocol and the location of the media file
on the BlackBerry® smartphone.
FileConnection fconn = (FileConnection)Connector.open("file:///store/home/user/audio/ newfile.mp3");

Access media on a microSD card

>Invoke Connector.open(). Pass the following parameters: the file protocol and the location of the media file
on the microSD card.
FileConnection fconn = (FileConnection)Connector.open("file:///SDCard/music/newfile.mp3");

Playing media in the BlackBerry Browser

A BlackBerry® Application can use the methods of the Browser class and BrowserSession class to play media in the BlackBerry® Browser.

Play audio in the BlackBerry Browser

1. Invoke Browser.getDefaultSession().
BrowserSession soundclip = Browser.getDefaultSession();
BlackBerry Java Development Environment Multimedia Development Guide
2. Invoke BrowserSession.displaypage().
soundclip.displayPage("file:///SDCard/BlackBerry/music/yourFile.mp3");

Play a video in the BlackBerry Browser

1. Invoke Browser.getDefaultSession().
BrowserSession soundclip = Browser.getDefaultSession();
2. Invoke BrowserSession.displaypage().
soundclip.displayPage("file:///SDCard/BlackBerry/Video/soccergame.avi");

Playing media in the media application

On the BlackBerry® smartphones that include the media application, a BlackBerry Application can use the
javax.microedition.content and net.rim.device.api.content packages to start the media application
with or without content.

Start the media application with or without content

1. Import the javax.microedition.content package into your application code.
Import javax.microedition.content;
2. Invoke Registry.getRegistry(). Pass the following parameter: the classname parameter which is the name of the class in the application that extends
net.rim.device.api.system.Application, or net.rim.device.api.ui.UiApplication. Store a
reference to the returned object in a
Registry reg = Registry.getRegistry(String classname);
Registry object.
3. Perform one of the following tasks:
javax.microedition.midlet.MIDlet,
Task Steps
Start the media application with no content.
Start the media application with content.
> Create a new instance of an Invocation object. Store a reference to the object in an Invocation
object.
Invocation invocation = new Invocation(null, null, BlackBerryContentHandler.ID_MEDIA_CONTENT_HANDLER);
> Create a new instance of an Invocation object. Use a media type supported by the media
application as a parameter, and store a reference to the object in an
Invocation invocation = new Invocation(file://...);
Invocation object.
4. To start the media application, invoke Registry.invoke(Invocation invocation). Pass the following parameter: an
reg.invoke(invocation)
10
Invocation object.
2: Using audio and video in a BlackBerry Application

Streaming data from a remote source to the media application

To stream data from a remote source to the media application, you must buffer the source and control how the media application reads the data. Create a custom data to the media application.
Code sample: Opening a connection to a remote audio file and buffering the data
The code sample opens a connection to a remote audio file and buffers some data, then starts to play the audio file. The media application cannot read data when the amount of data in the buffer is low. When a certain level of data, specified by an application, exists in the buffer, the media application can read the data from the buffer. To buffer data in the source, and tracks how much data has been transfered into the stream.
private class ConnectionThread extends Thread { // The thread reads the InputStream from a server, downloads the data into memory on the BlackBerry smartphone. public void run() { try {
byte[] data = new byte[READ_CHUNK]; int len = 0; //start buffering the file.
while (-1 != (len = readAhead.read(data))) { totalRead += len;
SourceStream, use SharedInputStreams. One thread reads from a connection to the remote
DataSource and SourceStream implementation that will send
11
BlackBerry Java Development Environment Multimedia Development Guide
// The bufferingComplete variable lets the application pause before play back of data from the buffer. if (!bufferingComplete && totalRead > getStartBuffer()) { bufferingComplete = true; System.out.println("Initial Buffering Complete");
// Buffering is complete
}
if (_stop) { return; } }
//The entire file is now in the InputStream and in memory on the BlackBerry
System.out.println("Downloading Complete"); System.out.println("Total Read: " + totalRead);
if (totalRead != s.getLength()) {
}
readAhead.setCurrentPosition(0); while (-1 != (len = readAhead.read(data))) { saveStream.write(data); } } catch (Exception e) { System.err.println(e.toString()); } } }
smartphone.
// If the size of the downloaded file does not match the size of the file reported by the server, print a message.
System.err.println("* Unable to Download entire file *");
downloadComplete = true;
// Write the data the application downloads to the local file
Reading data
The DataSource provides a SourceStream that handles the read requests that the media application makes. The
SourceStream uses a SharedInputStream based on the stream the application reads. The SharedInputStream
uses the
When the media application reads all of the data in the buffer, the application can block read requests that the media application makes until more data is buffered.The goal is to support playback as soon as possible and to stop only when necessary. Pause read operations to make sure the specified amount of audio data exists in the buffer for playback. For more information, visit www.blackberry.com/support to read DB00612.
12
available() method to determine how much of the stream the application reads.
2: Using audio and video in a BlackBerry Application
Specify the following four parameters in the DataSource to define how an application buffers data:
Item Description
Initial buffer the amount of audio data that the application downloads before it starts to play the data
Pause mark determines when the application’s attempts to read data from the buffer are blocked
Restart mark determines when the application can read data from the buffer
Limit determines the amount of data that the application can read from the buffer
Code sample: Reading data from a buffer
The Reading data from a buffer code sample displays the read method of the StreamingSource implementation and lets the four parameters in the buffering scheme be manually adjusted.
public int read(byte[] b, int off, int len) throws IOException { // Read from a SharedInputStream that is shared with the same Stream that the //ConnectionThread is downloading to. Although both streams handle the same data, each //stream has a separate read location. The ConnectionThread works to keep its read location //as far ahead of the current playback position as possible.
System.out.println("Read Request for: " + len + " bytes"); // limit bytes read to our readLimit. int readLength = len; if (readLength > getReadLimit()) { readLength = getReadLimit(); } int available; boolean restart_pause = false; for (;;) {
//A read action is be restricted by the amount of data available for a read operation. //The application needs to download a specific amount of data before the application can start to playback audio. //enough data must be available to perform a full read operation. In this sample, the application configures the amount of data that is required to start the playback of audio and to perform a read operation. //The application uses the ReadLimit value to control the amount of data the second reader //reads during a read operation.
//The application uses the PauseBytes setting to determine when the second reader has read //all the data currently provided by the first variable reader. When this occurs, the //application pauses until there is enough data in the buffer for a read operation.
//The application uses the RestartBytes setting to define when there is enough data in the //buffer to perform a read operation
available = _baseSharedStream.available(); if (downloadComplete) { // Ignore all restrictions if downloading is complete System.out.println("Complete, Reading: " + len + " - Available: " + available); return _baseSharedStream.read(b, off, len); } else if (bufferingComplete) { if (restart_pause && available > getRestartBytes()) {
//Perform a read operation as there is now enough data in the buffer.
System.out.println("Restarting - Available: " + available);
13
BlackBerry Java Development Environment Multimedia Development Guide
restart_pause = false; return _baseSharedStream.read(b, off, readLength); } else if (!restart_pause && (available > getPauseBytes() || available > readLength)) {
//Determine if a previous read operation was paused and if there is enough data
if (available < getPauseBytes()) {
} System.out.println("Reading: " + readLength + " - Available: " + available); return _baseSharedStream.read(b, off, readLength); } else if (!restart_pause) {
restart_pause = true;
} } else { // Pause the thread to allow the data to download before performing another read operation. try { Thread.sleep(100); } catch (Exception e) { System.err.println(e.getMessage()); } } } }
in the buffer to perform a read operation.
/* Perform a read operation, setting the pause flag so that future reads will pause until enough data for a read operation is loaded into the buffer*/ restart_pause = true;
// The buffer does not contain enough data for a read operation. Pause the read operation until enough data is loaded into the buffer.
Code sample: Downloading a media file
public void connect() throws IOException { System.out.println("Loading: " + getLocator());
//Open a connection to the remote file
s = (ContentConnection) Connector.open(getLocator(), Connector.READ);
//Get the length of the remote file.
System.out.println("Size: " + s.getLength());
//Get the name of the remote file.
int filenameStart = getLocator().lastIndexOf('/'); int paramStart = getLocator().indexOf(';'); if (paramStart < 0) { paramStart = getLocator().length(); } String filename = getLocator().substring(filenameStart, paramStart); System.out.println("Filename: " + filename);
//Open a connection to the file on the local file system.
14
2: Using audio and video in a BlackBerry Application
saveFile = (FileConnection) Connector.open("file:///SDCard/blackberry/music" + filename, Connector.READ_WRITE);
//If a file with the same name as the remote file doesn't exist on the local system, download the file again.
if (!saveFile.exists()) { saveFile.create(); }
//Configure the local file to be readable.
saveFile.setReadable(true);
//Create a shared input stream using the an input stream from the local file. SharedInputStream fileStream = SharedInputStream.getSharedInputStream(saveFile.openInputStream());
fileStream.setCurrentPosition(0);
//If the file on the local file system is smaller than the remote file, download the file again.
if (saveFile.fileSize() < s.getLength()) { // didn't get it all before, download again saveFile.setWritable(true); saveStream = saveFile.openOutputStream(); readAhead = SharedInputStream.getSharedInputStream(s.openInputStream()); } else { downloadComplete = true; readAhead = fileStream; s.close(); } if (forcedContentType != null) { feedToPlayer = new LimitedRateSourceStream(readAhead, forcedContentType); } else { feedToPlayer = new LimitedRateSourceStream(readAhead, s.getType()); } }

Creating a BlackBerry Application that plays media files

You can create a BlackBerry® Application that uses the Player interface and the javax.microedition.media (JSR 135) package to play media. The media application and control the playback of media files. An object of a class that implements the interface can be in one of five states.
State Description
UNREALIZED an instance of a class that implements the Player interface is constructed.
REALIZED An instance of a class that implements the Player interface can locate and get resources on a
Player interface provides the methods to manage the different states of a
Player
server or file system.
15
BlackBerry Java Development Environment Multimedia Development Guide
State Description
PREFETCHED An instance of a class that implements the Player interface can perform tasks that are required
to play media.
STARTED An instance of a class that implements the Player interface can start playing media.
CLOSED An instance of a class that implements the Player interface can release the resources it is using.
16
2: Using audio and video in a BlackBerry Application

Change the state of a Player

Task Step
Change the state of a Player from UNREALIZED to REALIZED. Invoke Player.realize().
Change the state of a Player from
Change the state of a Player from
Change the state of a Player from
REALIZED
PREFETCHED
STARTED
to
to
PREFETCHED
STARTED
to
CLOSED
.
.
.
Invoke Player.prefetch().
Invoke Player.start().
Invoke Player.close().

Play media in a BlackBerry Application

Task Steps
Prepare the media application.
Start the media application. >Invoke Player.start(). An instance of a class that implements the Player interface returns to the
Determine the controls that a media application supports.
1. Invoke Player.realize().
2. Invoke Player.prefetch().
PREFETCHED state when you invoke
try {
Player p = Manager.createPlayer("www.test.rim.net/abc.wav");
p.start();
} catch (MediaException pe) {
} catch (IOException ioe) {
}
Player.stop() or when it reaches the end of the media file.
You can use one object to access multiple controls. For example, one object can be both a VolumeControl and a
ToneControl. The javax.microedition.media package contains a number of Control
interfaces. For more information about the
javax.microedition.media package, see the API Reference
for the BlackBerry® Java® Development Environment.
1. Invoke Player.getControls().
2. To provide additional functionality for a media application, use one or more of the controls that the media application supports.
17
Loading...
+ 37 hidden pages