Show/Hide Toolbar

Bug: 20440792

- The Toolbar is now shown/hidden on demand depending on the part of the
  screen the user is interacting with.

Change-Id: Ie7ddcf449aaa2b74a93dd7495c58cce9b3665305
diff --git a/res/layout/display.xml b/res/layout/display.xml
index 638f0d2..12e8df6 100644
--- a/res/layout/display.xml
+++ b/res/layout/display.xml
@@ -15,7 +15,7 @@
   limitations under the License.
   -->
 
-<RelativeLayout
+<com.android.calculator2.CalculatorDisplay
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/display"
     android:layout_width="match_parent"
@@ -61,4 +61,4 @@
         android:singleLine="true"
         android:textColor="@color/display_result_text_color" />
 
-</RelativeLayout>
+</com.android.calculator2.CalculatorDisplay>
diff --git a/src/com/android/calculator2/Calculator.java b/src/com/android/calculator2/Calculator.java
index be83d7d..e29adee 100644
--- a/src/com/android/calculator2/Calculator.java
+++ b/src/com/android/calculator2/Calculator.java
@@ -31,8 +31,8 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
+import android.app.ActionBar;
 import android.app.Activity;
-import android.app.AlertDialog;
 import android.content.ClipData;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -43,11 +43,9 @@
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.v4.view.ViewPager;
-import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.style.ForegroundColorSpan;
-import android.text.TextUtils;
 import android.util.Property;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
@@ -196,7 +194,7 @@
     private CalculatorState mCurrentState;
     private Evaluator mEvaluator;
 
-    private View mDisplayView;
+    private CalculatorDisplay mDisplayView;
     private TextView mModeView;
     private CalculatorText mFormulaText;
     private CalculatorResult mResultText;
@@ -232,7 +230,15 @@
         // Hide all default options in the ActionBar.
         getActionBar().setDisplayOptions(0);
 
-        mDisplayView = findViewById(R.id.display);
+        // Ensure the toolbar stays visible while the options menu is displayed.
+        getActionBar().addOnMenuVisibilityListener(new ActionBar.OnMenuVisibilityListener() {
+            @Override
+            public void onMenuVisibilityChanged(boolean isVisible) {
+                mDisplayView.setForceToolbarVisible(isVisible);
+            }
+        });
+
+        mDisplayView = (CalculatorDisplay) findViewById(R.id.display);
         mModeView = (TextView) findViewById(R.id.mode);
         mFormulaText = (CalculatorText) findViewById(R.id.formula);
         mResultText = (CalculatorResult) findViewById(R.id.result);
@@ -316,6 +322,14 @@
     }
 
     @Override
+    protected void onResume() {
+        super.onResume();
+
+        // Always show the toolbar initially on launch.
+        mDisplayView.showToolbar();
+    }
+
+    @Override
     protected void onSaveInstanceState(@NonNull Bundle outState) {
         mEvaluator.cancelAll(true);
         // If there's an animation in progress, cancel it first to ensure our state is up-to-date.
@@ -452,6 +466,9 @@
             mModeToggle.setText(R.string.mode_deg);
             mModeToggle.setContentDescription(getString(R.string.desc_switch_deg));
         }
