blob: 1f01759253cf28703917eeabfbf9777786765088 [file] [log] [blame]
Stefan Kuhne61b47bb2015-07-28 14:04:25 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.widget;
18
Wale Ogunwale3797c222015-10-27 14:21:58 -070019import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
20
Stefan Kuhne61b47bb2015-07-28 14:04:25 -070021import android.content.Context;
22import android.os.RemoteException;
23import android.util.AttributeSet;
Skuhne81c524a2015-08-12 13:34:14 -070024import android.view.MotionEvent;
Stefan Kuhne61b47bb2015-07-28 14:04:25 -070025import android.view.View;
Skuhnef7b882c2015-08-11 17:18:58 -070026import android.widget.LinearLayout;
Stefan Kuhne61b47bb2015-07-28 14:04:25 -070027import android.view.ViewGroup;
Stefan Kuhnef4dd71a2015-08-07 09:28:52 -070028import android.view.ViewOutlineProvider;
29import android.view.Window;
Stefan Kuhne61b47bb2015-07-28 14:04:25 -070030import android.util.Log;
Stefan Kuhne61b47bb2015-07-28 14:04:25 -070031
Stefan Kuhne61b47bb2015-07-28 14:04:25 -070032import com.android.internal.R;
33import com.android.internal.policy.PhoneWindow;
34
Stefan Kuhne61b47bb2015-07-28 14:04:25 -070035/**
36 * This class represents the special screen elements to control a window on free form
Wale Ogunwale2b547c32015-11-18 10:33:22 -080037 * environment. All these screen elements are added in the "non client area" which is the area of
Stefan Kuhne61b47bb2015-07-28 14:04:25 -070038 * the window which is handled by the OS and not the application.
39 * As such this class handles the following things:
40 * <ul>
41 * <li>The caption, containing the system buttons like maximize, close and such as well as
42 * allowing the user to drag the window around.</li>
Stefan Kuhne61b47bb2015-07-28 14:04:25 -070043 * After creating the view, the function
Wale Ogunwalef3a62fb2015-10-14 17:07:29 -070044 * {@link #setPhoneWindow} needs to be called to make
Stefan Kuhne61b47bb2015-07-28 14:04:25 -070045 * the connection to it's owning PhoneWindow.
46 * Note: At this time the application can change various attributes of the DecorView which
47 * will break things (in settle/unexpected ways):
48 * <ul>
49 * <li>setElevation</li>
50 * <li>setOutlineProvider</li>
51 * <li>setSurfaceFormat</li>
52 * <li>..</li>
53 * </ul>
54 * This will be mitigated once b/22527834 will be addressed.
55 */
Chong Zhang509ea6b2015-09-30 14:09:52 -070056public class NonClientDecorView extends LinearLayout
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -080057 implements View.OnClickListener, View.OnTouchListener {
Stefan Kuhne61b47bb2015-07-28 14:04:25 -070058 private final static String TAG = "NonClientDecorView";
Stefan Kuhne61b47bb2015-07-28 14:04:25 -070059 private PhoneWindow mOwner = null;
Wale Ogunwale8cc5a742015-11-17 15:41:05 -080060 private boolean mShowDecor = false;
Skuhne81c524a2015-08-12 13:34:14 -070061
62 // True if the window is being dragged.
63 private boolean mDragging = false;
64
Skuhne81c524a2015-08-12 13:34:14 -070065 // True when the left mouse button got released while dragging.
66 private boolean mLeftMouseButtonReleased;
67
Stefan Kuhne61b47bb2015-07-28 14:04:25 -070068 public NonClientDecorView(Context context) {
69 super(context);
70 }
71
72 public NonClientDecorView(Context context, AttributeSet attrs) {
73 super(context, attrs);
74 }
75
76 public NonClientDecorView(Context context, AttributeSet attrs, int defStyle) {
77 super(context, attrs, defStyle);
78 }
79
Wale Ogunwale2b547c32015-11-18 10:33:22 -080080 public void setPhoneWindow(PhoneWindow owner, boolean showDecor) {
Stefan Kuhne61b47bb2015-07-28 14:04:25 -070081 mOwner = owner;
Stefan Kuhnef4dd71a2015-08-07 09:28:52 -070082 mShowDecor = showDecor;
Skuhnef7b882c2015-08-11 17:18:58 -070083 updateCaptionVisibility();
Stefan Kuhne61b47bb2015-07-28 14:04:25 -070084 // By changing the outline provider to BOUNDS, the window can remove its
85 // background without removing the shadow.
86 mOwner.getDecorView().setOutlineProvider(ViewOutlineProvider.BOUNDS);
Skuhneb8160872015-09-22 09:51:39 -070087
Stefan Kuhne61b47bb2015-07-28 14:04:25 -070088 findViewById(R.id.maximize_window).setOnClickListener(this);
89 findViewById(R.id.close_window).setOnClickListener(this);
90 }
91
Skuhne81c524a2015-08-12 13:34:14 -070092 @Override
Chong Zhang509ea6b2015-09-30 14:09:52 -070093 public boolean onTouch(View v, MotionEvent e) {
Skuhne81c524a2015-08-12 13:34:14 -070094 // Note: There are no mixed events. When a new device gets used (e.g. 1. Mouse, 2. touch)
95 // the old input device events get cancelled first. So no need to remember the kind of
96 // input device we are listening to.
97 switch (e.getActionMasked()) {
98 case MotionEvent.ACTION_DOWN:
Skuhnea635a262015-08-26 15:45:58 -070099 if (!mShowDecor) {
100 // When there is no decor we should not react to anything.
101 return false;
102 }
Skuhne81c524a2015-08-12 13:34:14 -0700103 // A drag action is started if we aren't dragging already and the starting event is
104 // either a left mouse button or any other input device.
105 if (!mDragging &&
106 (e.getToolType(e.getActionIndex()) != MotionEvent.TOOL_TYPE_MOUSE ||
107 (e.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0)) {
108 mDragging = true;
Skuhne81c524a2015-08-12 13:34:14 -0700109 mLeftMouseButtonReleased = false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700110 startMovingTask(e.getRawX(), e.getRawY());
Skuhne81c524a2015-08-12 13:34:14 -0700111 }
112 break;
113
114 case MotionEvent.ACTION_MOVE:
115 if (mDragging && !mLeftMouseButtonReleased) {
116 if (e.getToolType(e.getActionIndex()) == MotionEvent.TOOL_TYPE_MOUSE &&
117 (e.getButtonState() & MotionEvent.BUTTON_PRIMARY) == 0) {
118 // There is no separate mouse button up call and if the user mixes mouse
119 // button drag actions, we stop dragging once he releases the button.
120 mLeftMouseButtonReleased = true;
121 break;
122 }
Skuhne81c524a2015-08-12 13:34:14 -0700123 }
124 break;
125
126 case MotionEvent.ACTION_UP:
Skuhne81c524a2015-08-12 13:34:14 -0700127 case MotionEvent.ACTION_CANCEL:
Skuhnea5a93ee2015-08-20 15:43:57 -0700128 if (!mDragging) {
129 break;
Skuhne81c524a2015-08-12 13:34:14 -0700130 }
Skuhnea5a93ee2015-08-20 15:43:57 -0700131 // Abort the ongoing dragging.
132 mDragging = false;
Skuhnea5a93ee2015-08-20 15:43:57 -0700133 return true;
Skuhne81c524a2015-08-12 13:34:14 -0700134 }
135 return mDragging;
136 }
137
Stefan Kuhnef4dd71a2015-08-07 09:28:52 -0700138 /**
139 * The phone window configuration has changed and the decor needs to be updated.
140 * @param showDecor True if the decor should be shown.
Wale Ogunwale2b547c32015-11-18 10:33:22 -0800141 */
142 public void onConfigurationChanged(boolean showDecor) {
Stefan Kuhnef4dd71a2015-08-07 09:28:52 -0700143 mShowDecor = showDecor;
Skuhnef7b882c2015-08-11 17:18:58 -0700144 updateCaptionVisibility();
Stefan Kuhnef4dd71a2015-08-07 09:28:52 -0700145 }
146
Stefan Kuhne61b47bb2015-07-28 14:04:25 -0700147 @Override
148 public void onClick(View view) {
149 if (view.getId() == R.id.maximize_window) {
Stefan Kuhne1b420572015-08-07 10:50:19 -0700150 maximizeWindow();
Stefan Kuhne61b47bb2015-07-28 14:04:25 -0700151 } else if (view.getId() == R.id.close_window) {
Stefan Kuhne1b420572015-08-07 10:50:19 -0700152 mOwner.dispatchOnWindowDismissed(true /*finishTask*/);
Stefan Kuhne61b47bb2015-07-28 14:04:25 -0700153 }
154 }
155
156 @Override
Skuhnef7b882c2015-08-11 17:18:58 -0700157 public void addView(View child, int index, ViewGroup.LayoutParams params) {
Stefan Kuhnef4dd71a2015-08-07 09:28:52 -0700158 // Make sure that we never get more then one client area in our view.
Stefan Kuhne61b47bb2015-07-28 14:04:25 -0700159 if (index >= 2 || getChildCount() >= 2) {
160 throw new IllegalStateException("NonClientDecorView can only handle 1 client view");
161 }
162 super.addView(child, index, params);
163 }
164
Stefan Kuhnef4dd71a2015-08-07 09:28:52 -0700165 /**
166 * Determine if the workspace is entirely covered by the window.
167 * @return Returns true when the window is filling the entire screen/workspace.
168 **/
Stefan Kuhne61b47bb2015-07-28 14:04:25 -0700169 private boolean isFillingScreen() {
170 return (0 != ((getWindowSystemUiVisibility() | getSystemUiVisibility()) &
171 (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
172 View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_LOW_PROFILE)));
173 }
174
Stefan Kuhnef4dd71a2015-08-07 09:28:52 -0700175 /**
Skuhnef7b882c2015-08-11 17:18:58 -0700176 * Updates the visibility of the caption.
177 **/
178 private void updateCaptionVisibility() {
179 // Don't show the decor if the window has e.g. entered full screen.
180 boolean invisible = isFillingScreen() || !mShowDecor;
181 View caption = getChildAt(0);
182 caption.setVisibility(invisible ? GONE : VISIBLE);
Chong Zhang509ea6b2015-09-30 14:09:52 -0700183 caption.setOnTouchListener(this);
Stefan Kuhne61b47bb2015-07-28 14:04:25 -0700184 }
Stefan Kuhne1b420572015-08-07 10:50:19 -0700185
Stefan Kuhnef4dd71a2015-08-07 09:28:52 -0700186 /**
187 * Maximize the window by moving it to the maximized workspace stack.
188 **/
Stefan Kuhne1b420572015-08-07 10:50:19 -0700189 private void maximizeWindow() {
Skuhnece2faa52015-08-11 10:36:38 -0700190 Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
Stefan Kuhne1b420572015-08-07 10:50:19 -0700191 if (callback != null) {
192 try {
Wale Ogunwale3797c222015-10-27 14:21:58 -0700193 callback.changeWindowStack(FULLSCREEN_WORKSPACE_STACK_ID);
Stefan Kuhne1b420572015-08-07 10:50:19 -0700194 } catch (RemoteException ex) {
195 Log.e(TAG, "Cannot change task workspace.");
196 }
197 }
198 }
Wale Ogunwale8cc5a742015-11-17 15:41:05 -0800199
200 public boolean isShowingDecor() {
201 return mShowDecor;
202 }
203
204 public int getDecorCaptionHeight() {
205 final View caption = getChildAt(0);
206 return (caption != null) ? caption.getHeight() : 0;
207 }
Stefan Kuhne61b47bb2015-07-28 14:04:25 -0700208}