com.simas.vc.editor.EditorFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.simas.vc.editor.EditorFragment.java

Source

/*
 * Copyright (c) 2015. Simas Abramovas
 *
 * This file is part of VideoClipper.
 *
 * VideoClipper is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * VideoClipper is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with VideoClipper. If not, see <http://www.gnu.org/licenses/>.
 */
package com.simas.vc.editor;

import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.simas.vc.MainActivity;
import com.simas.vc.editor.tree_view.AttributeTreeAdapter;
import com.simas.vc.editor.tree_view.TreeAdapter;
import com.simas.vc.editor.tree_view.TreeView;
import com.simas.vc.helpers.DelayedHandler;
import com.simas.vc.helpers.Utils;
import com.simas.vc.attributes.FileAttributes;
import com.simas.vc.editor.player.PlayerFragment;
import com.simas.vc.nav_drawer.NavItem;
import com.simas.vc.R;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Fragment containing the information about the opened NavItem, as well as actions that can be
 * invoked. The fragment doesn't save its state because {@link #mItem} is a <b>shared</b>
 * object that's assigned by {@link com.simas.vc.MainActivity}.
 */
public class EditorFragment extends Fragment {

    private static final String STATE_PREVIOUS_ITEM = "previous_item";
    private final String TAG = getClass().getName();

    private NavItem mItem;
    private View mProgressOverlay;
    private PlayerFragment mPlayerFragment;

    private enum Data {
        ACTIONS, FILENAME, DURATION, SIZE, STREAM_TREE
    }

    private Map<Data, View> mDataMap = new HashMap<>();
    /**
     * Handler runs all the messages posted to it only when the fragment layout is ready, i.e. at
     * the end of {@code onCreateView}.
     */
    private DelayedHandler mDelayedHandler = new DelayedHandler(new Handler());

    /**
     * For progress overlay to be hidden, multiple states must be fulfilled
     * ({@link #modifyProgressOverlayStates(boolean, ProgressStates)}). To save the states we use
     * a {@link com.simas.vc.helpers.Utils.FlagContainer} with respective bits set for each flag.
     * Flags have a specific value of 1, 2, 4, 8, etc. (as every bit does). They are specified at
     * {@link com.simas.vc.editor.EditorFragment.ProgressStates}.
     */
    private Utils.FlagContainer mProgressOverlayStates = new Utils.FlagContainer();

    /**
     * @see #mProgressOverlayStates
     */
    private enum ProgressStates {
        PLAYER_CONTAINER_DRAWN(1), ITEM_VALID(2), PLAYER_ITEM_SET(4);

        private int mBit;

        ProgressStates(int bit) {
            mBit = bit;
        }

        public int getBit() {
            return mBit;
        }

        public static int getCombinedBitValue() {
            return (int) (Math.pow(2, values().length) - 1);
        }

    }

    public EditorFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
        View root = inflater.inflate(R.layout.fragment_editor, container, false);
        // Overlay a ProgressBar when preparing the PlayerFragment for the first time
        mProgressOverlay = root.findViewById(R.id.editor_progress_overlay);

        mPlayerFragment = (PlayerFragment) getChildFragmentManager().findFragmentById(R.id.player_fragment);

