SlideShare a Scribd company logo
INTERMEDIATE
ANDROID
Michael Galpin
                 Bump Technologies
ABOUT: ME
 ‣       Android engineer, Bump Technologies

 ‣       ex-eBay: eBay Mobile for Android

 ‣       Android in Practice

 ‣       Social info

     ‣    @michaelg
     ‣    +Michael Galpin
Bump
Technologies
     • Creators of Bump app
       • Android + iOS
       • 65M+ Downloads
     • Creators of BumpCube
     • Hiring!
You down with AIP?
• Chapter 11: “Appeal to
  the senses using
  multimedia.”

• Slideshow app
  MediaMogul.apk

• http://
  code.google.com/p/
  android-in-practice/
Android workshop
MULTIMEDIA
“Of all of our inventions for mass
communication, pictures still speak the
most universally understood language.”

                          -- Walt Disney
Android workshop
Android workshop
Android workshop
Android workshop
DETECTING
CAPABILITIES
Android workshop
AndroidManifest.xml
<uses-feature android:name="android.hardware.camera"
    android:required="true" />
<uses-feature android:name="android.hardware.camera.autofocus"
    android:required="true"/>
<uses-feature android:name="android.hardware.camera.flash"
    android:required="false" />
<uses-feature android:name="android.hardware.camera.front"
    android:required="false" />
<uses-feature android:name="android.hardware.microphone"
    android:required="true"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
Check the front
private boolean hasFrontFacingCamera(){
    PackageManager mgr = this.getPackageManager();
    for (FeatureInfo fi : mgr.getSystemAvailableFeatures()){
        if (fi.name.equals(PackageManager.FEATURE_CAMERA_FRONT)){
            return true;
        }
    }
    return false;
}

private Camera getFrontFacingCamera(){
    for (int i=0;i<Camera.getNumberOfCameras();i++){
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(i, info);
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){
            return Camera.open(i);
        }
    }
    return null;
}
Check the front
private boolean hasFrontFacingCamera(){
    PackageManager mgr = this.getPackageManager();
    for (FeatureInfo fi : mgr.getSystemAvailableFeatures()){
        if (fi.name.equals(PackageManager.FEATURE_CAMERA_FRONT)){
            return true;
        }
    }
    return false;
}

private Camera getFrontFacingCamera(){
    for (int i=0;i<Camera.getNumberOfCameras();i++){
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(i, info);
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){
            return Camera.open(i);
        }
    }
    return null;
}
But I have to
  support
Android 2.1
Android workshop
Android workshop
Reflection?
LOLZ

// Can't use Build.VERSION_CODES.GINGERBREAD
if (Build.VERSION.SDK_INT >= 9){
    PostFroyoClass wontThisBlowUp = new PostFroyoClass();
}
These are my customers!
These are my customers!
Proprietary APIs FTW!

import com.sprint.hardware.twinCamDevice.FrontFacingCamera;

Camera evoCam = FrontFacingCamera.getFrontFacingCamera();
RESOURCES
Android workshop
MediaPlayer.framework?
MediaPlayer.framework?

[MPMusicPlayerController iPodMusicPlayer] ??
MediaPlayer.framework?

[MPMusicPlayerController iPodMusicPlayer] ??

   [MusicLibrary sharedMusicLibrary] ???
MediaPlayer.framework?

[MPMusicPlayerController iPodMusicPlayer] ??

   [MusicLibrary sharedMusicLibrary] ???
        UIImagePickerController ?
MediaPlayer.framework?

[MPMusicPlayerController iPodMusicPlayer] ??

   [MusicLibrary sharedMusicLibrary] ???
        UIImagePickerController ?
MediaPlayer.framework?

[MPMusicPlayerController iPodMusicPlayer] ??

   [MusicLibrary sharedMusicLibrary] ???
        UIImagePickerController ?




            Files !!!!
Inside your APK




                  External
Just give it to me /res/raw

Resources res = this.getResources();


InputStream stream = res.openRawResource(R.raw.sunflower);


MediaPlayer.create(this, R.raw.constancy).start();
I like big /
     assets
and I cannot lie
MediaPlayer player = new MediaPlayer();
AssetManager mgr = getResources().getAssets();
String audioDir = "audio";
final LinkedList<FileDescriptor> queue =
    new LinkedList<FileDescriptor>();
for (String song : mgr.list("audio")){
    queue.add(
            mgr.openFd(audioDir + "/" + song).getFileDescriptor());
}
if (!queue.isEmpty()){
    FileDescriptor song = queue.poll();
    player.setDataSource(song);
    player.prepare();
    player.start();
}
player.setOnCompletionListener(new OnCompletionListener(){
    @Override
    public void onCompletion(MediaPlayer mp) {
        if (!queue.isEmpty()){
            FileDescriptor song = queue.poll();
            player.setDataSource(song);
            player.prepare();
            player.start();
        }
    }
});
Android workshop
File picturesDir =
    Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PICTURES);
File picturesDir =
    Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PICTURES);
IMAGES
Android workshop
Loading local images
InputStream stream = new FileInputStream(imgFile);
Bitmap bm = BitmapFactory.decodeStream(stream);
Loading local images
InputStream stream = new FileInputStream(imgFile);
Bitmap bm = BitmapFactory.decodeStream(stream);



// calculate desired width and height



Bitmap thumb = ThumbnailUtils.extractThumbnail(bm, width, height);

Bitmap thumb = Bitmap.createScaledBitmap(bm, width, height, false);
Android workshop
Loading without
                         exploding
public static Bitmap decodeDownsizedBitmapStream(File file, int target, Context context) throws
IOException {
    FileInputStream stream = new FileInputStream(file);
    Pair<Integer, Integer> source = getDimensionsForStream(stream);
    stream.close();
    FileInputStream in = new FileInputStream(file);
    Options options = new Options();
    options.inSampleSize = 1 + getDownSampleSize(max(source.first, source.second), target);
    return BitmapFactory.decodeStream(in, null, options);
}
public static Pair<Integer, Integer> getDimensionsForStream(InputStream in){
    Options options = new Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(in, null, options);
    return new Pair<Integer, Integer>(options.outWidth, options.outHeight);
}
public static int getDownSampleSize(int source, int target){
    int size = 1;
    if (source <= 2*target){
        int power = (int) ((log (source / target)) / log(2));
        size = (int) pow(2, power);
    }
    return size;
}
Loading web images
URL url = new URL(urlString);



//   NOTE, be careful about just doing "url.openStream()"
//   it's a shortcut for openConnection().getInputStream() and doesn't set timeouts
//   the defaults are "infinite" so it will wait forever if endpoint server is down
//   do it properly with a few more lines of code . . .



