CONTENT STREAM

ANDROID TRAGEDY

by Dmytro Danylyk & William Shakespeare

Dmytro Danylyk - Android Developer, Lemberg Solutions Limited

Nominated to apply for the Google Developer Expert Program

Finalist of Google Apps Developer Challenge 2012

Author of open source libraries 1000+ stars. 

Big fan of android, flat design, stackoverflow, git, Intellij IDEA. Eclipse IDE hater.

@dmytrodanylyk

CHARACTERS

  • Romeo - is the son of Montague (Developer)
  • Juliet - is the daughter of Capulet (Designer)

PREHISTORY

Montague (Developers) and Capulet (Designers) - are involved in a team feud that goes back years before any of the members were born.

ONCE UPON A TIME

Juliet meets Romeo on android project discussion where displaying video must take place.

"To Intent, or not to Intent, that is the question?" - Romeo

IMPLICIT INTENT

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("http://www.yourvideo.mp4"), "video/mp4");

String title = "Play this video with";
Intent chooser = Intent.createChooser(intent, title);

// Verify the intent will resolve to at least one activtiy
if(intent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}

"We need a custom..." - Juliet

WHEN TO INTENT?

  • when functionality is hard to implement (send email)
  • when you need social sharing (facebook, twitter)
  • when you don't need extra functionality (play video)

THE TRAGICAL HISTORY OF

WHAT ROMEO

THINK WILL WORK

VideoView videoView = (VideoView) findViewById(R.id.videoView);
videoView.setVideoPatn("http://www.yourvideo.mp4");
videoView.start();

"I forgot to tell..." - Juliet

"We need to make it draggable like in YouTube app"

"We need to display list of videos like in Instagram app"

"Could you remove black lines"

WELCOME TO WORLD OF BLACK PIXELS

WHAT HAPPENS WHEN YOU SCROLL

Title Text

WHAT HAPPENS WHEN YOU ANIMATE

Title Text

WHAT HAPPENS

IF YOU CROP

WHY ARE YOU DOING THIS TO ME?

"Surface view creates a new window, placed behind your application’s window, to manage content."

HOW IT WORKS?

Chestburster design pattern - make a hole in your application to inject extra functionality.

PROBLEMS

  • Unexpected behavior inside scrollable container
  • Unexpected behavior with animations
  • No way to crop content

TEXTURE VIEW

WHEN TO USE?

  • Play Video
  • Stream Camera
  • Render OpenGL

WHY TO USE?

"Texture View - does not create a separate window but behaves as a regular view."

"Because it uses hardware accelerated 2D rendering - it is so fast and efficient."

REQUIREMENTS

  • Android 4.0 APIs
  • Hardware Acceleration

QUICKSTART

BASIC LOGIC

TextureView textureView = findViewById(R.id.textureView);
textureView.setSurfaceTextureListener( new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture texture,
            int width, int height) {
        // all logic goes here
    }
    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface,
            int width, int height) {
        // ignore
    }
    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        return true;
    }
    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
        // ignore
    }
});

VIDEO FROM ASSETS

// all logic goes here
Surface surface = new Surface(texture);
AssetFileDescriptor afd = ...

mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDataSource(fileDescriptor, startOffset, length);
mMediaPlayer.setSurface(surface);
mMediaPlayer.prepareAsync();
// Play video when the media source is ready for playback.
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
    @Override
    public void onPrepared(MediaPlayer mediaPlayer) {
        mediaPlayer.start();
    }
});

CAMERA

// all logic goes here
mCamera = Camera.open();
if (mCamera == null) {
    Log.d(TAG, "Your device doesn't have camera.");
    return;
}
mCamera.setPreviewTexture(surface);
mCamera.startPreview();
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    if (mCamera != null) {
        mCamera.stopPreview();
        mCamera.release();
    }
    return true;
}

MORE INFO

  • Texture View - Intro
  • Texture View - Playing video
  • Texture View - Video Cropping

TEXTURE VIDEO VIEW

