/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.cooliris.media; import android.app.AlertDialog; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.DialogInterface.OnCancelListener; import android.content.DialogInterface.OnClickListener; import android.database.Cursor; import android.database.sqlite.SQLiteException; import android.media.MediaPlayer; import android.net.Uri; import android.os.Handler; import android.provider.MediaStore; import android.provider.MediaStore.Video; import android.view.View; import android.widget.MediaController; import android.widget.VideoView; import com.cooliris.app.Res; public class MovieViewControl implements MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener { @SuppressWarnings("unused") private static final String TAG = "MovieViewControl"; private static final int HALF_MINUTE = 30 * 1000; private static final int TWO_MINUTES = 4 * HALF_MINUTE; // Copied from MediaPlaybackService in the Music Player app. Should be // public, but isn't. private static final String SERVICECMD = "com.android.music.musicservicecommand"; private static final String CMDNAME = "command"; private static final String CMDPAUSE = "pause"; private final VideoView mVideoView; private final View mProgressView; private final Uri mUri; private final ContentResolver mContentResolver; Handler mHandler = new Handler(); Runnable mPlayingChecker = new Runnable() { public void run() { if (mVideoView.isPlaying()) { mProgressView.setVisibility(View.GONE); } else { mHandler.postDelayed(mPlayingChecker, 250); } } }; public static String formatDuration(final Context context, int durationMs) { int duration = durationMs / 1000; int h = duration / 3600; int m = (duration - h * 3600) / 60; int s = duration - (h * 3600 + m * 60); String durationValue; if (h == 0) { durationValue = String.format(context.getString(Res.string.details_ms), m, s); } else { durationValue = String.format(context.getString(Res.string.details_hms), h, m, s); } return durationValue; } public MovieViewControl(View rootView, Context context, Uri videoUri) { mContentResolver = context.getContentResolver(); mVideoView = (VideoView) rootView.findViewById(Res.id.surface_view); mProgressView = rootView.findViewById(Res.id.progress_indicator); mUri = videoUri; // For streams that we expect to be slow to start up, show a // progress spinner until playback starts. String scheme = mUri.getScheme(); if ("http".equalsIgnoreCase(scheme) || "rtsp".equalsIgnoreCase(scheme)) { mHandler.postDelayed(mPlayingChecker, 250); } else { mProgressView.setVisibility(View.GONE); } mVideoView.setOnErrorListener(this); mVideoView.setOnCompletionListener(this); mVideoView.setVideoURI(mUri); mVideoView.setMediaController(new MediaController(context)); // make the video view handle keys for seeking and pausing mVideoView.requestFocus(); Intent i = new Intent(SERVICECMD); i.putExtra(CMDNAME, CMDPAUSE); context.sendBroadcast(i); final Integer bookmark = getBookmark(); if (bookmark != null) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(Res.string.resume_playing_title); builder .setMessage(String .format(context.getString(Res.string.resume_playing_message), formatDuration(context, bookmark))); builder.setOnCancelListener(new OnCancelListener() { public void onCancel(DialogInterface dialog) { onCompletion(); } }); builder.setPositiveButton(Res.string.resume_playing_resume, new OnClickListener() { public void onClick(DialogInterface dialog, int which) { mVideoView.seekTo(bookmark); mVideoView.start(); } }); builder.setNegativeButton(Res.string.resume_playing_restart, new OnClickListener() { public void onClick(DialogInterface dialog, int which) { mVideoView.start(); } }); builder.show(); } else { mVideoView.start(); } } private static boolean uriSupportsBookmarks(Uri uri) { String scheme = uri.getScheme(); String authority = uri.getAuthority(); return ("content".equalsIgnoreCase(scheme) && MediaStore.AUTHORITY.equalsIgnoreCase(authority)); } private Integer getBookmark() { if (!uriSupportsBookmarks(mUri)) { return null; } String[] projection = new String[] { Video.VideoColumns.DURATION, Video.VideoColumns.BOOKMARK }; try { Cursor cursor = mContentResolver.query(mUri, projection, null, null, null); if (cursor != null) { try { if (cursor.moveToFirst()) { int duration = getCursorInteger(cursor, 0); int bookmark = getCursorInteger(cursor, 1); if ((bookmark < HALF_MINUTE) || (duration < TWO_MINUTES) || (bookmark > (duration - HALF_MINUTE))) { return null; } return Integer.valueOf(bookmark); } } finally { cursor.close(); } } } catch (SQLiteException e) { // ignore } return null; } private static int getCursorInteger(Cursor cursor, int index) { try { return cursor.getInt(index); } catch (SQLiteException e) { return 0; } catch (NumberFormatException e) { return 0; } } private void setBookmark(int bookmark, int duration) { if (!uriSupportsBookmarks(mUri)) { return; } ContentValues values = new ContentValues(); values.put(Video.VideoColumns.BOOKMARK, Integer.toString(bookmark)); values.put(Video.VideoColumns.DURATION, Integer.toString(duration)); try { mContentResolver.update(mUri, values, null, null); } catch (SecurityException ex) { // Ignore, can happen if we try to set the bookmark on a read-only // resource such as a video attached to GMail. } catch (SQLiteException e) { // ignore. can happen if the content doesn't support a bookmark // column. } catch (UnsupportedOperationException e) { // ignore. can happen if the external volume is already detached. } } public void onPause() { mHandler.removeCallbacksAndMessages(null); setBookmark(mVideoView.getCurrentPosition(), mVideoView.getDuration()); mVideoView.suspend(); } public void onResume() { mVideoView.resume(); } public void onDestroy() { mVideoView.stopPlayback(); } public boolean onError(MediaPlayer player, int arg1, int arg2) { mHandler.removeCallbacksAndMessages(null); mProgressView.setVisibility(View.GONE); return false; } public void onCompletion(MediaPlayer mp) { onCompletion(); } public void onCompletion() { } }