URLConnection conn = url.openConnection();
conn.setConnectTimeout(3000);
conn.setReadTimeout(5000);

Bitmap bitmap = BitmapFactory.decodeStream(conn.getInputStream());
Android workshop
AsyncTask FTW!
private class RetrieveImageTask extends AsyncTask<String, Void, Bitmap> {
   private ImageView imageView;

    public RetrieveImageTask(ImageView imageView) {
       this.imageView = imageView;
    }

    @Override
    protected Bitmap doInBackground(String... args) {
       try {
          URL url = new URL(args[0]);
          URLConnection conn = url.openConnection();
          conn.setConnectTimeout(3000);
          conn.setReadTimeout(5000);
          return BitmapFactory.decodeStream(conn.getInputStream());
       } catch (Exception e) {
       }
       return null;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
       if (bitmap != null) {
          imageView.setImageBitmap(bitmap);
       }
    }
}
Loading a dozen images
       isn’t cool.
 Y’know what’s cool?
Loading a billion images.
Hot or Not?
private class DealsAdapter extends ArrayAdapter<Item> {

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
       if (convertView == null) {
          LayoutInflater inflater = (LayoutInflater)
              getSystemService(Context.LAYOUT_INFLATER_SERVICE);
          convertView = inflater.inflate(R.layout.list_item, parent, false);
       }
       TextView text = (TextView) convertView.findViewById(R.id.deal_title);
       ImageView image = (ImageView) convertView.findViewById(R.id.deal_img);
       Item item = getItem(position);
       if (item != null) {
          text.setText(item.getTitle());
          new RetrieveImageTask(image).execute(item.getSmallPicUrl());
       }
       return convertView;
    }
}
Watch out for leaks
public class LeakProofActivity extends Activity {
   LeakProofAsyncTask task;
   private class LeakProofAsyncTask extends AsyncTask<String, Void, Bitmap>{
      Context potentialLeak = LeakProofActivity.this;

      @Override
      protected Bitmap doInBackground(String... arg0) {
         // Do something that takes a long time
         // Note if you need a Context here, use Application
         return null;
      }

      @Override
      protected void onPostExecute(Bitmap result){
         // update UI, etc.
      }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

      task = (LeakProofAsyncTask) getLastNonConfigurationInstance();
      if (task != null){
         task.potentialLeak = this;
      }
    }
    @Override
    protected void onDestroy() {
       super.onDestroy();
       if (task != null){
          task.potentialLeak = null;
       }
    }
    @Override
    public Object onRetainNonConfigurationInstance() {
       return task;
    }
}
Android workshop
How to build a cache?

• WeakReferences?
• SoftReferences?
• WeakHashMap?
• MapMaker?
LinkedHashMap!?
private static class ImageCache extends LinkedHashMap<String, Bitmap>{

    private final int capacity;

    public ImageCache(int capacity){
        super(capacity/2, 0.75f, true);
        this.capacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Entry<String, Bitmap> eldest) {
        return this.size() > capacity;
    }
}
Disk Cache

• Context.getCacheDir()
• SQLite
• Environment.getExternalStorage()
Sidebar: Bitmaps & Heap
Reported by andreas....@googlemail.com, May 22, 2010
Note: This is NOT a requst for assistance!

In my applications, I keep getting the following exception:

E/AndroidRuntime( 1420): java.lang.OutOfMemoryError: bitmap size exceeds
VM budget
E/AndroidRuntime( 1420): 
 at
android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
E/AndroidRuntime( 1420): 
 at
android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:459)
E/AndroidRuntime( 1420): 
 at
android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:515)
E/AndroidRuntime( 1420): 
 at
de.schildbach.bitmapbug.MyActivity.bitmap(MyActivity.java:38)

Comment 1 by romaingu...@gtempaccount.com, May 23, 2010
Your app needs to use less memory.

                       http://code.google.com/p/android/issues/detail?id=8488
Android workshop
Use Placeholders

      ImageView image =
          (ImageView) convertView.findViewById(R.id.deal_img);

      image.setImageBitmap(
          BitmapFactory.decodeResource(
              getResources(), R.drawable.temp));

      Item item = getItem(position);

      new RetrieveImageTask(image).
          execute(item.getSmallPicUrl());
@Override
                       View Holders
public View getView(final int position, View cell, ViewGroup parent) {
	 ViewHolder holder = (ViewHolder) cell.getTag();
	 if (holder == null){
	 	 holder = new ViewHolder(cell);
	 	 cell.setTag(holder);
	 }
	 Bitmap thumb = (Bitmap) getItem(position);
   holder.img.setImageBitmap(thumb);
	 File file = getImageFile(position);
	 if (selectedFiles.contains(file)){
	 	 holder.cbox.setChecked(true);
	 } else {
	 	 holder.cbox.setChecked(false);
	 }
	 return cell;
}

static class ViewHolder {
    final ImageView img;
    final CheckBox cbox;
    ViewHolder(View cell){
        img = (ImageView) cell.findViewById(R.id.thumb);
    	 cbox = (CheckBox) cell.findViewById(R.id.cbox);
    }
}
Sidebar: Strict Mode
CONTENT
PROVIDERS
SQL?


       NoSQL?


           SortOfSQL!
Queries
Cursor cursor = getContentResolver().query(table,
                                           columns,
                                           whereClause,
                                           paramValues,
                                           sortOrder);
while (cursor.moveToNext()){
    // process results
}
cursor.close();
Music
import   static   android.provider.BaseColumns._ID;
import   static   android.provider.MediaStore.Audio.AudioColumns.ARTIST;
import   static   android.provider.MediaStore.Audio.AudioColumns.IS_MUSIC;
import   static   android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
import   static   android.provider.MediaStore.MediaColumns.DATA;
import   static   android.provider.MediaStore.MediaColumns.TITLE;

String[] columns = {TITLE,ARTIST,_ID, DATA};
// where clause so we don't get ringtones, podcasts, etc.
String whereClause = IS_MUSIC + " = ?";
String[] whereValues = {"1"};
cursor = managedQuery(EXTERNAL_CONTENT_URI,
	 columns,
	 whereClause,
	 whereValues,
	 null
);
Contacts
String[] projection = {Phone.CONTACT_ID, Phone.NUMBER};
String selection = Data.IN_VISIBLE_GROUP + "=1 AND " +
    Phone.NUMBER + " LIKE ?";
String[] selectionArgs = {"%" + phoneSubStr + "%"};
Cursor phoneCursor = resolver.query(Phone.CONTENT_URI,
                                 projection,
                                 selection,
                                 selectionArgs,
                                 null);


