io.v.android.apps.syncslides.PresentationActivity.java Source code

Java tutorial

Introduction

Here is the source code for io.v.android.apps.syncslides.PresentationActivity.java

Source

// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package io.v.android.apps.syncslides;

import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import io.v.android.apps.syncslides.db.DB;
import io.v.android.apps.syncslides.db.VPerson;
import io.v.android.apps.syncslides.discovery.ParticipantPeer;
import io.v.android.apps.syncslides.misc.Config;
import io.v.android.apps.syncslides.misc.V23Manager;
import io.v.android.apps.syncslides.model.Deck;
import io.v.android.apps.syncslides.model.DeckFactory;
import io.v.android.apps.syncslides.model.Participant;
import io.v.android.apps.syncslides.model.Role;
import io.v.v23.security.Blessings;

public class PresentationActivity extends AppCompatActivity {
    private static final String TAG = "PresentationActivity";

    /**
     * The deck to present.
     */
    private Deck mDeck;
    /**
     * The current role of the user.  This value can change during the lifetime
     * of the activity.
     */
    private Role mRole;
    /**
     * Makes decks, handles deck conversions.
     */
    private DeckFactory mDeckFactory;

    /**
     * The presentation ID.
     */
    private String mPresentationId;

    /**
     * The syncgroup name.
     */
    private String mSyncgroupName;

    private boolean mSynced;

    /**
     * Once a user clicks 'present' - which happens at some unpredictable time
     * after onStart, the user begins presenting the deck, and the system must
     * advertise the presentation.   Once advertising is started, it doesn't
     * stop until onStop is called.  If the activity is paused, advertising
     * should continue.
     */
    private boolean mShouldBeAdvertising;
    private boolean mIsAdvertising;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");
        // Initialize the DeckFactory.
        mDeckFactory = DeckFactory.Singleton.get(getApplicationContext());
        // Immediately initialize V23, possibly sending user to the
        // AccountManager to get blessings.
        V23Manager.Singleton.get().init(getApplicationContext(), this);
        setContentView(R.layout.activity_presentation);

        mShouldBeAdvertising = false;
        mIsAdvertising = false;
        String deckId;
        if (savedInstanceState == null) {
            Log.d(TAG, "savedInstanceState is null");
            deckId = getIntent().getStringExtra(Deck.B.DECK_ID);
            mRole = (Role) getIntent().getSerializableExtra(Participant.B.PARTICIPANT_ROLE);
            mPresentationId = getIntent().getStringExtra(Participant.B.PRESENTATION_ID);
            mSyncgroupName = getIntent().getStringExtra(Participant.B.SYNCGROUP_NAME);
            mSynced = true;
        } else {
            Log.d(TAG, "savedInstanceState is NOT null");
            mRole = (Role) savedInstanceState.get(Participant.B.PARTICIPANT_ROLE);
            deckId = savedInstanceState.getString(Deck.B.DECK_ID);
            mPresentationId = savedInstanceState.getString(Participant.B.PRESENTATION_ID);
            mSyncgroupName = savedInstanceState.getString(Participant.B.SYNCGROUP_NAME);
            mSynced = savedInstanceState.getBoolean(Participant.B.PARTICIPANT_SYNCED);
            mShouldBeAdvertising = savedInstanceState.getBoolean(Participant.B.PARTICIPANT_SHOULD_ADV);
            if (mShouldBeAdvertising) {
                Log.d(TAG, "Need to restore advertising");
            }
        }

