工欲善其事,必先利其器篇-高效实现右滑退出功能

在一个偶然的机会遇见她

/** Copyright (C) 2006 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.internal.policy;import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.*;import android.app.ActivityManager;
import android.app.SearchManager;
import android.media.session.MediaSessionManager;
import android.os.UserHandle;import android.text.TextUtils;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.IRotationWatcher.Stub;
import android.view.IWindowManager;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputQueue;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SearchEvent;
import android.view.SurfaceHolder.Callback2;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewManager;
import android.view.ViewParent;
import android.view.ViewRootImpl;
import android.view.ViewRootImpl.ActivityConfigCallback;
import android.view.Window;
import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.view.menu.ContextMenuBuilder;
import com.android.internal.view.menu.IconMenuPresenter;
import com.android.internal.view.menu.ListMenuPresenter;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuDialogHelper;
import com.android.internal.view.menu.MenuHelper;
import com.android.internal.view.menu.MenuPresenter;
import com.android.internal.view.menu.MenuView;
import com.android.internal.widget.DecorContentParent;
import com.android.internal.widget.SwipeDismissLayout;import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.session.MediaController;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.transition.Scene;
import android.transition.Transition;
import android.transition.TransitionInflater;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.AndroidRuntimeException;
import android.util.EventLog;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;import java.lang.ref.WeakReference;
import java.util.ArrayList;/*** Android-specific Window.* <p>* todo: need to pull the generic functionality out into a base class* in android.widget.** @hide*/
public class PhoneWindow extends Window implements MenuBuilder.Callback {private final static String TAG = "PhoneWindow";private static final boolean DEBUG = false;private final static int DEFAULT_BACKGROUND_FADE_DURATION_MS = 300;private static final int CUSTOM_TITLE_COMPATIBLE_FEATURES = DEFAULT_FEATURES |(1 << FEATURE_CUSTOM_TITLE) |(1 << FEATURE_CONTENT_TRANSITIONS) |(1 << FEATURE_ACTIVITY_TRANSITIONS) |(1 << FEATURE_ACTION_MODE_OVERLAY);private static final Transition USE_DEFAULT_TRANSITION = new TransitionSet();/*** Simple callback used by the context menu and its submenus. The options* menu submenus do not use this (their behavior is more complex).*/final PhoneWindowMenuCallback mContextMenuCallback = new PhoneWindowMenuCallback(this);final TypedValue mMinWidthMajor = new TypedValue();final TypedValue mMinWidthMinor = new TypedValue();TypedValue mFixedWidthMajor;TypedValue mFixedWidthMinor;TypedValue mFixedHeightMajor;TypedValue mFixedHeightMinor;// This is the top-level view of the window, containing the window decor.private DecorView mDecor;// When we reuse decor views, we need to recreate the content root. This happens when the decor// view is requested, so we need to force the recreating without introducing an infinite loop.private boolean mForceDecorInstall = false;// This is the view in which the window contents are placed. It is either// mDecor itself, or a child of mDecor where the contents go.ViewGroup mContentParent;// Whether the client has explicitly set the content view. If false and mContentParent is not// null, then the content parent was set due to window preservation.private boolean mContentParentExplicitlySet = false;Callback2 mTakeSurfaceCallback;InputQueue.Callback mTakeInputQueueCallback;boolean mIsFloating;private boolean mIsTranslucent;private LayoutInflater mLayoutInflater;private TextView mTitleView;DecorContentParent mDecorContentParent;private ActionMenuPresenterCallback mActionMenuPresenterCallback;private PanelMenuPresenterCallback mPanelMenuPresenterCallback;private TransitionManager mTransitionManager;private Scene mContentScene;// The icon resource has been explicitly set elsewhere// and should not be overwritten with a default.static final int FLAG_RESOURCE_SET_ICON = 1 << 0;// The logo resource has been explicitly set elsewhere// and should not be overwritten with a default.static final int FLAG_RESOURCE_SET_LOGO = 1 << 1;// The icon resource is currently configured to use the system fallback// as no default was previously specified. Anything can override this.static final int FLAG_RESOURCE_SET_ICON_FALLBACK = 1 << 2;int mResourcesSetFlags;int mIconRes;int mLogoRes;private DrawableFeatureState[] mDrawables;private PanelFeatureState[] mPanels;/*** The panel that is prepared or opened (the most recent one if there are* multiple panels). Shortcuts will go to this panel. It gets set in* {@link #preparePanel} and cleared in {@link #closePanel}.*/PanelFeatureState mPreparedPanel;/*** The keycode that is currently held down (as a modifier) for chording. If* this is 0, there is no key held down.*/int mPanelChordingKey;// This stores if the system supports Picture-in-Picture// to see if KEYCODE_WINDOW should be handled here or not.private boolean mSupportsPictureInPicture;private ImageView mLeftIconView;private ImageView mRightIconView;private ProgressBar mCircularProgressBar;private ProgressBar mHorizontalProgressBar;int mBackgroundResource = 0;int mBackgroundFallbackResource = 0;private Drawable mBackgroundDrawable;private boolean mLoadElevation = true;private float mElevation;/** Whether window content should be clipped to the background outline. */private boolean mClipToOutline;private int mFrameResource = 0;private int mTextColor = 0;int mStatusBarColor = 0;int mNavigationBarColor = 0;int mNavigationBarDividerColor = 0;private boolean mForcedStatusBarColor = false;private boolean mForcedNavigationBarColor = false;private CharSequence mTitle = null;private int mTitleColor = 0;private boolean mAlwaysReadCloseOnTouchAttr = false;ContextMenuBuilder mContextMenu;MenuHelper mContextMenuHelper;private boolean mClosingActionMenu;private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;private MediaController mMediaController;private AudioManager mAudioManager;private KeyguardManager mKeyguardManager;private MediaSessionManager mMediaSessionManager;private int mUiOptions = 0;private boolean mInvalidatePanelMenuPosted;private int mInvalidatePanelMenuFeatures;private final Runnable mInvalidatePanelMenuRunnable = new Runnable() {@Override public void run() {for (int i = 0; i <= FEATURE_MAX; i++) {if ((mInvalidatePanelMenuFeatures & 1 << i) != 0) {doInvalidatePanelMenu(i);}}mInvalidatePanelMenuPosted = false;mInvalidatePanelMenuFeatures = 0;}};private Transition mEnterTransition = null;private Transition mReturnTransition = USE_DEFAULT_TRANSITION;private Transition mExitTransition = null;private Transition mReenterTransition = USE_DEFAULT_TRANSITION;private Transition mSharedElementEnterTransition = null;private Transition mSharedElementReturnTransition = USE_DEFAULT_TRANSITION;private Transition mSharedElementExitTransition = null;private Transition mSharedElementReenterTransition = USE_DEFAULT_TRANSITION;private Boolean mAllowReturnTransitionOverlap;private Boolean mAllowEnterTransitionOverlap;private long mBackgroundFadeDurationMillis = -1;private Boolean mSharedElementsUseOverlay;private boolean mIsStartingWindow;private int mTheme = -1;private int mDecorCaptionShade = DECOR_CAPTION_SHADE_AUTO;private boolean mUseDecorContext = false;/** @see ViewRootImpl#mActivityConfigCallback */private ActivityConfigCallback mActivityConfigCallback;static class WindowManagerHolder {static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));}static final RotationWatcher sRotationWatcher = new RotationWatcher();public PhoneWindow(Context context) {super(context);mLayoutInflater = LayoutInflater.from(context);}/*** Constructor for main window of an activity.*/public PhoneWindow(Context context, Window preservedWindow,ActivityConfigCallback activityConfigCallback) {this(context);// Only main activity windows use decor context, all the other windows depend on whatever// context that was given to them.mUseDecorContext = true;if (preservedWindow != null) {mDecor = (DecorView) preservedWindow.getDecorView();mElevation = preservedWindow.getElevation();mLoadElevation = false;mForceDecorInstall = true;// If we're preserving window, carry over the app token from the preserved// window, as we'll be skipping the addView in handleResumeActivity(), and// the token will not be updated as for a new window.getAttributes().token = preservedWindow.getAttributes().token;}// Even though the device doesn't support picture-in-picture mode,// an user can force using it through developer options.boolean forceResizable = Settings.Global.getInt(context.getContentResolver(),DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;mSupportsPictureInPicture = forceResizable || context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE);mActivityConfigCallback = activityConfigCallback;}@Overridepublic final void setContainer(Window container) {super.setContainer(container);}@Overridepublic boolean requestFeature(int featureId) {if (mContentParentExplicitlySet) {throw new AndroidRuntimeException("requestFeature() must be called before adding content");}final int features = getFeatures();final int newFeatures = features | (1 << featureId);if ((newFeatures & (1 << FEATURE_CUSTOM_TITLE)) != 0 &&(newFeatures & ~CUSTOM_TITLE_COMPATIBLE_FEATURES) != 0) {// Another feature is enabled and the user is trying to enable the custom title feature// or custom title feature is enabled and the user is trying to enable another featurethrow new AndroidRuntimeException("You cannot combine custom titles with other title features");}if ((features & (1 << FEATURE_NO_TITLE)) != 0 && featureId == FEATURE_ACTION_BAR) {return false; // Ignore. No title dominates.}if ((features & (1 << FEATURE_ACTION_BAR)) != 0 && featureId == FEATURE_NO_TITLE) {// Remove the action bar feature if we have no title. No title dominates.removeFeature(FEATURE_ACTION_BAR);}if ((features & (1 << FEATURE_ACTION_BAR)) != 0 && featureId == FEATURE_SWIPE_TO_DISMISS) {throw new AndroidRuntimeException("You cannot combine swipe dismissal and the action bar.");}if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0 && featureId == FEATURE_ACTION_BAR) {throw new AndroidRuntimeException("You cannot combine swipe dismissal and the action bar.");}if (featureId == FEATURE_INDETERMINATE_PROGRESS &&getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {throw new AndroidRuntimeException("You cannot use indeterminate progress on a watch.");}return super.requestFeature(featureId);}@Overridepublic void setUiOptions(int uiOptions) {mUiOptions = uiOptions;}@Overridepublic void setUiOptions(int uiOptions, int mask) {mUiOptions = (mUiOptions & ~mask) | (uiOptions & mask);}@Overridepublic TransitionManager getTransitionManager() {return mTransitionManager;}@Overridepublic void setTransitionManager(TransitionManager tm) {mTransitionManager = tm;}@Overridepublic Scene getContentScene() {return mContentScene;}@Overridepublic void setContentView(int layoutResID) {// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window// decor, when theme attributes and the like are crystalized. Do not check the feature// before this happens.if (mContentParent == null) {installDecor();} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {mContentParent.removeAllViews();}if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,getContext());transitionTo(newScene);} else {mLayoutInflater.inflate(layoutResID, mContentParent);}mContentParent.requestApplyInsets();final Callback cb = getCallback();if (cb != null && !isDestroyed()) {cb.onContentChanged();}mContentParentExplicitlySet = true;}@Overridepublic void setContentView(View view) {setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));}@Overridepublic void setContentView(View view, ViewGroup.LayoutParams params) {// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window// decor, when theme attributes and the like are crystalized. Do not check the feature// before this happens.if (mContentParent == null) {installDecor();} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {mContentParent.removeAllViews();}if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {view.setLayoutParams(params);final Scene newScene = new Scene(mContentParent, view);transitionTo(newScene);} else {mContentParent.addView(view, params);}mContentParent.requestApplyInsets();final Callback cb = getCallback();if (cb != null && !isDestroyed()) {cb.onContentChanged();}mContentParentExplicitlySet = true;}@Overridepublic void addContentView(View view, ViewGroup.LayoutParams params) {if (mContentParent == null) {installDecor();}if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {// TODO Augment the scenes/transitions API to support this.Log.v(TAG, "addContentView does not support content transitions");}mContentParent.addView(view, params);mContentParent.requestApplyInsets();final Callback cb = getCallback();if (cb != null && !isDestroyed()) {cb.onContentChanged();}}@Overridepublic void clearContentView() {if (mDecor != null) {mDecor.clearContentView();}}private void transitionTo(Scene scene) {if (mContentScene == null) {scene.enter();} else {mTransitionManager.transitionTo(scene);}mContentScene = scene;}@Overridepublic View getCurrentFocus() {return mDecor != null ? mDecor.findFocus() : null;}@Overridepublic void takeSurface(Callback2 callback) {mTakeSurfaceCallback = callback;}public void takeInputQueue(InputQueue.Callback callback) {mTakeInputQueueCallback = callback;}@Overridepublic boolean isFloating() {return mIsFloating;}public boolean isTranslucent() {return mIsTranslucent;}/*** @return Whether the window is currently showing the wallpaper.*/boolean isShowingWallpaper() {return (getAttributes().flags & FLAG_SHOW_WALLPAPER) != 0;}/*** Return a LayoutInflater instance that can be used to inflate XML view layout* resources for use in this Window.** @return LayoutInflater The shared LayoutInflater.*/@Overridepublic LayoutInflater getLayoutInflater() {return mLayoutInflater;}@Overridepublic void setTitle(CharSequence title) {setTitle(title, true);}public void setTitle(CharSequence title, boolean updateAccessibilityTitle) {if (mTitleView != null) {mTitleView.setText(title);} else if (mDecorContentParent != null) {mDecorContentParent.setWindowTitle(title);}mTitle = title;if (updateAccessibilityTitle) {WindowManager.LayoutParams params = getAttributes();if (!TextUtils.equals(title, params.accessibilityTitle)) {params.accessibilityTitle = TextUtils.stringOrSpannedString(title);if (mDecor != null) {// ViewRootImpl will make sure the change propagates to WindowManagerServiceViewRootImpl vr = mDecor.getViewRootImpl();if (vr != null) {vr.onWindowTitleChanged();}}dispatchWindowAttributesChanged(getAttributes());}}}@Override@Deprecatedpublic void setTitleColor(int textColor) {if (mTitleView != null) {mTitleView.setTextColor(textColor);}mTitleColor = textColor;}/*** Prepares the panel to either be opened or chorded. This creates the Menu* instance for the panel and populates it via the Activity callbacks.** @param st The panel state to prepare.* @param event The event that triggered the preparing of the panel.* @return Whether the panel was prepared. If the panel should not be shown,*         returns false.*/public final boolean preparePanel(PanelFeatureState st, KeyEvent event) {if (isDestroyed()) {return false;}// Already prepared (isPrepared will be reset to false later)if (st.isPrepared) {return true;}if ((mPreparedPanel != null) && (mPreparedPanel != st)) {// Another Panel is prepared and possibly open, so close itclosePanel(mPreparedPanel, false);}final Callback cb = getCallback();if (cb != null) {st.createdPanelView = cb.onCreatePanelView(st.featureId);}final boolean isActionBarMenu =(st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_ACTION_BAR);if (isActionBarMenu && mDecorContentParent != null) {// Enforce ordering guarantees around events so that the action bar never// dispatches menu-related events before the panel is prepared.mDecorContentParent.setMenuPrepared();}if (st.createdPanelView == null) {// Init the panel state's menu--return false if init failedif (st.menu == null || st.refreshMenuContent) {if (st.menu == null) {if (!initializePanelMenu(st) || (st.menu == null)) {return false;}}if (isActionBarMenu && mDecorContentParent != null) {if (mActionMenuPresenterCallback == null) {mActionMenuPresenterCallback = new ActionMenuPresenterCallback();}mDecorContentParent.setMenu(st.menu, mActionMenuPresenterCallback);}// Call callback, and return if it doesn't want to display menu.// Creating the panel menu will involve a lot of manipulation;// don't dispatch change events to presenters until we're done.st.menu.stopDispatchingItemsChanged();if ((cb == null) || !cb.onCreatePanelMenu(st.featureId, st.menu)) {// Ditch the menu created abovest.setMenu(null);if (isActionBarMenu && mDecorContentParent != null) {// Don't show it in the action bar eithermDecorContentParent.setMenu(null, mActionMenuPresenterCallback);}return false;}st.refreshMenuContent = false;}// Callback and return if the callback does not want to show the menu// Preparing the panel menu can involve a lot of manipulation;// don't dispatch change events to presenters until we're done.st.menu.stopDispatchingItemsChanged();// Restore action view state before we prepare. This gives apps// an opportunity to override frozen/restored state in onPrepare.if (st.frozenActionViewState != null) {st.menu.restoreActionViewStates(st.frozenActionViewState);st.frozenActionViewState = null;}if (!cb.onPreparePanel(st.featureId, st.createdPanelView, st.menu)) {if (isActionBarMenu && mDecorContentParent != null) {// The app didn't want to show the menu for now but it still exists.// Clear it out of the action bar.mDecorContentParent.setMenu(null, mActionMenuPresenterCallback);}st.menu.startDispatchingItemsChanged();return false;}// Set the proper keymapKeyCharacterMap kmap = KeyCharacterMap.load(event != null ? event.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD);st.qwertyMode = kmap.getKeyboardType() != KeyCharacterMap.NUMERIC;st.menu.setQwertyMode(st.qwertyMode);st.menu.startDispatchingItemsChanged();}// Set other statest.isPrepared = true;st.isHandled = false;mPreparedPanel = st;return true;}@Overridepublic void onConfigurationChanged(Configuration newConfig) {// Action bars handle their own menu stateif (mDecorContentParent == null) {PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);if ((st != null) && (st.menu != null)) {if (st.isOpen) {// Freeze statefinal Bundle state = new Bundle();if (st.iconMenuPresenter != null) {st.iconMenuPresenter.saveHierarchyState(state);}if (st.listMenuPresenter != null) {st.listMenuPresenter.saveHierarchyState(state);}// Remove the menu views since they need to be recreated// according to the new configurationclearMenuViews(st);// Re-open the same menureopenMenu(false);// Restore stateif (st.iconMenuPresenter != null) {st.iconMenuPresenter.restoreHierarchyState(state);}if (st.listMenuPresenter != null) {st.listMenuPresenter.restoreHierarchyState(state);}} else {// Clear menu views so on next menu opening, it will use// the proper layoutclearMenuViews(st);}}}}@Overridepublic void onMultiWindowModeChanged() {if (mDecor != null) {mDecor.onConfigurationChanged(getContext().getResources().getConfiguration());}}@Overridepublic void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {if (mDecor != null) {mDecor.updatePictureInPictureOutlineProvider(isInPictureInPictureMode);}}@Overridepublic void reportActivityRelaunched() {if (mDecor != null && mDecor.getViewRootImpl() != null) {mDecor.getViewRootImpl().reportActivityRelaunched();}}private static void clearMenuViews(PanelFeatureState st) {// This can be called on config changes, so we should make sure// the views will be reconstructed based on the new orientation, etc.// Allow the callback to create a new panel viewst.createdPanelView = null;// Causes the decor view to be recreatedst.refreshDecorView = true;st.clearMenuPresenters();}@Overridepublic final void openPanel(int featureId, KeyEvent event) {if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null &&mDecorContentParent.canShowOverflowMenu() &&!ViewConfiguration.get(getContext()).hasPermanentMenuKey()) {mDecorContentParent.showOverflowMenu();} else {openPanel(getPanelState(featureId, true), event);}}private void openPanel(final PanelFeatureState st, KeyEvent event) {// System.out.println("Open panel: isOpen=" + st.isOpen);// Already open, returnif (st.isOpen || isDestroyed()) {return;}// Don't open an options panel for honeycomb apps on xlarge devices.// (The app should be using an action bar for menu items.)if (st.featureId == FEATURE_OPTIONS_PANEL) {Context context = getContext();Configuration config = context.getResources().getConfiguration();boolean isXLarge = (config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) ==Configuration.SCREENLAYOUT_SIZE_XLARGE;boolean isHoneycombApp = context.getApplicationInfo().targetSdkVersion >=android.os.Build.VERSION_CODES.HONEYCOMB;if (isXLarge && isHoneycombApp) {return;}}Callback cb = getCallback();if ((cb != null) && (!cb.onMenuOpened(st.featureId, st.menu))) {// Callback doesn't want the menu to open, reset any stateclosePanel(st, true);return;}final WindowManager wm = getWindowManager();if (wm == null) {return;}// Prepare panel (should have been done before, but just in case)if (!preparePanel(st, event)) {return;}int width = WRAP_CONTENT;if (st.decorView == null || st.refreshDecorView) {if (st.decorView == null) {// Initialize the panel decor, this will populate st.decorViewif (!initializePanelDecor(st) || (st.decorView == null))return;} else if (st.refreshDecorView && (st.decorView.getChildCount() > 0)) {// Decor needs refreshing, so remove its viewsst.decorView.removeAllViews();}// This will populate st.shownPanelViewif (!initializePanelContent(st) || !st.hasPanelItems()) {return;}ViewGroup.LayoutParams lp = st.shownPanelView.getLayoutParams();if (lp == null) {lp = new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);}int backgroundResId;if (lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {// If the contents is fill parent for the width, set the// corresponding backgroundbackgroundResId = st.fullBackground;width = MATCH_PARENT;} else {// Otherwise, set the normal panel backgroundbackgroundResId = st.background;}st.decorView.setWindowBackground(getContext().getDrawable(backgroundResId));ViewParent shownPanelParent = st.shownPanelView.getParent();if (shownPanelParent != null && shownPanelParent instanceof ViewGroup) {((ViewGroup) shownPanelParent).removeView(st.shownPanelView);}st.decorView.addView(st.shownPanelView, lp);/** Give focus to the view, if it or one of its children does not* already have it.*/if (!st.shownPanelView.hasFocus()) {st.shownPanelView.requestFocus();}} else if (!st.isInListMode()) {width = MATCH_PARENT;} else if (st.createdPanelView != null) {// If we already had a panel view, carry width=MATCH_PARENT through// as we did above when it was created.ViewGroup.LayoutParams lp = st.createdPanelView.getLayoutParams();if (lp != null && lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {width = MATCH_PARENT;}}st.isHandled = false;WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, WRAP_CONTENT,st.x, st.y, WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG,WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,st.decorView.mDefaultOpacity);if (st.isCompact) {lp.gravity = getOptionsPanelGravity();sRotationWatcher.addWindow(this);} else {lp.gravity = st.gravity;}lp.windowAnimations = st.windowAnimations;wm.addView(st.decorView, lp);st.isOpen = true;// Log.v(TAG, "Adding main menu to window manager.");}@Overridepublic final void closePanel(int featureId) {if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null &&mDecorContentParent.canShowOverflowMenu() &&!ViewConfiguration.get(getContext()).hasPermanentMenuKey()) {mDecorContentParent.hideOverflowMenu();} else if (featureId == FEATURE_CONTEXT_MENU) {closeContextMenu();} else {closePanel(getPanelState(featureId, true), true);}}/*** Closes the given panel.** @param st The panel to be closed.* @param doCallback Whether to notify the callback that the panel was*            closed. If the panel is in the process of re-opening or*            opening another panel (e.g., menu opening a sub menu), the*            callback should not happen and this variable should be false.*            In addition, this method internally will only perform the*            callback if the panel is open.*/public final void closePanel(PanelFeatureState st, boolean doCallback) {// System.out.println("Close panel: isOpen=" + st.isOpen);if (doCallback && st.featureId == FEATURE_OPTIONS_PANEL &&mDecorContentParent != null && mDecorContentParent.isOverflowMenuShowing()) {checkCloseActionMenu(st.menu);return;}final ViewManager wm = getWindowManager();if ((wm != null) && st.isOpen) {if (st.decorView != null) {wm.removeView(st.decorView);// Log.v(TAG, "Removing main menu from window manager.");if (st.isCompact) {sRotationWatcher.removeWindow(this);}}if (doCallback) {callOnPanelClosed(st.featureId, st, null);}}st.isPrepared = false;st.isHandled = false;st.isOpen = false;// This view is no longer shown, so null it outst.shownPanelView = null;if (st.isInExpandedMode) {// Next time the menu opens, it should not be in expanded mode, so// force a refresh of the decorst.refreshDecorView = true;st.isInExpandedMode = false;}if (mPreparedPanel == st) {mPreparedPanel = null;mPanelChordingKey = 0;}}void checkCloseActionMenu(Menu menu) {if (mClosingActionMenu) {return;}mClosingActionMenu = true;mDecorContentParent.dismissPopups();Callback cb = getCallback();if (cb != null && !isDestroyed()) {cb.onPanelClosed(FEATURE_ACTION_BAR, menu);}mClosingActionMenu = false;}@Overridepublic final void togglePanel(int featureId, KeyEvent event) {PanelFeatureState st = getPanelState(featureId, true);if (st.isOpen) {closePanel(st, true);} else {openPanel(st, event);}}@Overridepublic void invalidatePanelMenu(int featureId) {mInvalidatePanelMenuFeatures |= 1 << featureId;if (!mInvalidatePanelMenuPosted && mDecor != null) {mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);mInvalidatePanelMenuPosted = true;}}void doPendingInvalidatePanelMenu() {if (mInvalidatePanelMenuPosted) {mDecor.removeCallbacks(mInvalidatePanelMenuRunnable);mInvalidatePanelMenuRunnable.run();}}void doInvalidatePanelMenu(int featureId) {PanelFeatureState st = getPanelState(featureId, false);if (st == null) {return;}Bundle savedActionViewStates = null;if (st.menu != null) {savedActionViewStates = new Bundle();st.menu.saveActionViewStates(savedActionViewStates);if (savedActionViewStates.size() > 0) {st.frozenActionViewState = savedActionViewStates;}// This will be started again when the panel is prepared.st.menu.stopDispatchingItemsChanged();st.menu.clear();}st.refreshMenuContent = true;st.refreshDecorView = true;// Prepare the options panel if we have an action barif ((featureId == FEATURE_ACTION_BAR || featureId == FEATURE_OPTIONS_PANEL)&& mDecorContentParent != null) {st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false);if (st != null) {st.isPrepared = false;preparePanel(st, null);}}}/*** Called when the panel key is pushed down.* @param featureId The feature ID of the relevant panel (defaults to FEATURE_OPTIONS_PANEL}.* @param event The key event.* @return Whether the key was handled.*/public final boolean onKeyDownPanel(int featureId, KeyEvent event) {final int keyCode = event.getKeyCode();if (event.getRepeatCount() == 0) {// The panel key was pushed, so set the chording keymPanelChordingKey = keyCode;PanelFeatureState st = getPanelState(featureId, false);if (st != null && !st.isOpen) {return preparePanel(st, event);}}return false;}/*** Called when the panel key is released.* @param featureId The feature ID of the relevant panel (defaults to FEATURE_OPTIONS_PANEL}.* @param event The key event.*/public final void onKeyUpPanel(int featureId, KeyEvent event) {// The panel key was released, so clear the chording keyif (mPanelChordingKey != 0) {mPanelChordingKey = 0;final PanelFeatureState st = getPanelState(featureId, false);if (event.isCanceled() || (mDecor != null && mDecor.mPrimaryActionMode != null) ||(st == null)) {return;}boolean playSoundEffect = false;if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null &&mDecorContentParent.canShowOverflowMenu() &&!ViewConfiguration.get(getContext()).hasPermanentMenuKey()) {if (!mDecorContentParent.isOverflowMenuShowing()) {if (!isDestroyed() && preparePanel(st, event)) {playSoundEffect = mDecorContentParent.showOverflowMenu();}} else {playSoundEffect = mDecorContentParent.hideOverflowMenu();}} else {if (st.isOpen || st.isHandled) {// Play the sound effect if the user closed an open menu (and not if// they just released a menu shortcut)playSoundEffect = st.isOpen;// Close menuclosePanel(st, true);} else if (st.isPrepared) {boolean show = true;if (st.refreshMenuContent) {// Something may have invalidated the menu since we prepared it.// Re-prepare it to refresh.st.isPrepared = false;show = preparePanel(st, event);}if (show) {// Write 'menu opened' to event logEventLog.writeEvent(50001, 0);// Show menuopenPanel(st, event);playSoundEffect = true;}}}if (playSoundEffect) {AudioManager audioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);if (audioManager != null) {audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);} else {Log.w(TAG, "Couldn't get audio manager");}}}}@Overridepublic final void closeAllPanels() {final ViewManager wm = getWindowManager();if (wm == null) {return;}final PanelFeatureState[] panels = mPanels;final int N = panels != null ? panels.length : 0;for (int i = 0; i < N; i++) {final PanelFeatureState panel = panels[i];if (panel != null) {closePanel(panel, true);}}closeContextMenu();}/*** Closes the context menu. This notifies the menu logic of the close, along* with dismissing it from the UI.*/private synchronized void closeContextMenu() {if (mContextMenu != null) {mContextMenu.close();dismissContextMenu();}}/*** Dismisses just the context menu UI. To close the context menu, use* {@link #closeContextMenu()}.*/private synchronized void dismissContextMenu() {mContextMenu = null;if (mContextMenuHelper != null) {mContextMenuHelper.dismiss();mContextMenuHelper = null;}}@Overridepublic boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) {return performPanelShortcut(getPanelState(featureId, false), keyCode, event, flags);}boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event,int flags) {if (event.isSystem() || (st == null)) {return false;}boolean handled = false;// Only try to perform menu shortcuts if preparePanel returned true (possible false// return value from application not wanting to show the menu).if ((st.isPrepared || preparePanel(st, event)) && st.menu != null) {// The menu is prepared now, perform the shortcut on ithandled = st.menu.performShortcut(keyCode, event, flags);}if (handled) {// Mark as handledst.isHandled = true;// Only close down the menu if we don't have an action bar keeping it open.if ((flags & Menu.FLAG_PERFORM_NO_CLOSE) == 0 && mDecorContentParent == null) {closePanel(st, true);}}return handled;}@Overridepublic boolean performPanelIdentifierAction(int featureId, int id, int flags) {PanelFeatureState st = getPanelState(featureId, true);if (!preparePanel(st, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU))) {return false;}if (st.menu == null) {return false;}boolean res = st.menu.performIdentifierAction(id, flags);// Only close down the menu if we don't have an action bar keeping it open.if (mDecorContentParent == null) {closePanel(st, true);}return res;}public PanelFeatureState findMenuPanel(Menu menu) {final PanelFeatureState[] panels = mPanels;final int N = panels != null ? panels.length : 0;for (int i = 0; i < N; i++) {final PanelFeatureState panel = panels[i];if (panel != null && panel.menu == menu) {return panel;}}return null;}public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {final Callback cb = getCallback();if (cb != null && !isDestroyed()) {final PanelFeatureState panel = findMenuPanel(menu.getRootMenu());if (panel != null) {return cb.onMenuItemSelected(panel.featureId, item);}}return false;}public void onMenuModeChange(MenuBuilder menu) {reopenMenu(true);}private void reopenMenu(boolean toggleMenuMode) {if (mDecorContentParent != null && mDecorContentParent.canShowOverflowMenu() &&(!ViewConfiguration.get(getContext()).hasPermanentMenuKey() ||mDecorContentParent.isOverflowMenuShowPending())) {final Callback cb = getCallback();if (!mDecorContentParent.isOverflowMenuShowing() || !toggleMenuMode) {if (cb != null && !isDestroyed()) {// If we have a menu invalidation pending, do it now.if (mInvalidatePanelMenuPosted &&(mInvalidatePanelMenuFeatures & (1 << FEATURE_OPTIONS_PANEL)) != 0) {mDecor.removeCallbacks(mInvalidatePanelMenuRunnable);mInvalidatePanelMenuRunnable.run();}final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);// If we don't have a menu or we're waiting for a full content refresh,// forget it. This is a lingering event that no longer matters.if (st != null && st.menu != null && !st.refreshMenuContent &&cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu);mDecorContentParent.showOverflowMenu();}}} else {mDecorContentParent.hideOverflowMenu();final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);if (st != null && cb != null && !isDestroyed()) {cb.onPanelClosed(FEATURE_ACTION_BAR, st.menu);}}return;}PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);if (st == null) {return;}// Save the future expanded mode state since closePanel will reset itboolean newExpandedMode = toggleMenuMode ? !st.isInExpandedMode : st.isInExpandedMode;st.refreshDecorView = true;closePanel(st, false);// Set the expanded mode statest.isInExpandedMode = newExpandedMode;openPanel(st, null);}/*** Initializes the menu associated with the given panel feature state. You* must at the very least set PanelFeatureState.menu to the Menu to be* associated with the given panel state. The default implementation creates* a new menu for the panel state.** @param st The panel whose menu is being initialized.* @return Whether the initialization was successful.*/protected boolean initializePanelMenu(final PanelFeatureState st) {Context context = getContext();// If we have an action bar, initialize the menu with the right theme.if ((st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_ACTION_BAR) &&mDecorContentParent != null) {final TypedValue outValue = new TypedValue();final Theme baseTheme = context.getTheme();baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);Theme widgetTheme = null;if (outValue.resourceId != 0) {widgetTheme = context.getResources().newTheme();widgetTheme.setTo(baseTheme);widgetTheme.applyStyle(outValue.resourceId, true);widgetTheme.resolveAttribute(R.attr.actionBarWidgetTheme, outValue, true);} else {baseTheme.resolveAttribute(R.attr.actionBarWidgetTheme, outValue, true);}if (outValue.resourceId != 0) {if (widgetTheme == null) {widgetTheme = context.getResources().newTheme();widgetTheme.setTo(baseTheme);}widgetTheme.applyStyle(outValue.resourceId, true);}if (widgetTheme != null) {context = new ContextThemeWrapper(context, 0);context.getTheme().setTo(widgetTheme);}}final MenuBuilder menu = new MenuBuilder(context);menu.setCallback(this);st.setMenu(menu);return true;}/*** Perform initial setup of a panel. This should at the very least set the* style information in the PanelFeatureState and must set* PanelFeatureState.decor to the panel's window decor view.** @param st The panel being initialized.*/protected boolean initializePanelDecor(PanelFeatureState st) {st.decorView = generateDecor(st.featureId);st.gravity = Gravity.CENTER | Gravity.BOTTOM;st.setStyle(getContext());TypedArray a = getContext().obtainStyledAttributes(null,R.styleable.Window, 0, st.listPresenterTheme);final float elevation = a.getDimension(R.styleable.Window_windowElevation, 0);if (elevation != 0) {st.decorView.setElevation(elevation);}a.recycle();return true;}/*** Determine the gravity value for the options panel. This can* differ in compact mode.** @return gravity value to use for the panel window*/private int getOptionsPanelGravity() {try {return WindowManagerHolder.sWindowManager.getPreferredOptionsPanelGravity();} catch (RemoteException ex) {Log.e(TAG, "Couldn't getOptionsPanelGravity; using default", ex);return Gravity.CENTER | Gravity.BOTTOM;}}void onOptionsPanelRotationChanged() {final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);if (st == null) return;final WindowManager.LayoutParams lp = st.decorView != null ?(WindowManager.LayoutParams) st.decorView.getLayoutParams() : null;if (lp != null) {lp.gravity = getOptionsPanelGravity();final ViewManager wm = getWindowManager();if (wm != null) {wm.updateViewLayout(st.decorView, lp);}}}/*** Initializes the panel associated with the panel feature state. You must* at the very least set PanelFeatureState.panel to the View implementing* its contents. The default implementation gets the panel from the menu.** @param st The panel state being initialized.* @return Whether the initialization was successful.*/protected boolean initializePanelContent(PanelFeatureState st) {if (st.createdPanelView != null) {st.shownPanelView = st.createdPanelView;return true;}if (st.menu == null) {return false;}if (mPanelMenuPresenterCallback == null) {mPanelMenuPresenterCallback = new PanelMenuPresenterCallback();}MenuView menuView = st.isInListMode()? st.getListMenuView(getContext(), mPanelMenuPresenterCallback): st.getIconMenuView(getContext(), mPanelMenuPresenterCallback);st.shownPanelView = (View) menuView;if (st.shownPanelView != null) {// Use the menu View's default animations if it has anyfinal int defaultAnimations = menuView.getWindowAnimations();if (defaultAnimations != 0) {st.windowAnimations = defaultAnimations;}return true;} else {return false;}}@Overridepublic boolean performContextMenuIdentifierAction(int id, int flags) {return (mContextMenu != null) ? mContextMenu.performIdentifierAction(id, flags) : false;}@Overridepublic final void setElevation(float elevation) {mElevation = elevation;final WindowManager.LayoutParams attrs = getAttributes();if (mDecor != null) {mDecor.setElevation(elevation);attrs.setSurfaceInsets(mDecor, true /*manual*/, false /*preservePrevious*/);}dispatchWindowAttributesChanged(attrs);}@Overridepublic float getElevation() {return mElevation;}@Overridepublic final void setClipToOutline(boolean clipToOutline) {mClipToOutline = clipToOutline;if (mDecor != null) {mDecor.setClipToOutline(clipToOutline);}}@Overridepublic final void setBackgroundDrawable(Drawable drawable) {if (drawable != mBackgroundDrawable || mBackgroundResource != 0) {mBackgroundResource = 0;mBackgroundDrawable = drawable;if (mDecor != null) {mDecor.setWindowBackground(drawable);}if (mBackgroundFallbackResource != 0) {mDecor.setBackgroundFallback(drawable != null ? 0 : mBackgroundFallbackResource);}}}@Overridepublic final void setFeatureDrawableResource(int featureId, int resId) {if (resId != 0) {DrawableFeatureState st = getDrawableState(featureId, true);if (st.resid != resId) {st.resid = resId;st.uri = null;st.local = getContext().getDrawable(resId);updateDrawable(featureId, st, false);}} else {setFeatureDrawable(featureId, null);}}@Overridepublic final void setFeatureDrawableUri(int featureId, Uri uri) {if (uri != null) {DrawableFeatureState st = getDrawableState(featureId, true);if (st.uri == null || !st.uri.equals(uri)) {st.resid = 0;st.uri = uri;st.local = loadImageURI(uri);updateDrawable(featureId, st, false);}} else {setFeatureDrawable(featureId, null);}}@Overridepublic final void setFeatureDrawable(int featureId, Drawable drawable) {DrawableFeatureState st = getDrawableState(featureId, true);st.resid = 0;st.uri = null;if (st.local != drawable) {st.local = drawable;updateDrawable(featureId, st, false);}}@Overridepublic void setFeatureDrawableAlpha(int featureId, int alpha) {DrawableFeatureState st = getDrawableState(featureId, true);if (st.alpha != alpha) {st.alpha = alpha;updateDrawable(featureId, st, false);}}protected final void setFeatureDefaultDrawable(int featureId, Drawable drawable) {DrawableFeatureState st = getDrawableState(featureId, true);if (st.def != drawable) {st.def = drawable;updateDrawable(featureId, st, false);}}@Overridepublic final void setFeatureInt(int featureId, int value) {// XXX Should do more management (as with drawable features) to// deal with interactions between multiple window policies.updateInt(featureId, value, false);}/*** Update the state of a drawable feature. This should be called, for every* drawable feature supported, as part of onActive(), to make sure that the* contents of a containing window is properly updated.** @see #onActive* @param featureId The desired drawable feature to change.* @param fromActive Always true when called from onActive().*/protected final void updateDrawable(int featureId, boolean fromActive) {final DrawableFeatureState st = getDrawableState(featureId, false);if (st != null) {updateDrawable(featureId, st, fromActive);}}/*** Called when a Drawable feature changes, for the window to update its* graphics.** @param featureId The feature being changed.* @param drawable The new Drawable to show, or null if none.* @param alpha The new alpha blending of the Drawable.*/protected void onDrawableChanged(int featureId, Drawable drawable, int alpha) {ImageView view;if (featureId == FEATURE_LEFT_ICON) {view = getLeftIconView();} else if (featureId == FEATURE_RIGHT_ICON) {view = getRightIconView();} else {return;}if (drawable != null) {drawable.setAlpha(alpha);view.setImageDrawable(drawable);view.setVisibility(View.VISIBLE);} else {view.setVisibility(View.GONE);}}/*** Called when an int feature changes, for the window to update its* graphics.** @param featureId The feature being changed.* @param value The new integer value.*/protected void onIntChanged(int featureId, int value) {if (featureId == FEATURE_PROGRESS || featureId == FEATURE_INDETERMINATE_PROGRESS) {updateProgressBars(value);} else if (featureId == FEATURE_CUSTOM_TITLE) {FrameLayout titleContainer = findViewById(R.id.title_container);if (titleContainer != null) {mLayoutInflater.inflate(value, titleContainer);}}}/*** Updates the progress bars that are shown in the title bar.** @param value Can be one of {@link Window#PROGRESS_VISIBILITY_ON},*            {@link Window#PROGRESS_VISIBILITY_OFF},*            {@link Window#PROGRESS_INDETERMINATE_ON},*            {@link Window#PROGRESS_INDETERMINATE_OFF}, or a value*            starting at {@link Window#PROGRESS_START} through*            {@link Window#PROGRESS_END} for setting the default*            progress (if {@link Window#PROGRESS_END} is given,*            the progress bar widgets in the title will be hidden after an*            animation), a value between*            {@link Window#PROGRESS_SECONDARY_START} -*            {@link Window#PROGRESS_SECONDARY_END} for the*            secondary progress (if*            {@link Window#PROGRESS_SECONDARY_END} is given, the*            progress bar widgets will still be shown with the secondary*            progress bar will be completely filled in.)*/private void updateProgressBars(int value) {ProgressBar circularProgressBar = getCircularProgressBar(true);ProgressBar horizontalProgressBar = getHorizontalProgressBar(true);final int features = getLocalFeatures();if (value == PROGRESS_VISIBILITY_ON) {if ((features & (1 << FEATURE_PROGRESS)) != 0) {if (horizontalProgressBar != null) {int level = horizontalProgressBar.getProgress();int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ?View.VISIBLE : View.INVISIBLE;horizontalProgressBar.setVisibility(visibility);} else {Log.e(TAG, "Horizontal progress bar not located in current window decor");}}if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {if (circularProgressBar != null) {circularProgressBar.setVisibility(View.VISIBLE);} else {Log.e(TAG, "Circular progress bar not located in current window decor");}}} else if (value == PROGRESS_VISIBILITY_OFF) {if ((features & (1 << FEATURE_PROGRESS)) != 0) {if (horizontalProgressBar != null) {horizontalProgressBar.setVisibility(View.GONE);} else {Log.e(TAG, "Horizontal progress bar not located in current window decor");}}if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {if (circularProgressBar != null) {circularProgressBar.setVisibility(View.GONE);} else {Log.e(TAG, "Circular progress bar not located in current window decor");}}} else if (value == PROGRESS_INDETERMINATE_ON) {if (horizontalProgressBar != null) {horizontalProgressBar.setIndeterminate(true);} else {Log.e(TAG, "Horizontal progress bar not located in current window decor");}} else if (value == PROGRESS_INDETERMINATE_OFF) {if (horizontalProgressBar != null) {horizontalProgressBar.setIndeterminate(false);} else {Log.e(TAG, "Horizontal progress bar not located in current window decor");}} else if (PROGRESS_START <= value && value <= PROGRESS_END) {// We want to set the progress value before testing for visibility// so that when the progress bar becomes visible again, it has the// correct level.if (horizontalProgressBar != null) {horizontalProgressBar.setProgress(value - PROGRESS_START);} else {Log.e(TAG, "Horizontal progress bar not located in current window decor");}if (value < PROGRESS_END) {showProgressBars(horizontalProgressBar, circularProgressBar);} else {hideProgressBars(horizontalProgressBar, circularProgressBar);}} else if (PROGRESS_SECONDARY_START <= value && value <= PROGRESS_SECONDARY_END) {if (horizontalProgressBar != null) {horizontalProgressBar.setSecondaryProgress(value - PROGRESS_SECONDARY_START);} else {Log.e(TAG, "Horizontal progress bar not located in current window decor");}showProgressBars(horizontalProgressBar, circularProgressBar);}}private void showProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar) {final int features = getLocalFeatures();if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 &&spinnyProgressBar != null && spinnyProgressBar.getVisibility() == View.INVISIBLE) {spinnyProgressBar.setVisibility(View.VISIBLE);}// Only show the progress bars if the primary progress is not completeif ((features & (1 << FEATURE_PROGRESS)) != 0 && horizontalProgressBar != null &&horizontalProgressBar.getProgress() < 10000) {horizontalProgressBar.setVisibility(View.VISIBLE);}}private void hideProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar) {final int features = getLocalFeatures();Animation anim = AnimationUtils.loadAnimation(getContext(), R.anim.fade_out);anim.setDuration(1000);if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 &&spinnyProgressBar != null &&spinnyProgressBar.getVisibility() == View.VISIBLE) {spinnyProgressBar.startAnimation(anim);spinnyProgressBar.setVisibility(View.INVISIBLE);}if ((features & (1 << FEATURE_PROGRESS)) != 0 && horizontalProgressBar != null &&horizontalProgressBar.getVisibility() == View.VISIBLE) {horizontalProgressBar.startAnimation(anim);horizontalProgressBar.setVisibility(View.INVISIBLE);}}@Overridepublic void setIcon(int resId) {mIconRes = resId;mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON;mResourcesSetFlags &= ~FLAG_RESOURCE_SET_ICON_FALLBACK;if (mDecorContentParent != null) {mDecorContentParent.setIcon(resId);}}@Overridepublic void setDefaultIcon(int resId) {if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0) {return;}mIconRes = resId;if (mDecorContentParent != null && (!mDecorContentParent.hasIcon() ||(mResourcesSetFlags & FLAG_RESOURCE_SET_ICON_FALLBACK) != 0)) {if (resId != 0) {mDecorContentParent.setIcon(resId);mResourcesSetFlags &= ~FLAG_RESOURCE_SET_ICON_FALLBACK;} else {mDecorContentParent.setIcon(getContext().getPackageManager().getDefaultActivityIcon());mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;}}}@Overridepublic void setLogo(int resId) {mLogoRes = resId;mResourcesSetFlags |= FLAG_RESOURCE_SET_LOGO;if (mDecorContentParent != null) {mDecorContentParent.setLogo(resId);}}@Overridepublic void setDefaultLogo(int resId) {if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0) {return;}mLogoRes = resId;if (mDecorContentParent != null && !mDecorContentParent.hasLogo()) {mDecorContentParent.setLogo(resId);}}@Overridepublic void setLocalFocus(boolean hasFocus, boolean inTouchMode) {getViewRootImpl().windowFocusChanged(hasFocus, inTouchMode);}@Overridepublic void injectInputEvent(InputEvent event) {getViewRootImpl().dispatchInputEvent(event);}private ViewRootImpl getViewRootImpl() {if (mDecor != null) {ViewRootImpl viewRootImpl = mDecor.getViewRootImpl();if (viewRootImpl != null) {return viewRootImpl;}}throw new IllegalStateException("view not added");}/*** Request that key events come to this activity. Use this if your activity* has no views with focus, but the activity still wants a chance to process* key events.*/@Overridepublic void takeKeyEvents(boolean get) {mDecor.setFocusable(get);}@Overridepublic boolean superDispatchKeyEvent(KeyEvent event) {return mDecor.superDispatchKeyEvent(event);}@Overridepublic boolean superDispatchKeyShortcutEvent(KeyEvent event) {return mDecor.superDispatchKeyShortcutEvent(event);}@Overridepublic boolean superDispatchTouchEvent(MotionEvent event) {return mDecor.superDispatchTouchEvent(event);}@Overridepublic boolean superDispatchTrackballEvent(MotionEvent event) {return mDecor.superDispatchTrackballEvent(event);}@Overridepublic boolean superDispatchGenericMotionEvent(MotionEvent event) {return mDecor.superDispatchGenericMotionEvent(event);}/*** A key was pressed down and not handled by anything else in the window.** @see #onKeyUp* @see android.view.KeyEvent*/protected boolean onKeyDown(int featureId, int keyCode, KeyEvent event) {/* ***************************************************************************** HOW TO DECIDE WHERE YOUR KEY HANDLING GOES.** If your key handling must happen before the app gets a crack at the event,* it goes in PhoneWindowManager.** If your key handling should happen in all windows, and does not depend on* the state of the current application, other than that the current* application can override the behavior by handling the event itself, it* should go in PhoneFallbackEventHandler.** Only if your handling depends on the window, and the fact that it has* a DecorView, should it go here.* ****************************************************************************/final KeyEvent.DispatcherState dispatcher =mDecor != null ? mDecor.getKeyDispatcherState() : null;//Log.i(TAG, "Key down: repeat=" + event.getRepeatCount()//        + " flags=0x" + Integer.toHexString(event.getFlags()));switch (keyCode) {case KeyEvent.KEYCODE_VOLUME_UP:case KeyEvent.KEYCODE_VOLUME_DOWN:case KeyEvent.KEYCODE_VOLUME_MUTE: {// If we have a session send it the volume command, otherwise// use the suggested stream.if (mMediaController != null) {mMediaController.dispatchVolumeButtonEventAsSystemService(event);} else {getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(event,mVolumeControlStreamType);}return true;}// These are all the recognized media key codes in// KeyEvent.isMediaKey()case KeyEvent.KEYCODE_MEDIA_PLAY:case KeyEvent.KEYCODE_MEDIA_PAUSE:case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:case KeyEvent.KEYCODE_MUTE:case KeyEvent.KEYCODE_HEADSETHOOK:case KeyEvent.KEYCODE_MEDIA_STOP:case KeyEvent.KEYCODE_MEDIA_NEXT:case KeyEvent.KEYCODE_MEDIA_PREVIOUS:case KeyEvent.KEYCODE_MEDIA_REWIND:case KeyEvent.KEYCODE_MEDIA_RECORD:case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {if (mMediaController != null) {if (mMediaController.dispatchMediaButtonEventAsSystemService(event)) {return true;}}return false;}case KeyEvent.KEYCODE_MENU: {onKeyDownPanel((featureId < 0) ? FEATURE_OPTIONS_PANEL : featureId, event);return true;}case KeyEvent.KEYCODE_BACK: {if (event.getRepeatCount() > 0) break;if (featureId < 0) break;// Currently don't do anything with long press.if (dispatcher != null) {dispatcher.startTracking(event, this);}return true;}}return false;}private KeyguardManager getKeyguardManager() {if (mKeyguardManager == null) {mKeyguardManager = (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);}return mKeyguardManager;}AudioManager getAudioManager() {if (mAudioManager == null) {mAudioManager = (AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE);}return mAudioManager;}private MediaSessionManager getMediaSessionManager() {if (mMediaSessionManager == null) {mMediaSessionManager = (MediaSessionManager) getContext().getSystemService(Context.MEDIA_SESSION_SERVICE);}return mMediaSessionManager;}/*** A key was released and not handled by anything else in the window.** @see #onKeyDown* @see android.view.KeyEvent*/protected boolean onKeyUp(int featureId, int keyCode, KeyEvent event) {final KeyEvent.DispatcherState dispatcher =mDecor != null ? mDecor.getKeyDispatcherState() : null;if (dispatcher != null) {dispatcher.handleUpEvent(event);}//Log.i(TAG, "Key up: repeat=" + event.getRepeatCount()//        + " flags=0x" + Integer.toHexString(event.getFlags()));switch (keyCode) {case KeyEvent.KEYCODE_VOLUME_UP:case KeyEvent.KEYCODE_VOLUME_DOWN: {// If we have a session send it the volume command, otherwise// use the suggested stream.if (mMediaController != null) {mMediaController.dispatchVolumeButtonEventAsSystemService(event);} else {getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(event, mVolumeControlStreamType);}return true;}case KeyEvent.KEYCODE_VOLUME_MUTE: {// Similar code is in PhoneFallbackEventHandler in case the window// doesn't have one of these.  In this case, we execute it here and// eat the event instead, because we have mVolumeControlStreamType// and they don't.getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(event, AudioManager.USE_DEFAULT_STREAM_TYPE);return true;}// These are all the recognized media key codes in// KeyEvent.isMediaKey()case KeyEvent.KEYCODE_MEDIA_PLAY:case KeyEvent.KEYCODE_MEDIA_PAUSE:case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:case KeyEvent.KEYCODE_MUTE:case KeyEvent.KEYCODE_HEADSETHOOK:case KeyEvent.KEYCODE_MEDIA_STOP:case KeyEvent.KEYCODE_MEDIA_NEXT:case KeyEvent.KEYCODE_MEDIA_PREVIOUS:case KeyEvent.KEYCODE_MEDIA_REWIND:case KeyEvent.KEYCODE_MEDIA_RECORD:case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {if (mMediaController != null) {if (mMediaController.dispatchMediaButtonEventAsSystemService(event)) {return true;}}return false;}case KeyEvent.KEYCODE_MENU: {onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId,event);return true;}case KeyEvent.KEYCODE_BACK: {if (featureId < 0) break;if (event.isTracking() && !event.isCanceled()) {if (featureId == FEATURE_OPTIONS_PANEL) {PanelFeatureState st = getPanelState(featureId, false);if (st != null && st.isInExpandedMode) {// If the user is in an expanded menu and hits back, it// should go back to the icon menureopenMenu(true);return true;}}closePanel(featureId);return true;}break;}case KeyEvent.KEYCODE_SEARCH: {/** Do this in onKeyUp since the Search key is also used for* chording quick launch shortcuts.*/if (isNotInstantAppAndKeyguardRestricted()) {break;}if ((getContext().getResources().getConfiguration().uiMode& Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_WATCH) {break;}if (event.isTracking() && !event.isCanceled()) {launchDefaultSearch(event);}return true;}case KeyEvent.KEYCODE_WINDOW: {if (mSupportsPictureInPicture && !event.isCanceled()) {getWindowControllerCallback().enterPictureInPictureModeIfPossible();}return true;}}return false;}private boolean isNotInstantAppAndKeyguardRestricted() {return !getContext().getPackageManager().isInstantApp()&& getKeyguardManager().inKeyguardRestrictedInputMode();}@Overrideprotected void onActive() {}@Overridepublic final View getDecorView() {if (mDecor == null || mForceDecorInstall) {installDecor();}return mDecor;}@Overridepublic final View peekDecorView() {return mDecor;}/** Notify when decor view is attached to window and {@link ViewRootImpl} is available. */void onViewRootImplSet(ViewRootImpl viewRoot) {viewRoot.setActivityConfigCallback(mActivityConfigCallback);}static private final String FOCUSED_ID_TAG = "android:focusedViewId";static private final String VIEWS_TAG = "android:views";static private final String PANELS_TAG = "android:Panels";static private final String ACTION_BAR_TAG = "android:ActionBar";/** {@inheritDoc} */@Overridepublic Bundle saveHierarchyState() {Bundle outState = new Bundle();if (mContentParent == null) {return outState;}SparseArray<Parcelable> states = new SparseArray<Parcelable>();mContentParent.saveHierarchyState(states);outState.putSparseParcelableArray(VIEWS_TAG, states);// Save the focused view ID.final View focusedView = mContentParent.findFocus();if (focusedView != null && focusedView.getId() != View.NO_ID) {outState.putInt(FOCUSED_ID_TAG, focusedView.getId());}// save the panelsSparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();savePanelState(panelStates);if (panelStates.size() > 0) {outState.putSparseParcelableArray(PANELS_TAG, panelStates);}if (mDecorContentParent != null) {SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();mDecorContentParent.saveToolbarHierarchyState(actionBarStates);outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);}return outState;}/** {@inheritDoc} */@Overridepublic void restoreHierarchyState(Bundle savedInstanceState) {if (mContentParent == null) {return;}SparseArray<Parcelable> savedStates= savedInstanceState.getSparseParcelableArray(VIEWS_TAG);if (savedStates != null) {mContentParent.restoreHierarchyState(savedStates);}// restore the focused viewint focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID);if (focusedViewId != View.NO_ID) {View needsFocus = mContentParent.findViewById(focusedViewId);if (needsFocus != null) {needsFocus.requestFocus();} else {Log.w(TAG,"Previously focused view reported id " + focusedViewId+ " during save, but can't be found during restore.");}}// Restore the panels.SparseArray<Parcelable> panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG);if (panelStates != null) {restorePanelState(panelStates);}if (mDecorContentParent != null) {SparseArray<Parcelable> actionBarStates =savedInstanceState.getSparseParcelableArray(ACTION_BAR_TAG);if (actionBarStates != null) {doPendingInvalidatePanelMenu();mDecorContentParent.restoreToolbarHierarchyState(actionBarStates);} else {Log.w(TAG, "Missing saved instance states for action bar views! " +"State will not be restored.");}}}/*** Invoked when the panels should freeze their state.** @param icicles Save state into this. This is usually indexed by the*            featureId. This will be given to {@link #restorePanelState} in the*            future.*/private void savePanelState(SparseArray<Parcelable> icicles) {PanelFeatureState[] panels = mPanels;if (panels == null) {return;}for (int curFeatureId = panels.length - 1; curFeatureId >= 0; curFeatureId--) {if (panels[curFeatureId] != null) {icicles.put(curFeatureId, panels[curFeatureId].onSaveInstanceState());}}}/*** Invoked when the panels should thaw their state from a previously frozen state.** @param icicles The state saved by {@link #savePanelState} that needs to be thawed.*/private void restorePanelState(SparseArray<Parcelable> icicles) {PanelFeatureState st;int curFeatureId;for (int i = icicles.size() - 1; i >= 0; i--) {curFeatureId = icicles.keyAt(i);st = getPanelState(curFeatureId, false /* required */);if (st == null) {// The panel must not have been required, and is currently not around, skip itcontinue;}st.onRestoreInstanceState(icicles.get(curFeatureId));invalidatePanelMenu(curFeatureId);}/** Implementation note: call openPanelsAfterRestore later to actually open the* restored panels.*/}/*** Opens the panels that have had their state restored. This should be* called sometime after {@link #restorePanelState} when it is safe to add* to the window manager.*/void openPanelsAfterRestore() {PanelFeatureState[] panels = mPanels;if (panels == null) {return;}PanelFeatureState st;for (int i = panels.length - 1; i >= 0; i--) {st = panels[i];// We restore the panel if it was last open; we skip it if it// now is open, to avoid a race condition if the user immediately// opens it when we are resuming.if (st != null) {st.applyFrozenState();if (!st.isOpen && st.wasLastOpen) {st.isInExpandedMode = st.wasLastExpanded;openPanel(st, null);}}}}private class PanelMenuPresenterCallback implements MenuPresenter.Callback {@Overridepublic void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {final Menu parentMenu = menu.getRootMenu();final boolean isSubMenu = parentMenu != menu;final PanelFeatureState panel = findMenuPanel(isSubMenu ? parentMenu : menu);if (panel != null) {if (isSubMenu) {callOnPanelClosed(panel.featureId, panel, parentMenu);closePanel(panel, true);} else {// Close the panel and only do the callback if the menu is being// closed completely, not if opening a sub menuclosePanel(panel, allMenusAreClosing);}}}@Overridepublic boolean onOpenSubMenu(MenuBuilder subMenu) {if (subMenu == null && hasFeature(FEATURE_ACTION_BAR)) {Callback cb = getCallback();if (cb != null && !isDestroyed()) {cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu);}}return true;}}private final class ActionMenuPresenterCallback implements MenuPresenter.Callback {@Overridepublic boolean onOpenSubMenu(MenuBuilder subMenu) {Callback cb = getCallback();if (cb != null) {cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu);return true;}return false;}@Overridepublic void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {checkCloseActionMenu(menu);}}protected DecorView generateDecor(int featureId) {// System process doesn't have application context and in that case we need to directly use// the context we have. Otherwise we want the application context, so we don't cling to the// activity.Context context;if (mUseDecorContext) {Context applicationContext = getContext().getApplicationContext();if (applicationContext == null) {context = getContext();} else {context = new DecorContext(applicationContext, getContext());if (mTheme != -1) {context.setTheme(mTheme);}}} else {context = getContext();}return new DecorView(context, featureId, this, getAttributes());}protected ViewGroup generateLayout(DecorView decor) {// Apply data from current theme.TypedArray a = getWindowStyle();if (false) {System.out.println("From style:");String s = "Attrs:";for (int i = 0; i < R.styleable.Window.length; i++) {s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "="+ a.getString(i);}System.out.println(s);}mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)& (~getForcedWindowFlags());if (mIsFloating) {setLayout(WRAP_CONTENT, WRAP_CONTENT);setFlags(0, flagsToUpdate);} else {setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);}if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {requestFeature(FEATURE_NO_TITLE);} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {// Don't allow an action bar if there is no title.requestFeature(FEATURE_ACTION_BAR);}if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {requestFeature(FEATURE_ACTION_BAR_OVERLAY);}if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {requestFeature(FEATURE_ACTION_MODE_OVERLAY);}if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {requestFeature(FEATURE_SWIPE_TO_DISMISS);}if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowTranslucentStatus,false)) {setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS& (~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation,false)) {setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION& (~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowOverscan, false)) {setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch,getContext().getApplicationInfo().targetSdkVersion>= android.os.Build.VERSION_CODES.HONEYCOMB)) {setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));}a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);if (DEBUG) Log.d(TAG, "Min width minor: " + mMinWidthMinor.coerceToString()+ ", major: " + mMinWidthMajor.coerceToString());if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) {if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();a.getValue(R.styleable.Window_windowFixedWidthMajor,mFixedWidthMajor);}if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) {if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();a.getValue(R.styleable.Window_windowFixedWidthMinor,mFixedWidthMinor);}if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) {if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();a.getValue(R.styleable.Window_windowFixedHeightMajor,mFixedHeightMajor);}if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) {if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();a.getValue(R.styleable.Window_windowFixedHeightMinor,mFixedHeightMinor);}if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) {requestFeature(FEATURE_CONTENT_TRANSITIONS);}if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) {requestFeature(FEATURE_ACTIVITY_TRANSITIONS);}mIsTranslucent = a.getBoolean(R.styleable.Window_windowIsTranslucent, false);final Context context = getContext();final int targetSdk = context.getApplicationInfo().targetSdkVersion;final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;final boolean targetHcNeedsOptions = context.getResources().getBoolean(R.bool.target_honeycomb_needs_options_menu);final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE);} else {setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE);}if (!mForcedStatusBarColor) {mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);}if (!mForcedNavigationBarColor) {mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,0x00000000);}WindowManager.LayoutParams params = getAttributes();// Non-floating windows on high end devices must put up decor beneath the system bars and// therefore must know about visibility changes of those.if (!mIsFloating) {if (!targetPreL && a.getBoolean(R.styleable.Window_windowDrawsSystemBarBackgrounds,false)) {setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());}if (mDecor.mForceWindowDrawsStatusBarBackground) {params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;}}if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {decor.setSystemUiVisibility(decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);}if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) {decor.setSystemUiVisibility(decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);}if (a.hasValue(R.styleable.Window_windowLayoutInDisplayCutoutMode)) {int mode = a.getInt(R.styleable.Window_windowLayoutInDisplayCutoutMode, -1);if (mode < LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT|| mode > LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER) {throw new UnsupportedOperationException("Unknown windowLayoutInDisplayCutoutMode: "+ a.getString(R.styleable.Window_windowLayoutInDisplayCutoutMode));}params.layoutInDisplayCutoutMode = mode;}if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion>= android.os.Build.VERSION_CODES.HONEYCOMB) {if (a.getBoolean(R.styleable.Window_windowCloseOnTouchOutside,false)) {setCloseOnTouchOutsideIfNotSet(true);}}if (!hasSoftInputMode()) {params.softInputMode = a.getInt(R.styleable.Window_windowSoftInputMode,params.softInputMode);}if (a.getBoolean(R.styleable.Window_backgroundDimEnabled,mIsFloating)) {/* All dialogs should have the window dimmed */if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;}if (!haveDimAmount()) {params.dimAmount = a.getFloat(android.R.styleable.Window_backgroundDimAmount, 0.5f);}}if (params.windowAnimations == 0) {params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0);}// The rest are only done if this window is not embedded; otherwise,// the values are inherited from our container.if (getContainer() == null) {if (mBackgroundDrawable == null) {if (mBackgroundResource == 0) {mBackgroundResource = a.getResourceId(R.styleable.Window_windowBackground, 0);}if (mFrameResource == 0) {mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);}mBackgroundFallbackResource = a.getResourceId(R.styleable.Window_windowBackgroundFallback, 0);if (false) {System.out.println("Background: "+ Integer.toHexString(mBackgroundResource) + " Frame: "+ Integer.toHexString(mFrameResource));}}if (mLoadElevation) {mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);}mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);}// Inflate the window decor.int layoutResource;int features = getLocalFeatures();// System.out.println("Features: 0x" + Integer.toHexString(features));if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {layoutResource = R.layout.screen_swipe_dismiss;setCloseOnSwipeEnabled(true);} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {if (mIsFloating) {TypedValue res = new TypedValue();getContext().getTheme().resolveAttribute(R.attr.dialogTitleIconsDecorLayout, res, true);layoutResource = res.resourceId;} else {layoutResource = R.layout.screen_title_icons;}// XXX Remove this once action bar supports these features.removeFeature(FEATURE_ACTION_BAR);// System.out.println("Title Icons!");} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {// Special case for a window with only a progress bar (and title).// XXX Need to have a no-title version of embedded windows.layoutResource = R.layout.screen_progress;// System.out.println("Progress!");} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {// Special case for a window with a custom title.// If the window is floating, we need a dialog layoutif (mIsFloating) {TypedValue res = new TypedValue();getContext().getTheme().resolveAttribute(R.attr.dialogCustomTitleDecorLayout, res, true);layoutResource = res.resourceId;} else {layoutResource = R.layout.screen_custom_title;}// XXX Remove this once action bar supports these features.removeFeature(FEATURE_ACTION_BAR);} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {// If no other features and not embedded, only need a title.// If the window is floating, we need a dialog layoutif (mIsFloating) {TypedValue res = new TypedValue();getContext().getTheme().resolveAttribute(R.attr.dialogTitleDecorLayout, res, true);layoutResource = res.resourceId;} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {layoutResource = a.getResourceId(R.styleable.Window_windowActionBarFullscreenDecorLayout,R.layout.screen_action_bar);} else {layoutResource = R.layout.screen_title;}// System.out.println("Title!");} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {layoutResource = R.layout.screen_simple_overlay_action_mode;} else {// Embedded, so no decoration is needed.layoutResource = R.layout.screen_simple;// System.out.println("Simple!");}mDecor.startChanging();mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);if (contentParent == null) {throw new RuntimeException("Window couldn't find content container view");}if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {ProgressBar progress = getCircularProgressBar(false);if (progress != null) {progress.setIndeterminate(true);}}if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {registerSwipeCallbacks(contentParent);}// Remaining setup -- of background and title -- that only applies// to top-level windows.if (getContainer() == null) {final Drawable background;if (mBackgroundResource != 0) {background = getContext().getDrawable(mBackgroundResource);} else {background = mBackgroundDrawable;}mDecor.setWindowBackground(background);final Drawable frame;if (mFrameResource != 0) {frame = getContext().getDrawable(mFrameResource);} else {frame = null;}mDecor.setWindowFrame(frame);mDecor.setElevation(mElevation);mDecor.setClipToOutline(mClipToOutline);if (mTitle != null) {setTitle(mTitle);}if (mTitleColor == 0) {mTitleColor = mTextColor;}setTitleColor(mTitleColor);}mDecor.finishChanging();return contentParent;}/** @hide */public void alwaysReadCloseOnTouchAttr() {mAlwaysReadCloseOnTouchAttr = true;}private void installDecor() {mForceDecorInstall = false;if (mDecor == null) {mDecor = generateDecor(-1);mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);mDecor.setIsRootNamespace(true);if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);}} else {mDecor.setWindow(this);}if (mContentParent == null) {mContentParent = generateLayout(mDecor);// Set up decor part of UI to ignore fitsSystemWindows if appropriate.mDecor.makeOptionalFitsSystemWindows();final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(R.id.decor_content_parent);if (decorContentParent != null) {mDecorContentParent = decorContentParent;mDecorContentParent.setWindowCallback(getCallback());if (mDecorContentParent.getTitle() == null) {mDecorContentParent.setWindowTitle(mTitle);}final int localFeatures = getLocalFeatures();for (int i = 0; i < FEATURE_MAX; i++) {if ((localFeatures & (1 << i)) != 0) {mDecorContentParent.initFeature(i);}}mDecorContentParent.setUiOptions(mUiOptions);if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||(mIconRes != 0 && !mDecorContentParent.hasIcon())) {mDecorContentParent.setIcon(mIconRes);} else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&mIconRes == 0 && !mDecorContentParent.hasIcon()) {mDecorContentParent.setIcon(getContext().getPackageManager().getDefaultActivityIcon());mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;}if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||(mLogoRes != 0 && !mDecorContentParent.hasLogo())) {mDecorContentParent.setLogo(mLogoRes);}// Invalidate if the panel menu hasn't been created before this.// Panel menu invalidation is deferred avoiding application onCreateOptionsMenu// being called in the middle of onCreate or similar.// A pending invalidation will typically be resolved before the posted message// would run normally in order to satisfy instance state restoration.PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) {invalidatePanelMenu(FEATURE_ACTION_BAR);}} else {mTitleView = findViewById(R.id.title);if (mTitleView != null) {if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {final View titleContainer = findViewById(R.id.title_container);if (titleContainer != null) {titleContainer.setVisibility(View.GONE);} else {mTitleView.setVisibility(View.GONE);}mContentParent.setForeground(null);} else {mTitleView.setText(mTitle);}}}if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {mDecor.setBackgroundFallback(mBackgroundFallbackResource);}// Only inflate or create a new TransitionManager if the caller hasn't// already set a custom one.if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {if (mTransitionManager == null) {final int transitionRes = getWindowStyle().getResourceId(R.styleable.Window_windowContentTransitionManager,0);if (transitionRes != 0) {final TransitionInflater inflater = TransitionInflater.from(getContext());mTransitionManager = inflater.inflateTransitionManager(transitionRes,mContentParent);} else {mTransitionManager = new TransitionManager();}}mEnterTransition = getTransition(mEnterTransition, null,R.styleable.Window_windowEnterTransition);mReturnTransition = getTransition(mReturnTransition, USE_DEFAULT_TRANSITION,R.styleable.Window_windowReturnTransition);mExitTransition = getTransition(mExitTransition, null,R.styleable.Window_windowExitTransition);mReenterTransition = getTransition(mReenterTransition, USE_DEFAULT_TRANSITION,R.styleable.Window_windowReenterTransition);mSharedElementEnterTransition = getTransition(mSharedElementEnterTransition, null,R.styleable.Window_windowSharedElementEnterTransition);mSharedElementReturnTransition = getTransition(mSharedElementReturnTransition,USE_DEFAULT_TRANSITION,R.styleable.Window_windowSharedElementReturnTransition);mSharedElementExitTransition = getTransition(mSharedElementExitTransition, null,R.styleable.Window_windowSharedElementExitTransition);mSharedElementReenterTransition = getTransition(mSharedElementReenterTransition,USE_DEFAULT_TRANSITION,R.styleable.Window_windowSharedElementReenterTransition);if (mAllowEnterTransitionOverlap == null) {mAllowEnterTransitionOverlap = getWindowStyle().getBoolean(R.styleable.Window_windowAllowEnterTransitionOverlap, true);}if (mAllowReturnTransitionOverlap == null) {mAllowReturnTransitionOverlap = getWindowStyle().getBoolean(R.styleable.Window_windowAllowReturnTransitionOverlap, true);}if (mBackgroundFadeDurationMillis < 0) {mBackgroundFadeDurationMillis = getWindowStyle().getInteger(R.styleable.Window_windowTransitionBackgroundFadeDuration,DEFAULT_BACKGROUND_FADE_DURATION_MS);}if (mSharedElementsUseOverlay == null) {mSharedElementsUseOverlay = getWindowStyle().getBoolean(R.styleable.Window_windowSharedElementsUseOverlay, true);}}}}private Transition getTransition(Transition currentValue, Transition defaultValue, int id) {if (currentValue != defaultValue) {return currentValue;}int transitionId = getWindowStyle().getResourceId(id, -1);Transition transition = defaultValue;if (transitionId != -1 && transitionId != R.transition.no_transition) {TransitionInflater inflater = TransitionInflater.from(getContext());transition = inflater.inflateTransition(transitionId);if (transition instanceof TransitionSet &&((TransitionSet)transition).getTransitionCount() == 0) {transition = null;}}return transition;}private Drawable loadImageURI(Uri uri) {try {return Drawable.createFromStream(getContext().getContentResolver().openInputStream(uri), null);} catch (Exception e) {Log.w(TAG, "Unable to open content: " + uri);}return null;}private DrawableFeatureState getDrawableState(int featureId, boolean required) {if ((getFeatures() & (1 << featureId)) == 0) {if (!required) {return null;}throw new RuntimeException("The feature has not been requested");}DrawableFeatureState[] ar;if ((ar = mDrawables) == null || ar.length <= featureId) {DrawableFeatureState[] nar = new DrawableFeatureState[featureId + 1];if (ar != null) {System.arraycopy(ar, 0, nar, 0, ar.length);}mDrawables = ar = nar;}DrawableFeatureState st = ar[featureId];if (st == null) {ar[featureId] = st = new DrawableFeatureState(featureId);}return st;}/*** Gets a panel's state based on its feature ID.** @param featureId The feature ID of the panel.* @param required Whether the panel is required (if it is required and it*            isn't in our features, this throws an exception).* @return The panel state.*/PanelFeatureState getPanelState(int featureId, boolean required) {return getPanelState(featureId, required, null);}/*** Gets a panel's state based on its feature ID.** @param featureId The feature ID of the panel.* @param required Whether the panel is required (if it is required and it*            isn't in our features, this throws an exception).* @param convertPanelState Optional: If the panel state does not exist, use*            this as the panel state.* @return The panel state.*/private PanelFeatureState getPanelState(int featureId, boolean required,PanelFeatureState convertPanelState) {if ((getFeatures() & (1 << featureId)) == 0) {if (!required) {return null;}throw new RuntimeException("The feature has not been requested");}PanelFeatureState[] ar;if ((ar = mPanels) == null || ar.length <= featureId) {PanelFeatureState[] nar = new PanelFeatureState[featureId + 1];if (ar != null) {System.arraycopy(ar, 0, nar, 0, ar.length);}mPanels = ar = nar;}PanelFeatureState st = ar[featureId];if (st == null) {ar[featureId] = st = (convertPanelState != null)? convertPanelState: new PanelFeatureState(featureId);}return st;}@Overridepublic final void setChildDrawable(int featureId, Drawable drawable) {DrawableFeatureState st = getDrawableState(featureId, true);st.child = drawable;updateDrawable(featureId, st, false);}@Overridepublic final void setChildInt(int featureId, int value) {updateInt(featureId, value, false);}@Overridepublic boolean isShortcutKey(int keyCode, KeyEvent event) {PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);return st != null && st.menu != null && st.menu.isShortcutKey(keyCode, event);}private void updateDrawable(int featureId, DrawableFeatureState st, boolean fromResume) {// Do nothing if the decor is not yet installed... an update will// need to be forced when we eventually become active.if (mContentParent == null) {return;}final int featureMask = 1 << featureId;if ((getFeatures() & featureMask) == 0 && !fromResume) {return;}Drawable drawable = null;if (st != null) {drawable = st.child;if (drawable == null)drawable = st.local;if (drawable == null)drawable = st.def;}if ((getLocalFeatures() & featureMask) == 0) {if (getContainer() != null) {if (isActive() || fromResume) {getContainer().setChildDrawable(featureId, drawable);}}} else if (st != null && (st.cur != drawable || st.curAlpha != st.alpha)) {// System.out.println("Drawable changed: old=" + st.cur// + ", new=" + drawable);st.cur = drawable;st.curAlpha = st.alpha;onDrawableChanged(featureId, drawable, st.alpha);}}private void updateInt(int featureId, int value, boolean fromResume) {// Do nothing if the decor is not yet installed... an update will// need to be forced when we eventually become active.if (mContentParent == null) {return;}final int featureMask = 1 << featureId;if ((getFeatures() & featureMask) == 0 && !fromResume) {return;}if ((getLocalFeatures() & featureMask) == 0) {if (getContainer() != null) {getContainer().setChildInt(featureId, value);}} else {onIntChanged(featureId, value);}}private ImageView getLeftIconView() {if (mLeftIconView != null) {return mLeftIconView;}if (mContentParent == null) {installDecor();}return (mLeftIconView = (ImageView)findViewById(R.id.left_icon));}@Overrideprotected void dispatchWindowAttributesChanged(WindowManager.LayoutParams attrs) {super.dispatchWindowAttributesChanged(attrs);if (mDecor != null) {mDecor.updateColorViews(null /* insets */, true /* animate */);}}private ProgressBar getCircularProgressBar(boolean shouldInstallDecor) {if (mCircularProgressBar != null) {return mCircularProgressBar;}if (mContentParent == null && shouldInstallDecor) {installDecor();}mCircularProgressBar = findViewById(R.id.progress_circular);if (mCircularProgressBar != null) {mCircularProgressBar.setVisibility(View.INVISIBLE);}return mCircularProgressBar;}private ProgressBar getHorizontalProgressBar(boolean shouldInstallDecor) {if (mHorizontalProgressBar != null) {return mHorizontalProgressBar;}if (mContentParent == null && shouldInstallDecor) {installDecor();}mHorizontalProgressBar = findViewById(R.id.progress_horizontal);if (mHorizontalProgressBar != null) {mHorizontalProgressBar.setVisibility(View.INVISIBLE);}return mHorizontalProgressBar;}private ImageView getRightIconView() {if (mRightIconView != null) {return mRightIconView;}if (mContentParent == null) {installDecor();}return (mRightIconView = (ImageView)findViewById(R.id.right_icon));}private void registerSwipeCallbacks(ViewGroup contentParent) {--------这里看----Log.w(TAG, "contentParent is not a SwipeDismissLayout: " + contentParent);return;}SwipeDismissLayout swipeDismiss = (SwipeDismissLayout) contentParent;swipeDismiss.setOnDismissedListener(new SwipeDismissLayout.OnDismissedListener() {@Overridepublic void onDismissed(SwipeDismissLayout layout) {dispatchOnWindowSwipeDismissed();dispatchOnWindowDismissed(false /*finishTask*/, true /*suppressWindowTransition*/);}});swipeDismiss.setOnSwipeProgressChangedListener(new SwipeDismissLayout.OnSwipeProgressChangedListener() {@Overridepublic void onSwipeProgressChanged(SwipeDismissLayout layout, float alpha, float translate) {WindowManager.LayoutParams newParams = getAttributes();newParams.x = (int) translate;newParams.alpha = alpha;setAttributes(newParams);int flags = 0;if (newParams.x == 0) {flags = FLAG_FULLSCREEN;} else {flags = FLAG_LAYOUT_NO_LIMITS;}setFlags(flags, FLAG_FULLSCREEN | FLAG_LAYOUT_NO_LIMITS);}@Overridepublic void onSwipeCancelled(SwipeDismissLayout layout) {WindowManager.LayoutParams newParams = getAttributes();// Swipe changes only affect the x-translation and alpha, check to see if// those values have changed first before resetting them.if (newParams.x != 0 || newParams.alpha != 1) {newParams.x = 0;newParams.alpha = 1;setAttributes(newParams);setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN | FLAG_LAYOUT_NO_LIMITS);}}});}/** @hide */@Overridepublic void setCloseOnSwipeEnabled(boolean closeOnSwipeEnabled) {if (hasFeature(Window.FEATURE_SWIPE_TO_DISMISS) // swipe-to-dismiss feature is requested&& mContentParent instanceof SwipeDismissLayout) { // check casting mContentParent((SwipeDismissLayout) mContentParent).setDismissable(closeOnSwipeEnabled);}super.setCloseOnSwipeEnabled(closeOnSwipeEnabled);}/*** Helper method for calling the {@link Callback#onPanelClosed(int, Menu)}* callback. This method will grab whatever extra state is needed for the* callback that isn't given in the parameters. If the panel is not open,* this will not perform the callback.** @param featureId Feature ID of the panel that was closed. Must be given.* @param panel Panel that was closed. Optional but useful if there is no*            menu given.* @param menu The menu that was closed. Optional, but give if you have.*/private void callOnPanelClosed(int featureId, PanelFeatureState panel, Menu menu) {final Callback cb = getCallback();if (cb == null)return;// Try to get a menuif (menu == null) {// Need a panel to grab the menu, so try to get thatif (panel == null) {if ((featureId >= 0) && (featureId < mPanels.length)) {panel = mPanels[featureId];}}if (panel != null) {// menu still may be null, which is okay--we tried our bestmenu = panel.menu;}}// If the panel is not open, do not callbackif ((panel != null) && (!panel.isOpen))return;if (!isDestroyed()) {cb.onPanelClosed(featureId, menu);}}/*** Check if Setup or Post-Setup update is completed on TV* @return true if completed*/private boolean isTvUserSetupComplete() {boolean isTvSetupComplete = Settings.Secure.getInt(getContext().getContentResolver(),Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;isTvSetupComplete &= Settings.Secure.getInt(getContext().getContentResolver(),Settings.Secure.TV_USER_SETUP_COMPLETE, 0) != 0;return isTvSetupComplete;}/*** Helper method for adding launch-search to most applications. Opens the* search window using default settings.** @return true if search window opened*/private boolean launchDefaultSearch(KeyEvent event) {if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)&& !isTvUserSetupComplete()) {// If we are in Setup or Post-Setup update mode on TV, consume the search keyreturn false;}boolean result;final Callback cb = getCallback();if (cb == null || isDestroyed()) {result = false;} else {sendCloseSystemWindows("search");int deviceId = event.getDeviceId();SearchEvent searchEvent = null;if (deviceId != 0) {searchEvent = new SearchEvent(InputDevice.getDevice(deviceId));}try {result = cb.onSearchRequested(searchEvent);} catch (AbstractMethodError e) {Log.e(TAG, "WindowCallback " + cb.getClass().getName() + " does not implement"+ " method onSearchRequested(SearchEvent); fa", e);result = cb.onSearchRequested();}}if (!result && (getContext().getResources().getConfiguration().uiMode& Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION) {// On TVs, if the app doesn't implement search, we want to launch assist.Bundle args = new Bundle();args.putInt(Intent.EXTRA_ASSIST_INPUT_DEVICE_ID, event.getDeviceId());return ((SearchManager)getContext().getSystemService(Context.SEARCH_SERVICE)).launchLegacyAssist(null, getContext().getUserId(), args);}return result;}@Overridepublic void setVolumeControlStream(int streamType) {mVolumeControlStreamType = streamType;}@Overridepublic int getVolumeControlStream() {return mVolumeControlStreamType;}@Overridepublic void setMediaController(MediaController controller) {mMediaController = controller;}@Overridepublic MediaController getMediaController() {return mMediaController;}@Overridepublic void setEnterTransition(Transition enterTransition) {mEnterTransition = enterTransition;}@Overridepublic void setReturnTransition(Transition transition) {mReturnTransition = transition;}@Overridepublic void setExitTransition(Transition exitTransition) {mExitTransition = exitTransition;}@Overridepublic void setReenterTransition(Transition transition) {mReenterTransition = transition;}@Overridepublic void setSharedElementEnterTransition(Transition sharedElementEnterTransition) {mSharedElementEnterTransition = sharedElementEnterTransition;}@Overridepublic void setSharedElementReturnTransition(Transition transition) {mSharedElementReturnTransition = transition;}@Overridepublic void setSharedElementExitTransition(Transition sharedElementExitTransition) {mSharedElementExitTransition = sharedElementExitTransition;}@Overridepublic void setSharedElementReenterTransition(Transition transition) {mSharedElementReenterTransition = transition;}@Overridepublic Transition getEnterTransition() {return mEnterTransition;}@Overridepublic Transition getReturnTransition() {return mReturnTransition == USE_DEFAULT_TRANSITION ? getEnterTransition(): mReturnTransition;}@Overridepublic Transition getExitTransition() {return mExitTransition;}@Overridepublic Transition getReenterTransition() {return mReenterTransition == USE_DEFAULT_TRANSITION ? getExitTransition(): mReenterTransition;}@Overridepublic Transition getSharedElementEnterTransition() {return mSharedElementEnterTransition;}@Overridepublic Transition getSharedElementReturnTransition() {return mSharedElementReturnTransition == USE_DEFAULT_TRANSITION? getSharedElementEnterTransition() : mSharedElementReturnTransition;}@Overridepublic Transition getSharedElementExitTransition() {return mSharedElementExitTransition;}@Overridepublic Transition getSharedElementReenterTransition() {return mSharedElementReenterTransition == USE_DEFAULT_TRANSITION? getSharedElementExitTransition() : mSharedElementReenterTransition;}@Overridepublic void setAllowEnterTransitionOverlap(boolean allow) {mAllowEnterTransitionOverlap = allow;}@Overridepublic boolean getAllowEnterTransitionOverlap() {return (mAllowEnterTransitionOverlap == null) ? true : mAllowEnterTransitionOverlap;}@Overridepublic void setAllowReturnTransitionOverlap(boolean allowExitTransitionOverlap) {mAllowReturnTransitionOverlap = allowExitTransitionOverlap;}@Overridepublic boolean getAllowReturnTransitionOverlap() {return (mAllowReturnTransitionOverlap == null) ? true : mAllowReturnTransitionOverlap;}@Overridepublic long getTransitionBackgroundFadeDuration() {return (mBackgroundFadeDurationMillis < 0) ? DEFAULT_BACKGROUND_FADE_DURATION_MS: mBackgroundFadeDurationMillis;}@Overridepublic void setTransitionBackgroundFadeDuration(long fadeDurationMillis) {if (fadeDurationMillis < 0) {throw new IllegalArgumentException("negative durations are not allowed");}mBackgroundFadeDurationMillis = fadeDurationMillis;}@Overridepublic void setSharedElementsUseOverlay(boolean sharedElementsUseOverlay) {mSharedElementsUseOverlay = sharedElementsUseOverlay;}@Overridepublic boolean getSharedElementsUseOverlay() {return (mSharedElementsUseOverlay == null) ? true : mSharedElementsUseOverlay;}private static final class DrawableFeatureState {DrawableFeatureState(int _featureId) {featureId = _featureId;}final int featureId;int resid;Uri uri;Drawable local;Drawable child;Drawable def;Drawable cur;int alpha = 255;int curAlpha = 255;}static final class PanelFeatureState {/** Feature ID for this panel. */int featureId;// Information pulled from the style for this panel.int background;/** The background when the panel spans the entire available width. */int fullBackground;int gravity;int x;int y;int windowAnimations;/** Dynamic state of the panel. */DecorView decorView;/** The panel that was returned by onCreatePanelView(). */View createdPanelView;/** The panel that we are actually showing. */View shownPanelView;/** Use {@link #setMenu} to set this. */MenuBuilder menu;IconMenuPresenter iconMenuPresenter;ListMenuPresenter listMenuPresenter;/** true if this menu will show in single-list compact mode */boolean isCompact;/** Theme resource ID for list elements of the panel menu */int listPresenterTheme;/*** Whether the panel has been prepared (see* {@link PhoneWindow#preparePanel}).*/boolean isPrepared;/*** Whether an item's action has been performed. This happens in obvious* scenarios (user clicks on menu item), but can also happen with* chording menu+(shortcut key).*/boolean isHandled;boolean isOpen;/*** True if the menu is in expanded mode, false if the menu is in icon* mode*/boolean isInExpandedMode;public boolean qwertyMode;boolean refreshDecorView;boolean refreshMenuContent;boolean wasLastOpen;boolean wasLastExpanded;/*** Contains the state of the menu when told to freeze.*/Bundle frozenMenuState;/*** Contains the state of associated action views when told to freeze.* These are saved across invalidations.*/Bundle frozenActionViewState;PanelFeatureState(int featureId) {this.featureId = featureId;refreshDecorView = false;}public boolean isInListMode() {return isInExpandedMode || isCompact;}public boolean hasPanelItems() {if (shownPanelView == null) return false;if (createdPanelView != null) return true;if (isCompact || isInExpandedMode) {return listMenuPresenter.getAdapter().getCount() > 0;} else {return ((ViewGroup) shownPanelView).getChildCount() > 0;}}/*** Unregister and free attached MenuPresenters. They will be recreated as needed.*/public void clearMenuPresenters() {if (menu != null) {menu.removeMenuPresenter(iconMenuPresenter);menu.removeMenuPresenter(listMenuPresenter);}iconMenuPresenter = null;listMenuPresenter = null;}void setStyle(Context context) {TypedArray a = context.obtainStyledAttributes(R.styleable.Theme);background = a.getResourceId(R.styleable.Theme_panelBackground, 0);fullBackground = a.getResourceId(R.styleable.Theme_panelFullBackground, 0);windowAnimations = a.getResourceId(R.styleable.Theme_windowAnimationStyle, 0);isCompact = a.getBoolean(R.styleable.Theme_panelMenuIsCompact, false);listPresenterTheme = a.getResourceId(R.styleable.Theme_panelMenuListTheme,R.style.Theme_ExpandedMenu);a.recycle();}void setMenu(MenuBuilder menu) {if (menu == this.menu) return;if (this.menu != null) {this.menu.removeMenuPresenter(iconMenuPresenter);this.menu.removeMenuPresenter(listMenuPresenter);}this.menu = menu;if (menu != null) {if (iconMenuPresenter != null) menu.addMenuPresenter(iconMenuPresenter);if (listMenuPresenter != null) menu.addMenuPresenter(listMenuPresenter);}}MenuView getListMenuView(Context context, MenuPresenter.Callback cb) {if (menu == null) return null;if (!isCompact) {getIconMenuView(context, cb); // Need this initialized to know where our offset goes}if (listMenuPresenter == null) {listMenuPresenter = new ListMenuPresenter(R.layout.list_menu_item_layout, listPresenterTheme);listMenuPresenter.setCallback(cb);listMenuPresenter.setId(R.id.list_menu_presenter);menu.addMenuPresenter(listMenuPresenter);}if (iconMenuPresenter != null) {listMenuPresenter.setItemIndexOffset(iconMenuPresenter.getNumActualItemsShown());}MenuView result = listMenuPresenter.getMenuView(decorView);return result;}MenuView getIconMenuView(Context context, MenuPresenter.Callback cb) {if (menu == null) return null;if (iconMenuPresenter == null) {iconMenuPresenter = new IconMenuPresenter(context);iconMenuPresenter.setCallback(cb);iconMenuPresenter.setId(R.id.icon_menu_presenter);menu.addMenuPresenter(iconMenuPresenter);}MenuView result = iconMenuPresenter.getMenuView(decorView);return result;}Parcelable onSaveInstanceState() {SavedState savedState = new SavedState();savedState.featureId = featureId;savedState.isOpen = isOpen;savedState.isInExpandedMode = isInExpandedMode;if (menu != null) {savedState.menuState = new Bundle();menu.savePresenterStates(savedState.menuState);}return savedState;}void onRestoreInstanceState(Parcelable state) {SavedState savedState = (SavedState) state;featureId = savedState.featureId;wasLastOpen = savedState.isOpen;wasLastExpanded = savedState.isInExpandedMode;frozenMenuState = savedState.menuState;/** A LocalActivityManager keeps the same instance of this class around.* The first time the menu is being shown after restoring, the* Activity.onCreateOptionsMenu should be called. But, if it is the* same instance then menu != null and we won't call that method.* We clear any cached views here. The caller should invalidatePanelMenu.*/createdPanelView = null;shownPanelView = null;decorView = null;}void applyFrozenState() {if (menu != null && frozenMenuState != null) {menu.restorePresenterStates(frozenMenuState);frozenMenuState = null;}}private static class SavedState implements Parcelable {int featureId;boolean isOpen;boolean isInExpandedMode;Bundle menuState;public int describeContents() {return 0;}public void writeToParcel(Parcel dest, int flags) {dest.writeInt(featureId);dest.writeInt(isOpen ? 1 : 0);dest.writeInt(isInExpandedMode ? 1 : 0);if (isOpen) {dest.writeBundle(menuState);}}private static SavedState readFromParcel(Parcel source) {SavedState savedState = new SavedState();savedState.featureId = source.readInt();savedState.isOpen = source.readInt() == 1;savedState.isInExpandedMode = source.readInt() == 1;if (savedState.isOpen) {savedState.menuState = source.readBundle();}return savedState;}public static final Parcelable.Creator<SavedState> CREATOR= new Parcelable.Creator<SavedState>() {public SavedState createFromParcel(Parcel in) {return readFromParcel(in);}public SavedState[] newArray(int size) {return new SavedState[size];}};}}static class RotationWatcher extends Stub {private Handler mHandler;private final Runnable mRotationChanged = new Runnable() {public void run() {dispatchRotationChanged();}};private final ArrayList<WeakReference<PhoneWindow>> mWindows =new ArrayList<WeakReference<PhoneWindow>>();private boolean mIsWatching;@Overridepublic void onRotationChanged(int rotation) throws RemoteException {mHandler.post(mRotationChanged);}public void addWindow(PhoneWindow phoneWindow) {synchronized (mWindows) {if (!mIsWatching) {try {WindowManagerHolder.sWindowManager.watchRotation(this,phoneWindow.getContext().getDisplay().getDisplayId());mHandler = new Handler();mIsWatching = true;} catch (RemoteException ex) {Log.e(TAG, "Couldn't start watching for device rotation", ex);}}mWindows.add(new WeakReference<PhoneWindow>(phoneWindow));}}public void removeWindow(PhoneWindow phoneWindow) {synchronized (mWindows) {int i = 0;while (i < mWindows.size()) {final WeakReference<PhoneWindow> ref = mWindows.get(i);final PhoneWindow win = ref.get();if (win == null || win == phoneWindow) {mWindows.remove(i);} else {i++;}}}}void dispatchRotationChanged() {synchronized (mWindows) {int i = 0;while (i < mWindows.size()) {final WeakReference<PhoneWindow> ref = mWindows.get(i);final PhoneWindow win = ref.get();if (win != null) {win.onOptionsPanelRotationChanged();i++;} else {mWindows.remove(i);}}}}}/*** Simple implementation of MenuBuilder.Callback that:* <li> Opens a submenu when selected.* <li> Calls back to the callback's onMenuItemSelected when an item is* selected.*/public static final class PhoneWindowMenuCallbackimplements MenuBuilder.Callback, MenuPresenter.Callback {private static final int FEATURE_ID = FEATURE_CONTEXT_MENU;private final PhoneWindow mWindow;private MenuDialogHelper mSubMenuHelper;private boolean mShowDialogForSubmenu;public PhoneWindowMenuCallback(PhoneWindow window) {mWindow = window;}@Overridepublic void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {if (menu.getRootMenu() != menu) {onCloseSubMenu(menu);}if (allMenusAreClosing) {final Callback callback = mWindow.getCallback();if (callback != null && !mWindow.isDestroyed()) {callback.onPanelClosed(FEATURE_ID, menu);}if (menu == mWindow.mContextMenu) {mWindow.dismissContextMenu();}// Dismiss the submenu, if it is showingif (mSubMenuHelper != null) {mSubMenuHelper.dismiss();mSubMenuHelper = null;}}}private void onCloseSubMenu(MenuBuilder menu) {final Callback callback = mWindow.getCallback();if (callback != null && !mWindow.isDestroyed()) {callback.onPanelClosed(FEATURE_ID, menu.getRootMenu());}}@Overridepublic boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {final Callback callback = mWindow.getCallback();return callback != null && !mWindow.isDestroyed()&& callback.onMenuItemSelected(FEATURE_ID, item);}@Overridepublic void onMenuModeChange(MenuBuilder menu) {}@Overridepublic boolean onOpenSubMenu(MenuBuilder subMenu) {if (subMenu == null) {return false;}// Set a simple callback for the submenusubMenu.setCallback(this);if (mShowDialogForSubmenu) {// The window manager will give us a valid window tokenmSubMenuHelper = new MenuDialogHelper(subMenu);mSubMenuHelper.show(null);return true;}return false;}public void setShowDialogForSubmenu(boolean enabled) {mShowDialogForSubmenu = enabled;}}int getLocalFeaturesPrivate() {return super.getLocalFeatures();}protected void setDefaultWindowFormat(int format) {super.setDefaultWindowFormat(format);}void sendCloseSystemWindows() {sendCloseSystemWindows(getContext(), null);}void sendCloseSystemWindows(String reason) {sendCloseSystemWindows(getContext(), reason);}public static void sendCloseSystemWindows(Context context, String reason) {if (ActivityManager.isSystemReady()) {try {ActivityManager.getService().closeSystemDialogs(reason);} catch (RemoteException e) {}}}@Overridepublic int getStatusBarColor() {return mStatusBarColor;}@Overridepublic void setStatusBarColor(int color) {mStatusBarColor = color;mForcedStatusBarColor = true;if (mDecor != null) {mDecor.updateColorViews(null, false /* animate */);}}@Overridepublic int getNavigationBarColor() {return mNavigationBarColor;}@Overridepublic void setNavigationBarColor(int color) {mNavigationBarColor = color;mForcedNavigationBarColor = true;if (mDecor != null) {mDecor.updateColorViews(null, false /* animate */);}}@Overridepublic void setNavigationBarDividerColor(int navigationBarDividerColor) {mNavigationBarDividerColor = navigationBarDividerColor;if (mDecor != null) {mDecor.updateColorViews(null, false /* animate */);}}@Overridepublic int getNavigationBarDividerColor() {return mNavigationBarDividerColor;}public void setIsStartingWindow(boolean isStartingWindow) {mIsStartingWindow = isStartingWindow;}@Overridepublic void setTheme(int resid) {mTheme = resid;if (mDecor != null) {Context context = mDecor.getContext();if (context instanceof DecorContext) {context.setTheme(resid);}}}@Overridepublic void setResizingCaptionDrawable(Drawable drawable) {mDecor.setUserCaptionBackgroundDrawable(drawable);}@Overridepublic void setDecorCaptionShade(int decorCaptionShade) {mDecorCaptionShade = decorCaptionShade;if (mDecor != null) {mDecor.updateDecorCaptionShade();}}int getDecorCaptionShade() {return mDecorCaptionShade;}@Overridepublic void setAttributes(WindowManager.LayoutParams params) {super.setAttributes(params);if (mDecor != null) {mDecor.updateLogTag(params);}}
}