        // Queue container modifications until it's measured
        final View playerContainer = mPlayerFragment.getContainer();
        final Runnable playerResize = new Runnable() {
            @Override
            public void run() {
                ViewGroup.LayoutParams params = playerContainer.getLayoutParams();
                params.width = MainActivity.sPlayerContainerSize;
                params.height = MainActivity.sPlayerContainerSize;
                playerContainer.setLayoutParams(params);

                playerContainer.post(new Runnable() {
                    @Override
                    public void run() {
                        modifyProgressOverlayStates(true, ProgressStates.PLAYER_CONTAINER_DRAWN);
                    }
                });
            }
        };
        if (MainActivity.sPlayerContainerSize != 0) {
            playerResize.run();
        } else {
            playerContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
                @Override
                public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
                        int oldTop, int oldRight, int oldBottom) {
                    int size = Math.max(playerContainer.getWidth(), playerContainer.getHeight());
                    if (size <= 0)
                        return;
                    MainActivity.sPlayerContainerSize = size;
                    playerContainer.removeOnLayoutChangeListener(this);
                    playerResize.run();
                }
            });
        }

        mDelayedHandler.resume();

        View actions = root.findViewById(R.id.editor_actions);
        mDataMap.put(Data.ACTIONS, actions);
        mDataMap.put(Data.FILENAME, actions.findViewById(R.id.filename_value));
        mDataMap.put(Data.SIZE, actions.findViewById(R.id.size_value));
        mDataMap.put(Data.DURATION, actions.findViewById(R.id.duration_value));
        mDataMap.put(Data.STREAM_TREE, actions.findViewById(R.id.tree_view));

        if (savedState != null) {
            NavItem previousItem = savedState.getParcelable(STATE_PREVIOUS_ITEM);
            if (previousItem != null) {
                setItem(previousItem);
            }
        }

        return root;
    }

    private NavItem.OnUpdatedListener mItemValidationListener = new NavItem.OnUpdatedListener() {
        @Override
        public void onUpdated(final NavItem.ItemAttribute attribute, final Object oldValue, final Object newValue) {
            post(new Runnable() {
                @Override
                public void run() {
                    switch (attribute) {
                    case STATE:
                        // Full update if changed to valid from in-progress
                        if (newValue == NavItem.State.VALID && oldValue == NavItem.State.INPROGRESS) {
                            updateFields();
                            modifyProgressOverlayStates(true, ProgressStates.ITEM_VALID);
                        }
                        break;
                    }
                }
            });
        }
    };

    /**
     * Set the item. Editor attributes will be changed once the item has been validated.
     * @return true if the item was changed, false otherwise
     */
    public boolean setItem(final NavItem newItem) {
        if (getItem() == newItem) {
            return false;
        }

        // Clear listener from the previous item (if it's set)
        if (getItem() != null) {
            getItem().unregisterUpdateListener(mItemValidationListener);
        }

        // 2 flags will be re-set, remove them now to reveal the progress overlay
        modifyProgressOverlayStates(false, ProgressStates.PLAYER_ITEM_SET);
        modifyProgressOverlayStates(false, ProgressStates.ITEM_VALID);

        mItem = newItem;

        // Update attributes and add listeners if the new item is not null
        if (getItem() != null) {
            // Present the new item if it's ready, otherwise wait for it
            switch (getItem().getState()) {
            case VALID:
                updateFields();
                modifyProgressOverlayStates(true, ProgressStates.ITEM_VALID);
                break;
            case INPROGRESS:
                modifyProgressOverlayStates(false, ProgressStates.ITEM_VALID);
                break;
            }

            // Add an update listener
            getItem().registerUpdateListener(mItemValidationListener);
        }

        return true;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelable(STATE_PREVIOUS_ITEM, mItem);
    }

    public PlayerFragment getPlayerFragment() {
        return mPlayerFragment;
    }

    /**
     * Update attributes to match {@link #mItem}.
     */
    private void updateFields() {
        if (getActivity() == null)
            return;

        final NavItem item = getItem();
        final FileAttributes attributes = item.getAttributes();
        final TreeView treeView = (TreeView) mDataMap.get(Data.STREAM_TREE);
        final View actions = mDataMap.get(Data.ACTIONS);

        /* Update text fields */
        final TextView filename = (TextView) mDataMap.get(Data.FILENAME);
        final TextView size = (TextView) mDataMap.get(Data.SIZE);
        final TextView duration = (TextView) mDataMap.get(Data.DURATION);

        filename.setText(item.getFile().getName());
        size.setText(Utils.bytesToMb(attributes.getSize()));
        duration.setText(Utils.secsToFullTime(attributes.getDuration().intValue()));

        /* Parse attributes to a TreeView*/
        TreeAdapter treeAdapter = new AttributeTreeAdapter(item);
        treeView.setAdapter(treeAdapter);

        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                actions.setVisibility(View.VISIBLE);

                getPlayerFragment().post(new Runnable() {
                    @Override
                    public void run() {
                        getPlayerFragment().setItem(item);
                        modifyProgressOverlayStates(true, ProgressStates.PLAYER_ITEM_SET);
                    }
                });
            }
        });
    }

    public NavItem getItem() {
        return mItem;
    }

    /**
     * @see #mProgressOverlayStates
     */
    private void modifyProgressOverlayStates(boolean fulfill, ProgressStates state) {
        if (fulfill) {
            if (!mProgressOverlayStates.addFlag(state.getBit())) {
                Log.e(TAG, state.name() + " flag was already set.");
            }
            // If all flags set, hide the progress overlay
            if (mProgressOverlayStates.getFlags() == ProgressStates.getCombinedBitValue()) {
                mProgressOverlay.setVisibility(View.GONE);
            }
        } else {
            mProgressOverlayStates.removeFlag(state.getBit());
            mProgressOverlay.setVisibility(View.VISIBLE);
        }
    }

    /**
     * Queues the given runnable
     * @param runnable    message to be queued
     */
    public void post(Runnable runnable) {
        mDelayedHandler.add(runnable);
    }

}