String[] projection = new String[] {StructuredName.GIVEN_NAME,
                                    StructuredName.FAMILY_NAME,
                                    StructuredName.RAW_CONTACT_ID,
                                    StructuredName.CONTACT_ID};
String selection = StructuredName.CONTACT_ID+ " = ? AND " +
    Data.MIMETYPE + " = '" + StructuredName.CONTENT_ITEM_TYPE +"'";
String[] selectionArgs = new String[] {contact.id};
Cursor nameCursor = resolver.query(Data.CONTENT_URI,
                                projection,
                                selection,
                                selectionArgs,
                                null);
Android workshop
Scanning media
Scanning 101
MediaScannerConnection conn = new MediaScannerConnection(this,
  new MediaScannerConnectionClient(){
    public void onMediaScannerConnected(){
        scanFile("/some/path/SomeSong.mp3", "audio/mpeg3");
        scanFile("/some/other/path/IMG1999.jpg", "image/jpeg");
    }

      public void onScanCompleted(String path, Uri uri){
          Log.d("LogTag", "Media scanned uir=" + uri.toString());
      }
});

conn.connect();
Cursor   Adapter
CursorAdapter
Avoid
Use Other Apps
private static final int SELECT_VIDEO = 1;
private Uri videoUri;
@Override
protected void onCreate(Bundle savedInstanceState) {
    Button vidBtn = (Button) findViewById(R.id.vidBtn);
    vidBtn.setOnClickListener(new OnClickListener(){
        @Override
        public void onClick(View button) {
            Intent videoChooser = new Intent(Intent.ACTION_GET_CONTENT);
            videoChooser.setType("video/*");
            startActivityForResult(videoChooser, SELECT_VIDEO);
        }
    });
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
        Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == SELECT_VIDEO && resultCode == RESULT_OK){
        videoUri = data.getData();
    }
}
Android workshop
ANIMATION
Dissolve Animation
private void nextSlide() {
    AlphaAnimation animation = new AlphaAnimation(0.0f, 1.0f);
    if ((count % 2) == 0) {
        animation = new AlphaAnimation(1.0f, 0.0f);
    }
    animation.setStartOffset(TIME_PER_SLIDE);
    animation.setDuration(TIME_PER_SLIDE);
    animation.setFillAfter(true);
    animation.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {}
        @Override
        public void onAnimationRepeat(Animation animation) {}
        @Override
        public void onAnimationEnd(Animation animation) {
            if (playingSlides){
                nextImage = getNextImage();
                ImageView backgroundImage =
                    (count % 2 == 0) ? rightSlide : leftSlide;
                backgroundImage.setImageBitmap(nextImage);
                count++;
                nextSlide();
            }
        }
    });
    rightSlide.startAnimation(animation);
    currentImage = nextImage;
}
Honeycomb+
Animators
ImageView backgroundImage = (count % 2 == 0) ? rightSlide : leftSlide;
ObjectAnimator anim =
    ObjectAnimator.ofFloat(backgroundImage, "alpha", 0.0f, 1.0f);
anim.addListener(new AnimatorListenerAdapter(){
    public void onAnimationEnd(Animator animator){
        nextSlide();
    }
});
Android workshop
Android workshop
A/V
PLAYBACK
playBtn.setOnClickListener(new OnClickListener(){
    private Handler handler = new Handler();
    MediaPlayer player = null;
    long maxTime = 15L*1000; // 15 seconds
    long timeLeft = maxTime;
                                                      Audio Preview
    Runnable autoStop;
    @Override
    public void onClick(View view) {
        if (player == null){
            player = MediaPlayer.create(activity, song.uri);
        }
        if (!playingSongs.contains(song.id)){           Start/Resume
            player.start();
            playingSongs.add(song.id);
            autoStop = new Runnable(){
                                                     Timer
                 @Override
                 public void run() {
                     player.pause();
                     player.seekTo(0);
                     playingSongs.remove(song.id);
                     playBtn.setText(R.string.play);
                     timeLeft = maxTime;
                }
            };
            handler.postDelayed(autoStop, timeLeft);
            playBtn.setText(R.string.pause);
        } else {
            player.pause();
                                                                   Pause
            playingSongs.remove(song.id);
            timeLeft = maxTime - player.getCurrentPosition();
            playBtn.setText(R.string.play);
                                                                Calc time left
            handler.removeCallbacks(autoStop);
        }
    }
});
Handler?
Handler?
Pipeline Thread
Looper.prepare();
handler = new Handler();

// handle tasks in queue



Looper.loop();
Handler?
      Thread                 Pipeline Thread
                             Looper.prepare();
                             handler = new Handler();

                             // handle tasks in queue



                             Looper.loop();

// long running task
Message msg =
  handler.obtainMessage();
handler.sendMessage(msg);
Handler?
      Thread                 Pipeline Thread                 Thread
                             Looper.prepare();
                             handler = new Handler();

                             // handle tasks in queue
                                                        // long running task
                                                        handler.post(
                             Looper.loop();                 new Runnable() {...
                                                        });
// long running task
Message msg =
  handler.obtainMessage();
handler.sendMessage(msg);
Theme Muzak
private MediaPlayer player;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setupThemeMusic();
}

@Override
public void onResume() {
    super.onResume();
    if (player != null){
        player.start();
    }
                                               @Override
}
                                               public void onPause(){
                                                   super.onPause();
                                                   if (player != null && player.isPlaying()){
                                                       player.pause();
                                                   }
                                               }

                                               @Override
                                               protected void onDestroy() {
                                                   super.onDestroy();
                                                   if (player != null && player.isPlaying()){
                                                       player.stop();
                                                   }
                                                   player.release();
                                               }
Activity Lifecycle
Android workshop
Video Playback
Uri videoUri = ...;

VideoView video = (VideoView) findViewById(R.id.video);
video.setVideoURI(videoUri);

MediaController controller = new MediaController(this);
controller.setMediaPlayer(video);
video.setMediaController(controller);
video.requestFocus();
video.start();
Android workshop
TAKE A
PICTURE
Use the Camera (app)
import static android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
photoUri = getContentResolver().insert(
        EXTERNAL_CONTENT_URI, new ContentValues());
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
startActivityForResult(intent,TAKE_PHOTO);




