org.mariotaku.twidere.activity.support.MediaViewerActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.mariotaku.twidere.activity.support.MediaViewerActivity.java

Source

/*
 * 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 org.mariotaku.twidere.activity.support;

import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.Uri;
import android.os.AsyncTask.Status;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.v4.util.Pair;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.ShareActionProvider;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnGenericMotionListener;
import android.view.ViewGroup;
import android.webkit.MimeTypeMap;
import android.widget.ImageButton;
import android.widget.MediaController;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.TextView;

import com.davemorrissey.labs.subscaleview.ImageSource;
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
import com.desmond.asyncmanager.AsyncManager;
import com.desmond.asyncmanager.TaskRunnable;
import com.pnikosis.materialishprogress.ProgressWheel;
import com.sprylab.android.widget.TextureVideoView;

import org.apache.commons.lang3.ArrayUtils;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.support.SupportFixedFragmentStatePagerAdapter;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.fragment.support.BaseSupportFragment;
import org.mariotaku.twidere.fragment.support.ViewStatusDialogFragment;
import org.mariotaku.twidere.loader.support.TileImageLoader;
import org.mariotaku.twidere.loader.support.TileImageLoader.DownloadListener;
import org.mariotaku.twidere.loader.support.TileImageLoader.Result;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableMedia.VideoInfo.Variant;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.util.AsyncTaskUtils;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
import org.mariotaku.twidere.util.MenuUtils;
import org.mariotaku.twidere.util.SaveFileTask;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.VideoLoader;
import org.mariotaku.twidere.util.VideoLoader.VideoLoadingListener;

import java.io.File;

import pl.droidsonroids.gif.GifSupportChecker;
import pl.droidsonroids.gif.GifTextureView;
import pl.droidsonroids.gif.InputSource.FileSource;

public final class MediaViewerActivity extends BaseAppCompatActivity implements Constants, OnPageChangeListener {

    private static final String EXTRA_LOOP = "loop";
    private static boolean ANIMATED_GIF_SUPPORTED = GifSupportChecker.isSupported();
    private ViewPager mViewPager;
    private MediaPagerAdapter mPagerAdapter;
    private View mMediaStatusContainer;

    @Override
    public int getThemeColor() {
        return ThemeUtils.getUserAccentColor(this);
    }

    @Override
    public int getThemeResourceId() {
        return ThemeUtils.getViewerThemeResource(this);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home: {
            finish();
            return true;
        }
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
        }
        setContentView(R.layout.activity_media_viewer);
        mPagerAdapter = new MediaPagerAdapter(this);
        mViewPager.setAdapter(mPagerAdapter);
        mViewPager.setPageMargin(getResources().getDimensionPixelSize(R.dimen.element_spacing_normal));
        mViewPager.setOnPageChangeListener(this);
        final Intent intent = getIntent();
        final long accountId = intent.getLongExtra(EXTRA_ACCOUNT_ID, -1);
        final ParcelableMedia[] media = Utils.newParcelableArray(intent.getParcelableArrayExtra(EXTRA_MEDIA),
                ParcelableMedia.CREATOR);
        final ParcelableMedia currentMedia = intent.getParcelableExtra(EXTRA_CURRENT_MEDIA);
        mPagerAdapter.setMedia(accountId, media);
        final int currentIndex = ArrayUtils.indexOf(media, currentMedia);
        if (currentIndex != -1) {
            mViewPager.setCurrentItem(currentIndex, false);
        }
        if (isMediaStatusEnabled() && intent.hasExtra(EXTRA_STATUS)) {
            mMediaStatusContainer.setVisibility(View.VISIBLE);
            final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
            final Fragment f = new ViewStatusDialogFragment();
            final Bundle args = new Bundle();
            args.putParcelable(EXTRA_STATUS, intent.getParcelableExtra(EXTRA_STATUS));
            args.putBoolean(EXTRA_SHOW_MEDIA_PREVIEW, false);
            args.putBoolean(EXTRA_SHOW_EXTRA_TYPE, false);
            f.setArguments(args);
            ft.replace(R.id.media_status, f);
            ft.commit();
        } else {
            mMediaStatusContainer.setVisibility(View.GONE);
        }
        updatePositionTitle();
    }

    public boolean hasStatus() {
        return getIntent().hasExtra(EXTRA_STATUS);
    }

    @Override
    public void onContentChanged() {
        super.onContentChanged();
        mViewPager = (ViewPager) findViewById(R.id.view_pager);
        mMediaStatusContainer = findViewById(R.id.media_status_container);
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        updatePositionTitle();
        setBarVisibility(true);
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }

    @Override
    public boolean handleKeyboardShortcutSingle(@NonNull KeyboardShortcutsHandler handler, int keyCode,
            @NonNull KeyEvent event) {
        final String action = handler.getKeyAction(CONTEXT_TAG_NAVIGATION, keyCode, event);
        if (action != null) {
            switch (action) {
            case ACTION_NAVIGATION_PREVIOUS_TAB: {
                final int previous = mViewPager.getCurrentItem() - 1;
                if (previous < 0) {
                } else if (previous < mPagerAdapter.getCount()) {
                    mViewPager.setCurrentItem(previous, true);
                }
                return true;
            }
            case ACTION_NAVIGATION_NEXT_TAB: {
                final int next = mViewPager.getCurrentItem() + 1;
                if (next >= mPagerAdapter.getCount()) {
                } else if (next >= 0) {
                    mViewPager.setCurrentItem(next, true);
                }
                return true;
            }
            case ACTION_NAVIGATION_BACK: {
                onBackPressed();
                return true;
            }
            }
        }
        return super.handleKeyboardShortcutSingle(handler, keyCode, event);
    }

    @Override
    protected boolean shouldApplyWindowBackground() {
        return false;
    }

    private ParcelableStatus getStatus() {
        return getIntent().getParcelableExtra(EXTRA_STATUS);
    }

    private boolean isBarShowing() {
        final ActionBar actionBar = getSupportActionBar();
        if (actionBar == null)
            return false;
        return actionBar.isShowing();
    }

    private boolean isMediaStatusEnabled() {
        return Boolean.parseBoolean("false");
    }

    private void setBarVisibility(boolean visible) {
        final ActionBar actionBar = getSupportActionBar();
        if (actionBar == null)
            return;
        if (visible) {
            actionBar.show();
        } else {
            actionBar.hide();
        }

        mMediaStatusContainer.setVisibility(isMediaStatusEnabled() && visible ? View.VISIBLE : View.GONE);
    }

    private void toggleBar() {
        setBarVisibility(!isBarShowing());
    }

    private void updatePositionTitle() {
        setTitle(String.format("%d / %d", mViewPager.getCurrentItem() + 1, mPagerAdapter.getCount()));
    }

    public static class BaseImagePageFragment extends BaseSupportFragment
            implements DownloadListener, LoaderCallbacks<Result>, OnClickListener {

        private SubsamplingScaleImageView mImageView;
        private ProgressWheel mProgressBar;
        private boolean mLoaderInitialized;
        private float mContentLength;
        private SaveFileTask mSaveFileTask;

        private File mImageFile;

        @Override
        public void onBaseViewCreated(View view, @Nullable Bundle savedInstanceState) {
            super.onBaseViewCreated(view, savedInstanceState);
            mImageView = (SubsamplingScaleImageView) view.findViewById(R.id.image_view);
            mProgressBar = (ProgressWheel) view.findViewById(R.id.load_progress);
        }

        @Override
        public void onClick(View v) {
            final MediaViewerActivity activity = (MediaViewerActivity) getActivity();
            if (activity == null)
                return;
            activity.toggleBar();
        }

        @Override
        public Loader<Result> onCreateLoader(final int id, final Bundle args) {
            setLoadProgressVisibility(View.VISIBLE);
            mProgressBar.spin();
            invalidateOptionsMenu();
            final ParcelableMedia media = getMedia();
            final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1);
            return new TileImageLoader(getActivity(), this, accountId, Uri.parse(media.media_url));
        }

        @Override
        public void onLoadFinished(final Loader<TileImageLoader.Result> loader, final TileImageLoader.Result data) {
            if (data.hasData()) {
                mImageFile = data.file;
                if (data.useDecoder) {
                    setImageViewVisibility(View.VISIBLE);
                    mImageView.setImage(ImageSource.uri(Uri.fromFile(data.file)));
                } else {
                    setImageViewVisibility(View.VISIBLE);
                    mImageView.setImage(ImageSource.bitmap(data.bitmap));
                }
            } else {
                mImageView.recycle();
                mImageFile = null;
                setImageViewVisibility(View.GONE);
                Utils.showErrorMessage(getActivity(), null, data.exception, true);
            }
            setLoadProgressVisibility(View.GONE);
            setLoadProgress(0);
            invalidateOptionsMenu();
        }

        @Override
        public void onLoaderReset(final Loader<TileImageLoader.Result> loader) {
        }

        @Override
        public void onDownloadError(final Throwable t) {
            mContentLength = 0;
        }

        @Override
        public void onDownloadFinished() {
            mContentLength = 0;
        }

        @Override
        public void onDownloadStart(final long total) {
            mContentLength = total;
            mProgressBar.spin();
        }

        @Override
        public void onProgressUpdate(final long downloaded) {
            if (mContentLength <= 0) {
                if (!mProgressBar.isSpinning()) {
                    mProgressBar.spin();
                }
                return;
            }
            setLoadProgress(downloaded / mContentLength);
        }

        protected void setImageViewVisibility(int visible) {
            mImageView.setVisibility(visible);

        }

        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                @Nullable Bundle savedInstanceState) {
            return inflater.inflate(R.layout.fragment_media_page_image_compat, container, false);
        }

        protected void setLoadProgress(float progress) {
            mProgressBar.setProgress(progress);
        }

        protected void setLoadProgressVisibility(int visibility) {
            mProgressBar.setVisibility(visibility);
        }

        private ParcelableMedia getMedia() {
            final Bundle args = getArguments();
            return args.getParcelable(EXTRA_MEDIA);
        }

        private void loadImage() {
            getLoaderManager().destroyLoader(0);
            if (!mLoaderInitialized) {
                getLoaderManager().initLoader(0, getArguments(), this);
                mLoaderInitialized = true;
            } else {
                getLoaderManager().restartLoader(0, getArguments(), this);
            }
        }

        private void openInBrowser() {
            final ParcelableMedia media = getMedia();
            final Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.addCategory(Intent.CATEGORY_BROWSABLE);
            if (media.page_url != null) {
                intent.setData(Uri.parse(media.page_url));
            } else {
                intent.setData(Uri.parse(media.media_url));
            }
            startActivity(intent);
        }

        private void saveToGallery() {
            if (mSaveFileTask != null && mSaveFileTask.getStatus() == Status.RUNNING)
                return;
            final File file = mImageFile;
            final boolean hasImage = file != null && file.exists();
            if (!hasImage)
                return;
            mSaveFileTask = SaveFileTask.saveImage(getActivity(), file);
            mSaveFileTask.execute();
        }

        @Override
        public void onPrepareOptionsMenu(Menu menu) {
            super.onPrepareOptionsMenu(menu);
            final boolean isLoading = getLoaderManager().hasRunningLoaders();
            final TaskRunnable<File, Pair<Boolean, Intent>, Menu> checkState = new TaskRunnable<File, Pair<Boolean, Intent>, Menu>() {
                @Override
                public Pair<Boolean, Intent> doLongOperation(File file) throws InterruptedException {
                    final boolean hasImage = file != null && file.exists();
                    if (!hasImage) {
                        return Pair.create(false, null);
                    }
                    final Intent intent = new Intent(Intent.ACTION_SEND);
                    final Uri fileUri = Uri.fromFile(file);
                    intent.setDataAndType(fileUri, Utils.getImageMimeType(file));
                    intent.putExtra(Intent.EXTRA_STREAM, fileUri);
                    final MediaViewerActivity activity = (MediaViewerActivity) getActivity();
                    if (activity.hasStatus()) {
                        final ParcelableStatus status = activity.getStatus();
                        intent.putExtra(Intent.EXTRA_TEXT, Utils.getStatusShareText(activity, status));
                        intent.putExtra(Intent.EXTRA_SUBJECT, Utils.getStatusShareSubject(activity, status));
                    }
                    return Pair.create(true, intent);
                }

                @Override
                public void callback(Menu menu, Pair<Boolean, Intent> result) {
                    final boolean hasImage = result.first;
                    MenuUtils.setMenuItemAvailability(menu, R.id.refresh, !hasImage && !isLoading);
                    MenuUtils.setMenuItemAvailability(menu, R.id.share, hasImage && !isLoading);
                    MenuUtils.setMenuItemAvailability(menu, R.id.save, hasImage && !isLoading);
                    if (!hasImage)
                        return;
                    final MenuItem shareItem = menu.findItem(R.id.share);
                    final ShareActionProvider shareProvider = (ShareActionProvider) MenuItemCompat
                            .getActionProvider(shareItem);
                    shareProvider.setShareIntent(result.second);
                }
            };
            checkState.setParams(mImageFile);
            checkState.setResultHandler(menu);
            AsyncManager.runBackgroundTask(checkState);
        }

        @Override
        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
            inflater.inflate(R.menu.menu_media_viewer_image_page, menu);
            final MenuItem shareItem = menu.findItem(R.id.share);
            final ShareActionProvider shareProvider = (ShareActionProvider) MenuItemCompat
                    .getActionProvider(shareItem);
            shareProvider.setShareHistoryFileName(null);
        }

        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
            case MENU_OPEN_IN_BROWSER: {
                openInBrowser();
                return true;
            }
            case MENU_SAVE: {
                saveToGallery();
                return true;
            }
            case MENU_REFRESH: {
                loadImage();
                return true;
            }
            }
            return super.onOptionsItemSelected(item);
        }

        @Override
        public void onActivityCreated(@Nullable Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            setHasOptionsMenu(true);
            mImageView.setOnClickListener(this);
            mImageView.setOnGenericMotionListener(new OnGenericMotionListener() {
                @Override
                public boolean onGenericMotion(View v, MotionEvent event) {
                    final SubsamplingScaleImageView iv = (SubsamplingScaleImageView) v;
                    return false;
                }
            });
            loadImage();
        }

    }

    public static final class ImagePageFragment extends BaseImagePageFragment
            implements DownloadListener, LoaderCallbacks<Result>, OnClickListener {

        private GifTextureView mGifImageView;

        @Override
        public void onBaseViewCreated(View view, @Nullable Bundle savedInstanceState) {
            super.onBaseViewCreated(view, savedInstanceState);
            mGifImageView = (GifTextureView) view.findViewById(R.id.gif_image_view);
        }

        @Override
        public void onLoadFinished(final Loader<TileImageLoader.Result> loader, final TileImageLoader.Result data) {
            if (data.hasData() && "image/gif".equals(data.options.outMimeType)) {
                mGifImageView.setVisibility(View.VISIBLE);
                setImageViewVisibility(View.GONE);
                mGifImageView.setInputSource(new FileSource(data.file));
                setLoadProgressVisibility(View.GONE);
                setLoadProgress(0);
                invalidateOptionsMenu();
                return;
            }
            super.onLoadFinished(loader, data);
        }

        @Override
        public void onLoaderReset(final Loader<TileImageLoader.Result> loader) {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                @Nullable Bundle savedInstanceState) {
            return inflater.inflate(R.layout.fragment_media_page_image, container, false);
        }

    }

    private static class MediaPagerAdapter extends SupportFixedFragmentStatePagerAdapter {

        private final MediaViewerActivity mActivity;
        private long mAccountId;
        private ParcelableMedia[] mMedia;

        public MediaPagerAdapter(MediaViewerActivity activity) {
            super(activity.getSupportFragmentManager());
            mActivity = activity;
        }

        @Override
        public int getCount() {
            if (mMedia == null)
                return 0;
            return mMedia.length;
        }

        @Override
        public Fragment getItem(int position) {
            final ParcelableMedia media = mMedia[position];
            final Bundle args = new Bundle();
            args.putLong(EXTRA_ACCOUNT_ID, mAccountId);
            args.putParcelable(EXTRA_MEDIA, media);
            switch (media.type) {
            case ParcelableMedia.TYPE_ANIMATED_GIF:
            case ParcelableMedia.TYPE_CARD_ANIMATED_GIF: {
                args.putBoolean(EXTRA_LOOP, true);
                return Fragment.instantiate(mActivity, VideoPageFragment.class.getName(), args);
            }
            case ParcelableMedia.TYPE_VIDEO: {
                return Fragment.instantiate(mActivity, VideoPageFragment.class.getName(), args);
            }
            default: {
                if (ANIMATED_GIF_SUPPORTED) {
                    return Fragment.instantiate(mActivity, ImagePageFragment.class.getName(), args);
                }
                return Fragment.instantiate(mActivity, BaseImagePageFragment.class.getName(), args);
            }
            }
        }

        public void setMedia(long accountId, ParcelableMedia[] media) {
            mAccountId = accountId;
            mMedia = media;
            notifyDataSetChanged();
        }
    }

    public static final class VideoPageFragment extends BaseSupportFragment implements VideoLoadingListener,
            OnPreparedListener, OnErrorListener, OnCompletionListener, OnClickListener {

        private static final String[] SUPPORTED_VIDEO_TYPES;

        static {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                SUPPORTED_VIDEO_TYPES = new String[] { "video/mp4" };
            } else {
                SUPPORTED_VIDEO_TYPES = new String[] { "video/webm", "video/mp4" };
            }
        }

        private VideoLoader mVideoLoader;

        private TextureVideoView mVideoView;
        private View mVideoViewOverlay;
        private SeekBar mVideoViewProgress;
        private TextView mDurationLabel, mPositionLabel;
        private ImageButton mPlayPauseButton, mVolumeButton;
        private ProgressWheel mProgressBar;
        private View mVideoControl;

        private boolean mPlayAudio;
        private VideoPlayProgressRunnable mVideoProgressRunnable;
        private SaveFileTask mSaveFileTask;
        private File mVideoFile;
        private Pair<String, String> mVideoUrlAndType;
        private MediaPlayer mMediaPlayer;

        public boolean isLoopEnabled() {
            return getArguments().getBoolean(EXTRA_LOOP, false);
        }

        public void loadVideo() {
            Pair<String, String> urlAndType = getBestVideoUrlAndType(getMedia());
            if (urlAndType == null)
                return;
            mVideoUrlAndType = urlAndType;
            mVideoLoader.loadVideo(urlAndType.first, this);
        }

        @Override
        public void onStart() {
            super.onStart();
        }

        @Override
        public void onStop() {
            super.onStop();
        }

        @Override
        public void onCompletion(MediaPlayer mp) {
            mMediaPlayer = null;
            //            mVideoViewProgress.removeCallbacks(mVideoProgressRunnable);
            //            mVideoViewProgress.setVisibility(View.GONE);
        }

        @Override
        public boolean onError(MediaPlayer mp, int what, int extra) {
            mMediaPlayer = null;
            mVideoViewProgress.removeCallbacks(mVideoProgressRunnable);
            mVideoViewProgress.setVisibility(View.GONE);
            return true;
        }

        @Override
        public void onPrepared(MediaPlayer mp) {
            if (getUserVisibleHint()) {
                mMediaPlayer = mp;
                mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
                updateVolume();
                mp.setLooping(isLoopEnabled());
                mp.start();
                mVideoViewProgress.setVisibility(View.VISIBLE);
                mVideoViewProgress.post(mVideoProgressRunnable);
                updatePlayerState();
                mVideoControl.setVisibility(View.VISIBLE);
            }
        }

        private void updateVolume() {
            final ImageButton b = mVolumeButton;
            if (b != null) {
                b.setImageResource(
                        mPlayAudio ? R.drawable.ic_action_speaker_max : R.drawable.ic_action_speaker_muted);
            }
            final MediaPlayer mp = mMediaPlayer;
            if (mp == null)
                return;
            if (mPlayAudio) {
                mp.setVolume(1, 1);
            } else {
                mp.setVolume(0, 0);
            }
        }

        @Override
        public void onVideoLoadingCancelled(String uri, VideoLoadingListener listener) {
            mProgressBar.setVisibility(View.GONE);
            mProgressBar.setProgress(0);
            invalidateOptionsMenu();
        }

        @Override
        public void onBaseViewCreated(View view, Bundle savedInstanceState) {
            super.onBaseViewCreated(view, savedInstanceState);
            mVideoView = (TextureVideoView) view.findViewById(R.id.video_view);
            mVideoViewOverlay = view.findViewById(R.id.video_view_overlay);
            mVideoViewProgress = (SeekBar) view.findViewById(R.id.video_view_progress);
            mProgressBar = (ProgressWheel) view.findViewById(R.id.load_progress);
            mDurationLabel = (TextView) view.findViewById(R.id.duration_label);
            mPositionLabel = (TextView) view.findViewById(R.id.position_label);
            mPlayPauseButton = (ImageButton) view.findViewById(R.id.play_pause_button);
            mVolumeButton = (ImageButton) view.findViewById(R.id.volume_button);
            mVideoControl = view.findViewById(R.id.video_control);
        }

        @Override
        public void onVideoLoadingComplete(String uri, VideoLoadingListener listener, File file) {
            mVideoView.setVideoURI(Uri.fromFile(file));
            mVideoFile = file;
            mProgressBar.setVisibility(View.GONE);
            mProgressBar.setProgress(0);
            invalidateOptionsMenu();
        }

        @Override
        public void onVideoLoadingFailed(String uri, VideoLoadingListener listener, Exception e) {
            mProgressBar.setVisibility(View.GONE);
            mProgressBar.setProgress(0);
            invalidateOptionsMenu();
        }

        @Override
        public void onVideoLoadingProgressUpdate(String uri, VideoLoadingListener listener, int current,
                int total) {
            if (total <= 0) {
                if (!mProgressBar.isSpinning()) {
                    mProgressBar.spin();
                }
                return;
            }
            mProgressBar.setProgress(current / (float) total);
        }

        @Override
        public void onVideoLoadingStarted(String uri, VideoLoadingListener listener) {
            mProgressBar.setVisibility(View.VISIBLE);
            mProgressBar.spin();
            invalidateOptionsMenu();
        }

        @Override
        public void setUserVisibleHint(boolean isVisibleToUser) {
            super.setUserVisibleHint(isVisibleToUser);
            if (!isVisibleToUser && mVideoView != null && mVideoView.isPlaying()) {
                mVideoView.pause();
                updatePlayerState();
            }
        }

        @Override
        public void onActivityCreated(@Nullable Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            setHasOptionsMenu(true);
            mVideoLoader = TwidereApplication.getInstance(getActivity()).getVideoLoader();

            Handler handler = mVideoViewProgress.getHandler();
            if (handler == null) {
                handler = new Handler(getActivity().getMainLooper());
            }
            mVideoProgressRunnable = new VideoPlayProgressRunnable(handler, mVideoViewProgress, mDurationLabel,
                    mPositionLabel, mVideoView);

            mVideoViewOverlay.setOnClickListener(this);
            mVideoView.setOnPreparedListener(this);
            mVideoView.setOnErrorListener(this);
            mVideoView.setOnCompletionListener(this);

            mPlayPauseButton.setOnClickListener(this);
            mVolumeButton.setOnClickListener(this);
            loadVideo();
            updateVolume();
        }

        private Pair<String, String> getBestVideoUrlAndType(ParcelableMedia media) {
            if (media == null)
                return null;
            switch (media.type) {
            case ParcelableMedia.TYPE_VIDEO:
            case ParcelableMedia.TYPE_ANIMATED_GIF: {
                if (media.video_info == null)
                    return null;
                for (String supportedType : SUPPORTED_VIDEO_TYPES) {
                    for (Variant variant : media.video_info.variants) {
                        if (supportedType.equalsIgnoreCase(variant.content_type))
                            return new Pair<>(variant.url, variant.content_type);
                    }
                }
                return null;
            }
            case ParcelableMedia.TYPE_CARD_ANIMATED_GIF: {
                return new Pair<>(media.media_url, "video/mp4");
            }
            default: {
                return null;
            }
            }
        }

        private ParcelableMedia getMedia() {
            final Bundle args = getArguments();
            return args.getParcelable(EXTRA_MEDIA);
        }

        private void saveToGallery() {
            if (mSaveFileTask != null && mSaveFileTask.getStatus() == Status.RUNNING)
                return;
            final File file = mVideoFile;
            final Pair<String, String> urlAndType = mVideoUrlAndType;
            final boolean hasVideo = file != null && file.exists() && urlAndType != null;
            if (!hasVideo)
                return;
            final String mimeType = urlAndType.second;
            final MimeTypeMap map = MimeTypeMap.getSingleton();
            final String extension = map.getExtensionFromMimeType(mimeType);
            if (extension == null)
                return;
            final File pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES);
            final File saveDir = new File(pubDir, "Twidere");
            mSaveFileTask = AsyncTaskUtils.executeTask(new SaveFileTask(getActivity(), file, mimeType, saveDir));
        }

        @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.volume_button: {
                mPlayAudio = !mPlayAudio;
                updateVolume();
                break;
            }
            case R.id.play_pause_button: {
                final MediaPlayer mp = mMediaPlayer;
                if (mp != null) {
                    if (mp.isPlaying()) {
                        mp.pause();
                    } else {
                        mp.start();
                    }
                }
                updatePlayerState();
                break;
            }
            case R.id.video_view_overlay: {
                if (mVideoControl.getVisibility() == View.VISIBLE) {
                    mVideoControl.setVisibility(View.GONE);
                } else {
                    mVideoControl.setVisibility(View.VISIBLE);
                }
                break;
            }
            }
        }

        private void updatePlayerState() {
            final MediaPlayer mp = mMediaPlayer;
            if (mp != null) {
                final boolean playing = mp.isPlaying();
                mPlayPauseButton.setContentDescription(getString(playing ? R.string.pause : R.string.play));
                mPlayPauseButton
                        .setImageResource(playing ? R.drawable.ic_action_pause : R.drawable.ic_action_play_arrow);
            } else {
                mPlayPauseButton.setContentDescription(getString(R.string.play));
                mPlayPauseButton.setImageResource(R.drawable.ic_action_play_arrow);
            }
        }

        private static class VideoPlayProgressRunnable implements Runnable {

            private final Handler mHandler;
            private final ProgressBar mProgressBar;
            private final TextView mDurationLabel, mPositionLabel;
            private final MediaController.MediaPlayerControl mMediaPlayerControl;

            VideoPlayProgressRunnable(Handler handler, ProgressBar progressBar, TextView durationLabel,
                    TextView positionLabel, MediaController.MediaPlayerControl mediaPlayerControl) {
                mHandler = handler;
                mProgressBar = progressBar;
                mDurationLabel = durationLabel;
                mPositionLabel = positionLabel;
                mMediaPlayerControl = mediaPlayerControl;
                mProgressBar.setMax(1000);
            }

            @Override
            public void run() {
                final int duration = mMediaPlayerControl.getDuration();
                final int position = mMediaPlayerControl.getCurrentPosition();
                if (duration <= 0 || position < 0)
                    return;
                mProgressBar.setProgress(Math.round(1000 * position / (float) duration));
                final int durationSecs = duration / 1000, positionSecs = position / 1000;
                mDurationLabel.setText(String.format("%02d:%02d", durationSecs / 60, durationSecs % 60));
                mPositionLabel.setText(String.format("%02d:%02d", positionSecs / 60, positionSecs % 60));
                mHandler.postDelayed(this, 16);
            }
        }

        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                @Nullable Bundle savedInstanceState) {
            return inflater.inflate(R.layout.fragment_media_page_video, container, false);
        }

        @Override
        public void onPrepareOptionsMenu(Menu menu) {
            super.onPrepareOptionsMenu(menu);
            final File file = mVideoFile;
            final Pair<String, String> linkAndType = mVideoUrlAndType;
            final boolean isLoading = linkAndType != null && mVideoLoader.isLoading(linkAndType.first);
            final boolean hasVideo = file != null && file.exists() && linkAndType != null;
            MenuUtils.setMenuItemAvailability(menu, R.id.refresh, !hasVideo && !isLoading);
            MenuUtils.setMenuItemAvailability(menu, R.id.share, hasVideo && !isLoading);
            MenuUtils.setMenuItemAvailability(menu, R.id.save, hasVideo && !isLoading);
            if (!hasVideo)
                return;
            final MenuItem shareItem = menu.findItem(R.id.share);
            final ShareActionProvider shareProvider = (ShareActionProvider) MenuItemCompat
                    .getActionProvider(shareItem);
            final Intent intent = new Intent(Intent.ACTION_SEND);
            final Uri fileUri = Uri.fromFile(file);
            intent.setDataAndType(fileUri, linkAndType.second);
            intent.putExtra(Intent.EXTRA_STREAM, fileUri);
            final MediaViewerActivity activity = (MediaViewerActivity) getActivity();
            if (activity.hasStatus()) {
                final ParcelableStatus status = activity.getStatus();
                intent.putExtra(Intent.EXTRA_TEXT, Utils.getStatusShareText(activity, status));
                intent.putExtra(Intent.EXTRA_SUBJECT, Utils.getStatusShareSubject(activity, status));
            }
            shareProvider.setShareIntent(intent);
        }

        @Override
        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
            inflater.inflate(R.menu.menu_media_viewer_video_page, menu);
        }

        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
            case MENU_SAVE: {
                saveToGallery();
                return true;
            }
            case MENU_REFRESH: {
                loadVideo();
                return true;
            }
            }
            return super.onOptionsItemSelected(item);
        }

        @Override
        public void onResume() {
            super.onResume();
        }

        @Override
        public void onDestroyView() {
            super.onDestroyView();
        }

        @Override
        public void onPause() {
            super.onPause();
        }

    }
}