syncslides: Fullscreen slide view.

Change-Id: I1770811a17253c99e3a7adcf56243cac14327057
diff --git a/android/app/src/main/java/io/v/syncslides/FullscreenSlideFragment.java b/android/app/src/main/java/io/v/syncslides/FullscreenSlideFragment.java
new file mode 100644
index 0000000..da8d140
--- /dev/null
+++ b/android/app/src/main/java/io/v/syncslides/FullscreenSlideFragment.java
@@ -0,0 +1,150 @@
+// 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.syncslides;
+
+import android.os.Bundle;
+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.ImageView;
+import android.widget.Toast;
+
+import io.v.syncslides.db.DB;
+import io.v.syncslides.model.DynamicList;
+import io.v.syncslides.model.ListListener;
+import io.v.syncslides.model.Session;
+import io.v.syncslides.model.Slide;
+import io.v.v23.verror.VException;
+
+public class FullscreenSlideFragment extends Fragment {
+    private static final String SESSION_ID_KEY = "session_id_key";
+    private static final String TAG = "FullscreenSlide";
+
+    private Session mSession;
+    private ImageView mFullScreenImage;
+    private DynamicList<Slide> mSlides;
+    private int mSlideNum = 0;
+    private final SlideNumberListener mSlideNumberListener = new SlideNumberListener();
+    private final ListListener mSlideListListener = new SlideListListener();
+
+    public static FullscreenSlideFragment newInstance(String sessionId) {
+        FullscreenSlideFragment fragment = new FullscreenSlideFragment();
+        Bundle args = new Bundle();
+        args.putString(SESSION_ID_KEY, sessionId);
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        Bundle bundle = savedInstanceState;
+        if (bundle == null) {
+            bundle = getArguments();
+        }
+        String sessionId = bundle.getString(SESSION_ID_KEY);
+        try {
+            mSession = DB.Singleton.get().getSession(sessionId);
+        } catch (VException e) {
+            handleFatalError("Failed to fetch Session", e);
+        }
+        // See comment at the top of fragment_slide_list.xml.
+        ((PresentationActivity) getActivity()).setUiImmersive(true);
+        // Inflate the layout for this fragment
+        View rootView = inflater.inflate(R.layout.fragment_fullscreen_slide, container, false);
+        mFullScreenImage = (ImageView) rootView.findViewById(R.id.fullscreen_slide_image);
+        mFullScreenImage.setOnClickListener(
+                v -> ((PresentationActivity) getActivity()).showNavigation());
+        return rootView;
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mSlides = mSession.getSlides();
+        mSlides.addListener(mSlideListListener);
+        mSession.addSlideNumberListener(mSlideNumberListener);
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        mSession.removeSlideNumberListener(mSlideNumberListener);
+        mSlides.removeListener(mSlideListListener);
+        mSlides = null;
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putString(SESSION_ID_KEY, mSession.getId());
+    }
+
+    private void updateView() {
+        if (mSlideNum < 0 ||  mSlideNum >= mSlides.getItemCount()) {
+            // Still loading...
+            return;
+        }
+        // TODO(kash): Display the fullsize image instead of the thumbnail.
+        mFullScreenImage.setImageBitmap(mSlides.get(mSlideNum).getThumb());
+    }
+
+    /**
+     * Updates the view whenever the list of slides changes.
+     */
+    private class SlideListListener implements ListListener {
+        @Override
+        public void notifyDataSetChanged() {
+            updateView();
+        }
+
+        @Override
+        public void notifyItemChanged(int position) {
+            updateView();
+        }
+
+        @Override
+        public void notifyItemInserted(int position) {
+            updateView();
+        }
+
+        @Override
+        public void notifyItemRemoved(int position) {
+            updateView();
+        }
+
+        @Override
+        public void onError(Exception e) {
+            handleFatalError("Error watching slide list", e);
+        }
+    }
+
+    private class SlideNumberListener implements Session.SlideNumberListener {
+        @Override
+        public void onChange(int slideNum) {
+            Log.i(TAG, "onChange " + slideNum);
+            mSlideNum = slideNum;
+            updateView();
+        }
+
+        @Override
+        public void onError(Exception e) {
+            handleFatalError("Error listening to slide number changes", e);
+        }
+    }
+
+    private void handleError(String msg, Throwable throwable) {
+        Log.e(TAG, msg + ": " + Log.getStackTraceString(throwable));
+        Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show();
+    }
+
+    private void handleFatalError(String msg, Throwable throwable) {
+        handleError(msg, throwable);
+        getActivity().finish();
+    }
+
+}
diff --git a/android/app/src/main/java/io/v/syncslides/NavigateFragment.java b/android/app/src/main/java/io/v/syncslides/NavigateFragment.java
index e475c0b..e0f7245 100644
--- a/android/app/src/main/java/io/v/syncslides/NavigateFragment.java
+++ b/android/app/src/main/java/io/v/syncslides/NavigateFragment.java
@@ -135,16 +135,14 @@
 //            }
 //        });
         mCurrentSlide = (ImageView) rootView.findViewById(R.id.slide_current_medium);