SwipeDismissLayout

/** Copyright (C) 2017 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 androidx.wear.widget;import android.content.Context;
import android.content.res.Resources;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.FrameLayout;import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.annotation.UiThread;/*** Special layout that finishes its activity when swiped away.** <p>This is a modified copy of the internal framework class* com.android.internal.widget.SwipeDismissLayout.** @hide*/
@RestrictTo(Scope.LIBRARY)
@UiThread
class SwipeDismissLayout extends FrameLayout {private static final String TAG = "SwipeDismissLayout";public static final float DEFAULT_DISMISS_DRAG_WIDTH_RATIO = .33f;// A value between 0.0 and 1.0 determining the percentage of the screen on the left-hand-side// where edge swipe gestures are permitted to begin.private static final float EDGE_SWIPE_THRESHOLD = 0.1f;/** Called when the layout is about to consider a swipe. */@UiThreadinterface OnPreSwipeListener {/*** Notifies listeners that the view is now considering to start a dismiss gesture from a* particular point on the screen. The default implementation returns true for all* coordinates so that is is possible to start a swipe-to-dismiss gesture from any location.* If any one instance of this Callback returns false for a given set of coordinates,* swipe-to-dismiss will not be allowed to start in that point.** @param xDown the x coordinate of the initial {@link android.view.MotionEvent#ACTION_DOWN}*              event for this motion* @param yDown the y coordinate of the initial {@link android.view.MotionEvent#ACTION_DOWN}*              event for this motion* @return {@code true} if these coordinates should be considered as a start of a swipe* gesture, {@code false} otherwise*/boolean onPreSwipe(SwipeDismissLayout swipeDismissLayout, float xDown, float yDown);}/*** Interface enabling listeners to react to when the swipe gesture is done and the view should* probably be dismissed from the UI.*/@UiThreadinterface OnDismissedListener {void onDismissed(SwipeDismissLayout layout);}/*** Interface enabling listeners to react to changes in the progress of the swipe-to-dismiss* gesture.*/@UiThreadinterface OnSwipeProgressChangedListener {/*** Called when the layout has been swiped and the position of the window should change.** @param layout    the layout associated with this listener.* @param progress  a number in [0, 1] representing how far to the right the window has*                  been swiped* @param translate a number in [0, w], where w is the width of the layout. This is*                  equivalent to progress * layout.getWidth()*/void onSwipeProgressChanged(SwipeDismissLayout layout, float progress, float translate);/*** Called when the layout started to be swiped away but then the gesture was canceled.** @param layout    the layout associated with this listener*/void onSwipeCanceled(SwipeDismissLayout layout);}// Cached ViewConfiguration and system-wide constant valuesprivate int mSlop;private int mMinFlingVelocity;private float mGestureThresholdPx;// Transient propertiesprivate int mActiveTouchId;private float mDownX;private float mDownY;private boolean mSwipeable;private boolean mSwiping;// This variable holds information about whether the initial move of a longer swipe// (consisting of multiple move events) has conformed to the definition of a horizontal// swipe-to-dismiss. A swipe gesture is only ever allowed to be recognized if this variable is// set to true. Otherwise, the motion events will be allowed to propagate to the children.private boolean mCanStartSwipe = true;private boolean mDismissed;private boolean mDiscardIntercept;private VelocityTracker mVelocityTracker;private float mTranslationX;private boolean mDisallowIntercept;@Nullableprivate OnPreSwipeListener mOnPreSwipeListener;private OnDismissedListener mDismissedListener;private OnSwipeProgressChangedListener mProgressListener;private float mLastX;private float mDismissMinDragWidthRatio = DEFAULT_DISMISS_DRAG_WIDTH_RATIO;SwipeDismissLayout(Context context) {this(context, null);}SwipeDismissLayout(Context context, AttributeSet attrs) {this(context, attrs, 0);}SwipeDismissLayout(Context context, AttributeSet attrs, int defStyle) {this(context, attrs, defStyle, 0);}SwipeDismissLayout(Context context, AttributeSet attrs, int defStyle, int defStyleRes) {super(context, attrs, defStyle, defStyleRes);ViewConfiguration vc = ViewConfiguration.get(context);mSlop = vc.getScaledTouchSlop();mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();mGestureThresholdPx =Resources.getSystem().getDisplayMetrics().widthPixels * EDGE_SWIPE_THRESHOLD;// By default, the view is swipeable.setSwipeable(true);}/*** Sets the minimum ratio of the screen after which the swipe gesture is treated as swipe-to-* dismiss.** @param ratio  the ratio of the screen at which the swipe gesture is treated as*               swipe-to-dismiss. should be provided as a fraction of the screen*/public void setDismissMinDragWidthRatio(float ratio) {mDismissMinDragWidthRatio = ratio;}/*** Returns the current ratio of te screen at which the swipe gesture is treated as* swipe-to-dismiss.** @return the current ratio of te screen at which the swipe gesture is treated as* swipe-to-dismiss*/public float getDismissMinDragWidthRatio() {return mDismissMinDragWidthRatio;}/*** Sets the layout to swipeable or not. This effectively turns the functionality of this layout* on or off.** @param swipeable whether the layout should react to the swipe gesture*/public void setSwipeable(boolean swipeable) {mSwipeable = swipeable;}/** Returns true if the layout reacts to swipe gestures. */public boolean isSwipeable() {return mSwipeable;}void setOnPreSwipeListener(@Nullable OnPreSwipeListener listener) {mOnPreSwipeListener = listener;}void setOnDismissedListener(@Nullable OnDismissedListener listener) {mDismissedListener = listener;}void setOnSwipeProgressChangedListener(@Nullable OnSwipeProgressChangedListener listener) {mProgressListener = listener;}@Overridepublic void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {mDisallowIntercept = disallowIntercept;if (getParent() != null) {getParent().requestDisallowInterceptTouchEvent(disallowIntercept);}}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {if (!mSwipeable) {return super.onInterceptTouchEvent(ev);}// offset because the view is translated during swipeev.offsetLocation(mTranslationX, 0);switch (ev.getActionMasked()) {case MotionEvent.ACTION_DOWN:resetMembers();mDownX = ev.getRawX();mDownY = ev.getRawY();mActiveTouchId = ev.getPointerId(0);mVelocityTracker = VelocityTracker.obtain();mVelocityTracker.addMovement(ev);break;case MotionEvent.ACTION_POINTER_DOWN:int actionIndex = ev.getActionIndex();mActiveTouchId = ev.getPointerId(actionIndex);break;case MotionEvent.ACTION_POINTER_UP:actionIndex = ev.getActionIndex();int pointerId = ev.getPointerId(actionIndex);if (pointerId == mActiveTouchId) {// This was our active pointer going up. Choose a new active pointer.int newActionIndex = actionIndex == 0 ? 1 : 0;mActiveTouchId = ev.getPointerId(newActionIndex);}break;case MotionEvent.ACTION_CANCEL:case MotionEvent.ACTION_UP:resetMembers();break;case MotionEvent.ACTION_MOVE:if (mVelocityTracker == null || mDiscardIntercept) {break;}int pointerIndex = ev.findPointerIndex(mActiveTouchId);if (pointerIndex == -1) {Log.e(TAG, "Invalid pointer index: ignoring.");mDiscardIntercept = true;break;}float dx = ev.getRawX() - mDownX;float x = ev.getX(pointerIndex);float y = ev.getY(pointerIndex);if (dx != 0 && mDownX >= mGestureThresholdPx && canScroll(this, false, dx, x, y)) {mDiscardIntercept = true;break;}updateSwiping(ev);break;}if ((mOnPreSwipeListener == null && !mDisallowIntercept)|| mOnPreSwipeListener.onPreSwipe(this, mDownX, mDownY)) {return (!mDiscardIntercept && mSwiping);}return false;}@Overridepublic boolean canScrollHorizontally(int direction) {// This view can only be swiped horizontally from left to right - this means a negative// SCROLLING direction. We return false if the view is not visible to avoid capturing swipe// gestures when the view is hidden.return direction < 0 && isSwipeable() && getVisibility() == View.VISIBLE;}/*** Helper function determining if a particular move gesture was verbose enough to qualify as a* beginning of a swipe.** @param dx distance traveled in the x direction, from the initial touch down* @param dy distance traveled in the y direction, from the initial touch down* @return {@code true} if the gesture was long enough to be considered a potential swipe*/private boolean isPotentialSwipe(float dx, float dy) {return (dx * dx) + (dy * dy) > mSlop * mSlop;}@Overridepublic boolean onTouchEvent(MotionEvent ev) {if (!mSwipeable) {return super.onTouchEvent(ev);}if (mVelocityTracker == null) {return super.onTouchEvent(ev);}if (mOnPreSwipeListener != null && !mOnPreSwipeListener.onPreSwipe(this, mDownX, mDownY)) {return super.onTouchEvent(ev);}// offset because the view is translated during swipeev.offsetLocation(mTranslationX, 0);switch (ev.getActionMasked()) {case MotionEvent.ACTION_UP:updateDismiss(ev);if (mDismissed) {dismiss();} else if (mSwiping) {cancel();}resetMembers();break;case MotionEvent.ACTION_CANCEL:cancel();resetMembers();break;case MotionEvent.ACTION_MOVE:mVelocityTracker.addMovement(ev);mLastX = ev.getRawX();updateSwiping(ev);if (mSwiping) {setProgress(ev.getRawX() - mDownX);break;}}return true;}private void setProgress(float deltaX) {mTranslationX = deltaX;if (mProgressListener != null && deltaX >= 0) {mProgressListener.onSwipeProgressChanged(this, deltaX / getWidth(), deltaX);}}private void dismiss() {if (mDismissedListener != null) {mDismissedListener.onDismissed(this);}}private void cancel() {if (mProgressListener != null) {mProgressListener.onSwipeCanceled(this);}}/** Resets internal members when canceling or finishing a given gesture. */private void resetMembers() {if (mVelocityTracker != null) {mVelocityTracker.recycle();}mVelocityTracker = null;mTranslationX = 0;mDownX = 0;mDownY = 0;mSwiping = false;mDismissed = false;mDiscardIntercept = false;mCanStartSwipe = true;mDisallowIntercept = false;}private void updateSwiping(MotionEvent ev) {if (!mSwiping) {float deltaX = ev.getRawX() - mDownX;float deltaY = ev.getRawY() - mDownY;if (isPotentialSwipe(deltaX, deltaY)) {// There are three conditions on which we want want to start swiping:// 1. The swipe is from left to right AND// 2. It is horizontal AND// 3. We actually can start swipingmSwiping = mCanStartSwipe && Math.abs(deltaY) < Math.abs(deltaX) && deltaX > 0;mCanStartSwipe = mSwiping;}}}private void updateDismiss(MotionEvent ev) {float deltaX = ev.getRawX() - mDownX;mVelocityTracker.addMovement(ev);mVelocityTracker.computeCurrentVelocity(1000);if (!mDismissed) {if ((deltaX > (getWidth() * mDismissMinDragWidthRatio) && ev.getRawX() >= mLastX)|| mVelocityTracker.getXVelocity() >= mMinFlingVelocity) {mDismissed = true;}}// Check if the user tried to undo this.if (mDismissed && mSwiping) {// Check if the user's finger is actually flinging back to leftif (mVelocityTracker.getXVelocity() < -mMinFlingVelocity) {mDismissed = false;}}}/*** Tests scrollability within child views of v in the direction of dx.** @param v      view to test for horizontal scrollability* @param checkV whether the view v passed should itself be checked for scrollability*               ({@code true}), or just its children ({@code false})* @param dx     delta scrolled in pixels. Only the sign of this is used* @param x      x coordinate of the active touch point* @param y      y coordinate of the active touch point* @return {@code true} if child views of v can be scrolled by delta of dx*/protected boolean canScroll(View v, boolean checkV, float dx, float x, float y) {if (v instanceof ViewGroup) {final ViewGroup group = (ViewGroup) v;final int scrollX = v.getScrollX();final int scrollY = v.getScrollY();final int count = group.getChildCount();for (int i = count - 1; i >= 0; i--) {final View child = group.getChildAt(i);if (x + scrollX >= child.getLeft()&& x + scrollX < child.getRight()&& y + scrollY >= child.getTop()&& y + scrollY < child.getBottom()&& canScroll(child, true, dx, x + scrollX - child.getLeft(),y + scrollY - child.getTop())) {return true;}}}return checkV && v.canScrollHorizontally((int) -dx);}
}

