Java tutorial
// 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; } }