-//        mCurrentSlide.setOnClickListener(new NavigateClickListener() {
-//            @Override
-//            public void onClick(View v) {
-//                super.onClick(v);
-//                if (mRole == Role.AUDIENCE || mRole == Role.BROWSER) {
-//                    ((PresentationActivity) getActivity()).showFullscreenSlide(mSlideNum);
-//                }
-//            }
-//        });
-//
+        mCurrentSlide.setOnClickListener(new NavigateClickListener() {
+            @Override
+            public void onNavigate() {
+                // TODO(kash): Disallow presenter from switching to fullscreen.
+                ((PresentationActivity) getActivity()).showFullscreenSlide();
+            }
+        });
+
         mSlideNumText = (TextView) rootView.findViewById(R.id.slide_num_text);
         mNotes = (EditText) rootView.findViewById(R.id.notes);
         mNotes.setOnFocusChangeListener((v, hasFocus) -> {
diff --git a/android/app/src/main/java/io/v/syncslides/PresentationActivity.java b/android/app/src/main/java/io/v/syncslides/PresentationActivity.java
index c56fe3e..b013f7a 100644
--- a/android/app/src/main/java/io/v/syncslides/PresentationActivity.java
+++ b/android/app/src/main/java/io/v/syncslides/PresentationActivity.java
@@ -118,6 +118,14 @@
     }
 
     /**
+     * Shows the current slide in fullscreen mode.
+     */
+    public void showFullscreenSlide() {
+        FullscreenSlideFragment fragment = FullscreenSlideFragment.newInstance(mSession.getId());
+        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.
      */
@@ -128,6 +136,14 @@
             handleError("Could not update session", e);
             return;
         }
+        showNavigation();
+    }
+
+    /**
+     * Shows the navigate fragment where the user can see the current slide and navigate
+     * to other components of the slide presentation.
+     */
+    public void showNavigation() {
         NavigateFragment fragment = NavigateFragment.newInstance(mSession.getId());
         FragmentTransaction transaction = getSupportFragmentManager()
                 .beginTransaction()
diff --git a/android/app/src/main/java/io/v/syncslides/db/SlideNumberWatcher.java b/android/app/src/main/java/io/v/syncslides/db/SlideNumberWatcher.java
index 1ed2f5b..db2edeb 100644
--- a/android/app/src/main/java/io/v/syncslides/db/SlideNumberWatcher.java
+++ b/android/app/src/main/java/io/v/syncslides/db/SlideNumberWatcher.java
@@ -124,7 +124,6 @@
     // Runs in a background thread.
     private void watchCurrentSlide() {
         try {
-            Log.i(TAG, "watchCurrentSlide");
             String rowKey = NamingUtil.join(mDeckId, mPresentationId, SyncbaseDB.CURRENT_SLIDE);
             BatchDatabase batch = sync(mDb.beginBatch(mCurrentContext, null));
             Table presentations = batch.getTable(SyncbaseDB.PRESENTATIONS_TABLE);
diff --git a/android/app/src/main/res/layout/fragment_fullscreen_slide.xml b/android/app/src/main/res/layout/fragment_fullscreen_slide.xml
new file mode 100644
index 0000000..1ddfcb8
--- /dev/null
+++ b/android/app/src/main/res/layout/fragment_fullscreen_slide.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView
+    android:id="@+id/fullscreen_slide_image"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/matte_black"
+    android:scaleType="fitCenter">
+</ImageView>
\ No newline at end of file