"Texture Video View - is custom view based on android Texture View which gives you ability easily play and crop video.

WHY TO USE?

W/MediaPlayer﹕ info/warning (1, 26)
E/MediaPlayer﹕ error (1, -4)
E/MediaPlayer: error (1, -2147483648)
E/MediaPlayer﹕ start called in state 4
E/MediaPlayer﹕ error (-38, 0)
E/MediaPlayer﹕ Error (-38,0)

HOW IT LOOKS?

QUICKSTART

TextureVideoView cropTextureView = 
        (TextureVideoView) findViewById(R.id.cropTextureView);
// Use `setScaleType` method to crop video
cropTextureView.setScaleType(TextureVideoView.ScaleType.TOP);
// Use `setDataSource` method to set data source, this could be url, assets folder or path
cropTextureView.setDataSource("http://www.w3schools.com/html/mov_bbb.mp4");
cropTextureView.play();

WHAT INSIDE?

float scaleX = 1.0f;
float scaleY = 1.0f;

if (mVideoWidth > viewWidth && mVideoHeight > viewHeight) {
    scaleX = mVideoWidth / viewWidth;
    scaleY = mVideoHeight / viewHeight;
} else if (mVideoWidth < viewWidth && mVideoHeight < viewHeight) {
    scaleY = viewWidth / mVideoWidth;
    scaleX = viewHeight / mVideoHeight;
} else if (viewWidth > mVideoWidth) {
    scaleY = (viewWidth / mVideoWidth) / (viewHeight / mVideoHeight);
} else if (viewHeight > mVideoHeight) {
    scaleX = (viewHeight / mVideoHeight) / (viewWidth / mVideoWidth);
}

int pivotPointX = viewWidth / 2; // Calculate pivot points,
int pivotPointY = viewHeight / 2; // in our case crop from center

Matrix matrix = new Matrix();
matrix.setScale(scaleX, scaleY, pivotPointX, pivotPointY);

mTextureView.setTransform(matrix);

MORE INFO

87

CAMERA ISSUES

PREVENT FROM APPEARING IN GOOGLE PLAY

<uses-feature
    android:name="android.hardware.camera"
    android:required="true" />

CHECK IF DEVICE HAS CAMERA

if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
    // This device has a Camera 
} else {
    // This device doesn't have a Camera
}

CHECK IF CAMERA IS AVAILABLE

Camera c = null;

try {
    c = Camera.open(); // attempt to get a Camera instance 
} catch (Exception e) {
    // Camera is not available 
}

LOCK ACTIVITY SCREEN ORIENTATION

<activity android:name=".CameraActivity" android:screenOrientation="landscape" />

CALCULATE PREVIEW SIZE

Camera.Size getBestPreviewSize(int requiredWidth, int requiredHeight, Camera.Parameters parameters) {
    Camera.Size result = null;
    for (Camera.Size currentSize : parameters.getSupportedPreviewSizes()) {
        if (currentSize.width <= requiredWidth 
                && currentSize.height <= requiredHeight) {

            if (result == null) {
                result = currentSize;
            } else {
                int resultArea = result.width * result.height;
                int newArea = currentSize.width * currentSize.height;

                if (newArea > resultArea) {
                    result = currentSize;
                }
            }
        }
    }
    return result;
}

CHECK IF FEATURE IS AVAILABLE BEFORE USING IT

// get Camera parameters
Camera.Parameters params = mCamera.getParameters();

List focusModes = params.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
    // Auto-focus mode is supported
    params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
    // set Camera parameters
    mCamera.setParameters(params);
}

DON'T LOCK CAMERA FOREVER, YOU ARE NOT ALONE

@Override
protected void onPause() {
    super.onPause();

    // release the camera for other applications
    if(mCamera != null) {
        mCamera.release();
        mCamera = null;
    }
}

Q / A

Content Stream - Android Tragedy

By Dmytro Danylyk

Content Stream - Android Tragedy

  • 4,755