        // TODO(kash): This is a total hack.  I thought that the deck would be
        // loaded by this point, but we aren't actually guaranteed that.  After
        // this is fixed, we can uncomment handleError in SyncbaseDB.getDeck().
        while ((mDeck = DB.Singleton.get(getApplicationContext()).getDeck(deckId)) == null) {
            Log.d(TAG, "Waiting for deck to load...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if (mDeck == null) {
            throw new IllegalArgumentException("Unusable deckId: " + deckId);
        }
        Log.d(TAG, "Unpacked state:");
        Log.d(TAG, "  mShouldBeAdvertising = " + mShouldBeAdvertising);
        Log.d(TAG, "                 mRole = " + mRole);
        Log.d(TAG, "       mPresentationId = " + mPresentationId);
        Log.d(TAG, "        mSyncgroupName = " + mSyncgroupName);
        Log.d(TAG, "                  Deck = " + mDeck);
        Log.d(TAG, "               mSynced = " + mSynced);

        if (mRole.equals(Role.AUDIENCE)) {
            if (mPresentationId.equals(Participant.Unknown.PRESENTATION_ID)
                    || mSyncgroupName.equals(Participant.Unknown.SYNCGROUP_NAME)) {
                throw new IllegalArgumentException("Cannot be an audience.");
            }
        }

        // TODO(jregan): This appears to be an attempt to avoid fragment
        // re-inflation, possibly the right thing to do is move the code
        // below to another flow step, e.g. onRestoreInstanceState.
        if (savedInstanceState != null) {
            return;
        }

        if (mShouldBeAdvertising) {
            startAdvertising();
        }

        getSupportActionBar().setTitle(mDeck.getTitle());

        // If this is an audience member, we want them to jump straight to the fullscreen view.
        if (mRole == Role.AUDIENCE) {
            showFullscreenSlide(0);
        } else {
            showSlideList();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Log.d(TAG, "onActivityResult");
        if (V23Manager.onActivityResult(getApplicationContext(), requestCode, resultCode, data)) {
            Log.d(TAG, "did the v23 result");
            return;
        }
        // Any other activity results would be handled here.
    }

    @Override
    protected void onSaveInstanceState(Bundle b) {
        super.onSaveInstanceState(b);
        Log.d(TAG, "onSaveInstanceState");
        b.putSerializable(Participant.B.PARTICIPANT_ROLE, mRole);
        b.putString(Participant.B.PRESENTATION_ID, mPresentationId);
        b.putString(Participant.B.SYNCGROUP_NAME, mSyncgroupName);
        b.putString(Deck.B.DECK_ID, mDeck.getId());
        b.putBoolean(Participant.B.PARTICIPANT_SYNCED, mSynced);
        b.putBoolean(Participant.B.PARTICIPANT_SHOULD_ADV, mShouldBeAdvertising);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "onStart");
        if (mShouldBeAdvertising) {
            startAdvertising();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop");
        stopAdvertising();
    }

    /**
     * Set the system UI to be immersive or not.
     */
    public void setUiImmersive(boolean immersive) {
        if (immersive) {
            getSupportActionBar().hide();
            getWindow().getDecorView().setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
        } else {
            getSupportActionBar().show();
            // See the comment at the top of fragment_slide_list.xml for why we don't simply
            // use View.SYSTEM_UI_FLAG_VISIBLE.
            getWindow().getDecorView().setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
        }
    }

    private boolean shouldUseV23() {
        return Config.MtDiscovery.ENABLE && V23Manager.Singleton.get().isBlessed();
    }

    private void startAdvertising() {
        Log.d(TAG, "startAdvertising");
        mShouldBeAdvertising = true;
        if (mIsAdvertising) {
            Log.d(TAG, "Already advertising.");
            return;
        }
        if (shouldUseV23()) {
            V23Manager v23Manager = V23Manager.Singleton.get();
            Blessings blessings = v23Manager.getBlessings();
            v23Manager.mount(Config.MtDiscovery.makeMountName(mDeck),
                    new ParticipantPeer.Server(mDeckFactory.make(mDeck), mDeck.getId(),
                            new VPerson(blessings.toString(), SignInActivity.getUserName(this)), mSyncgroupName,
                            mPresentationId));
            Log.d(TAG, "MT advertising started:");
            Log.d(TAG, "    mSyncgroupName = " + mSyncgroupName);
            Log.d(TAG, "   mPresentationId = " + mPresentationId);
            Log.d(TAG, "             mDeck = " + mDeck);
        } else {
            Log.d(TAG, "No means to start advertising.");
        }
        mIsAdvertising = true;
    }

    private void stopAdvertising() {
        Log.d(TAG, "stopAdvertising");
        if (!mIsAdvertising) {
            Log.d(TAG, "Not advertising.");
            return;
        }
        if (shouldUseV23()) {
            // At the moment, only one service can be mounted, and this call
            // will unmount it if mounted, else do nothing.
            V23Manager.Singleton.get().unMount();
            Log.d(TAG, "MT advertising stopped.");
        } else {
            Log.d(TAG, "No advertising to stop.");
        }
        mIsAdvertising = false;
    }

    /**
     * Starts a live presentation.  The presentation will be advertised to other
     * devices as long as this activity is alive.
     */
    public void startPresentation() {
        DB db = DB.Singleton.get(getApplicationContext());
        db.createPresentation(mDeck.getId(), new DB.Callback<DB.CreatePresentationResult>() {
            @Override
            public void done(DB.CreatePresentationResult result) {
                Log.i(TAG, "Started presentation");
                Toast.makeText(getApplicationContext(), "Started presentation", Toast.LENGTH_SHORT).show();
                mPresentationId = result.presentationId;
                mSyncgroupName = result.syncgroupName;
                startAdvertising();
                navigateToSlide(0);
            }
        });
        mRole = Role.PRESENTER;
    }

    /**
     * Switches to the fullscreen immersive slide presentation view.
     *
     * @param slideNum the number of the slide to show fullscreen
     */
    public void showFullscreenSlide(int slideNum) {
        FullscreenSlideFragment fragment = FullscreenSlideFragment.newInstance(mDeck.getId(), mPresentationId,
                slideNum, mRole);
        getSupportFragmentManager().beginTransaction().replace(R.id.fragment, fragment).commit();
    }

    /**
     * Shows the navigate fragment where the user can see the current slide and
     * navigate to other components of the slide presentation.
     *
     * @param slideNum the number of the current slide to show in the fragment
     */
    public void showNavigateFragment(int slideNum) {
        Log.d(TAG, String.valueOf(mSynced));
        NavigateFragment fragment = NavigateFragment.newInstance(mDeck.getId(), mPresentationId, slideNum, mRole);
        getSupportFragmentManager().beginTransaction().replace(R.id.fragment, fragment).commit();
    }

    /**
     * Shows the navigate fragment where the user can see the given slide and
     * navigate to other components of the slide presentation. If the role is
     * not AUDIENCE, this version includes an add to the back stack so that
     * the user can back out from the navigate fragment to slide list.
     *
     * @param slideNum the number of the slide to show in the fragment
     */
    public void navigateToSlide(int slideNum) {
        // The user picked this specific slide.  Don't try to stay synced.
        setUnsynced();

        NavigateFragment fragment = NavigateFragment.newInstance(mDeck.getId(), mPresentationId, slideNum, mRole);
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
                fragment);
        if (mRole != Role.AUDIENCE) {
            transaction.addToBackStack("");
        }
        transaction.commit();
    }

    /**
     * Shows the slide list, where users can see the slides in a presentation
     * and click on one to browse the deck, or press the play FAB to start
     * presenting.
     */
    public void showSlideList() {
        SlideListFragment fragment = SlideListFragment.newInstance(mDeck.getId(), mRole);
        getSupportFragmentManager().beginTransaction().replace(R.id.fragment, fragment).commit();
    }

    /**
     * Return if the device is synced with the presenter (true if the device is
     * the presenter).
     */
    public boolean getSynced() {
        return mSynced;
    }

    /**
     * Set the device to sync with the presenter.
     */
    public void setSynced() {
        mSynced = true;
    }

    /**
     * Unsync the current device with the presenter.
     */
    public void setUnsynced() {
        mSynced = false;
    }
}