@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == Activity.RESULT_OK && requestCode == TAKE_PHOTO){
        ImageView img = (ImageView) findViewById(R.id.photoThumb);
        InputStream stream =
            getContentResolver().openInputStream(photoUri);
        Bitmap bmp = BitmapFactory.decodeStream(stream);
        img.setImageBitmap(bmp);
    }
}
Android workshop
EXIF!
Popular on Facebook
Correctly Oriented
Options photoImageOptions = new BitmapFactory.Options();
Matrix matrix = new Matrix();
ExifInterface ei = new ExifInterface(somePath);
int orientation = ei.getAttributeInt(TAG_ORIENTATION, ORIENTATION_UNDEFINED);
int angle = 0;
switch (orientation) {
    case ORIENTATION_ROTATE_90 : angle=90; break;
    case ORIENTATION_ROTATE_180 : angle=180; break;
    case ORIENTATION_ROTATE_270 : angle=270; break;
    default: break;
}
Bitmap bm = BitmapFactory.decodeFile(path, photoImageOptions);
if (angle > 0){
    matrix.setRotate(angle);
    Bitmap bm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(),
                                    matrix, true);
}
Geo Tagging


ExifInterface exif = new ExifInterface(filename);
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, latitude);
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, longitude);
exif.saveAttributes();




                                                    “dd/1,mm/1,ss/1”
Where?
Getting a (geo) fix

LocationManager mgr = getSystemService(LOCATION_SERVICE);

for (String provider : mgr.getAllProviders()){
    Location last = mgr.getLastKnownLocation(last);

    mgr.requestLocationUpdates(provider, 60000, 500, new LocationListener(){
        public void onLocationChanged(Location loc){
            // do stuff
        }
        // other methods
    });
}
To the cloud!
Uploading a photo

String url = "http://my/server";
HttpClient client = new DefaultHttpClient();
HttpContext context = new BasicHttpContext();
HttpPost post = new HttpPost(url);

File imgFile = new File("/path/to/my/image");
MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
entity.addPart("image", new FileBody(imgFile));
post.setEntity(entity);

HttpResponse response = client.execute(httpPost, localContext);
Android workshop
Upload Service
UploadService extends IntentService {

    protected void onHandleIntent(Intent i){
        String imgPath = i.getStringExtra("file");
        // upload code goes here
    }
}




Intent i = new Intent(this, UploadService.class);
i.putExtra("file", "/path/to/my/image");
startService(i);
Android workshop
Process Priority
       Foreground

         Visible


         Service

       Background
         Empty
A/V
RECORDING
Android workshop
Video Preview
private   SurfaceHolder holder;
private   Camera camera;
private   MediaRecorder mediaRecorder;
private   File tempFile;
private   SurfaceView preview;
private   boolean isRecording = false;
private   final int maxDurationInMs = 20000;
private   final long maxFileSizeInBytes = 500000;
private   final int videoFramesPerSecond = 20;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    preview = new SurfaceView(this);
    holder = preview.getHolder();
    holder.addCallback(cameraman);
    holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    setContentView(preview);
    tempFile = new File(getCacheDir(), "temp.mov");
    if (tempFile.length() > 0){
        tempFile.delete();
    }
}
Video Preview
private Callback cameraman = new Callback(){
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        camera = Camera.open();
        camera.setPreviewDisplay(holder);
    }
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format,
            int width,int height) {
        Parameters params = camera.getParameters();
        List<Size> sizes = params.getSupportedPreviewSizes();
        Size optimalSize = getOptimalPreviewSize(sizes, width, height);
        params.setPreviewSize(optimalSize.width, optimalSize.height);
        camera.setParameters(params);
        camera.startPreview();
    }
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        camera.stopPreview();
        camera.release();
    }
};
Video Preview
private void startRecording(){
    if (isRecording){
        return;
    }
    isRecording = true;
    camera.unlock();
    mediaRecorder = new MediaRecorder();
    mediaRecorder.setCamera(camera);
    mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
    mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
    mediaRecorder.setMaxDuration(maxDurationInMs);

    mediaRecorder.setOutputFile(tempFile.getPath());
    mediaRecorder.setVideoFrameRate(videoFramesPerSecond);
    mediaRecorder.setVideoSize(preview.getWidth(), preview.getHeight());
    mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
    mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
    mediaRecorder.setPreviewDisplay(holder.getSurface());
    mediaRecorder.setMaxFileSize(maxFileSizeInBytes);

    mediaRecorder.prepare();
    mediaRecorder.start();
}

More Related Content

PDF
Connecting your phone and home with firebase and android things - James Cogga...
PDF
Redux Sagas - React Alicante
PDF
Kotlin Generation
PDF
Eddystone beacons demo
PDF
What's in Kotlin for us - Alexandre Greschon, MyHeritage
PDF
Performance #1: Memory
PDF
Redux saga: managing your side effects. Also: generators in es6
PPTX
Using Redux-Saga for Handling Side Effects
Connecting your phone and home with firebase and android things - James Cogga...
Redux Sagas - React Alicante
Kotlin Generation
Eddystone beacons demo
What's in Kotlin for us - Alexandre Greschon, MyHeritage
Performance #1: Memory
Redux saga: managing your side effects. Also: generators in es6
Using Redux-Saga for Handling Side Effects

What's hot (19)

ODP
CompletableFuture
PDF
Паразитируем на React-экосистеме (Angular 4+) / Алексей Охрименко (IPONWEB)
PPTX
Rxjs marble-testing
PDF
Android Best Practices
PPTX
Physical web
PDF
Integrating React.js with PHP projects
PPTX
Angular mix chrisnoring
PDF
Google Fit, Android Wear & Xamarin
PPTX
The redux saga begins
PDF
Under the Hood: Using Spring in Grails
PDF
Under the Hood: Using Spring in Grails
PDF
망고100 보드로 놀아보자 19
PDF
【Unite 2017 Tokyo】もっと気軽に、動的なコンテンツ配信を ~アセットバンドルの未来と開発ロードマップ
PDF
Asynchronous programming done right - Node.js
PDF
Programming with ZooKeeper - A basic tutorial
PDF
Event driven javascript
PPTX
Firebase ng2 zurich
PDF
G*なクラウド 雲のかなたに ショートバージョン
PDF
My way to clean android - Android day salamanca edition
CompletableFuture
Паразитируем на React-экосистеме (Angular 4+) / Алексей Охрименко (IPONWEB)
Rxjs marble-testing
Android Best Practices
Physical web
Integrating React.js with PHP projects
Angular mix chrisnoring
Google Fit, Android Wear & Xamarin
The redux saga begins
Under the Hood: Using Spring in Grails
Under the Hood: Using Spring in Grails
망고100 보드로 놀아보자 19
【Unite 2017 Tokyo】もっと気軽に、動的なコンテンツ配信を ~アセットバンドルの未来と開発ロードマップ
Asynchronous programming done right - Node.js
Programming with ZooKeeper - A basic tutorial
Event driven javascript
Firebase ng2 zurich
G*なクラウド 雲のかなたに ショートバージョン
My way to clean android - Android day salamanca edition
Ad

