Java tutorial
/* * Copyright 2016 Google Inc. All Rights Reserved. * * 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 io.mattcarroll.hover.defaulthovermenu.window; import android.content.Context; import android.graphics.PointF; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.ViewConfiguration; import android.view.WindowManager; import org.json.JSONException; import org.json.JSONObject; import java.util.HashSet; import java.util.Set; import io.mattcarroll.hover.HoverMenu; import io.mattcarroll.hover.HoverMenuAdapter; import io.mattcarroll.hover.Navigator; import io.mattcarroll.hover.defaulthovermenu.HoverMenuView; /** * {@link HoverMenu} implementation that displays within a {@code Window}. */ public class WindowHoverMenu implements HoverMenu { private static final String TAG = "WindowHoverMenu"; private WindowViewController mWindowViewController; // Shows/hides/positions Views in a Window. private HoverMenuView mHoverMenuView; // The visual presentation of the Hover menu. private boolean mIsShowingHoverMenu; // Are we currently display mHoverMenuView? private boolean mIsInDragMode; // If we're not in drag mode then we're in menu mode. private Set<OnExitListener> mOnExitListeners = new HashSet<>(); private HoverMenuView.HoverMenuTransitionListener mHoverMenuTransitionListener = new HoverMenuView.HoverMenuTransitionListener() { @Override public void onCollapsing() { } @Override public void onCollapsed() { mIsInDragMode = true; // When collapsed, we make mHoverMenuView untouchable so that the WindowDragWatcher can // take over. We do this so that touch events outside the drag area can propagate to // applications on screen. mWindowViewController.makeUntouchable(mHoverMenuView); } @Override public void onExpanding() { mIsInDragMode = false; } @Override public void onExpanded() { mWindowViewController.makeTouchable(mHoverMenuView); } }; private HoverMenuView.HoverMenuExitRequestListener mMenuExitRequestListener = new HoverMenuView.HoverMenuExitRequestListener() { @Override public void onExitRequested() { hide(); } }; public WindowHoverMenu(@NonNull Context context, @NonNull WindowManager windowManager, @Nullable Navigator navigator, @Nullable String savedVisualState) { mWindowViewController = new WindowViewController(windowManager); InWindowDragger inWindowDragger = new InWindowDragger(context, mWindowViewController, ViewConfiguration.get(context).getScaledTouchSlop()); PointF anchorState = new PointF(2, 0.5f); // Default to right side, half way down. See CollapsedMenuAnchor. if (null != savedVisualState) { try { VisualStateMemento visualStateMemento = VisualStateMemento.fromJsonString(savedVisualState); anchorState.set(visualStateMemento.getAnchorSide(), visualStateMemento.getNormalizedPositionY()); } catch (JSONException e) { e.printStackTrace(); } } mHoverMenuView = new HoverMenuView(context, navigator, inWindowDragger, anchorState); mHoverMenuView.setHoverMenuExitRequestListener(mMenuExitRequestListener); } @Override public String getVisualState() { PointF anchor = mHoverMenuView.getAnchorState(); return new VisualStateMemento((int) anchor.x, anchor.y).toJsonString(); } @Override public void restoreVisualState(@NonNull String savedVisualState) { try { VisualStateMemento memento = VisualStateMemento.fromJsonString(savedVisualState); mHoverMenuView.setAnchorState(new PointF(memento.getAnchorSide(), memento.getNormalizedPositionY())); } catch (JSONException e) { e.printStackTrace(); } } @Override public void setAdapter(@Nullable HoverMenuAdapter adapter) { mHoverMenuView.setAdapter(adapter); } /** * Initializes and displays the Hover menu. To destroy and remove the Hover menu, use {@link #hide()}. */ @Override public void show() { if (!mIsShowingHoverMenu) { mWindowViewController.addView(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, false, mHoverMenuView); // Sync our control state with the HoverMenuView state. if (mHoverMenuView.isExpanded()) { mWindowViewController.makeTouchable(mHoverMenuView); } else { collapseMenu(); } mIsShowingHoverMenu = true; } } /** * Exits the Hover menu system. This method is the inverse of {@link #show()}. */ @Override public void hide() { if (mIsShowingHoverMenu) { mIsShowingHoverMenu = false; // Notify our exit listeners that we're exiting. notifyOnExitListeners(); // Cleanup the control structures and Views. mWindowViewController.removeView(mHoverMenuView); mHoverMenuView.release(); } } /** * Expands the Hover menu to show all of its tabs and a content area for the selected tab. To * collapse the menu down a single active tab, use {@link #collapseMenu()}. */ @Override public void expandMenu() { if (mIsInDragMode) { mHoverMenuView.expand(); } } /** * Collapses the Hover menu down to its single active tab and allows the tab to be dragged * around the display. This method is the inverse of {@link #expandMenu()}. */ @Override public void collapseMenu() { if (!mIsInDragMode) { mHoverMenuView.setHoverMenuTransitionListener(mHoverMenuTransitionListener); mHoverMenuView.collapse(); } } @Override public void addOnExitListener(@NonNull OnExitListener onExitListener) { mOnExitListeners.add(onExitListener); } @Override public void removeOnExitListener(@NonNull OnExitListener onExitListener) { mOnExitListeners.remove(onExitListener); } private void notifyOnExitListeners() { for (OnExitListener listener : mOnExitListeners) { listener.onExitByUserRequest(); } } private static class VisualStateMemento { private static final String JSON_KEY_ANCHOR_SIDE = "anchor_side"; private static final String JSON_KEY_NORMALIZED_POSITION_Y = "normalized_position_y"; public static VisualStateMemento fromJsonString(@NonNull String jsonString) throws JSONException { JSONObject jsonObject = new JSONObject(jsonString); int anchorSide = jsonObject.getInt(JSON_KEY_ANCHOR_SIDE); float normalizedPositionY = (float) jsonObject.getDouble(JSON_KEY_NORMALIZED_POSITION_Y); return new VisualStateMemento(anchorSide, normalizedPositionY); } private int mAnchorSide; private float mNormalizedPositionY; public VisualStateMemento(int anchorSide, float normalizedPositionY) { mAnchorSide = anchorSide; mNormalizedPositionY = normalizedPositionY; } public int getAnchorSide() { return mAnchorSide; } public float getNormalizedPositionY() { return mNormalizedPositionY; } public String toJsonString() { try { JSONObject jsonObject = new JSONObject(); jsonObject.put(JSON_KEY_ANCHOR_SIDE, mAnchorSide); jsonObject.put(JSON_KEY_NORMALIZED_POSITION_Y, mNormalizedPositionY); return jsonObject.toString(); } catch (JSONException e) { e.printStackTrace(); return null; } } } }