废话不说如何使用了:、
1、getWindow().requestFeature(Window.FEATURE_SWIPE_TO_DISMISS);
2.通过主题配置, 在主题样式中加入:true

工欲善其事,必先利其器篇-高效实现右滑退出功能相关推荐

  1. android滑动背景变透明,Android右滑退出+沉浸式(透明)状态栏

    背景 上篇文章一个千万量级的APP使用的一些第三方库中,在说到一个使用很广泛的滑动退出库SwipeBackLayout时有提过有时间会分享自己在项目中引入这个库的时候填过的一些坑.前段时间项目加入沉浸 ...

  2. ios7自定义返回按钮后,右滑返回功能失效解决方法

    ios7自定义返回按钮后,右滑返回功能失效解决方法 -(void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; //开 ...

  3. android右滑返回动画,Android仿微信右滑返回功能的实例代码

    先上效果图,如下: 先分析一下功能的主要技术点,右滑即手势判断,当滑到一直距离时才执行返回,并且手指按下的位置是在屏幕的最左边(这个也是有一定范围的),  这些可以实现onTouchEvent来实现. ...

  4. Android8.1系统添加屏幕左侧边缘向右滑退出当前页面功能

    功能说明:客户定制的系统需要一直隐藏导航栏,但是客户又想要能够触摸退出当前的页面,返回上一个打开的页面.客户也要求要加悬浮点按钮,于是想到了手势操作. 这个实现比较简单,监听屏幕左侧边缘向右滑的动作, ...

  5. 利用swipelistview完成qq聊天列表右滑删除功能

    感谢HarryWeasley整理:http://blog.csdn.net/harryweasley/article/details/41413547 前言:前段时间,由于自己比较悠闲,没有什么工作上 ...

  6. delphi fmx防止左滑,右滑退出app

    现在的手机,很多没返回键,通过左右滑动返回 一般的app当在主界面滑动时,不是退出,而是到后台运行 而自己的,是退出了 主要是在keydown事件里做文章 当返回键按下时,让它无效,就解决了 proc ...

  7. 华为手机如何锁定计算机,华为手机如何设定右划退出

    华为手机如何设定右划退出以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 华为手机如何设定右划退出 手机自带并没有全域性返 ...

  8. 【CS-Notes】工欲善其事必先利其器(Code、Git、Docker、Linux)

    文章目录 Code工作台(工欲善其事必先利其器) 1.通用工具 1.1.编辑器 1.2.Git 基本版本控制 1.3.Docker容器 2.Linux工作台 2.1.Linux命令 2.2.开发工具 ...

  9. 工欲善其事必先利其器 之 VirtualBox

    工欲善其事必先利其器 之 VirtualBox 文章目录 工欲善其事必先利其器 之 VirtualBox 前言 一.VirtualBox 概述 二.VirtualBox 功能特性 三.搭建你自己的 V ...

  10. 毕业总结:工欲善其事 必先利其器(公开版)(更新中)

    短短的研究生生涯已然结束,回望初到上海的情景,自己还是一无所知的科研小白.在老师的悉心教导以及实验室兄弟姐妹的相互帮助下,自己获得了能力上的提升,也取得了一定的科研成果.科研路漫漫,站在巨人的肩膀上才 ...

最新文章

  1. php接收流文件,PHP传输文件流及文件流的保存
  2. 第21届国际C语言混乱代码大赛获奖作品
  3. centos设置mysql初始l密码_CentOS 7下安装MySQL后重置root密码方法
  4. .net 事务处理的三种方法
  5. springboot aop记录日志
  6. 高级工作流模式深入业务场景分析(1)——多路合并
  7. (转) 淘淘商城系列——解决KindEditor上传图片浏览器兼容性问题
  8. 自学几小时,斯坦福AI推测并复现了元素周期表 | 华裔团队研究
  9. JDK1.8与spring3.x的不兼容
  10. Havok Vision Engine
  11. 2022国产芯片技术创新与市场应用论坛即将召开
  12. [TODO]高维空间求近似最近邻
  13. Mac OS 名称问题及修改方法(修改终端中显示的hostname(主机名称) / 电脑名称 / 管理员名称 / LocalHostName(本地主机名称))
  14. 如何写出高效的代码?(持续更新)
  15. 统计二叉树的叶子节点个数
  16. MathType怎么编辑双箭头
  17. CEO、COO、CFO、CTO、CIO、CBO、CDO……日常必知
  18. 放射性核废料处理matlab模型,放射性废物处理问题模型.doc
  19. npm install xxxx --legacy-peer-deps命令解决node下载时的依赖冲突
  20. 在win7中解决手写功能

热门文章

  1. Ubuntu 16.04 安装wine-qq 7.8 并解决不能输入中文问题
  2. nividia retinanet使用笔记
  3. 【字符编码转换】使用iconv
  4. 中国有哪些优秀的民谣音乐人?
  5. 有关《大道至简》的几点讨论~
  6. 查看漏洞库平台有哪些?
  7. 关于新正方教务系统(湖北工程学院)的one day越权漏洞的说明
  8. 解决win10安装失败原因和方法
  9. 《小鑫发现》之GraphQL框架Prisma
  10. 【室内园艺】——栀子花