Viewers also liked (20)

PPT
Unmanned Eyes In The Commercial Skies Presentation
PPTX
Jisc RSC Wales ISS 260213
PDF
The socio political culture and economy of the thirteen american colonies
PPSX
Mis 2 Princesas Chico 2
PDF
Overview for Technical Nearshore Investment in Costa Rica
PDF
Presentation of the SIG TEL 4 Health
PPT
Digital literacy and competences as essential life skills
PPTX
Open Access Week | Dag van het onderzoek
PPT
Pero florecera
ZIP
Rik Panganiban's Keynote at SLCC 2011
PDF
Miejsca opuszczone
PPT
Szablon strony www
PPTX
Lift'11 Venture Night Presentation
PDF
Mobile Web 5.0
ZIP
Riks tips on giving great presentations
PDF
PPT
Digital literacies supporting learning
PPT
110217 Adam Presentatie
PPT
Prairie Dog by DeAirick
PPT
Zakonczenie studiow podyplomowych: INFORMATYKA 2007/2008
Unmanned Eyes In The Commercial Skies Presentation
Jisc RSC Wales ISS 260213
The socio political culture and economy of the thirteen american colonies
Mis 2 Princesas Chico 2
Overview for Technical Nearshore Investment in Costa Rica
Presentation of the SIG TEL 4 Health
Digital literacy and competences as essential life skills
Open Access Week | Dag van het onderzoek
Pero florecera
Rik Panganiban's Keynote at SLCC 2011
Miejsca opuszczone
Szablon strony www
Lift'11 Venture Night Presentation
Mobile Web 5.0
Riks tips on giving great presentations
Digital literacies supporting learning
110217 Adam Presentatie
Prairie Dog by DeAirick
Zakonczenie studiow podyplomowych: INFORMATYKA 2007/2008
Ad

Similar to Android workshop (20)

PPTX
Work With Images
PDF
Android - Displaying images
PDF
A comprehensive tutorial to upload, cache, save and share image in Android apps
PPT
Naive application development
PDF
Android memory and performance optimization
PDF
Android memory and performance optimization
PDF
Android memory and performance optimization
PDF
Android, the life of your app
PPT
Getting the Magic on Android Tablets
PPT
Android - Api & Debugging in Android
PPT
Android programming
PDF
Don't Make Android Bad... Again
PPTX
Getting Intimate with Images on Android with James Halpern
PPTX
Android UI Tips & Tricks
PDF
Android ui tips & tricks
PDF
Android Workshop 2013
PPT
Beautifully Usable, Multiple Screens Too
PPT
Ui patterns
PDF
Android programming -_pushing_the_limits
PPTX
From newbie to ...
Work With Images
Android - Displaying images
A comprehensive tutorial to upload, cache, save and share image in Android apps
Naive application development
Android memory and performance optimization
Android memory and performance optimization
Android memory and performance optimization
Android, the life of your app
Getting the Magic on Android Tablets
Android - Api & Debugging in Android
Android programming
Don't Make Android Bad... Again
Getting Intimate with Images on Android with James Halpern
Android UI Tips & Tricks
Android ui tips & tricks
Android Workshop 2013
Beautifully Usable, Multiple Screens Too
Ui patterns
Android programming -_pushing_the_limits
From newbie to ...

More from Michael Galpin (11)

KEY
Android lessons you won't learn in school
KEY
Design Patterns for Tablets and Smartphones
PDF
Scala on Android: Experiences at Bump Technologies
KEY
That’s My App - Running in Your Background - Draining Your Battery
PDF
Persistent Data Structures And Managed References
KEY
Scala on Your Phone
KEY
Mobile Development 101
KEY
RIAs Done Right: Grails, Flex, and EXT GWT
KEY
Eclipse @eBay 2009
PDF
Introduction to Scala for Java Developers
PDF
Eclipse@eBay
Android lessons you won't learn in school
Design Patterns for Tablets and Smartphones
Scala on Android: Experiences at Bump Technologies
That’s My App - Running in Your Background - Draining Your Battery
Persistent Data Structures And Managed References
Scala on Your Phone
Mobile Development 101
RIAs Done Right: Grails, Flex, and EXT GWT
Eclipse @eBay 2009
Introduction to Scala for Java Developers
Eclipse@eBay

Recently uploaded (20)

PPTX
Big Data Technologies - Introduction.pptx
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
Encapsulation_ Review paper, used for researhc scholars
PPTX
MYSQL Presentation for SQL database connectivity
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
KodekX | Application Modernization Development
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PDF
Empathic Computing: Creating Shared Understanding
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Approach and Philosophy of On baking technology
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Big Data Technologies - Introduction.pptx
Network Security Unit 5.pdf for BCA BBA.
Dropbox Q2 2025 Financial Results & Investor Presentation
Encapsulation_ Review paper, used for researhc scholars
MYSQL Presentation for SQL database connectivity
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Understanding_Digital_Forensics_Presentation.pptx
CIFDAQ's Market Insight: SEC Turns Pro Crypto
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Unlocking AI with Model Context Protocol (MCP)
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Per capita expenditure prediction using model stacking based on satellite ima...
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
KodekX | Application Modernization Development
20250228 LYD VKU AI Blended-Learning.pptx
Empathic Computing: Creating Shared Understanding
Review of recent advances in non-invasive hemoglobin estimation
Approach and Philosophy of On baking technology
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Build a system with the filesystem maintained by OSTree @ COSCUP 2025