+
+        // Show the toolbar to highlight the mode change.
+        mDisplayView.showToolbar();
     }
 
     /**
@@ -514,11 +531,16 @@
         // Any animation is ended before we get here.
         mCurrentButton = view;
         stopActionMode();
+
+        // Attempt to hide the toolbar whenever an interaction has occurred.
+        mDisplayView.hideToolbar();
+
         // See onKey above for the rationale behind some of the behavior below:
         if (mCurrentState != CalculatorState.EVALUATE) {
             // Cancel evaluations that were not specifically requested.
             mEvaluator.cancelAll(true);
         }
+
         final int id = view.getId();
         switch (id) {
             case R.id.eq:
@@ -582,6 +604,9 @@
     public boolean onLongClick(View view) {
         mCurrentButton = view;
 
+        // Attempt to hide the toolbar whenever an interaction has occurred.
+        mDisplayView.hideToolbar();
+
         if (view.getId() == R.id.del) {
             onClear();
             return true;
diff --git a/src/com/android/calculator2/CalculatorDisplay.java b/src/com/android/calculator2/CalculatorDisplay.java
new file mode 100644
index 0000000..8d704cf
--- /dev/null
+++ b/src/com/android/calculator2/CalculatorDisplay.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2016 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 com.android.calculator2;
+
+import android.content.Context;
+import android.transition.Fade;
+import android.transition.Transition;
+import android.transition.TransitionManager;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.RelativeLayout;
+import android.widget.Toolbar;
+
+public class CalculatorDisplay extends RelativeLayout
+        implements AccessibilityManager.AccessibilityStateChangeListener {
+
+    /**
+     * The duration in milliseconds after which to hide the toolbar.
+     */
+    private static final long AUTO_HIDE_DELAY_MILLIS = 3000L;
+
+    /**
+     * The duration in milliseconds to fade in/out the toolbar.
+     */
+    private static final long FADE_DURATION = 200L;
+
+    private final Runnable mHideToolbarRunnable = new Runnable() {
+        @Override
+        public void run() {
+            // Remove any duplicate callbacks to hide the toolbar.
+            removeCallbacks(this);
+
+            // Only animate if we have been laid out at least once.
+            if (isLaidOut()) {
+                TransitionManager.beginDelayedTransition(CalculatorDisplay.this, mTransition);
+            }
+            mToolbar.setVisibility(View.INVISIBLE);
+        }
+    };
+
+    private final AccessibilityManager mAccessibilityManager;
+    private final GestureDetector mTapDetector;
+
+    private Toolbar mToolbar;
+    private Transition mTransition;
+
+    private boolean mForceToolbarVisible;
+
+    public CalculatorDisplay(Context context) {
+        this(context, null /* attrs */);
+    }
+
+    public CalculatorDisplay(Context context, AttributeSet attrs) {
+        this(context, attrs, 0 /* defStyleAttr */);
+    }
+
+    public CalculatorDisplay(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        mAccessibilityManager =
+                (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+
+        mTapDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
+            @Override
+            public boolean onDown(MotionEvent e) {
+                // Remove callbacks to hide the toolbar.
+                removeCallbacks(mHideToolbarRunnable);
+
+                return true;
+            }
+
+            @Override
+            public boolean onSingleTapConfirmed(MotionEvent e) {
+                if (mToolbar.getVisibility() != View.VISIBLE) {
+                    showToolbar();
+                } else {
+                    hideToolbar();
+                }
+
+                return true;
+            }
+        });
+        mTapDetector.setIsLongpressEnabled(false);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mToolbar = (Toolbar) findViewById(R.id.toolbar);
+        mTransition = new Fade()
+                .setDuration(FADE_DURATION)
+                .addTarget(mToolbar);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mAccessibilityManager.addAccessibilityStateChangeListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mAccessibilityManager.removeAccessibilityStateChangeListener(this);
+    }
+
+    @Override
+    public void onAccessibilityStateChanged(boolean enabled) {
+        // Always show the toolbar whenever accessibility is enabled.
+        showToolbar();
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent event) {
+        mTapDetector.onTouchEvent(event);
+        return super.onInterceptTouchEvent(event);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        return mTapDetector.onTouchEvent(event) || super.onTouchEvent(event);
+    }
+
+    /**
+     * Returns {@code true} if the toolbar should remain visible.
+     */
+    public boolean getForceToolbarVisible() {
+        return mForceToolbarVisible || mAccessibilityManager.isEnabled();
+    }
+
+    /**
+     * Forces the toolbar to remain visible.
+     *
+     * @param forceToolbarVisible {@code true} to keep the toolbar visible
+     */
+    public void setForceToolbarVisible(boolean forceToolbarVisible) {
+        mForceToolbarVisible = forceToolbarVisible;
+        showToolbar();
+    }
+
+    /**
+     * Shows the toolbar.
+     */
+    public void showToolbar() {
+        // Only animate if we have been laid out at least once.
+        if (isLaidOut()) {
+            TransitionManager.beginDelayedTransition(this, mTransition);
+        }
+        mToolbar.setVisibility(View.VISIBLE);
+
+        // Remove callbacks to hide the toolbar.
+        removeCallbacks(mHideToolbarRunnable);
+
+        // Auto hide the toolbar after 3 seconds.
+        if (!getForceToolbarVisible()) {
+            postDelayed(mHideToolbarRunnable, AUTO_HIDE_DELAY_MILLIS);
+        }
+    }
+
+    /**
+     * Hides the toolbar.
+     */
+    public void hideToolbar() {
+        if (!getForceToolbarVisible()) {
+            post(mHideToolbarRunnable);
+        }
+    }
+}