Android workshop

  • 2. ABOUT: ME ‣ Android engineer, Bump Technologies ‣ ex-eBay: eBay Mobile for Android ‣ Android in Practice ‣ Social info ‣ @michaelg ‣ +Michael Galpin
  • 3. Bump Technologies • Creators of Bump app • Android + iOS • 65M+ Downloads • Creators of BumpCube • Hiring!
  • 4. You down with AIP? • Chapter 11: “Appeal to the senses using multimedia.” • Slideshow app MediaMogul.apk • http:// code.google.com/p/ android-in-practice/
  • 7. “Of all of our inventions for mass communication, pictures still speak the most universally understood language.” -- Walt Disney
  • 14. AndroidManifest.xml <uses-feature android:name="android.hardware.camera" android:required="true" /> <uses-feature android:name="android.hardware.camera.autofocus" android:required="true"/> <uses-feature android:name="android.hardware.camera.flash" android:required="false" /> <uses-feature android:name="android.hardware.camera.front" android:required="false" /> <uses-feature android:name="android.hardware.microphone" android:required="true"/> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
  • 15. Check the front private boolean hasFrontFacingCamera(){ PackageManager mgr = this.getPackageManager(); for (FeatureInfo fi : mgr.getSystemAvailableFeatures()){ if (fi.name.equals(PackageManager.FEATURE_CAMERA_FRONT)){ return true; } } return false; } private Camera getFrontFacingCamera(){ for (int i=0;i<Camera.getNumberOfCameras();i++){ Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(i, info); if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){ return Camera.open(i); } } return null; }
  • 16. Check the front private boolean hasFrontFacingCamera(){ PackageManager mgr = this.getPackageManager(); for (FeatureInfo fi : mgr.getSystemAvailableFeatures()){ if (fi.name.equals(PackageManager.FEATURE_CAMERA_FRONT)){ return true; } } return false; } private Camera getFrontFacingCamera(){ for (int i=0;i<Camera.getNumberOfCameras();i++){ Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(i, info); if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){ return Camera.open(i); } } return null; }
  • 17. But I have to support Android 2.1
  • 21. LOLZ // Can't use Build.VERSION_CODES.GINGERBREAD if (Build.VERSION.SDK_INT >= 9){ PostFroyoClass wontThisBlowUp = new PostFroyoClass(); }
  • 22. These are my customers!
  • 23. These are my customers!
  • 24. Proprietary APIs FTW! import com.sprint.hardware.twinCamDevice.FrontFacingCamera; Camera evoCam = FrontFacingCamera.getFrontFacingCamera();
  • 30. MediaPlayer.framework? [MPMusicPlayerController iPodMusicPlayer] ?? [MusicLibrary sharedMusicLibrary] ??? UIImagePickerController ?
  • 31. MediaPlayer.framework? [MPMusicPlayerController iPodMusicPlayer] ?? [MusicLibrary sharedMusicLibrary] ??? UIImagePickerController ?
  • 32. MediaPlayer.framework? [MPMusicPlayerController iPodMusicPlayer] ?? [MusicLibrary sharedMusicLibrary] ??? UIImagePickerController ? Files !!!!
  • 33. Inside your APK External
  • 34. Just give it to me /res/raw Resources res = this.getResources(); InputStream stream = res.openRawResource(R.raw.sunflower); MediaPlayer.create(this, R.raw.constancy).start();
  • 35. I like big / assets and I cannot lie
  • 36. MediaPlayer player = new MediaPlayer(); AssetManager mgr = getResources().getAssets(); String audioDir = "audio"; final LinkedList<FileDescriptor> queue = new LinkedList<FileDescriptor>(); for (String song : mgr.list("audio")){ queue.add( mgr.openFd(audioDir + "/" + song).getFileDescriptor()); } if (!queue.isEmpty()){ FileDescriptor song = queue.poll(); player.setDataSource(song); player.prepare(); player.start(); } player.setOnCompletionListener(new OnCompletionListener(){ @Override public void onCompletion(MediaPlayer mp) { if (!queue.isEmpty()){ FileDescriptor song = queue.poll(); player.setDataSource(song); player.prepare(); player.start(); } } });
  • 38. File picturesDir = Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES);
  • 39. File picturesDir = Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES);
  • 42. Loading local images InputStream stream = new FileInputStream(imgFile); Bitmap bm = BitmapFactory.decodeStream(stream);
  • 43. Loading local images InputStream stream = new FileInputStream(imgFile); Bitmap bm = BitmapFactory.decodeStream(stream); // calculate desired width and height Bitmap thumb = ThumbnailUtils.extractThumbnail(bm, width, height); Bitmap thumb = Bitmap.createScaledBitmap(bm, width, height, false);
  • 45. Loading without exploding public static Bitmap decodeDownsizedBitmapStream(File file, int target, Context context) throws IOException { FileInputStream stream = new FileInputStream(file); Pair<Integer, Integer> source = getDimensionsForStream(stream); stream.close(); FileInputStream in = new FileInputStream(file); Options options = new Options(); options.inSampleSize = 1 + getDownSampleSize(max(source.first, source.second), target); return BitmapFactory.decodeStream(in, null, options); } public static Pair<Integer, Integer> getDimensionsForStream(InputStream in){ Options options = new Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(in, null, options); return new Pair<Integer, Integer>(options.outWidth, options.outHeight); } public static int getDownSampleSize(int source, int target){ int size = 1; if (source <= 2*target){ int power = (int) ((log (source / target)) / log(2)); size = (int) pow(2, power); } return size; }
  • 46. Loading web images URL url = new URL(urlString); // NOTE, be careful about just doing "url.openStream()" // it's a shortcut for openConnection().getInputStream() and doesn't set timeouts // the defaults are "infinite" so it will wait forever if endpoint server is down // do it properly with a few more lines of code . . . URLConnection conn = url.openConnection(); conn.setConnectTimeout(3000); conn.setReadTimeout(5000); Bitmap bitmap = BitmapFactory.decodeStream(conn.getInputStream());
  • 48. AsyncTask FTW! private class RetrieveImageTask extends AsyncTask<String, Void, Bitmap> { private ImageView imageView; public RetrieveImageTask(ImageView imageView) { this.imageView = imageView; } @Override protected Bitmap doInBackground(String... args) { try { URL url = new URL(args[0]); URLConnection conn = url.openConnection(); conn.setConnectTimeout(3000); conn.setReadTimeout(5000); return BitmapFactory.decodeStream(conn.getInputStream()); } catch (Exception e) { } return null; } @Override protected void onPostExecute(Bitmap bitmap) { if (bitmap != null) { imageView.setImageBitmap(bitmap); } } }
  • 49. Loading a dozen images isn’t cool. Y’know what’s cool? Loading a billion images.
  • 50. Hot or Not? private class DealsAdapter extends ArrayAdapter<Item> { @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.list_item, parent, false); } TextView text = (TextView) convertView.findViewById(R.id.deal_title); ImageView image = (ImageView) convertView.findViewById(R.id.deal_img); Item item = getItem(position); if (item != null) { text.setText(item.getTitle()); new RetrieveImageTask(image).execute(item.getSmallPicUrl()); } return convertView; } }
  • 51. Watch out for leaks public class LeakProofActivity extends Activity { LeakProofAsyncTask task; private class LeakProofAsyncTask extends AsyncTask<String, Void, Bitmap>{ Context potentialLeak = LeakProofActivity.this; @Override protected Bitmap doInBackground(String... arg0) { // Do something that takes a long time // Note if you need a Context here, use Application return null; } @Override protected void onPostExecute(Bitmap result){ // update UI, etc. } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); task = (LeakProofAsyncTask) getLastNonConfigurationInstance(); if (task != null){ task.potentialLeak = this; } } @Override protected void onDestroy() { super.onDestroy(); if (task != null){ task.potentialLeak = null; } } @Override public Object onRetainNonConfigurationInstance() { return task; } }
  • 53. How to build a cache? • WeakReferences? • SoftReferences? • WeakHashMap? • MapMaker?
  • 54. LinkedHashMap!? private static class ImageCache extends LinkedHashMap<String, Bitmap>{ private final int capacity; public ImageCache(int capacity){ super(capacity/2, 0.75f, true); this.capacity = capacity; } @Override protected boolean removeEldestEntry(Entry<String, Bitmap> eldest) { return this.size() > capacity; } }
  • 55. Disk Cache • Context.getCacheDir() • SQLite • Environment.getExternalStorage()
  • 56. Sidebar: Bitmaps & Heap Reported by andreas....@googlemail.com, May 22, 2010 Note: This is NOT a requst for assistance! In my applications, I keep getting the following exception: E/AndroidRuntime( 1420): java.lang.OutOfMemoryError: bitmap size exceeds VM budget E/AndroidRuntime( 1420): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method) E/AndroidRuntime( 1420): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:459) E/AndroidRuntime( 1420): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:515) E/AndroidRuntime( 1420): at de.schildbach.bitmapbug.MyActivity.bitmap(MyActivity.java:38) Comment 1 by romaingu...@gtempaccount.com, May 23, 2010 Your app needs to use less memory. http://code.google.com/p/android/issues/detail?id=8488
  • 58. Use Placeholders ImageView image = (ImageView) convertView.findViewById(R.id.deal_img); image.setImageBitmap( BitmapFactory.decodeResource( getResources(), R.drawable.temp)); Item item = getItem(position); new RetrieveImageTask(image). execute(item.getSmallPicUrl());
  • 59. @Override View Holders public View getView(final int position, View cell, ViewGroup parent) { ViewHolder holder = (ViewHolder) cell.getTag(); if (holder == null){ holder = new ViewHolder(cell); cell.setTag(holder); } Bitmap thumb = (Bitmap) getItem(position); holder.img.setImageBitmap(thumb); File file = getImageFile(position); if (selectedFiles.contains(file)){ holder.cbox.setChecked(true); } else { holder.cbox.setChecked(false); } return cell; } static class ViewHolder { final ImageView img; final CheckBox cbox; ViewHolder(View cell){ img = (ImageView) cell.findViewById(R.id.thumb); cbox = (CheckBox) cell.findViewById(R.id.cbox); } }
  • 62. SQL? NoSQL? SortOfSQL!
  • 63. Queries Cursor cursor = getContentResolver().query(table, columns, whereClause, paramValues, sortOrder); while (cursor.moveToNext()){ // process results } cursor.close();
  • 64. Music import static android.provider.BaseColumns._ID; import static android.provider.MediaStore.Audio.AudioColumns.ARTIST; import static android.provider.MediaStore.Audio.AudioColumns.IS_MUSIC; import static android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; import static android.provider.MediaStore.MediaColumns.DATA; import static android.provider.MediaStore.MediaColumns.TITLE; String[] columns = {TITLE,ARTIST,_ID, DATA}; // where clause so we don't get ringtones, podcasts, etc. String whereClause = IS_MUSIC + " = ?"; String[] whereValues = {"1"}; cursor = managedQuery(EXTERNAL_CONTENT_URI, columns, whereClause, whereValues, null );
  • 65. Contacts String[] projection = {Phone.CONTACT_ID, Phone.NUMBER}; String selection = Data.IN_VISIBLE_GROUP + "=1 AND " + Phone.NUMBER + " LIKE ?"; String[] selectionArgs = {"%" + phoneSubStr + "%"}; Cursor phoneCursor = resolver.query(Phone.CONTENT_URI, projection, selection, selectionArgs, null); String[] projection = new String[] {StructuredName.GIVEN_NAME, StructuredName.FAMILY_NAME, StructuredName.RAW_CONTACT_ID, StructuredName.CONTACT_ID}; String selection = StructuredName.CONTACT_ID+ " = ? AND " + Data.MIMETYPE + " = '" + StructuredName.CONTENT_ITEM_TYPE +"'"; String[] selectionArgs = new String[] {contact.id}; Cursor nameCursor = resolver.query(Data.CONTENT_URI, projection, selection, selectionArgs, null);
  • 68. Scanning 101 MediaScannerConnection conn = new MediaScannerConnection(this, new MediaScannerConnectionClient(){ public void onMediaScannerConnected(){ scanFile("/some/path/SomeSong.mp3", "audio/mpeg3"); scanFile("/some/other/path/IMG1999.jpg", "image/jpeg"); } public void onScanCompleted(String path, Uri uri){ Log.d("LogTag", "Media scanned uir=" + uri.toString()); } }); conn.connect();
  • 69. Cursor Adapter
  • 71. Avoid
  • 72. Use Other Apps private static final int SELECT_VIDEO = 1; private Uri videoUri; @Override protected void onCreate(Bundle savedInstanceState) { Button vidBtn = (Button) findViewById(R.id.vidBtn); vidBtn.setOnClickListener(new OnClickListener(){ @Override public void onClick(View button) { Intent videoChooser = new Intent(Intent.ACTION_GET_CONTENT); videoChooser.setType("video/*"); startActivityForResult(videoChooser, SELECT_VIDEO); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == SELECT_VIDEO && resultCode == RESULT_OK){ videoUri = data.getData(); } }
  • 75. Dissolve Animation private void nextSlide() { AlphaAnimation animation = new AlphaAnimation(0.0f, 1.0f); if ((count % 2) == 0) { animation = new AlphaAnimation(1.0f, 0.0f); } animation.setStartOffset(TIME_PER_SLIDE); animation.setDuration(TIME_PER_SLIDE); animation.setFillAfter(true); animation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) {} @Override public void onAnimationRepeat(Animation animation) {} @Override public void onAnimationEnd(Animation animation) { if (playingSlides){ nextImage = getNextImage(); ImageView backgroundImage = (count % 2 == 0) ? rightSlide : leftSlide; backgroundImage.setImageBitmap(nextImage); count++; nextSlide(); } } }); rightSlide.startAnimation(animation); currentImage = nextImage; }
  • 77. Animators ImageView backgroundImage = (count % 2 == 0) ? rightSlide : leftSlide; ObjectAnimator anim = ObjectAnimator.ofFloat(backgroundImage, "alpha", 0.0f, 1.0f); anim.addListener(new AnimatorListenerAdapter(){ public void onAnimationEnd(Animator animator){ nextSlide(); } });
  • 81. playBtn.setOnClickListener(new OnClickListener(){ private Handler handler = new Handler(); MediaPlayer player = null; long maxTime = 15L*1000; // 15 seconds long timeLeft = maxTime; Audio Preview Runnable autoStop; @Override public void onClick(View view) { if (player == null){ player = MediaPlayer.create(activity, song.uri); } if (!playingSongs.contains(song.id)){ Start/Resume player.start(); playingSongs.add(song.id); autoStop = new Runnable(){ Timer @Override public void run() { player.pause(); player.seekTo(0); playingSongs.remove(song.id); playBtn.setText(R.string.play); timeLeft = maxTime; } }; handler.postDelayed(autoStop, timeLeft); playBtn.setText(R.string.pause); } else { player.pause(); Pause playingSongs.remove(song.id); timeLeft = maxTime - player.getCurrentPosition(); playBtn.setText(R.string.play); Calc time left handler.removeCallbacks(autoStop); } } });
  • 83. Handler? Pipeline Thread Looper.prepare(); handler = new Handler(); // handle tasks in queue Looper.loop();
  • 84. Handler? Thread Pipeline Thread Looper.prepare(); handler = new Handler(); // handle tasks in queue Looper.loop(); // long running task Message msg = handler.obtainMessage(); handler.sendMessage(msg);
  • 85. Handler? Thread Pipeline Thread Thread Looper.prepare(); handler = new Handler(); // handle tasks in queue // long running task handler.post( Looper.loop(); new Runnable() {... }); // long running task Message msg = handler.obtainMessage(); handler.sendMessage(msg);
  • 86. Theme Muzak private MediaPlayer player; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setupThemeMusic(); } @Override public void onResume() { super.onResume(); if (player != null){ player.start(); } @Override } public void onPause(){ super.onPause(); if (player != null && player.isPlaying()){ player.pause(); } } @Override protected void onDestroy() { super.onDestroy(); if (player != null && player.isPlaying()){ player.stop(); } player.release(); }
  • 89. Video Playback Uri videoUri = ...; VideoView video = (VideoView) findViewById(R.id.video); video.setVideoURI(videoUri); MediaController controller = new MediaController(this); controller.setMediaPlayer(video); video.setMediaController(controller); video.requestFocus(); video.start();
  • 92. Use the Camera (app) import static android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI; Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); photoUri = getContentResolver().insert( EXTERNAL_CONTENT_URI, new ContentValues()); intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); startActivityForResult(intent,TAKE_PHOTO); @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == Activity.RESULT_OK && requestCode == TAKE_PHOTO){ ImageView img = (ImageView) findViewById(R.id.photoThumb); InputStream stream = getContentResolver().openInputStream(photoUri); Bitmap bmp = BitmapFactory.decodeStream(stream); img.setImageBitmap(bmp); } }
  • 94. EXIF!
  • 96. Correctly Oriented Options photoImageOptions = new BitmapFactory.Options(); Matrix matrix = new Matrix(); ExifInterface ei = new ExifInterface(somePath); int orientation = ei.getAttributeInt(TAG_ORIENTATION, ORIENTATION_UNDEFINED); int angle = 0; switch (orientation) { case ORIENTATION_ROTATE_90 : angle=90; break; case ORIENTATION_ROTATE_180 : angle=180; break; case ORIENTATION_ROTATE_270 : angle=270; break; default: break; } Bitmap bm = BitmapFactory.decodeFile(path, photoImageOptions); if (angle > 0){ matrix.setRotate(angle); Bitmap bm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); }
  • 97. Geo Tagging ExifInterface exif = new ExifInterface(filename); exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, latitude); exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, longitude); exif.saveAttributes(); “dd/1,mm/1,ss/1”
  • 99. Getting a (geo) fix LocationManager mgr = getSystemService(LOCATION_SERVICE); for (String provider : mgr.getAllProviders()){ Location last = mgr.getLastKnownLocation(last); mgr.requestLocationUpdates(provider, 60000, 500, new LocationListener(){ public void onLocationChanged(Location loc){ // do stuff } // other methods }); }
  • 101. Uploading a photo String url = "http://my/server"; HttpClient client = new DefaultHttpClient(); HttpContext context = new BasicHttpContext(); HttpPost post = new HttpPost(url); File imgFile = new File("/path/to/my/image"); MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); entity.addPart("image", new FileBody(imgFile)); post.setEntity(entity); HttpResponse response = client.execute(httpPost, localContext);
  • 103. Upload Service UploadService extends IntentService { protected void onHandleIntent(Intent i){ String imgPath = i.getStringExtra("file"); // upload code goes here } } Intent i = new Intent(this, UploadService.class); i.putExtra("file", "/path/to/my/image"); startService(i);
  • 105. Process Priority Foreground Visible Service Background Empty
  • 108. Video Preview private SurfaceHolder holder; private Camera camera; private MediaRecorder mediaRecorder; private File tempFile; private SurfaceView preview; private boolean isRecording = false; private final int maxDurationInMs = 20000; private final long maxFileSizeInBytes = 500000; private final int videoFramesPerSecond = 20; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); preview = new SurfaceView(this); holder = preview.getHolder(); holder.addCallback(cameraman); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); setContentView(preview); tempFile = new File(getCacheDir(), "temp.mov"); if (tempFile.length() > 0){ tempFile.delete(); } }
  • 109. Video Preview private Callback cameraman = new Callback(){ @Override public void surfaceCreated(SurfaceHolder holder) { camera = Camera.open(); camera.setPreviewDisplay(holder); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) { Parameters params = camera.getParameters(); List<Size> sizes = params.getSupportedPreviewSizes(); Size optimalSize = getOptimalPreviewSize(sizes, width, height); params.setPreviewSize(optimalSize.width, optimalSize.height); camera.setParameters(params); camera.startPreview(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { camera.stopPreview(); camera.release(); } };
  • 110. Video Preview private void startRecording(){ if (isRecording){ return; } isRecording = true; camera.unlock(); mediaRecorder = new MediaRecorder(); mediaRecorder.setCamera(camera); mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); mediaRecorder.setMaxDuration(maxDurationInMs); mediaRecorder.setOutputFile(tempFile.getPath()); mediaRecorder.setVideoFrameRate(videoFramesPerSecond); mediaRecorder.setVideoSize(preview.getWidth(), preview.getHeight()); mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT); mediaRecorder.setPreviewDisplay(holder.getSurface()); mediaRecorder.setMaxFileSize(maxFileSizeInBytes); mediaRecorder.prepare(); mediaRecorder.start(); }

Editor's Notes