-
-
Save Volcanoscar/5634139f32077e49b1c1e958f82959c6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 34b73571a4d322ab2afe59a80912337783f29fb7 Mon Sep 17 00:00:00 2001 | |
From: BigBrother1984 <[email protected]> | |
Date: Thu, 10 Apr 2014 18:07:26 +0200 | |
Subject: [PATCH] base: floating window | |
Squash commits from aospa | |
Change-Id: I81ba0bfaf8e6f29388e07beb78197dd316a11de7 | |
Signed-off-by: jrizzoli <[email protected]> | |
--- | |
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java | |
index 9568897..89b2373 100644 | |
--- a/core/java/android/app/Activity.java | |
+++ b/core/java/android/app/Activity.java | |
@@ -28,11 +28,13 @@ | |
import com.android.internal.app.WindowDecorActionBar; | |
import com.android.internal.app.ToolbarActionBar; | |
import com.android.internal.policy.PolicyManager; | |
+import com.android.internal.widget.FloatingWindowView; | |
import android.annotation.IntDef; | |
import android.annotation.Nullable; | |
import android.annotation.SystemApi; | |
import android.app.admin.DevicePolicyManager; | |
+import android.app.ActivityManager; | |
import android.content.ComponentCallbacks2; | |
import android.content.ComponentName; | |
import android.content.ContentResolver; | |
@@ -49,8 +51,10 @@ | |
import android.content.res.Resources; | |
import android.content.res.TypedArray; | |
import android.database.Cursor; | |
+import android.graphics.Color; | |
import android.graphics.Bitmap; | |
import android.graphics.Canvas; | |
+import android.graphics.Rect; | |
import android.graphics.drawable.Drawable; | |
import android.media.AudioManager; | |
import android.media.session.MediaController; | |
@@ -72,18 +76,22 @@ | |
import android.util.EventLog; | |
import android.util.Log; | |
import android.util.PrintWriterPrinter; | |
+import android.util.TypedValue; | |
import android.util.Slog; | |
import android.util.SparseArray; | |
import android.view.ActionMode; | |
import android.view.ContextMenu; | |
import android.view.ContextMenu.ContextMenuInfo; | |
import android.view.ContextThemeWrapper; | |
+import android.view.Gravity; | |
+import android.view.IWindowManager; | |
import android.view.KeyEvent; | |
import android.view.LayoutInflater; | |
import android.view.Menu; | |
import android.view.MenuInflater; | |
import android.view.MenuItem; | |
import android.view.MotionEvent; | |
+import android.view.ScaleGestureDetector; | |
import android.view.View; | |
import android.view.View.OnCreateContextMenuListener; | |
import android.view.ViewGroup; | |
@@ -94,6 +102,9 @@ | |
import android.view.WindowManagerGlobal; | |
import android.view.accessibility.AccessibilityEvent; | |
import android.widget.AdapterView; | |
+import android.widget.FrameLayout; | |
+ | |
+import com.android.internal.util.paranoid.ColorUtils; | |
import java.io.FileDescriptor; | |
import java.io.PrintWriter; | |
@@ -101,6 +112,7 @@ | |
import java.lang.annotation.RetentionPolicy; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
+import java.util.List; | |
/** | |
* An activity is a single, focused thing that the user can do. Almost all | |
@@ -680,6 +692,9 @@ | |
private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_"; | |
private static final String SAVED_DIALOG_ARGS_KEY_PREFIX = "android:dialog_args_"; | |
+ private static final String SAVED_WINDOW_LAYOUT_IDS_KEY = "android:savedWindowLayoutIds"; | |
+ private static final String SAVED_WINDOW_LAYOUT_TAG = "android:savedWindowLayout"; | |
+ | |
private static class ManagedDialog { | |
Dialog mDialog; | |
Bundle mArgs; | |
@@ -733,6 +748,7 @@ | |
/*package*/ boolean mVisibleFromServer = false; | |
/*package*/ boolean mVisibleFromClient = true; | |
/*package*/ ActionBar mActionBar = null; | |
+ /*package*/ WindowDecorActionBar mDecorActionBar = null; | |
private boolean mEnableDefaultActionBarUp; | |
private VoiceInteractor mVoiceInteractor; | |
@@ -796,6 +812,52 @@ | |
ActivityTransitionState mActivityTransitionState = new ActivityTransitionState(); | |
SharedElementCallback mEnterTransitionListener = SharedElementCallback.NULL_CALLBACK; | |
SharedElementCallback mExitTransitionListener = SharedElementCallback.NULL_CALLBACK; | |
+ | |
+ private Rect mOriginalBounds; | |
+ private boolean mIsSplitView; | |
+ | |
+ private final int UNKNOWN = -10000; | |
+ private final int SNAP_NONE = 0; | |
+ private final int SNAP_LEFT = 1; | |
+ private final int SNAP_TOP = 2; | |
+ private final int SNAP_RIGHT = 3; | |
+ private final int SNAP_BOTTOM = 4; | |
+ private final int MOVE_MAX_RANGE = 10; | |
+ | |
+ private Runnable mRunnable; | |
+ private Runnable mLayoutRunnable; | |
+ private int mRange = 100; | |
+ private int mSensitivity = 50; | |
+ private int mSnap = SNAP_NONE; | |
+ private int[] mSnapParam = new int[3]; // w,h,g | |
+ private int[] mOldParam = new int[2]; // w,h | |
+ private boolean mSnapped; | |
+ private int[] mOldLayout; | |
+ private int[] mLastLayout; | |
+ private boolean mTimeoutRunning; | |
+ private boolean mTimeoutDone; | |
+ private boolean mRestorePosition; | |
+ private boolean mChangedPreviousRange; | |
+ private float[] mPreviousRange = new float[2]; | |
+ | |
+ private Float screenX ; | |
+ private Float screenY ; | |
+ private Float viewX ; | |
+ private Float viewY ; | |
+ private Float leftFromScreen ; | |
+ private Float topFromScreen ; | |
+ private int mCurrentScreenHeight; | |
+ private int mCurrentScreenWidth; | |
+ private int mPreviousOrientation; | |
+ private int mAppMinimumWidth; | |
+ private int mAppMinimumHeight; | |
+ private int mAppFloatViewWidth; | |
+ private int mAppFloatViewHeight; | |
+ private boolean mChangedFlags = false; | |
+ private boolean isAlreadyAttachToWindow = false; | |
+ public boolean mIsFullscreenApp = false; | |
+ private ScaleGestureDetector mScaleGestureDetector; | |
+ private FloatingWindowView mFloatingWindowView; | |
/** Return the intent that started this activity. */ | |
public Intent getIntent() { | |
@@ -939,6 +1001,9 @@ | |
mVoiceInteractor.attachActivity(this); | |
} | |
mCalled = true; | |
+ mPreviousOrientation = getResources().getConfiguration().orientation; | |
+ mScaleGestureDetector = new ScaleGestureDetector(getApplicationContext(), mScaleGestureListener); | |
+ mScaleGestureDetector.setQuickScaleEnabled(false); | |
} | |
/** | |
@@ -977,6 +1042,7 @@ | |
final void performRestoreInstanceState(Bundle savedInstanceState) { | |
onRestoreInstanceState(savedInstanceState); | |
restoreManagedDialogs(savedInstanceState); | |
+ restoreManagedWindowLayout(savedInstanceState); | |
} | |
/** | |
@@ -1085,6 +1151,54 @@ | |
} | |
} | |
+ private void restoreManagedWindowLayout(Bundle savedInstanceState) { | |
+ final Bundle b = savedInstanceState.getBundle(SAVED_WINDOW_LAYOUT_TAG); | |
+ if (b == null) { | |
+ return; | |
+ } | |
+ | |
+ if (mWindow == null) { | |
+ return; | |
+ } | |
+ | |
+ if (!mWindow.mIsFloatingWindow) { | |
+ return; | |
+ } | |
+ | |
+ final int[] ids = b.getIntArray(SAVED_WINDOW_LAYOUT_IDS_KEY); | |
+ final int x = ids[0]; | |
+ final int y = ids[1]; | |
+ final int width = ids[2]; | |
+ final int height = ids[3]; | |
+ mSnap = ids[4]; | |
+ WindowManager.LayoutParams params = mWindow.getAttributes(); | |
+ params.x = x; | |
+ params.y = y; | |
+ switch (mSnap) { | |
+ case SNAP_LEFT: | |
+ params.width = (mCurrentScreenWidth / 2); | |
+ params.height = ViewGroup.LayoutParams.MATCH_PARENT; | |
+ break; | |
+ case SNAP_RIGHT: | |
+ params.width = (mCurrentScreenWidth / 2); | |
+ params.height = ViewGroup.LayoutParams.MATCH_PARENT; | |
+ break; | |
+ case SNAP_TOP: | |
+ params.width = ViewGroup.LayoutParams.MATCH_PARENT; | |
+ params.height = (mCurrentScreenHeight / 2); | |
+ break; | |
+ case SNAP_BOTTOM: | |
+ params.width = ViewGroup.LayoutParams.MATCH_PARENT; | |
+ params.height = (mCurrentScreenHeight / 2); | |
+ break; | |
+ case SNAP_NONE: | |
+ params.width = width; | |
+ params.height = height; | |
+ break; | |
+ } | |
+ mWindow.setAttributes(params); | |
+ } | |
+ | |
private Dialog createDialog(Integer dialogId, Bundle state, Bundle args) { | |
final Dialog dialog = onCreateDialog(dialogId, args); | |
if (dialog == null) { | |
@@ -1156,6 +1270,7 @@ | |
*/ | |
protected void onStart() { | |
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this); | |
+ setupFloatingActionBar(false); | |
mCalled = true; | |
if (!mLoadersStarted) { | |
@@ -1169,6 +1284,87 @@ | |
} | |
getApplication().dispatchActivityStarted(this); | |
+ } | |
+ | |
+ private void setupFloatingActionBar(boolean reload) { | |
+ if (mWindow == null) { | |
+ return; | |
+ } | |
+ | |
+ if (!mWindow.mIsFloatingWindow) { | |
+ return; | |
+ } | |
+ | |
+ if (mWindow.peekDecorView() == null) { | |
+ return; | |
+ } | |
+ | |
+ mWindow.getDecorView().setSystemUiVisibility( | |
+ View.SYSTEM_UI_FLAG_FULLSCREEN | |
+ | View.SYSTEM_UI_FLAG_IMMERSIVE); | |
+ | |
+ FrameLayout decorFloatingView = (FrameLayout) mWindow.peekDecorView().getRootView(); | |
+ if (decorFloatingView == null) { | |
+ return; | |
+ } | |
+ if (!reload) { | |
+ decorFloatingView.setFitsSystemWindows(true); | |
+ mFloatingWindowView = new FloatingWindowView(this, getActionBarHeight(true)); | |
+ decorFloatingView.addView(mFloatingWindowView, -1, FloatingWindowView.getParams()); | |
+ decorFloatingView.setTagInternal(android.R.id.extractArea, mFloatingWindowView); | |
+ changeTitleBarColor(); | |
+ } else { | |
+ mFloatingWindowView = (FloatingWindowView) decorFloatingView.getTag(android.R.id.extractArea); | |
+ changeTitleBarColor(); | |
+ decorFloatingView.bringChildToFront(mFloatingWindowView); | |
+ } | |
+ } | |
+ | |
+ private void changeFloatingWindowColor(int bg_color, int ic_color) { | |
+ mFloatingWindowView.setFloatingBackgroundColor(bg_color); | |
+ mFloatingWindowView.setFloatingColorFilter(ic_color); | |
+ } | |
+ | |
+ | |
+ private void changeTitleBarColor() { | |
+ if (mFloatingWindowView != null) { | |
+ try { | |
+ final PackageManager pm = getPackageManager(); | |
+ // Getting current task packagename | |
+ final ActivityManager am = (ActivityManager) getSystemService(Activity.ACTIVITY_SERVICE); | |
+ List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1); | |
+ ActivityManager.RunningTaskInfo task = tasks.get(0); // current task | |
+ ComponentName rootActivity = task.baseActivity; | |
+ String appPackageName = rootActivity.getPackageName(); | |
+ // Get theme from packagename | |
+ final Resources res = pm.getResourcesForApplication(appPackageName); | |
+ final int[] attrs = new int[] { | |
+ res.getIdentifier("colorPrimary", "attr", appPackageName), | |
+ android.R.attr.colorPrimary | |
+ }; | |
+ | |
+ final Resources.Theme theme = res.newTheme(); | |
+ final ComponentName cn = pm.getLaunchIntentForPackage(appPackageName).getComponent(); | |
+ theme.applyStyle(pm.getActivityInfo(cn, 0).theme, false); | |
+ // Obtain the colorPrimary color from the attrs | |
+ TypedArray a = theme.obtainStyledAttributes(attrs); | |
+ // Do something with the color | |
+ final int colorPrimary = a.getColor(0, a.getColor(1, Color.WHITE)); | |
+ // Make sure you recycle the TypedArray | |
+ a.recycle(); | |
+ a = null; | |
+ int iconTint; | |
+ | |
+ if (ColorUtils.isBrightColor(colorPrimary)) { | |
+ iconTint = Color.BLACK; | |
+ } else { | |
+ iconTint = Color.WHITE; | |
+ } | |
+ changeFloatingWindowColor(colorPrimary, iconTint); | |
+ } catch (final NameNotFoundException e) { | |
+ e.printStackTrace(); | |
+ } | |
+ } | |
} | |
/** | |
@@ -1238,6 +1434,7 @@ | |
final Window win = getWindow(); | |
if (win != null) win.makeActive(); | |
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true); | |
+ setupFloatingActionBar(true); | |
mCalled = true; | |
} | |
@@ -1298,6 +1495,7 @@ | |
onSaveInstanceState(outState); | |
saveManagedDialogs(outState); | |
mActivityTransitionState.saveState(outState); | |
+ saveManagedWindowLayout(outState); | |
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState); | |
} | |
@@ -1424,6 +1622,27 @@ | |
outState.putBundle(SAVED_DIALOGS_TAG, dialogState); | |
} | |
+ private void saveManagedWindowLayout(Bundle outState) { | |
+ if (mWindow == null) { | |
+ return; | |
+ } | |
+ | |
+ if (!mWindow.mIsFloatingWindow) { | |
+ return; | |
+ } | |
+ | |
+ Bundle windowLayoutState = new Bundle(); | |
+ | |
+ int[] ids = new int[5]; | |
+ WindowManager.LayoutParams params = mWindow.getAttributes(); | |
+ ids[0] = params.x; | |
+ ids[1] = params.y; | |
+ ids[2] = params.width; | |
+ ids[3] = params.height; | |
+ ids[4] = mSnap; | |
+ windowLayoutState.putIntArray(SAVED_WINDOW_LAYOUT_IDS_KEY, ids); | |
+ outState.putBundle(SAVED_WINDOW_LAYOUT_TAG, windowLayoutState); | |
+ } | |
/** | |
* Called as part of the activity lifecycle when an activity is going into | |
@@ -1604,8 +1823,8 @@ | |
*/ | |
protected void onDestroy() { | |
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onDestroy " + this); | |
+ sendAppEndBroadcast(); | |
mCalled = true; | |
- | |
// dismiss any dialogs we are managing. | |
if (mManagedDialogs != null) { | |
final int numDialogs = mManagedDialogs.size(); | |
@@ -1686,6 +1905,51 @@ | |
if (mWindow != null) { | |
// Pass the configuration changed event to the window | |
mWindow.onConfigurationChanged(newConfig); | |
+ if (mWindow.mIsFloatingWindow) { | |
+ refreshAppLayoutSize(); | |
+ Configuration config = getResources().getConfiguration(); | |
+ if (config.orientation != mPreviousOrientation) { | |
+ mWindow.setGravity(Gravity.LEFT | Gravity.TOP); | |
+ if (!isUnSnap()) { | |
+ requestChangingFlagsLayout(); | |
+ } | |
+ WindowManager.LayoutParams params = mWindow.getAttributes(); | |
+ switch (mSnap) { | |
+ case SNAP_LEFT: | |
+ params.width = (mCurrentScreenWidth / 2); | |
+ params.height = ViewGroup.LayoutParams.MATCH_PARENT; | |
+ params.x = 0; | |
+ params.y = 0; | |
+ break; | |
+ case SNAP_RIGHT: | |
+ params.width = (mCurrentScreenWidth / 2); | |
+ params.height = ViewGroup.LayoutParams.MATCH_PARENT; | |
+ params.x = (mCurrentScreenWidth / 2); | |
+ params.y = 0; | |
+ break; | |
+ case SNAP_TOP: | |
+ params.width = ViewGroup.LayoutParams.MATCH_PARENT; | |
+ params.height = (mCurrentScreenHeight / 2); | |
+ params.x = 0; | |
+ params.y = 0; | |
+ break; | |
+ case SNAP_BOTTOM: | |
+ params.width = ViewGroup.LayoutParams.MATCH_PARENT; | |
+ params.height = (mCurrentScreenHeight / 2); | |
+ params.x = 0; | |
+ params.y = (mCurrentScreenHeight / 2); | |
+ break; | |
+ case SNAP_NONE: | |
+ int width = params.width; | |
+ int height = params.height; | |
+ params.width = height; | |
+ params.height = width; | |
+ break; | |
+ } | |
+ mWindow.setAttributes(params); | |
+ mPreviousOrientation = config.orientation; | |
+ } | |
+ } | |
} | |
if (mActionBar != null) { | |
@@ -2736,14 +3000,550 @@ | |
* @return boolean Return true if this event was consumed. | |
*/ | |
public boolean dispatchTouchEvent(MotionEvent ev) { | |
- if (ev.getAction() == MotionEvent.ACTION_DOWN) { | |
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) | |
onUserInteraction(); | |
+ if (mWindow.mIsFloatingWindow) { | |
+ if (isUnSnap()) { | |
+ mScaleGestureDetector.onTouchEvent(ev); | |
+ } | |
+ int actionBarHeight = getActionBarHeight(false); | |
+ switch (ev.getAction()) { | |
+ case MotionEvent.ACTION_DOWN: | |
+ setTouchViewDown(ev.getX(), ev.getY()); | |
+ onUserInteraction(); | |
+ updateFocusApp(); | |
+ if (viewY < actionBarHeight) { | |
+ if (!mChangedPreviousRange) { | |
+ setPreviousTouchRange(ev.getRawX(), ev.getRawY()); | |
+ mChangedPreviousRange = true; | |
+ } | |
+ } | |
+ break; | |
+ case MotionEvent.ACTION_MOVE: | |
+ if (viewY < actionBarHeight) { | |
+ changeFlagsLayoutParams(); | |
+ setTouchViewMove(ev.getRawX(), ev.getRawY()); | |
+ if (mRestorePosition && moveRangeAboveLimit(ev)) { | |
+ restoreOldPosition(); | |
+ } | |
+ showSnap((int) ev.getRawX(), (int) ev.getRawY()); | |
+ } | |
+ break; | |
+ case MotionEvent.ACTION_UP: | |
+ if (viewY < actionBarHeight) { | |
+ mChangedFlags = false; | |
+ finishSnap(isValidSnap() && mTimeoutDone); | |
+ discardTimeout(); | |
+ mChangedPreviousRange = false; | |
+ } | |
+ break; | |
+ } | |
+ } else { | |
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) { | |
+ onUserInteraction(); | |
+ } | |
} | |
if (getWindow().superDispatchTouchEvent(ev)) { | |
return true; | |
} | |
return onTouchEvent(ev); | |
} | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public void setTouchViewDown(float x , float y) { | |
+ viewX = x; | |
+ viewY = y; | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public void setPreviousTouchRange(float x , float y) { | |
+ mPreviousRange[0] = x; | |
+ mPreviousRange[1] = y; | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public void setTouchViewMove(float x , float y) { | |
+ screenX = x; | |
+ screenY = y; | |
+ leftFromScreen = (screenX - viewX); | |
+ topFromScreen = (screenY - viewY); | |
+ initLayoutParams(leftFromScreen, topFromScreen); | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public void setChangedPreviousRange(boolean needed) { | |
+ mChangedPreviousRange = needed; | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public boolean getChangedPreviousRange() { | |
+ return mChangedPreviousRange; | |
+ } | |
+ | |
+ private int getActionBarHeight(boolean smaller) { | |
+ ActionBar actionBar = getActionBar(); | |
+ int actionBarHeight = (actionBar != null) ? actionBar.getHeight() | |
+ : getAppDimensionPixel(smaller); | |
+ return (smaller ? getAppDimensionPixel(smaller) : actionBarHeight); | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public void updateFocusApp() { | |
+ IWindowManager wm = (IWindowManager) WindowManagerGlobal.getWindowManagerService(); | |
+ try { | |
+ wm.notifyFloatActivityTouched(mToken, false); | |
+ } catch (RemoteException e) { | |
+ Log.e(TAG, "Cannot notify activity touched", e); | |
+ } | |
+ } | |
+ | |
+ private void initLayoutParams(float x , float y) { | |
+ WindowManager.LayoutParams param = mWindow.getAttributes(); | |
+ param.x = (int) x; | |
+ param.y = (int) y; | |
+ mWindow.setAttributes(param); | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public void changeFlagsLayoutParams() { | |
+ mWindow.setGravity(Gravity.LEFT | Gravity.TOP); | |
+ if (!mChangedFlags) { | |
+ mChangedFlags = true; | |
+ requestChangingFlagsLayout(); | |
+ } | |
+ } | |
+ | |
+ private void requestChangingFlagsLayout() { | |
+ mWindow.setCloseOnTouchOutside(false); | |
+ mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); | |
+ mWindow.addFlags(WindowManager.LayoutParams.FLAG_SPLIT_TOUCH); | |
+ if (ActivityManager.isHighEndGfx()) { | |
+ mWindow.addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); | |
+ } | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public void setChangedFlags(boolean changed) { | |
+ mChangedFlags = changed; | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public boolean moveRangeAboveLimit(MotionEvent event) { | |
+ final float x = event.getRawX(); | |
+ final float y = event.getRawY(); | |
+ | |
+ boolean returnValue = false; | |
+ if (Math.abs(mPreviousRange[0] - x) > MOVE_MAX_RANGE) { | |
+ returnValue = true; | |
+ } else if (Math.abs(mPreviousRange[1] - y) > MOVE_MAX_RANGE) { | |
+ returnValue = true; | |
+ } | |
+ return returnValue; | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public void showSnap(int x, int y) { | |
+ initSnappable(x, y); | |
+ calculateSnap(); | |
+ if (isValidSnap()) { | |
+ setupTimeout(); | |
+ } | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public void finishSnap(boolean apply) { | |
+ if (apply) { | |
+ if (saveOldPosition()) { | |
+ mRestorePosition = true; | |
+ } | |
+ WindowManager.LayoutParams lpp = mWindow.getAttributes(); | |
+ lpp.width = mSnapParam[0]; | |
+ lpp.height = mSnapParam[1]; | |
+ int gravity = mSnapParam[2]; | |
+ lpp.x = (gravity == Gravity.RIGHT) ? (mCurrentScreenWidth / 2) : 0; | |
+ lpp.y = (gravity == Gravity.BOTTOM) ? (mCurrentScreenHeight / 2) : 0; | |
+ mWindow.setAttributes(lpp); | |
+ } else { | |
+ mSnap = SNAP_NONE; | |
+ } | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public boolean isValidSnap() { | |
+ return (mSnapParam[0] != UNKNOWN) && | |
+ (mSnapParam[1] != UNKNOWN) && | |
+ (mSnapParam[2] != UNKNOWN); | |
+ } | |
+ | |
+ private boolean saveOldPosition() { | |
+ if (mRestorePosition) return true; | |
+ if (mSnapped) { | |
+ return (isUnSnap() || mTimeoutRunning); | |
+ } | |
+ mSnapped = true; | |
+ final WindowManager.LayoutParams params = mWindow.getAttributes(); | |
+ int[] layout = { params.x, params.y, params.width, params.height }; | |
+ mOldLayout = layout; | |
+ return true; | |
+ } | |
+ | |
+ private boolean isUnSnap() { | |
+ return (mSnap == SNAP_NONE); | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public boolean restoreOldPosition() { | |
+ if (!mSnapped) return false; | |
+ restoreOldPositionWithoutRefresh(); | |
+ return true; | |
+ } | |
+ | |
+ private void restoreOldPositionWithoutRefresh() { | |
+ if (!mSnapped) return; | |
+ WindowManager.LayoutParams params = mWindow.getAttributes(); | |
+ params.x = mOldLayout[0]; | |
+ params.y = mOldLayout[1]; | |
+ params.width = mOldLayout[2]; | |
+ params.height = mOldLayout[3]; | |
+ mWindow.setAttributes(params); | |
+ mSnapped = false; | |
+ mRestorePosition = false; | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public boolean getRestorePosition() { | |
+ return mRestorePosition; | |
+ } | |
+ | |
+ private boolean initSnappable(int x, int y) { | |
+ if ((Math.abs(mOldParam[0] - x) > mSensitivity) || | |
+ (Math.abs(mOldParam[1] - y) > mSensitivity)) { | |
+ mOldParam[0] = x; | |
+ mOldParam[1] = y; | |
+ return false; | |
+ } | |
+ mOldParam[0] = x; | |
+ mOldParam[1] = y; | |
+ | |
+ if (x < mRange) { | |
+ mSnap = SNAP_LEFT; | |
+ } else if (x > (mCurrentScreenWidth - mRange)) { | |
+ mSnap = SNAP_RIGHT; | |
+ } else if (y < mRange) { | |
+ mSnap = SNAP_TOP; | |
+ } else if (y > (mCurrentScreenHeight - mRange)) { | |
+ mSnap = SNAP_BOTTOM; | |
+ } else { | |
+ mSnap = SNAP_NONE; | |
+ return false; | |
+ } | |
+ return true; | |
+ } | |
+ | |
+ private void refreshAppLayoutSize() { | |
+ final IWindowManager wm = (IWindowManager) WindowManagerGlobal.getWindowManagerService(); | |
+ | |
+ try { | |
+ Rect windowFloatViewBounds = wm.getFloatViewRect(); | |
+ mAppFloatViewWidth = (windowFloatViewBounds.right - windowFloatViewBounds.left); | |
+ mAppFloatViewHeight = (windowFloatViewBounds.bottom - windowFloatViewBounds.top); | |
+ | |
+ Rect windowCurrentScreenBounds = wm.getAppFullscreenViewRect(); | |
+ mCurrentScreenWidth = (windowCurrentScreenBounds.right - windowCurrentScreenBounds.left); | |
+ mCurrentScreenHeight = (windowCurrentScreenBounds.bottom - windowCurrentScreenBounds.top); | |
+ | |
+ Rect windowMinimumBounds = wm.getAppMinimumViewRect(); | |
+ mAppMinimumWidth = (windowMinimumBounds.right - windowMinimumBounds.left); | |
+ mAppMinimumHeight = (windowMinimumBounds.bottom - windowMinimumBounds.top); | |
+ | |
+ } catch (RemoteException e) { | |
+ Log.e(TAG, "Could not perform get size view layout", e); | |
+ } | |
+ } | |
+ | |
+ private void refreshAppFloatViewSize() { | |
+ final IWindowManager wm = (IWindowManager) WindowManagerGlobal.getWindowManagerService(); | |
+ | |
+ try { | |
+ Rect windowBounds = wm.getFloatViewRect(); | |
+ mAppFloatViewWidth = (windowBounds.right - windowBounds.left); | |
+ mAppFloatViewHeight = (windowBounds.bottom - windowBounds.top); | |
+ } catch (RemoteException e) { | |
+ Log.e(TAG, "Could not perform float view layout", e); | |
+ } | |
+ } | |
+ | |
+ private void refreshCurrentScreenSize() { | |
+ final IWindowManager wm = (IWindowManager) WindowManagerGlobal.getWindowManagerService(); | |
+ | |
+ try { | |
+ Rect windowBounds = wm.getAppFullscreenViewRect(); | |
+ mCurrentScreenWidth = (windowBounds.right - windowBounds.left); | |
+ mCurrentScreenHeight = (windowBounds.bottom - windowBounds.top); | |
+ } catch (RemoteException e) { | |
+ Log.e(TAG, "Could not perform get app fullscreen view layout", e); | |
+ } | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public void forceSnap(int side) { | |
+ if (side == SNAP_NONE) { | |
+ restoreOldPosition(); | |
+ return; | |
+ } | |
+ if (mSnapped) { | |
+ restoreOldPositionWithoutRefresh(); | |
+ } | |
+ mSnap = side; | |
+ calculateSnap(); | |
+ finishSnap(true); | |
+ } | |
+ | |
+ private void calculateSnap() { | |
+ switch (mSnap) { | |
+ case SNAP_LEFT: | |
+ mSnapParam[0] = (mCurrentScreenWidth / 2); | |
+ mSnapParam[1] = ViewGroup.LayoutParams.MATCH_PARENT; | |
+ mSnapParam[2] = Gravity.TOP | Gravity.LEFT; | |
+ break; | |
+ case SNAP_RIGHT: | |
+ mSnapParam[0] = (mCurrentScreenWidth / 2); | |
+ mSnapParam[1] = ViewGroup.LayoutParams.MATCH_PARENT; | |
+ mSnapParam[2] = Gravity.RIGHT; | |
+ break; | |
+ case SNAP_TOP: | |
+ mSnapParam[0] = ViewGroup.LayoutParams.MATCH_PARENT; | |
+ mSnapParam[1] = (mCurrentScreenHeight / 2); | |
+ mSnapParam[2] = Gravity.TOP; | |
+ break; | |
+ case SNAP_BOTTOM: | |
+ mSnapParam[0] = ViewGroup.LayoutParams.MATCH_PARENT; | |
+ mSnapParam[1] = (mCurrentScreenHeight / 2); | |
+ mSnapParam[2] = Gravity.BOTTOM; | |
+ break; | |
+ case SNAP_NONE: | |
+ mSnapParam[0] = UNKNOWN; | |
+ mSnapParam[1] = UNKNOWN; | |
+ mSnapParam[2] = UNKNOWN; | |
+ break; | |
+ } | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public boolean getTimeoutDone() { | |
+ return mTimeoutDone; | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public void discardTimeout() { | |
+ mTimeoutDone = false; | |
+ mTimeoutRunning = false; | |
+ mHandler.removeCallbacks(mRunnable); | |
+ } | |
+ | |
+ private void setupTimeout() { | |
+ if (mTimeoutRunning) return; | |
+ if (mRunnable == null) { | |
+ mRunnable = new Runnable() { | |
+ @Override | |
+ public void run() { | |
+ mHandler.postDelayed(new Runnable() { | |
+ @Override | |
+ public void run() { | |
+ mTimeoutRunning = false; | |
+ mTimeoutDone = true; | |
+ } | |
+ }, 250); | |
+ } | |
+ }; | |
+ } | |
+ mTimeoutRunning = true; | |
+ mHandler.postDelayed(mRunnable, 750); | |
+ } | |
+ | |
+ private int getAppDimensionPixel(boolean smaller) { | |
+ float scale = getResources().getDisplayMetrics().density; | |
+ int sized = smaller ? 32 : 48; | |
+ int pixel = (int) (sized * scale + 0.5f); | |
+ return pixel; | |
+ } | |
+ | |
+ private void refreshAppMinimumSize() { | |
+ final IWindowManager wm = (IWindowManager) WindowManagerGlobal.getWindowManagerService(); | |
+ | |
+ try { | |
+ Rect windowBounds = wm.getAppMinimumViewRect(); | |
+ mAppMinimumWidth = (windowBounds.right - windowBounds.left); | |
+ mAppMinimumHeight = (windowBounds.bottom - windowBounds.top); | |
+ } catch (RemoteException e) { | |
+ Log.e(TAG, "Could not perform get app minimum view layout", e); | |
+ } | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public void sendAppLaunchBroadcast() { | |
+ Intent appIntent = new Intent(Intent.ACTION_ACTIVITY_LAUNCH_DETECTOR); | |
+ appIntent.putExtra("packagename", getPackageName()); | |
+ appIntent.putExtra("packagetoken", mToken); | |
+ appIntent.addFlags( | |
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); | |
+ sendBroadcastAsUser(appIntent, UserHandle.CURRENT_OR_SELF); | |
+ moveTaskToBack(true); | |
+ } | |
+ | |
+ private void sendAppEndBroadcast() { | |
+ Intent endIntent = new Intent(Intent.ACTION_ACTIVITY_END_DETECTOR); | |
+ endIntent.putExtra("packagename", getPackageName()); | |
+ endIntent.addFlags( | |
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); | |
+ sendBroadcastAsUser(endIntent, UserHandle.CURRENT_OR_SELF); | |
+ } | |
+ | |
+ private void setProgressLayoutApp() { | |
+ if (mLayoutRunnable == null) { | |
+ mLayoutRunnable = new Runnable() { | |
+ @Override | |
+ public void run() { | |
+ setCurrentLayoutApp(); | |
+ } | |
+ }; | |
+ } | |
+ mHandler.postDelayed(mLayoutRunnable, 500); | |
+ } | |
+ | |
+ private void discardLayoutProgress() { | |
+ mHandler.removeCallbacks(mLayoutRunnable); | |
+ } | |
+ | |
+ private void setCurrentLayoutApp() { | |
+ if (isUnSnap()) { | |
+ final WindowManager.LayoutParams params = mWindow.getAttributes(); | |
+ final int width = params.width; | |
+ final int height = params.height; | |
+ if (width >= mCurrentScreenWidth) { | |
+ params.width = ViewGroup.LayoutParams.MATCH_PARENT; | |
+ } else if (width <= mAppMinimumWidth) { | |
+ params.width = mAppMinimumWidth; | |
+ } | |
+ if (height >= mCurrentScreenHeight) { | |
+ params.height = ViewGroup.LayoutParams.MATCH_PARENT; | |
+ } else if (height <= mAppMinimumHeight) { | |
+ params.height = mAppMinimumHeight; | |
+ } | |
+ mWindow.setAttributes(params); | |
+ } | |
+ mHandler.removeCallbacks(mLayoutRunnable); | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public void setFullscreenApp() { | |
+ if (mIsFullscreenApp) { | |
+ return; | |
+ } | |
+ WindowManager.LayoutParams params = mWindow.getAttributes(); | |
+ int[] layout = { params.x, params.y, params.width, params.height }; | |
+ mLastLayout = layout; | |
+ params.width = ViewGroup.LayoutParams.MATCH_PARENT; | |
+ params.height = ViewGroup.LayoutParams.MATCH_PARENT; | |
+ mWindow.getDecorView().setSystemUiVisibility( | |
+ View.SYSTEM_UI_FLAG_FULLSCREEN | |
+ | View.SYSTEM_UI_FLAG_IMMERSIVE); | |
+ mWindow.setAttributes(params); | |
+ mIsFullscreenApp = true; | |
+ } | |
+ | |
+ /** | |
+ * @hide | |
+ */ | |
+ public void restorePreviousLayoutApp() { | |
+ if (!mIsFullscreenApp) { | |
+ return; | |
+ } | |
+ WindowManager.LayoutParams params = mWindow.getAttributes(); | |
+ params.x = mLastLayout[0]; | |
+ params.y = mLastLayout[1]; | |
+ params.width = mLastLayout[2]; | |
+ params.height = mLastLayout[3]; | |
+ mWindow.getDecorView().setSystemUiVisibility( | |
+ View.SYSTEM_UI_FLAG_FULLSCREEN | |
+ | View.SYSTEM_UI_FLAG_IMMERSIVE); | |
+ mWindow.setAttributes(params); | |
+ mIsFullscreenApp = false; | |
+ } | |
+ | |
+ private final ScaleGestureDetector.OnScaleGestureListener mScaleGestureListener | |
+ = new ScaleGestureDetector.SimpleOnScaleGestureListener() { | |
+ | |
+ private float lastSpanX; | |
+ private float lastSpanY; | |
+ private WindowManager.LayoutParams params; | |
+ | |
+ @Override | |
+ public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) { | |
+ discardLayoutProgress(); | |
+ lastSpanX = scaleGestureDetector.getCurrentSpanX(); | |
+ lastSpanY = scaleGestureDetector.getCurrentSpanY(); | |
+ params = mWindow.getAttributes(); | |
+ return true; | |
+ } | |
+ | |
+ @Override | |
+ public boolean onScale(ScaleGestureDetector scaleGestureDetector) { | |
+ float spanX = scaleGestureDetector.getCurrentSpanX(); | |
+ float spanY = scaleGestureDetector.getCurrentSpanY(); | |
+ float newWidth = spanX / lastSpanX * (float) params.width; | |
+ float newHeight = spanY / lastSpanY * (float) params.height; | |
+ params.width = (int) newWidth; | |
+ params.height = (int) newHeight; | |
+ mWindow.setAttributes(params); | |
+ lastSpanX = spanX; | |
+ lastSpanY = spanY; | |
+ return true; | |
+ } | |
+ | |
+ @Override | |
+ public void onScaleEnd(ScaleGestureDetector detector) { | |
+ setProgressLayoutApp(); | |
+ } | |
+ }; | |
/** | |
* Called to process trackball events. You can override this to | |
@@ -4651,6 +5451,11 @@ | |
} | |
} | |
+ public void finishFloating() { | |
+ getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); | |
+ mMainThread.performFinishFloating(); | |
+ } | |
+ | |
/** | |
* Call this when your activity is done and should be closed. The | |
* ActivityResult is propagated back to whoever launched you via | |
@@ -5929,7 +6734,10 @@ | |
mFragments.attachActivity(this, mContainer, null); | |
- mWindow = PolicyManager.makeNewWindow(this); | |
+ if (makeNewWindow(context, intent, info)) { | |
+ parent = null; | |
+ } | |
+ | |
mWindow.setCallback(this); | |
mWindow.setOnWindowDismissedCallback(this); | |
mWindow.getLayoutInflater().setPrivateFactory(this); | |
@@ -5974,6 +6782,64 @@ | |
mCurrentConfig = config; | |
} | |
+ private boolean makeNewWindow(Context context, Intent intent, ActivityInfo info) { | |
+ boolean floating = (intent.getFlags() & Intent.FLAG_FLOATING_WINDOW) == Intent.FLAG_FLOATING_WINDOW; | |
+ if (intent != null && floating) { | |
+ | |
+ TypedArray styleArray = context.obtainStyledAttributes(info.theme, com.android.internal.R.styleable.Window); | |
+ TypedValue backgroundValue = styleArray.peekValue(com.android.internal.R.styleable.Window_windowBackground); | |
+ | |
+ // Apps that have no title don't need no title bar | |
+ TypedValue outValue = new TypedValue(); | |
+ boolean result = styleArray.getValue(com.android.internal.R.styleable.Window_windowNoTitle, outValue); | |
+ | |
+ if (backgroundValue != null && backgroundValue.toString().contains("light")) { | |
+ context.getTheme().applyStyle(com.android.internal.R.style.Theme_DeviceDefault_FloatingWindowLight, true); | |
+ } else { | |
+ context.getTheme().applyStyle(com.android.internal.R.style.Theme_DeviceDefault_FloatingWindow, true); | |
+ } | |
+ | |
+ // Create our new window | |
+ mWindow = PolicyManager.makeNewWindow(this); | |
+ mWindow.mIsFloatingWindow = true; | |
+ if (!isAlreadyAttachToWindow) { | |
+ isAlreadyAttachToWindow = true; | |
+ mWindow.setCloseOnTouchOutsideIfNotSet(true); | |
+ mWindow.setGravity(Gravity.CENTER); | |
+ // Scale it | |
+ scaleFloatingWindow(); | |
+ } | |
+ | |
+ WindowManager.LayoutParams params = mWindow.getAttributes(); | |
+ params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; | |
+ if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID) { | |
+ mWindow.setFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND, | |
+ WindowManager.LayoutParams.FLAG_DIM_BEHIND); | |
+ params.alpha = 1f; | |
+ params.dimAmount = 0.25f; | |
+ } | |
+ mWindow.setAttributes(params); | |
+ | |
+ refreshAppLayoutSize(); | |
+ return true; | |
+ } else { | |
+ mWindow = PolicyManager.makeNewWindow(this); | |
+ return false; | |
+ } | |
+ } | |
+ | |
+ private void scaleFloatingWindow() { | |
+ final IWindowManager wm = (IWindowManager) WindowManagerGlobal.getWindowManagerService(); | |
+ | |
+ try { | |
+ Rect windowBounds = wm.getFloatViewRect(); | |
+ mWindow.setLayout(windowBounds.right - windowBounds.left, | |
+ windowBounds.bottom - windowBounds.top); | |
+ } catch (RemoteException e) { | |
+ Log.e(TAG, "Could not perform float view layout", e); | |
+ } | |
+ } | |
+ | |
/** @hide */ | |
public final IBinder getActivityToken() { | |
return mParent != null ? mParent.getActivityToken() : mToken; | |
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java | |
index d2cc60c..637de6f 100644 | |
--- a/core/java/android/app/ActivityManagerNative.java | |
+++ b/core/java/android/app/ActivityManagerNative.java | |
@@ -803,6 +803,16 @@ | |
return true; | |
} | |
+ case GET_ACTIVITY_FOR_TASK_TRANSACTION: { | |
+ data.enforceInterface(IActivityManager.descriptor); | |
+ int task = data.readInt(); | |
+ boolean onlyRoot = data.readInt() != 0; | |
+ IBinder res = getActivityForTask(task, onlyRoot); | |
+ reply.writeNoException(); | |
+ reply.writeStrongBinder(res); | |
+ return true; | |
+ } | |
+ | |
case GET_CONTENT_PROVIDER_TRANSACTION: { | |
data.enforceInterface(IActivityManager.descriptor); | |
IBinder b = data.readStrongBinder(); | |
@@ -3323,6 +3333,21 @@ | |
data.recycle(); | |
reply.recycle(); | |
} | |
+ | |
+ public IBinder getActivityForTask(int task, boolean onlyRoot) throws RemoteException | |
+ { | |
+ Parcel data = Parcel.obtain(); | |
+ Parcel reply = Parcel.obtain(); | |
+ data.writeInterfaceToken(IActivityManager.descriptor); | |
+ data.writeInt(task); | |
+ data.writeInt(onlyRoot ? 1 : 0); | |
+ mRemote.transact(GET_ACTIVITY_FOR_TASK_TRANSACTION, data, reply, 0); | |
+ reply.readException(); | |
+ IBinder res = reply.readStrongBinder(); | |
+ data.recycle(); | |
+ reply.recycle(); | |
+ return res; | |
+ } | |
public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException | |
{ | |
Parcel data = Parcel.obtain(); | |
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java | |
index 67f6d7e..971f069 100644 | |
--- a/core/java/android/app/ActivityThread.java | |
+++ b/core/java/android/app/ActivityThread.java | |
@@ -2184,6 +2184,18 @@ | |
return mActivities.get(token).activity; | |
} | |
+ protected void performFinishFloating() { | |
+ synchronized (mPackages) { | |
+ Activity a = null; | |
+ for (ActivityClientRecord ar : mActivities.values()) { | |
+ a = ar.activity; | |
+ if (a != null && !a.mFinished && a.getWindow() != null && a.getWindow().mIsFloatingWindow) { | |
+ a.finish(); | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
public final void sendActivityResult( | |
IBinder token, String id, int requestCode, | |
int resultCode, Intent data) { | |
@@ -3013,12 +3025,12 @@ | |
r.state = null; | |
r.persistentState = null; | |
} catch (Exception e) { | |
- if (!mInstrumentation.onException(r.activity, e)) { | |
+ /*if (!mInstrumentation.onException(r.activity, e)) { | |
throw new RuntimeException( | |
"Unable to resume activity " | |
+ r.intent.getComponent().toShortString() | |
+ ": " + e.toString(), e); | |
- } | |
+ }*/ | |
} | |
} | |
return r; | |
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java | |
index 9d15783..75943c2 100644 | |
--- a/core/java/android/app/IActivityManager.java | |
+++ b/core/java/android/app/IActivityManager.java | |
@@ -143,6 +143,7 @@ | |
public void setFocusedStack(int stackId) throws RemoteException; | |
public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException; | |
public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException; | |
+ public IBinder getActivityForTask(int task, boolean onlyRoot) throws RemoteException; | |
public ContentProviderHolder getContentProvider(IApplicationThread caller, | |
String name, int userId, boolean stable) throws RemoteException; | |
public ContentProviderHolder getContentProviderExternal(String name, int userId, IBinder token) | |
@@ -757,8 +758,8 @@ | |
int GET_HOME_ACTIVITY_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+183; | |
int GET_ACTIVITY_DISPLAY_ID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+184; | |
int DELETE_ACTIVITY_CONTAINER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+185; | |
- | |
- | |
+ /* FLOAT VIEW */ | |
+ int GET_ACTIVITY_FOR_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+200; | |
// Start of L transactions | |
int GET_TAG_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+210; | |
int START_USER_IN_BACKGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+211; | |
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java | |
index ad2b61f..c7966a3 100644 | |
--- a/core/java/android/app/Instrumentation.java | |
+++ b/core/java/android/app/Instrumentation.java | |
@@ -1546,10 +1546,18 @@ | |
} | |
} | |
try { | |
+ // we must resolve if the last intent in the stack is floating to give the flag to the previous | |
+ boolean floating = false; | |
+ if (intents.length > 0) { | |
+ floating = (intents[intents.length - 1].getFlags()&Intent.FLAG_FLOATING_WINDOW) == Intent.FLAG_FLOATING_WINDOW; | |
+ } | |
String[] resolvedTypes = new String[intents.length]; | |
for (int i=0; i<intents.length; i++) { | |
intents[i].migrateExtraStreamToClipData(); | |
intents[i].prepareToLeaveProcess(); | |
+ if (floating) { | |
+ intents[i].addFlags(Intent.FLAG_FLOATING_WINDOW); | |
+ } | |
resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver()); | |
} | |
int result = ActivityManagerNative.getDefault() | |
diff --git a/core/java/android/app/TaskStackBuilder.java b/core/java/android/app/TaskStackBuilder.java | |
index 0077db1..d30872e 100644 | |
--- a/core/java/android/app/TaskStackBuilder.java | |
+++ b/core/java/android/app/TaskStackBuilder.java | |
@@ -63,6 +63,7 @@ | |
private final ArrayList<Intent> mIntents = new ArrayList<Intent>(); | |
private final Context mSourceContext; | |
+ private boolean mFirstTaskOnHome = true; | |
private TaskStackBuilder(Context a) { | |
mSourceContext = a; | |
@@ -77,6 +78,10 @@ | |
*/ | |
public static TaskStackBuilder create(Context context) { | |
return new TaskStackBuilder(context); | |
+ } | |
+ | |
+ public void setTaskOnHome(boolean firstTaskOnHome) { | |
+ mFirstTaskOnHome = firstTaskOnHome; | |
} | |
/** | |
@@ -301,9 +306,16 @@ | |
Intent[] intents = new Intent[mIntents.size()]; | |
if (intents.length == 0) return intents; | |
- intents[0] = new Intent(mIntents.get(0)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | | |
- Intent.FLAG_ACTIVITY_CLEAR_TASK | | |
- Intent.FLAG_ACTIVITY_TASK_ON_HOME); | |
+ Intent newIntent = new Intent(mIntents.get(0)); | |
+ newIntent.addFlags( | |
+ Intent.FLAG_ACTIVITY_NEW_TASK | | |
+ Intent.FLAG_ACTIVITY_CLEAR_TASK); | |
+ | |
+ if (mFirstTaskOnHome) { | |
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME); | |
+ } | |
+ | |
+ intents[0] = newIntent; | |
for (int i = 1; i < intents.length; i++) { | |
intents[i] = new Intent(mIntents.get(i)); | |
} | |
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java | |
index 0b8f76c..35a5a13 100644 | |
--- a/core/java/android/content/Intent.java | |
+++ b/core/java/android/content/Intent.java | |
@@ -696,6 +696,20 @@ | |
public static final String ACTION_PICK = "android.intent.action.PICK"; | |
/** | |
+ * Registered and foreground services only | |
+ * @hide | |
+ */ | |
+ public static final String ACTION_ACTIVITY_LAUNCH_DETECTOR = | |
+ "android.intent.action.ACTIVITY_LAUNCH_DETECTOR"; | |
+ | |
+ /** | |
+ * Registered and foreground services only | |
+ * @hide | |
+ */ | |
+ public static final String ACTION_ACTIVITY_END_DETECTOR = | |
+ "android.intent.action.ACTIVITY_END_DETECTOR"; | |
+ | |
+ /** | |
* Activity Action: Creates a shortcut. | |
* <p>Input: Nothing.</p> | |
* <p>Output: An Intent representing the shortcut. The intent must contain three | |
@@ -3995,7 +4009,19 @@ | |
* Activity.finishAndRemoveTask()}. | |
*/ | |
public static final int FLAG_ACTIVITY_RETAIN_IN_RECENTS = 0x00002000; | |
- | |
+ /** | |
+ * If set, this intent will always match start up as a floating window | |
+ * in multi window scenarios. | |
+ * @hide | |
+ */ | |
+ public static final int FLAG_FLOATING_WINDOW = 0x00003000; | |
+ /** | |
+ * If set in an Intent passed to {@link Context#startActivity Context.startActivity()}, | |
+ * this flag will cause a newly launching task to be resized according to the split | |
+ * view metrics, making it running alongside another app. | |
+ * @hide | |
+ */ | |
+ public static final int FLAG_ACTIVITY_SPLIT_VIEW = 0x00001000; | |
/** | |
* If set, when sending a broadcast only registered receivers will be | |
* called -- no BroadcastReceiver components will be launched. | |
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java | |
index 5e441a5..65173e5 100644 | |
--- a/core/java/android/provider/Settings.java | |
+++ b/core/java/android/provider/Settings.java | |
@@ -6143,7 +6143,6 @@ | |
UI_NIGHT_MODE, | |
SLEEP_TIMEOUT, | |
PRIVACY_GUARD_DEFAULT, | |
- ADVANCED_REBOOT, | |
DEVELOPMENT_SHORTCUT | |
}; | |
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java | |
index b84c3aa..19c66b2 100644 | |
--- a/core/java/android/text/Layout.java | |
+++ b/core/java/android/text/Layout.java | |
@@ -135,8 +135,9 @@ | |
int width, Alignment align, TextDirectionHeuristic textDir, | |
float spacingMult, float spacingAdd) { | |
- if (width < 0) | |
- throw new IllegalArgumentException("Layout: " + width + " < 0"); | |
+ if (width < 0) { | |
+ width = 0; | |
+ } | |
// Ensure paint doesn't have baselineShift set. | |
// While normally we don't modify the paint the user passed in, | |
@@ -165,7 +166,7 @@ | |
int width, Alignment align, | |
float spacingmult, float spacingadd) { | |
if (width < 0) { | |
- throw new IllegalArgumentException("Layout: " + width + " < 0"); | |
+ width = 0; | |
} | |
mText = text; | |
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl | |
index 6877665..6dd5633 100644 | |
--- a/core/java/android/view/IWindowManager.aidl | |
+++ b/core/java/android/view/IWindowManager.aidl | |
@@ -279,4 +279,10 @@ | |
* @return The frame statistics or null if the window does not exist. | |
*/ | |
WindowContentFrameStats getWindowContentFrameStats(IBinder token); | |
+ | |
+ /** FLOAT VIEW **/ | |
+ Rect getAppFullscreenViewRect(); | |
+ Rect getAppMinimumViewRect(); | |
+ Rect getFloatViewRect(); | |
+ void notifyFloatActivityTouched(IBinder token, boolean force); | |
} | |
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java | |
index 4fdbe29..1ca6b3a 100644 | |
--- a/core/java/android/view/View.java | |
+++ b/core/java/android/view/View.java | |
@@ -19876,7 +19876,9 @@ | |
/** @hide */ | |
public void hackTurnOffWindowResizeAnim(boolean off) { | |
- mAttachInfo.mTurnOffWindowResizeAnim = off; | |
+ if (mAttachInfo != null) { | |
+ mAttachInfo.mTurnOffWindowResizeAnim = off; | |
+ } | |
} | |
/** | |
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java | |
index 47c7202..c5d4323 100644 | |
--- a/core/java/android/view/Window.java | |
+++ b/core/java/android/view/Window.java | |
@@ -202,6 +202,8 @@ | |
private boolean mDestroyed; | |
+ public boolean mIsFloatingWindow = false; | |
+ | |
// The current window attributes. | |
private final WindowManager.LayoutParams mWindowAttributes = | |
new WindowManager.LayoutParams(); | |
diff --git a/core/java/com/android/internal/util/paranoid/ColorUtils.java b/core/java/com/android/internal/util/paranoid/ColorUtils.java | |
new file mode 100644 | |
index 0000000..d2c3b0e | |
--- /dev/null | |
+++ b/core/java/com/android/internal/util/paranoid/ColorUtils.java | |
@@ -0,0 +1,404 @@ | |
+/* | |
+* Copyright (C) 2014 The OmniROM 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.util.paranoid; | |
+ | |
+import android.content.Context; | |
+import android.graphics.Bitmap; | |
+import android.graphics.Bitmap.Config; | |
+import android.graphics.Canvas; | |
+import android.graphics.Color; | |
+import android.graphics.drawable.BitmapDrawable; | |
+import android.graphics.drawable.ColorDrawable; | |
+import android.graphics.drawable.Drawable; | |
+import android.graphics.drawable.GradientDrawable; | |
+import android.graphics.drawable.GradientDrawable.Orientation; | |
+import android.renderscript.Allocation; | |
+import android.renderscript.Allocation.MipmapControl; | |
+import android.renderscript.Element; | |
+import android.renderscript.RenderScript; | |
+import android.renderscript.ScriptIntrinsicBlur; | |
+ | |
+import java.util.ArrayList; | |
+import java.util.HashMap; | |
+import java.util.Map; | |
+import java.util.List; | |
+ | |
+public class ColorUtils { | |
+ | |
+ public static Drawable getGradientDrawable(boolean isNav, int color) { | |
+ int color2 = Color.argb(0, Color.red(color), Color.green(color), Color.blue(color)); | |
+ GradientDrawable drawable = new GradientDrawable( | |
+ (isNav ? Orientation.BOTTOM_TOP : Orientation.TOP_BOTTOM), | |
+ new int[]{color, color2}); | |
+ if (isBrightColor(color)) { | |
+ color = isNav ? Color.BLACK : Color.WHITE; | |
+ } else { | |
+ color = isNav ? Color.WHITE : Color.BLACK; | |
+ } | |
+ drawable.setDither(true); | |
+ drawable.setStroke(1, color); | |
+ return drawable; | |
+ } | |
+ | |
+ public static int darken(final int color, float fraction) { | |
+ return blendColors(Color.BLACK, color, fraction); | |
+ } | |
+ | |
+ public static int lighten(final int color, float fraction) { | |
+ return blendColors(Color.WHITE, color, fraction); | |
+ } | |
+ | |
+ public static int blendColors(int color1, int color2, float ratio) { | |
+ final float inverseRatio = 1f - ratio; | |
+ float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRatio); | |
+ float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRatio); | |
+ float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRatio); | |
+ return Color.rgb((int) r, (int) g, (int) b); | |
+ } | |
+ | |
+ public static int opposeColor(int ColorToInvert) { | |
+ if (ColorToInvert == -3) { | |
+ return ColorToInvert; | |
+ } | |
+ int RGBMAX = 255; | |
+ float[] hsv = new float[3]; | |
+ float H; | |
+ Color.RGBToHSV(Color.red(ColorToInvert), | |
+ RGBMAX - Color.green(ColorToInvert), | |
+ Color.blue(ColorToInvert), hsv); | |
+ H = (float) (hsv[0] + 0.5); | |
+ if (H > 1) H -= 1; | |
+ return Color.HSVToColor(hsv); | |
+ } | |
+ | |
+ public static int changeColorTransparency(int colorToChange, int reduce) { | |
+ if (colorToChange == -3) { | |
+ return colorToChange; | |
+ } | |
+ int nots = 255 / 100; | |
+ int red = Color.red(colorToChange); | |
+ int blue = Color.blue(colorToChange); | |
+ int green = Color.green(colorToChange); | |
+ int alpha = nots * reduce; | |
+ return Color.argb(alpha, red, green, blue); | |
+ } | |
+ | |
+ public static boolean isColorTransparency(int color) { | |
+ if (color == -3) { | |
+ return false; | |
+ } | |
+ int nots = 255 / 100; | |
+ int alpha = Color.alpha(color) / nots; | |
+ return (alpha < 100); | |
+ } | |
+ | |
+ public static boolean isBrightColor(int color) { | |
+ if (color == -3) { | |
+ return false; | |
+ } else if (color == Color.TRANSPARENT) { | |
+ return false; | |
+ } else if (color == Color.WHITE) { | |
+ return true; | |
+ } | |
+ int[] rgb = { Color.red(color), Color.green(color), Color.blue(color) }; | |
+ int brightness = (int) Math.sqrt(rgb[0] * rgb[0] * .241 + rgb[1] | |
+ * rgb[1] * .691 + rgb[2] * rgb[2] * .068); | |
+ if (brightness >= 170) { | |
+ return true; | |
+ } | |
+ return false; | |
+ } | |
+ | |
+ public static int getMainColorFromBitmap(Bitmap bitmap, int x, int y) { | |
+ if (bitmap == null) { | |
+ return -3; | |
+ } | |
+ int pixel = bitmap.getPixel(x, y); | |
+ int red = Color.red(pixel); | |
+ int blue = Color.blue(pixel); | |
+ int green = Color.green(pixel); | |
+ int alpha = Color.alpha(pixel); | |
+ return Color.argb(alpha, red, green, blue); | |
+ } | |
+ | |
+ public static boolean getIconWhiteBlackTransparent(Drawable drawable) { | |
+ int color = getDominantColor(drawable); | |
+ if (color == Color.WHITE) { | |
+ return true; | |
+ } else if (color == Color.BLACK) { | |
+ return true; | |
+ } else if (color == Color.TRANSPARENT) { | |
+ return true; | |
+ } else if (color == -3) { | |
+ return true; | |
+ } | |
+ return false; | |
+ } | |
+ | |
+ public static int getIconColorFromDrawable(Drawable drawable) { | |
+ if (drawable == null) { | |
+ return -3; | |
+ } | |
+ if (drawable.getConstantState() == null) { | |
+ return -3; | |
+ } | |
+ Drawable copyDrawable = drawable.getConstantState().newDrawable(); | |
+ if (copyDrawable == null) { | |
+ return -3; | |
+ } | |
+ if (copyDrawable instanceof ColorDrawable) { | |
+ return ((ColorDrawable) drawable).getColor(); | |
+ } | |
+ Bitmap bitmap = drawableToBitmap(copyDrawable); | |
+ if (bitmap == null) { | |
+ return -3; | |
+ } | |
+ return getDominantColor(bitmap); | |
+ } | |
+ | |
+ public static int getAverageColor(Drawable image) { | |
+ int hSamples = 20; | |
+ int vSamples = 20; | |
+ int sampleSize = hSamples * vSamples; | |
+ float[] sampleTotals = {0, 0, 0}; | |
+ float minimumSaturation = 0.1f; | |
+ int minimumAlpha = 200; | |
+ Bitmap b = drawableToBitmap(image); | |
+ if (b == null) { | |
+ return -3; | |
+ } | |
+ int width = b.getWidth(); | |
+ int height = b.getHeight(); | |
+ float[] hsv = new float[3]; | |
+ int sample; | |
+ for (int i = 0; i < width; i += (width / hSamples)) { | |
+ for (int j = 0; j < height; j += (height / vSamples)) { | |
+ sample = b.getPixel(i, j); | |
+ Color.colorToHSV(sample, hsv); | |
+ if ((Color.alpha(sample) > minimumAlpha) && (hsv[1] >= minimumSaturation)) { | |
+ sampleTotals[0] += hsv[0]; | |
+ sampleTotals[1] += hsv[1]; | |
+ sampleTotals[2] += hsv[2]; | |
+ } | |
+ } | |
+ } | |
+ | |
+ float[] average = new float[3]; | |
+ average[0] = sampleTotals[0] / sampleSize; | |
+ average[1] = sampleTotals[1] / sampleSize; | |
+ average[2] = 0.8f; | |
+ | |
+ return Color.HSVToColor(average); | |
+ } | |
+ | |
+ public static int getDominantExampleColor(Bitmap bitmap) { | |
+ if (bitmap == null) { | |
+ return -3; | |
+ } | |
+ | |
+ int width = bitmap.getWidth(); | |
+ int height = bitmap.getHeight(); | |
+ int size = width * height; | |
+ int pixels[] = new int[size]; | |
+ | |
+ Bitmap bitmap2 = bitmap.copy(Config.ARGB_4444, false); | |
+ | |
+ bitmap2.getPixels(pixels, 0, width, 0, 0, width, height); | |
+ | |
+ final List<HashMap<Integer, Integer>> colorMap = new ArrayList<HashMap<Integer, Integer>>(); | |
+ colorMap.add(new HashMap<Integer, Integer>()); | |
+ colorMap.add(new HashMap<Integer, Integer>()); | |
+ colorMap.add(new HashMap<Integer, Integer>()); | |
+ | |
+ int color = 0; | |
+ int r = 0; | |
+ int g = 0; | |
+ int b = 0; | |
+ Integer rC, gC, bC; | |
+ for (int i = 0; i < pixels.length; i++) { | |
+ color = pixels[i]; | |
+ | |
+ r = Color.red(color); | |
+ g = Color.green(color); | |
+ b = Color.blue(color); | |
+ | |
+ rC = colorMap.get(0).get(r); | |
+ if (rC == null) { | |
+ rC = 0; | |
+ } | |
+ colorMap.get(0).put(r, rC++); | |
+ | |
+ gC = colorMap.get(1).get(g); | |
+ if (gC == null) { | |
+ gC = 0; | |
+ } | |
+ colorMap.get(1).put(g, gC++); | |
+ | |
+ bC = colorMap.get(2).get(b); | |
+ if (bC == null) { | |
+ bC = 0; | |
+ } | |
+ colorMap.get(2).put(b, bC++); | |
+ } | |
+ | |
+ int[] rgb = new int[3]; | |
+ for (int i = 0; i < 3; i++) { | |
+ int max = 0; | |
+ int val = 0; | |
+ for (Map.Entry<Integer, Integer> entry : colorMap.get(i).entrySet()) { | |
+ if (entry.getValue() > max) { | |
+ max = entry.getValue(); | |
+ val = entry.getKey(); | |
+ } | |
+ } | |
+ rgb[i] = val; | |
+ } | |
+ | |
+ int dominantColor = Color.rgb(rgb[0], rgb[1], rgb[2]); | |
+ return dominantColor; | |
+ } | |
+ | |
+ public static int getDominantColor(Drawable drawable) { | |
+ if (drawable == null) { | |
+ return -3; | |
+ } | |
+ Bitmap bitmap = drawableToBitmap(drawable); | |
+ if (bitmap == null) { | |
+ return -3; | |
+ } | |
+ return getDominantColor(bitmap, true); | |
+ } | |
+ | |
+ public static int getDominantColor(Bitmap source) { | |
+ return getDominantColor(source, true); | |
+ } | |
+ | |
+ public static int getDominantColor(Bitmap source, boolean applyThreshold) { | |
+ if (source == null) { | |
+ return -3; | |
+ } | |
+ int[] colorBins = new int[36]; | |
+ int maxBin = -1; | |
+ float[] sumHue = new float[36]; | |
+ float[] sumSat = new float[36]; | |
+ float[] sumVal = new float[36]; | |
+ float[] hsv = new float[3]; | |
+ | |
+ int height = source.getHeight(); | |
+ int width = source.getWidth(); | |
+ int[] pixels = new int[width * height]; | |
+ source.getPixels(pixels, 0, width, 0, 0, width, height); | |
+ for (int row = 0; row < height; row++) { | |
+ for (int col = 0; col < width; col++) { | |
+ int c = pixels[col + row * width]; | |
+ if (Color.alpha(c) < 128) { | |
+ continue; | |
+ } | |
+ Color.colorToHSV(c, hsv); | |
+ | |
+ if (applyThreshold && (hsv[1] <= 0.35f || hsv[2] <= 0.35f)) { | |
+ continue; | |
+ } | |
+ | |
+ int bin = (int) Math.floor(hsv[0] / 10.0f); | |
+ sumHue[bin] = sumHue[bin] + hsv[0]; | |
+ sumSat[bin] = sumSat[bin] + hsv[1]; | |
+ sumVal[bin] = sumVal[bin] + hsv[2]; | |
+ colorBins[bin]++; | |
+ if (maxBin < 0 || colorBins[bin] > colorBins[maxBin]) { | |
+ maxBin = bin; | |
+ } | |
+ } | |
+ } | |
+ | |
+ if (maxBin < 0) { | |
+ return -3; | |
+ } | |
+ hsv[0] = sumHue[maxBin]/colorBins[maxBin]; | |
+ hsv[1] = sumSat[maxBin]/colorBins[maxBin]; | |
+ hsv[2] = sumVal[maxBin]/colorBins[maxBin]; | |
+ return Color.HSVToColor(hsv); | |
+ } | |
+ | |
+ public static int getMainColorFromDrawable(Drawable drawable) { | |
+ if (drawable == null) { | |
+ return -3; | |
+ } | |
+ if (drawable.getConstantState() == null) { | |
+ return -3; | |
+ } | |
+ Drawable copyDrawable = drawable.getConstantState().newDrawable(); | |
+ if (copyDrawable == null) { | |
+ return -3; | |
+ } | |
+ if (copyDrawable instanceof ColorDrawable) { | |
+ return ((ColorDrawable) drawable).getColor(); | |
+ } | |
+ Bitmap bitmap = drawableToBitmap(copyDrawable); | |
+ if (bitmap == null) { | |
+ return -3; | |
+ } | |
+ if (bitmap.getHeight() > 5) { | |
+ int pixel = bitmap.getPixel(0, 5); | |
+ int red = Color.red(pixel); | |
+ int blue = Color.blue(pixel); | |
+ int green = Color.green(pixel); | |
+ int alpha = Color.alpha(pixel); | |
+ return Color.argb(alpha, red, green, blue); | |
+ } | |
+ return -3; | |
+ } | |
+ | |
+ public static Bitmap drawableToBitmap(Drawable drawable) { | |
+ if (drawable == null) { | |
+ return null; | |
+ } | |
+ | |
+ if (drawable instanceof BitmapDrawable) { | |
+ return ((BitmapDrawable) drawable).getBitmap(); | |
+ } | |
+ | |
+ Bitmap bitmap = null; | |
+ int width = drawable.getIntrinsicWidth(); | |
+ int height = drawable.getIntrinsicHeight(); | |
+ if (width > 0 && height > 0) { | |
+ bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); | |
+ Canvas canvas = new Canvas(bitmap); | |
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); | |
+ drawable.draw(canvas); | |
+ } | |
+ return bitmap; | |
+ } | |
+ | |
+ public static Bitmap blurBitmap(Context context, Bitmap bmp, int radius) { | |
+ Bitmap out = Bitmap.createBitmap(bmp); | |
+ RenderScript rs = RenderScript.create(context); | |
+ | |
+ Allocation input = Allocation.createFromBitmap( | |
+ rs, bmp, MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); | |
+ Allocation output = Allocation.createTyped(rs, input.getType()); | |
+ | |
+ ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); | |
+ script.setInput(input); | |
+ script.setRadius(radius); | |
+ script.forEach(output); | |
+ | |
+ output.copyTo(out); | |
+ | |
+ rs.destroy(); | |
+ return out; | |
+ } | |
+} | |
diff --git a/core/java/com/android/internal/widget/FloatingWindowView.java b/core/java/com/android/internal/widget/FloatingWindowView.java | |
new file mode 100644 | |
index 0000000..79a0a8d | |
--- /dev/null | |
+++ b/core/java/com/android/internal/widget/FloatingWindowView.java | |
@@ -0,0 +1,273 @@ | |
+package com.android.internal.widget; | |
+ | |
+import android.annotation.SuppressLint; | |
+import android.app.Activity; | |
+import android.content.res.Resources; | |
+import android.content.res.XmlResourceParser; | |
+import android.graphics.drawable.ShapeDrawable; | |
+import android.graphics.drawable.shapes.RectShape; | |
+import android.graphics.Paint; | |
+import android.graphics.PorterDuff.Mode; | |
+import android.view.Menu; | |
+import android.view.MenuItem; | |
+import android.view.MotionEvent; | |
+import android.view.View; | |
+import android.view.ViewGroup; | |
+import android.widget.FrameLayout; | |
+import android.widget.ImageButton; | |
+import android.widget.PopupMenu; | |
+import android.widget.RelativeLayout; | |
+import android.widget.TextView; | |
+ | |
+import com.android.internal.R; | |
+ | |
+@SuppressLint("ViewConstructor") | |
+/** | |
+ * @hide | |
+ */ | |
+public class FloatingWindowView extends RelativeLayout { | |
+ | |
+ private static final int ID_OVERLAY_VIEW = 1000000; | |
+ | |
+ private final int SNAP_LEFT = 1; | |
+ private final int SNAP_TOP = 2; | |
+ private final int SNAP_RIGHT = 3; | |
+ private final int SNAP_BOTTOM = 4; | |
+ | |
+ private Resources mResource; | |
+ private RelativeLayout mTitleBarHeader; | |
+ private ImageButton mTitleBarMin; | |
+ private ImageButton mTitleBarMax; | |
+ private TextView mAppLabel; | |
+ private ImageButton mTitleBarClose; | |
+ private ImageButton mTitleBarMore; | |
+ private View mContentViews; | |
+ private View mDividerViews; | |
+ | |
+ public FloatingWindowView(final Activity activity, int height) { | |
+ super(activity); | |
+ mResource = activity.getResources(); | |
+ | |
+ XmlResourceParser parser = mResource.getLayout(R.layout.floating_window_layout); | |
+ activity.getLayoutInflater().inflate(parser, this); | |
+ | |
+ setId(ID_OVERLAY_VIEW); | |
+ setIsRootNamespace(false); | |
+ | |
+ mContentViews = findViewById(R.id.floating_window_background); | |
+ mContentViews.bringToFront(); | |
+ | |
+ setIsRootNamespace(true); | |
+ | |
+ final FrameLayout decorView = | |
+ (FrameLayout) activity.getWindow().peekDecorView().getRootView(); | |
+ | |
+ View child = decorView.getChildAt(0); | |
+ FrameLayout.LayoutParams params = | |
+ (FrameLayout.LayoutParams) child.getLayoutParams(); | |
+ params.setMargins(0, height, 0, 0); | |
+ child.setLayoutParams(params); | |
+ | |
+ mTitleBarHeader = (RelativeLayout) findViewByIdHelper(this, R.id.floating_window_titlebar, | |
+ "floating_window_titlebar"); | |
+ mTitleBarMore = (ImageButton) findViewByIdHelper(mTitleBarHeader, R.id.floating_window_more, | |
+ "floating_window_more"); | |
+ mTitleBarClose = (ImageButton) findViewByIdHelper(mTitleBarHeader, R.id.floating_window_close, | |
+ "floating_window_close"); | |
+ mTitleBarMin = (ImageButton) findViewByIdHelper(mTitleBarHeader, R.id.floating_window_min, | |
+ "floating_window_min"); | |
+ mTitleBarMax = (ImageButton) findViewByIdHelper(mTitleBarHeader, R.id.floating_window_max, | |
+ "floating_window_max"); | |
+ mAppLabel = (TextView) findViewByIdHelper(mTitleBarHeader, R.id.floating_window_label, | |
+ "floating_window_label"); | |
+ mDividerViews = findViewByIdHelper(mTitleBarHeader, R.id.floating_window_line, | |
+ "floating_window_line"); | |
+ | |
+ if (mTitleBarHeader == null | |
+ || mTitleBarClose == null | |
+ || mTitleBarMore == null | |
+ || mTitleBarMin == null | |
+ || mTitleBarMax == null | |
+ || mAppLabel == null | |
+ || mDividerViews == null) { | |
+ return; | |
+ } | |
+ | |
+ mTitleBarClose.setImageDrawable(mResource.getDrawable(R.drawable.ic_floating_window_close)); | |
+ mTitleBarClose.setOnClickListener(new OnClickListener() { | |
+ public void onClick(View v) { | |
+ activity.finish(); | |
+ } | |
+ }); | |
+ | |
+ mTitleBarMin.setImageDrawable(mResource.getDrawable(R.drawable.ic_floating_window_min)); | |
+ mTitleBarMin.setVisibility(View.GONE); | |
+ mTitleBarMin.setOnClickListener(new OnClickListener() { | |
+ public void onClick(View v) { | |
+ activity.restorePreviousLayoutApp(); | |
+ mTitleBarMin.setVisibility(View.GONE); | |
+ mTitleBarMax.setVisibility(View.VISIBLE); | |
+ } | |
+ }); | |
+ | |
+ mTitleBarMax.setImageDrawable(mResource.getDrawable(R.drawable.ic_floating_window_max)); | |
+ mTitleBarMax.setVisibility(View.VISIBLE); | |
+ mTitleBarMax.setOnClickListener(new OnClickListener() { | |
+ public void onClick(View v) { | |
+ activity.setFullscreenApp(); | |
+ mTitleBarMin.setVisibility(View.VISIBLE); | |
+ mTitleBarMax.setVisibility(View.GONE); | |
+ } | |
+ }); | |
+ | |
+ mAppLabel.setText(activity.getApplicationInfo().loadLabel(activity.getPackageManager())); | |
+ | |
+ mTitleBarMore.setImageDrawable(mResource.getDrawable(R.drawable.ic_floating_window_more)); | |
+ | |
+ final String menu_item1 = mResource.getString(R.string.floating_window_snap_top); | |
+ final String menu_item2 = mResource.getString(R.string.floating_window_snap_bottom); | |
+ final String menu_item3 = mResource.getString(R.string.floating_window_snap_left); | |
+ final String menu_item4 = mResource.getString(R.string.floating_window_snap_right); | |
+ final String menu_item5 = mResource.getString(R.string.floating_window_minimize); | |
+ | |
+ final PopupMenu popupMenu = new PopupMenu(mTitleBarMore.getContext(), mTitleBarMore); | |
+ Menu menu = popupMenu.getMenu(); | |
+ menu.add(menu_item1); | |
+ menu.add(menu_item2); | |
+ menu.add(menu_item3); | |
+ menu.add(menu_item4); | |
+ menu.add(menu_item5); | |
+ | |
+ mTitleBarMore.setOnClickListener(new OnClickListener() { | |
+ public void onClick(View v) { | |
+ popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { | |
+ @Override | |
+ public boolean onMenuItemClick(MenuItem item) { | |
+ if (item.getTitle().equals(menu_item1)) { | |
+ activity.forceSnap(SNAP_TOP); | |
+ } else if (item.getTitle().equals(menu_item2)) { | |
+ activity.forceSnap(SNAP_BOTTOM); | |
+ } else if (item.getTitle().equals(menu_item3)) { | |
+ activity.forceSnap(SNAP_LEFT); | |
+ } else if (item.getTitle().equals(menu_item4)) { | |
+ activity.forceSnap(SNAP_RIGHT); | |
+ } else if (item.getTitle().equals(menu_item5)) { | |
+ activity.sendAppLaunchBroadcast(); | |
+ } | |
+ return false; | |
+ } | |
+ }); | |
+ popupMenu.show(); | |
+ } | |
+ }); | |
+ | |
+ RelativeLayout.LayoutParams header_param = | |
+ (LayoutParams) mTitleBarHeader.getLayoutParams(); | |
+ header_param.height = height; | |
+ mTitleBarHeader.setLayoutParams(header_param); | |
+ mTitleBarHeader.setOnTouchListener(new View.OnTouchListener() { | |
+ @Override | |
+ public boolean onTouch(View view, MotionEvent event) { | |
+ switch (event.getAction()) { | |
+ case MotionEvent.ACTION_DOWN: | |
+ activity.setTouchViewDown(event.getX(), event.getY()); | |
+ activity.onUserInteraction(); | |
+ activity.updateFocusApp(); | |
+ if (!activity.getChangedPreviousRange()) { | |
+ activity.setPreviousTouchRange(event.getRawX(), event.getRawY()); | |
+ activity.setChangedPreviousRange(true); | |
+ } | |
+ break; | |
+ case MotionEvent.ACTION_MOVE: | |
+ activity.changeFlagsLayoutParams(); | |
+ activity.setTouchViewMove(event.getRawX(), event.getRawY()); | |
+ if (activity.getRestorePosition() | |
+ && activity.moveRangeAboveLimit(event)) { | |
+ activity.restoreOldPosition(); | |
+ } | |
+ activity.showSnap((int) event.getRawX(), (int) event.getRawY()); | |
+ break; | |
+ case MotionEvent.ACTION_UP: | |
+ activity.setChangedFlags(false); | |
+ activity.finishSnap(activity.isValidSnap() | |
+ && activity.getTimeoutDone()); | |
+ activity.discardTimeout(); | |
+ activity.setChangedPreviousRange(false); | |
+ break; | |
+ } | |
+ return view.onTouchEvent(event); | |
+ } | |
+ }); | |
+ | |
+ ViewGroup.LayoutParams divider_param = mDividerViews.getLayoutParams(); | |
+ divider_param.height = 2; | |
+ mDividerViews.setLayoutParams(divider_param); | |
+ } | |
+ | |
+ private View findViewByIdHelper(View view, int id, String tag) { | |
+ View v = view.findViewById(id); | |
+ if (v == null) { | |
+ v = findViewWithTag(view, tag); | |
+ } | |
+ return v; | |
+ } | |
+ | |
+ private View findViewWithTag(View view, String text) { | |
+ if (view.getTag() instanceof String) { | |
+ if (((String) view.getTag()).equals(text)) { | |
+ return view; | |
+ } | |
+ } | |
+ if (view instanceof ViewGroup) { | |
+ final ViewGroup group = (ViewGroup) view; | |
+ for (int i = 0; i < group.getChildCount(); ++i) { | |
+ final View child = group.getChildAt(i); | |
+ final View found = findViewWithTag(child, text); | |
+ if (found != null) { | |
+ return found; | |
+ } | |
+ } | |
+ } | |
+ return null; | |
+ } | |
+ | |
+ public void setFloatingBackgroundColor(int color) { | |
+ if (mTitleBarHeader == null | |
+ || mContentViews == null) { | |
+ return; | |
+ } | |
+ mTitleBarHeader.setBackgroundColor(color); | |
+ } | |
+ | |
+ public void setFloatingColorFilter(int color) { | |
+ if (mTitleBarClose == null | |
+ || mTitleBarMin == null | |
+ || mTitleBarMax == null | |
+ || mTitleBarMore == null | |
+ || mDividerViews == null) { | |
+ return; | |
+ } | |
+ mTitleBarMore.setColorFilter(color, Mode.SRC_ATOP); | |
+ mTitleBarClose.setColorFilter(color, Mode.SRC_ATOP); | |
+ mTitleBarMin.setColorFilter(color, Mode.SRC_ATOP); | |
+ mTitleBarMax.setColorFilter(color, Mode.SRC_ATOP); | |
+ mAppLabel.setTextColor(color); | |
+ mDividerViews.setBackgroundColor(color); | |
+ } | |
+ | |
+ private ShapeDrawable makeOutline(int color, int thickness) { | |
+ ShapeDrawable rectShapeDrawable = new ShapeDrawable(new RectShape()); | |
+ Paint paint = rectShapeDrawable.getPaint(); | |
+ paint.setColor(color); | |
+ paint.setStyle(Paint.Style.STROKE); | |
+ paint.setStrokeWidth(thickness); | |
+ return rectShapeDrawable; | |
+ } | |
+ | |
+ public static final RelativeLayout.LayoutParams getParams() { | |
+ final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( | |
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); | |
+ params.setMargins(0, 0, 0, 0); | |
+ return params; | |
+ } | |
+} | |
diff --git a/core/res/res/drawable-hdpi/ic_floating_window_close.png b/core/res/res/drawable-hdpi/ic_floating_window_close.png | |
new file mode 100644 | |
index 0000000..905ae92 | |
--- /dev/null | |
+++ b/core/res/res/drawable-hdpi/ic_floating_window_close.png | |
Binary files differ | |
diff --git a/core/res/res/drawable-hdpi/ic_floating_window_max.png b/core/res/res/drawable-hdpi/ic_floating_window_max.png | |
new file mode 100644 | |
index 0000000..f4778a7 | |
--- /dev/null | |
+++ b/core/res/res/drawable-hdpi/ic_floating_window_max.png | |
Binary files differ | |
diff --git a/core/res/res/drawable-hdpi/ic_floating_window_min.png b/core/res/res/drawable-hdpi/ic_floating_window_min.png | |
new file mode 100644 | |
index 0000000..b99016c | |
--- /dev/null | |
+++ b/core/res/res/drawable-hdpi/ic_floating_window_min.png | |
Binary files differ | |
diff --git a/core/res/res/drawable-hdpi/ic_floating_window_more.png b/core/res/res/drawable-hdpi/ic_floating_window_more.png | |
new file mode 100644 | |
index 0000000..94bd31f | |
--- /dev/null | |
+++ b/core/res/res/drawable-hdpi/ic_floating_window_more.png | |
Binary files differ | |
diff --git a/core/res/res/drawable-mdpi/ic_floating_window_close.png b/core/res/res/drawable-mdpi/ic_floating_window_close.png | |
new file mode 100644 | |
index 0000000..34b50b5 | |
--- /dev/null | |
+++ b/core/res/res/drawable-mdpi/ic_floating_window_close.png | |
Binary files differ | |
diff --git a/core/res/res/drawable-mdpi/ic_floating_window_max.png b/core/res/res/drawable-mdpi/ic_floating_window_max.png | |
new file mode 100644 | |
index 0000000..69067ce | |
--- /dev/null | |
+++ b/core/res/res/drawable-mdpi/ic_floating_window_max.png | |
Binary files differ | |
diff --git a/core/res/res/drawable-mdpi/ic_floating_window_min.png b/core/res/res/drawable-mdpi/ic_floating_window_min.png | |
new file mode 100644 | |
index 0000000..ec5a22a | |
--- /dev/null | |
+++ b/core/res/res/drawable-mdpi/ic_floating_window_min.png | |
Binary files differ | |
diff --git a/core/res/res/drawable-mdpi/ic_floating_window_more.png b/core/res/res/drawable-mdpi/ic_floating_window_more.png | |
new file mode 100644 | |
index 0000000..3c7d1ef | |
--- /dev/null | |
+++ b/core/res/res/drawable-mdpi/ic_floating_window_more.png | |
Binary files differ | |
diff --git a/core/res/res/drawable-xhdpi/ic_floating_window_close.png b/core/res/res/drawable-xhdpi/ic_floating_window_close.png | |
new file mode 100644 | |
index 0000000..b9d236f | |
--- /dev/null | |
+++ b/core/res/res/drawable-xhdpi/ic_floating_window_close.png | |
Binary files differ | |
diff --git a/core/res/res/drawable-xhdpi/ic_floating_window_max.png b/core/res/res/drawable-xhdpi/ic_floating_window_max.png | |
new file mode 100644 | |
index 0000000..d1c9a52 | |
--- /dev/null | |
+++ b/core/res/res/drawable-xhdpi/ic_floating_window_max.png | |
Binary files differ | |
diff --git a/core/res/res/drawable-xhdpi/ic_floating_window_min.png b/core/res/res/drawable-xhdpi/ic_floating_window_min.png | |
new file mode 100644 | |
index 0000000..fcadd56 | |
--- /dev/null | |
+++ b/core/res/res/drawable-xhdpi/ic_floating_window_min.png | |
Binary files differ | |
diff --git a/core/res/res/drawable-xhdpi/ic_floating_window_more.png b/core/res/res/drawable-xhdpi/ic_floating_window_more.png | |
new file mode 100644 | |
index 0000000..296267d | |
--- /dev/null | |
+++ b/core/res/res/drawable-xhdpi/ic_floating_window_more.png | |
Binary files differ | |
diff --git a/core/res/res/drawable-xxhdpi/ic_floating_window_close.png b/core/res/res/drawable-xxhdpi/ic_floating_window_close.png | |
new file mode 100644 | |
index 0000000..dec0d2e | |
--- /dev/null | |
+++ b/core/res/res/drawable-xxhdpi/ic_floating_window_close.png | |
Binary files differ | |
diff --git a/core/res/res/drawable-xxhdpi/ic_floating_window_max.png b/core/res/res/drawable-xxhdpi/ic_floating_window_max.png | |
new file mode 100644 | |
index 0000000..91b5d34 | |
--- /dev/null | |
+++ b/core/res/res/drawable-xxhdpi/ic_floating_window_max.png | |
Binary files differ | |
diff --git a/core/res/res/drawable-xxhdpi/ic_floating_window_min.png b/core/res/res/drawable-xxhdpi/ic_floating_window_min.png | |
new file mode 100644 | |
index 0000000..d52c750 | |
--- /dev/null | |
+++ b/core/res/res/drawable-xxhdpi/ic_floating_window_min.png | |
Binary files differ | |
diff --git a/core/res/res/drawable-xxhdpi/ic_floating_window_more.png b/core/res/res/drawable-xxhdpi/ic_floating_window_more.png | |
new file mode 100644 | |
index 0000000..b20ab8b | |
--- /dev/null | |
+++ b/core/res/res/drawable-xxhdpi/ic_floating_window_more.png | |
Binary files differ | |
diff --git a/core/res/res/layout/floating_window_layout.xml b/core/res/res/layout/floating_window_layout.xml | |
new file mode 100644 | |
index 0000000..b630b98 | |
--- /dev/null | |
+++ b/core/res/res/layout/floating_window_layout.xml | |
@@ -0,0 +1,117 @@ | |
+<?xml version="1.0" encoding="utf-8"?> | |
+<RelativeLayout xmlns:tools="http://schemas.android.com/tools" | |
+ xmlns:android="http://schemas.android.com/apk/res/android" | |
+ android:layout_width="match_parent" | |
+ android:layout_height="match_parent" | |
+ android:background="@null" > | |
+ | |
+ <View | |
+ android:id="@+id/floating_window_background" | |
+ android:layout_width="wrap_content" | |
+ android:layout_height="wrap_content" | |
+ android:layout_alignParentBottom="true" | |
+ android:layout_alignParentLeft="true" | |
+ android:layout_alignParentRight="true" | |
+ android:layout_alignParentTop="true" | |
+ android:clickable="false" | |
+ android:focusable="false" | |
+ android:focusableInTouchMode="false" /> | |
+ | |
+ <RelativeLayout | |
+ android:id="@+id/floating_window_titlebar" | |
+ android:layout_width="wrap_content" | |
+ android:layout_height="0dp" | |
+ android:background="#f000" | |
+ android:tag="floating_window_titlebar" > | |
+ | |
+ <ImageButton | |
+ android:id="@+id/floating_window_close" | |
+ style="@android:style/Widget.Holo.Button.Borderless" | |
+ android:layout_width="wrap_content" | |
+ android:layout_height="match_parent" | |
+ android:layout_alignParentBottom="true" | |
+ android:layout_alignParentRight="true" | |
+ android:layout_alignParentTop="true" | |
+ android:adjustViewBounds="true" | |
+ android:paddingLeft="0dp" | |
+ android:paddingRight="0dp" | |
+ android:scaleType="fitCenter" | |
+ android:src="#FFF" | |
+ android:tag="floating_window_close" /> | |
+ | |
+ <ImageButton | |
+ android:id="@+id/floating_window_min" | |
+ style="@android:style/Widget.Holo.Button.Borderless" | |
+ android:layout_width="wrap_content" | |
+ android:layout_height="match_parent" | |
+ android:layout_alignParentBottom="true" | |
+ android:layout_alignParentTop="true" | |
+ android:layout_toLeftOf="@+id/floating_window_max" | |
+ android:adjustViewBounds="true" | |
+ android:paddingLeft="0dp" | |
+ android:paddingRight="0dp" | |
+ android:scaleType="fitCenter" | |
+ android:src="#FFF" | |
+ android:tag="floating_window_min" /> | |
+ | |
+ <ImageButton | |
+ android:id="@+id/floating_window_max" | |
+ style="@android:style/Widget.Holo.Button.Borderless" | |
+ android:layout_width="wrap_content" | |
+ android:layout_height="match_parent" | |
+ android:layout_alignParentBottom="true" | |
+ android:layout_alignParentTop="true" | |
+ android:layout_toLeftOf="@+id/floating_window_close" | |
+ android:adjustViewBounds="true" | |
+ android:paddingLeft="0dp" | |
+ android:paddingRight="0dp" | |
+ android:scaleType="fitCenter" | |
+ android:src="#FFF" | |
+ android:tag="floating_window_max" /> | |
+ | |
+ <TextView | |
+ android:id="@+id/floating_window_label" | |
+ android:layout_width="wrap_content" | |
+ android:layout_height="wrap_content" | |
+ android:layout_alignParentBottom="true" | |
+ android:layout_alignParentTop="true" | |
+ android:layout_toLeftOf="@+id/floating_window_control" | |
+ android:layout_toRightOf="@+id/floating_window_more" | |
+ android:ellipsize="end" | |
+ android:focusable="false" | |
+ android:gravity="center_vertical|center_horizontal" | |
+ android:maxLines="1" | |
+ android:tag="floating_window_label" | |
+ android:textAppearance="?android:attr/textAppearanceMedium" | |
+ android:textSize="14sp" | |
+ android:textStyle="bold" /> | |
+ | |
+ <ImageButton | |
+ android:id="@+id/floating_window_more" | |
+ style="@android:style/Widget.Holo.Button.Borderless" | |
+ android:layout_width="wrap_content" | |
+ android:layout_height="match_parent" | |
+ android:layout_alignParentBottom="true" | |
+ android:layout_alignParentLeft="true" | |
+ android:layout_alignParentTop="true" | |
+ android:adjustViewBounds="true" | |
+ android:paddingLeft="0dp" | |
+ android:paddingRight="2dp" | |
+ android:scaleType="fitCenter" | |
+ android:src="#FFF" | |
+ android:tag="floating_window_more" /> | |
+ | |
+ <View | |
+ android:id="@+id/floating_window_line" | |
+ android:layout_width="wrap_content" | |
+ android:layout_height="2dp" | |
+ android:layout_alignParentBottom="true" | |
+ android:layout_alignParentLeft="true" | |
+ android:layout_alignParentRight="true" | |
+ android:background="#fff" | |
+ android:minHeight="5dp" | |
+ android:tag="floating_window_line" /> | |
+ | |
+ </RelativeLayout> | |
+ | |
+</RelativeLayout> | |
diff --git a/core/res/res/values/cm_strings.xml b/core/res/res/values/cm_strings.xml | |
index 5ec6f3c..84fce2a 100644 | |
--- a/core/res/res/values/cm_strings.xml | |
+++ b/core/res/res/values/cm_strings.xml | |
@@ -346,4 +346,12 @@ | |
<!-- [CHAR LIMIT=NONE] Description of an application permission, listed so the user can choose | |
whether they want to allow the application to do this. --> | |
<string name="permdesc_resetBatteryStats">Allows an application to reset the current low-level battery usage data.</string> | |
+ | |
+ <!-- Floating Window --> | |
+ <string name="floating_window_snap_top">Snap top</string> | |
+ <string name="floating_window_snap_bottom">Snap bottom</string> | |
+ <string name="floating_window_snap_left">Snap left</string> | |
+ <string name="floating_window_snap_right">Snap right</string> | |
+ <string name="floating_window_minimize">Minimize</string> | |
+ | |
</resources> | |
diff --git a/core/res/res/values/cm_symbols.xml b/core/res/res/values/cm_symbols.xml | |
index 27e759b..b9b45fe 100644 | |
--- a/core/res/res/values/cm_symbols.xml | |
+++ b/core/res/res/values/cm_symbols.xml | |
@@ -338,9 +338,30 @@ | |
<!-- Quick Settings tile defaults --> | |
<java-symbol type="string" name="config_defaultQuickSettingsTiles" /> | |
- | |
+ | |
<java-symbol type="array" name="config_externalCMServices" /> | |
<!-- Fingerprint default name --> | |
<java-symbol type="string" name="fingerprint_default_name" /> | |
+ | |
+ <!-- Floating Window --> | |
+ <java-symbol type="drawable" name="ic_floating_window_close" /> | |
+ <java-symbol type="drawable" name="ic_floating_window_max" /> | |
+ <java-symbol type="drawable" name="ic_floating_window_min" /> | |
+ <java-symbol type="drawable" name="ic_floating_window_more" /> | |
+ <java-symbol type="id" name="floating_window_background" /> | |
+ <java-symbol type="id" name="floating_window_titlebar" /> | |
+ <java-symbol type="id" name="floating_window_close" /> | |
+ <java-symbol type="id" name="floating_window_label" /> | |
+ <java-symbol type="id" name="floating_window_max" /> | |
+ <java-symbol type="id" name="floating_window_min" /> | |
+ <java-symbol type="id" name="floating_window_more" /> | |
+ <java-symbol type="id" name="floating_window_line" /> | |
+ <java-symbol type="layout" name="floating_window_layout" /> | |
+ <java-symbol type="string" name="floating_window_snap_top"/> | |
+ <java-symbol type="string" name="floating_window_snap_bottom"/> | |
+ <java-symbol type="string" name="floating_window_snap_left"/> | |
+ <java-symbol type="string" name="floating_window_snap_right"/> | |
+ <java-symbol type="string" name="floating_window_minimize"/> | |
+ | |
</resources> | |
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml | |
index d3d6aa2..c3acd98 100644 | |
--- a/core/res/res/values/symbols.xml | |
+++ b/core/res/res/values/symbols.xml | |
@@ -1392,6 +1392,8 @@ | |
<java-symbol type="style" name="Theme.DeviceDefault.Dialog.NoFrame" /> | |
<java-symbol type="style" name="Theme.IconMenu" /> | |
<java-symbol type="style" name="Theme.DeviceDefault.VoiceInteractionSession" /> | |
+ <java-symbol type="style" name="Theme.DeviceDefault.FloatingWindow" /> | |
+ <java-symbol type="style" name="Theme.DeviceDefault.FloatingWindowLight" /> | |
<java-symbol type="attr" name="mediaRouteButtonStyle" /> | |
<java-symbol type="attr" name="externalRouteEnabledDrawable" /> | |
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml | |
index def8659..afae2b4 100644 | |
--- a/core/res/res/values/themes_device_defaults.xml | |
+++ b/core/res/res/values/themes_device_defaults.xml | |
@@ -555,4 +555,28 @@ | |
<item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item> | |
</style> | |
+ <style name="Theme.DeviceDefault.FloatingWindow"> | |
+ <item name="android:windowIsFloating">false</item> | |
+ <item name="android:windowIsTranslucent">true</item> | |
+ <item name="android:windowFrame">@null</item> | |
+ <item name="android:windowContentOverlay">@null</item> | |
+ <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item> | |
+ <item name="android:windowActionModeOverlay">true</item> | |
+ <item name="android:windowCloseOnTouchOutside">true</item> | |
+ <item name="android:windowFullscreen">false</item> | |
+ <item name="android:windowSoftInputMode">stateAlwaysHidden|adjustPan</item> | |
+ </style> | |
+ | |
+ <style name="Theme.DeviceDefault.FloatingWindowLight" parent="Theme.Material.Light.Dialog"> | |
+ <item name="android:windowIsFloating">false</item> | |
+ <item name="android:windowIsTranslucent">true</item> | |
+ <item name="android:windowFrame">@null</item> | |
+ <item name="android:windowContentOverlay">@null</item> | |
+ <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item> | |
+ <item name="android:windowActionModeOverlay">true</item> | |
+ <item name="android:windowCloseOnTouchOutside">true</item> | |
+ <item name="android:windowFullscreen">false</item> | |
+ <item name="android:windowSoftInputMode">stateAlwaysHidden|adjustPan</item> | |
+ </style> | |
+ | |
</resources> | |
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_floating_on.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_floating_on.png | |
new file mode 100644 | |
index 0000000..b37f6b2 | |
--- /dev/null | |
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_floating_on.png | |
Binary files differ | |
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_floating_on.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_floating_on.png | |
new file mode 100644 | |
index 0000000..b83b21c | |
--- /dev/null | |
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_floating_on.png | |
Binary files differ | |
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_floating_on.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_floating_on.png | |
new file mode 100644 | |
index 0000000..371f88a | |
--- /dev/null | |
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_floating_on.png | |
Binary files differ | |
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_floating_on.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_floating_on.png | |
new file mode 100644 | |
index 0000000..964cd3a | |
--- /dev/null | |
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_floating_on.png | |
Binary files differ | |
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml | |
index effa302..acbdf15 100644 | |
--- a/packages/SystemUI/res/layout/notification_guts.xml | |
+++ b/packages/SystemUI/res/layout/notification_guts.xml | |
@@ -105,5 +105,16 @@ | |
android:contentDescription="@string/status_bar_notification_inspect_item_title" | |
android:src="@drawable/ic_info" | |
/> | |
+ | |
+ <ImageButton style="@android:style/Widget.Material.Light.Button.Borderless.Small" | |
+ android:id="@+id/notification_float_item" | |
+ android:layout_width="52dp" | |
+ android:layout_height="match_parent" | |
+ android:layout_weight="0" | |
+ android:gravity="center" | |
+ android:contentDescription="@string/recent_float_mode_title" | |
+ android:src="@drawable/ic_qs_floating_on" | |
+ /> | |
+ | |
</LinearLayout> | |
</com.android.systemui.statusbar.NotificationGuts> | |
diff --git a/packages/SystemUI/res/menu/recent_popup_menu.xml b/packages/SystemUI/res/menu/recent_popup_menu.xml | |
index 9b605f9..db42adb 100644 | |
--- a/packages/SystemUI/res/menu/recent_popup_menu.xml | |
+++ b/packages/SystemUI/res/menu/recent_popup_menu.xml | |
@@ -23,4 +23,5 @@ | |
<item android:id="@+id/recent_force_stop" android:title="@string/advanced_dev_option_force_stop" /> | |
<item android:id="@+id/recent_wipe_app" android:title="@string/advanced_dev_option_wipe_app" /> | |
<item android:id="@+id/recent_uninstall" android:title="@string/advanced_dev_option_uninstall" /> | |
+ <item android:id="@+id/recent_float_mode" android:title="@string/recent_float_mode_title" /> | |
</menu> | |
diff --git a/packages/SystemUI/res/values/cm_strings.xml b/packages/SystemUI/res/values/cm_strings.xml | |
index 80b4f50..27bd45c 100644 | |
--- a/packages/SystemUI/res/values/cm_strings.xml | |
+++ b/packages/SystemUI/res/values/cm_strings.xml | |
@@ -306,4 +306,6 @@ | |
<!-- Content description of the dock battery level icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> | |
<string name="accessibility_dock_battery_level">Dock battery <xliff:g id="number">%d</xliff:g> percent.</string> | |
+ <string name="recent_float_mode_title">Floating mode</string> | |
+ | |
</resources> | |
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java | |
index 33f6564..bb70ebe 100644 | |
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java | |
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java | |
@@ -45,6 +45,10 @@ | |
public static final int X = 0; | |
public static final int Y = 1; | |
+ public static final int LEFT = 0; | |
+ public static final int RIGHT = 1; | |
+ public static final int UP = 2; | |
+ public static final int DOWN = 3; | |
private static LinearInterpolator sLinearInterpolator = new LinearInterpolator(); | |
private final Interpolator mFastOutLinearInInterpolator; | |
@@ -66,7 +70,12 @@ | |
private Callback mCallback; | |
private Handler mHandler; | |
private int mSwipeDirection; | |
+ private int mSwipeDirectionDetail; | |
private VelocityTracker mVelocityTracker; | |
+ | |
+ private boolean mTriggerEnabled = false; | |
+ private int mTriggerDirection; | |
+ private boolean mTriggerChild; | |
private float mInitialTouchPos; | |
private boolean mDragging; | |
@@ -109,6 +118,14 @@ | |
public void setPagingTouchSlop(float pagingTouchSlop) { | |
mPagingTouchSlop = pagingTouchSlop; | |
+ } | |
+ | |
+ public void setTriggerEnabled(boolean triggerEnabled) { | |
+ mTriggerEnabled = triggerEnabled; | |
+ } | |
+ | |
+ public void setTriggerDirection(int triggerDirection) { | |
+ mTriggerDirection = triggerDirection; | |
} | |
private float getPos(MotionEvent ev) { | |
@@ -183,6 +200,11 @@ | |
} | |
} | |
invalidateGlobalRegion(animView); | |
+ } | |
+ | |
+ private boolean canChildBeDismissed(View view) { | |
+ return mCallback.canChildBeDismissed(view) && | |
+ !(mTriggerEnabled && mSwipeDirectionDetail == mTriggerDirection); | |
} | |
// invalidate the view's own bounds all the way up the view hierarchy | |
@@ -368,7 +390,7 @@ | |
public void snapChild(final View view, float velocity) { | |
final View animView = mCallback.getChildContentView(view); | |
- final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(animView); | |
+ final boolean canAnimViewBeDismissed = canChildBeDismissed(animView); | |
ObjectAnimator anim = createTranslationAnimation(animView, 0); | |
int duration = SNAP_ANIM_LEN; | |
anim.setDuration(duration); | |
@@ -381,6 +403,9 @@ | |
public void onAnimationEnd(Animator animator) { | |
updateSwipeProgressFromOffset(animView, canAnimViewBeDismissed); | |
mCallback.onChildSnappedBack(animView); | |
+ if (mTriggerChild) { | |
+ mCallback.onChildTriggered(view); | |
+ } | |
} | |
}); | |
anim.start(); | |
@@ -414,13 +439,18 @@ | |
case MotionEvent.ACTION_MOVE: | |
if (mCurrView != null) { | |
float delta = getPos(ev) - mInitialTouchPos; | |
+ | |
+ mSwipeDirectionDetail = delta < 0 ? | |
+ (mSwipeDirection == X ? LEFT : UP) : | |
+ (mSwipeDirection == X ? RIGHT : DOWN); | |
+ | |
float absDelta = Math.abs(delta); | |
if (absDelta >= getFalsingThreshold()) { | |
mTouchAboveFalsingThreshold = true; | |
} | |
// don't let items that can't be dismissed be dragged more than | |
// maxScrollDistance | |
- if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissed(mCurrView)) { | |
+ if (CONSTRAIN_SWIPE && !canChildBeDismissed(mCurrView)) { | |
float size = getSize(mCurrAnimView); | |
float maxScrollDistance = 0.15f * size; | |
if (absDelta >= size) { | |
@@ -431,7 +461,7 @@ | |
} | |
setTranslation(mCurrAnimView, delta); | |
- updateSwipeProgressFromOffset(mCurrAnimView, mCanCurrViewBeDimissed); | |
+ updateSwipeProgressFromOffset(mCurrAnimView, canChildBeDismissed(mCurrView)); | |
} | |
break; | |
case MotionEvent.ACTION_UP: | |
@@ -452,9 +482,13 @@ | |
boolean falsingDetected = mCallback.isAntiFalsingNeeded() | |
&& !mTouchAboveFalsingThreshold; | |
- boolean dismissChild = mCallback.canChildBeDismissed(mCurrView) | |
+ boolean dismissChild = canChildBeDismissed(mCurrView) | |
&& !falsingDetected && (childSwipedFastEnough || childSwipedFarEnough) | |
&& ev.getActionMasked() == MotionEvent.ACTION_UP; | |
+ | |
+ mTriggerChild = mTriggerEnabled && | |
+ mSwipeDirectionDetail == mTriggerDirection && | |
+ (childSwipedFastEnough || childSwipedFarEnough); | |
if (dismissChild) { | |
// flingadingy | |
@@ -488,6 +522,8 @@ | |
void onChildDismissed(View v); | |
+ void onChildTriggered(View v); | |
+ | |
void onDragCancelled(View v); | |
void onChildSnappedBack(View animView); | |
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java | |
index 330333a..35585c9 100644 | |
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java | |
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java | |
@@ -216,6 +216,9 @@ | |
contentView.setTranslationY(0); | |
} | |
+ public void onChildTriggered(View v) { | |
+ } | |
+ | |
public void onBeginDrag(View v) { | |
// We do this so the underlying ScrollView knows that it won't get | |
// the chance to intercept events anymore | |
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java | |
index 1e247be..7d6a358 100644 | |
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java | |
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java | |
@@ -224,6 +224,9 @@ | |
contentView.setTranslationX(0); | |
} | |
+ public void onChildTriggered(View v) { | |
+ } | |
+ | |
public void onBeginDrag(View v) { | |
// We do this so the underlying ScrollView knows that it won't get | |
// the chance to intercept events anymore | |
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java | |
index 32d3cdc..53d9ee7 100644 | |
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java | |
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java | |
@@ -630,6 +630,18 @@ | |
} | |
@Override | |
+ public void onTaskFloatClicked(Task t) { | |
+ Intent baseIntent = t.key.baseIntent; | |
+ // Hide and go home | |
+ onRecentsHidden(); | |
+ mCb.onTaskLaunchFailed(); | |
+ // Launch task in floating mode | |
+ baseIntent.setFlags(Intent.FLAG_FLOATING_WINDOW | |
+ | Intent.FLAG_ACTIVITY_NEW_TASK); | |
+ mContext.startActivity(baseIntent); | |
+ } | |
+ | |
+ @Override | |
public void onTaskViewDismissed(Task t) { | |
// Remove any stored data from the loader. We currently don't bother notifying the views | |
// that the data has been unloaded because at the point we call onTaskViewDismissed(), the views | |
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java | |
index be5ed77..953421d 100644 | |
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java | |
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java | |
@@ -37,7 +37,6 @@ | |
import android.view.accessibility.AccessibilityEvent; | |
import android.widget.FrameLayout; | |
import android.widget.PopupMenu; | |
- | |
import com.android.systemui.R; | |
import com.android.systemui.recents.Constants; | |
import com.android.systemui.recents.RecentsConfiguration; | |
@@ -65,6 +64,7 @@ | |
public void onTaskViewClicked(TaskStackView stackView, TaskView tv, TaskStack stack, Task t, | |
boolean lockToTask); | |
public void onTaskViewAppInfoClicked(Task t); | |
+ public void onTaskFloatClicked(Task t); | |
public void onTaskViewDismissed(Task t); | |
public void onAllTaskViewsDismissed(); | |
public void onTaskStackFilterTriggered(); | |
@@ -1132,6 +1132,13 @@ | |
} | |
@Override | |
+ public void onTaskFloatClicked(TaskView tv) { | |
+ if (mCb != null) { | |
+ mCb.onTaskFloatClicked(tv.getTask()); | |
+ } | |
+ } | |
+ | |
+ @Override | |
public void onTaskViewLongClicked(final TaskView tv) { | |
final PopupMenu popup = new PopupMenu(getContext(), tv.mHeaderView.mApplicationIcon); | |
mPopup = popup; | |
@@ -1166,6 +1173,9 @@ | |
case R.id.recent_inspect_item: | |
onTaskViewAppInfoClicked(tv); | |
break; | |
+ case R.id.recent_float_mode: | |
+ onTaskFloatClicked(tv); | |
+ break; | |
case R.id.recent_force_stop: | |
{ | |
ActivityManager am = (ActivityManager) getContext() | |
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java | |
index ab2f57d..ef48842 100644 | |
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java | |
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java | |
@@ -42,6 +42,7 @@ | |
interface TaskViewCallbacks { | |
public void onTaskViewAppIconClicked(TaskView tv); | |
public void onTaskViewAppInfoClicked(TaskView tv); | |
+ public void onTaskFloatClicked(TaskView tv); | |
public void onTaskViewLongClicked(TaskView tv); | |
public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask); | |
public void onTaskViewDismissed(TaskView tv); | |
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java | |
index d232c2e..5f439b7 100755 | |
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java | |
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java | |
@@ -826,6 +826,41 @@ | |
startNotificationGutsIntent(intent, appUid); | |
} | |
+ private void launchFloating(PendingIntent pIntent) { | |
+ Intent overlay = new Intent(); | |
+ overlay.addFlags(Intent.FLAG_FLOATING_WINDOW | Intent.FLAG_ACTIVITY_CLEAR_TASK); | |
+ try { | |
+ ActivityManagerNative.getDefault().resumeAppSwitches(); | |
+ } catch (RemoteException e) { | |
+ } | |
+ try { | |
+ pIntent.send(mContext, 0, overlay); | |
+ } catch (PendingIntent.CanceledException e) { | |
+ // the stack trace isn't very helpful here. Just log the exception message. | |
+ Slog.w(TAG, "Sending contentIntent failed: " + e); | |
+ } | |
+ final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); | |
+ dismissKeyguardThenExecute(new OnDismissAction() { | |
+ @Override | |
+ public boolean onDismiss() { | |
+ AsyncTask.execute(new Runnable() { | |
+ public void run() { | |
+ try { | |
+ if (keyguardShowing) { | |
+ ActivityManagerNative.getDefault() | |
+ .keyguardWaitingForActivityDrawn(); | |
+ } | |
+ overrideActivityPendingAppTransition(keyguardShowing); | |
+ } catch (RemoteException e) { | |
+ } | |
+ } | |
+ }); | |
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); | |
+ return true; | |
+ } | |
+ }, false /* afterKeyguardGone */); | |
+ } | |
+ | |
private void startNotificationGutsIntent(final Intent intent, final int appUid) { | |
final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); | |
dismissKeyguardThenExecute(new OnDismissAction() { | |
@@ -864,6 +899,7 @@ | |
row.setTag(sbn.getPackageName()); | |
final View guts = row.findViewById(R.id.notification_guts); | |
final String pkg = sbn.getPackageName(); | |
+ final PendingIntent contentIntent = sbn.getNotification().contentIntent; | |
String appname = pkg; | |
Drawable pkgicon = null; | |
int appUid = -1; | |
@@ -883,6 +919,7 @@ | |
((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(pkgicon); | |
((DateTimeView) row.findViewById(R.id.timestamp)).setTime(sbn.getPostTime()); | |
((TextView) row.findViewById(R.id.pkgname)).setText(appname); | |
+ final View floatButton = guts.findViewById(R.id.notification_float_item); | |
final View settingsButton = guts.findViewById(R.id.notification_inspect_item); | |
final View appSettingsButton | |
= guts.findViewById(R.id.notification_inspect_app_provided_settings); | |
@@ -906,6 +943,13 @@ | |
mContext.getContentResolver().insert(SPAM_MESSAGE_URI, values); | |
removeNotification(sbn.getKey(), null); | |
onNotificationClear(sbn); | |
+ } | |
+ }); | |
+ | |
+ floatButton.setVisibility(View.VISIBLE); | |
+ floatButton.setOnClickListener(new View.OnClickListener() { | |
+ public void onClick(View v) { | |
+ launchFloating(contentIntent); | |
} | |
}); | |
@@ -934,8 +978,9 @@ | |
} else { | |
appSettingsButton.setVisibility(View.GONE); | |
} | |
- } else { | |
- settingsButton.setVisibility(View.GONE); | |
+ } | |
+ if (appUid < 0) { | |
+ floatButton.setVisibility(View.GONE); | |
appSettingsButton.setVisibility(View.GONE); | |
filterButton.setVisibility(View.GONE); | |
} | |
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java | |
index d45c68b..303d66c 100644 | |
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java | |
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java | |
@@ -397,6 +397,10 @@ | |
} | |
@Override | |
+ public void onChildTriggered(View v) { | |
+ } | |
+ | |
+ @Override | |
public void onBeginDrag(View v) { | |
} | |
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java | |
index 7939121..fc91d5d 100644 | |
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java | |
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java | |
@@ -578,6 +578,9 @@ | |
return mPhoneStatusBar.isScreenOnComingFromTouch() ? 1.5f : 1.0f; | |
} | |
+ public void onChildTriggered(View v) { | |
+ } | |
+ | |
public void onBeginDrag(View v) { | |
setSwipingInProgress(true); | |
mAmbientState.onBeginDrag(v); | |
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java | |
index c203c75..77e271b 100644 | |
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java | |
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java | |
@@ -39,6 +39,7 @@ | |
import com.android.internal.widget.DecorContentParent; | |
import com.android.internal.widget.SwipeDismissLayout; | |
+import android.app.Activity; | |
import android.app.ActivityManager; | |
import android.app.KeyguardManager; | |
import android.content.ComponentName; | |
@@ -2583,8 +2584,17 @@ | |
return false; | |
} | |
final Callback cb = getCallback(); | |
- return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) | |
- : super.dispatchTouchEvent(ev); | |
+ if (cb != null && !isDestroyed() && mFeatureId < 0) { | |
+ if (cb instanceof android.app.Activity && mIsFloatingWindow) { | |
+ android.app.Activity act = (android.app.Activity)cb; | |
+ if (shouldCloseOnTouch(act, ev)) { | |
+ act.finishFloating(); | |
+ return true; | |
+ } | |
+ } | |
+ return cb.dispatchTouchEvent(ev); | |
+ } | |
+ return super.dispatchTouchEvent(ev); | |
} | |
@Override | |
@@ -3434,7 +3444,7 @@ | |
void updateWindowResizeState() { | |
Drawable bg = getBackground(); | |
hackTurnOffWindowResizeAnim(bg == null || bg.getOpacity() | |
- != PixelFormat.OPAQUE); | |
+ != PixelFormat.OPAQUE || mIsFloatingWindow); | |
} | |
@Override | |
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java | |
index d97aec7..aae09dd 100644 | |
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java | |
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java | |
@@ -4242,6 +4242,10 @@ | |
} | |
} | |
+ public Rect getContentRect() { | |
+ return new Rect(mContentLeft, mContentTop, mContentRight, mContentBottom); | |
+ } | |
+ | |
/** {@inheritDoc} */ | |
@Override | |
public void layoutWindowLw(WindowState win, WindowState attached) { | |
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java | |
index bd2a7af..ae33233 100644 | |
--- a/services/core/java/com/android/server/am/ActivityManagerService.java | |
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java | |
@@ -8978,6 +8978,23 @@ | |
} | |
} | |
+ public IBinder getActivityForTask(int task, boolean onlyRoot) { | |
+ final ActivityStack mainStack = mStackSupervisor.getFocusedStack(); | |
+ synchronized(this) { | |
+ ArrayList<ActivityStack> stacks = mStackSupervisor.getStacks(); | |
+ for (ActivityStack stack : stacks) { | |
+ TaskRecord r = stack.taskForIdLocked(task); | |
+ | |
+ if (r != null && r.getTopActivity() != null) { | |
+ return r.getTopActivity().appToken; | |
+ } else { | |
+ return null; | |
+ } | |
+ } | |
+ } | |
+ return null; | |
+ } | |
+ | |
private boolean isLockTaskAuthorized(String pkg) { | |
final DevicePolicyManager dpm = (DevicePolicyManager) | |
mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); | |
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java | |
index e10acdf..e1800cf 100755 | |
--- a/services/core/java/com/android/server/am/ActivityRecord.java | |
+++ b/services/core/java/com/android/server/am/ActivityRecord.java | |
@@ -34,6 +34,7 @@ | |
import android.app.ActivityOptions; | |
import android.app.ResultInfo; | |
import android.content.ComponentName; | |
+import android.content.Context; | |
import android.content.Intent; | |
import android.content.pm.ActivityInfo; | |
import android.content.pm.ApplicationInfo; | |
@@ -54,6 +55,7 @@ | |
import android.util.Slog; | |
import android.util.TimeUtils; | |
import android.view.IApplicationToken; | |
+import android.view.ContextThemeWrapper; | |
import android.view.WindowManager; | |
import org.xmlpull.v1.XmlPullParser; | |
@@ -166,6 +168,10 @@ | |
long lastLaunchTime; // time of last lauch of this activity | |
ArrayList<ActivityContainer> mChildContainers = new ArrayList<ActivityContainer>(); | |
+ boolean topIntent; | |
+ boolean newAppTask; | |
+ boolean floatingWindow; | |
+ | |
String stringName; // for caching of toString(). | |
private boolean inHistory; // are we in the history stack? | |
@@ -423,7 +429,9 @@ | |
// is that we have everything, and we shouldn't never consider it | |
// lacking in state to be removed if it dies. | |
haveState = true; | |
- | |
+ topIntent = false; | |
+ floatingWindow = false; | |
+ | |
if (aInfo != null) { | |
if (aInfo.targetActivity == null | |
|| aInfo.launchMode == ActivityInfo.LAUNCH_MULTIPLE | |
@@ -454,6 +462,44 @@ | |
? android.R.style.Theme | |
: android.R.style.Theme_Holo; | |
} | |
+ // This is where the package gets its first context from the attribute-cache | |
+ // In order to hook its attributes we set up our check for floating multi windows here. | |
+ topIntent = true; | |
+ ActivityStack stack = supervisor.getFocusedStack(); | |
+ floatingWindow = (intent.getFlags() & Intent.FLAG_FLOATING_WINDOW) == Intent.FLAG_FLOATING_WINDOW; | |
+ TaskRecord baseRecord = stack != null && stack.mTaskHistory.size() > 0 ? stack.mTaskHistory.get(stack.mTaskHistory.size() -1) : null; | |
+ | |
+ if (baseRecord != null) { | |
+ ActivityRecord record = baseRecord.mActivities.size() > 0 ? baseRecord.mActivities.get(baseRecord.mActivities.size() - 1) : null; | |
+ final boolean floats = (baseRecord.intent.getFlags() & Intent.FLAG_FLOATING_WINDOW) == Intent.FLAG_FLOATING_WINDOW; | |
+ final boolean taskAppAffinity = record == null ? false : aInfo.applicationInfo.packageName.equals(record.packageName); | |
+ newAppTask = (intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == Intent.FLAG_ACTIVITY_NEW_TASK; | |
+ // If the current intent is not a new task we will check its top parent. | |
+ // Perhaps it started out as a multiwindow in which case we pass the flag on | |
+ if (floats && (!newAppTask || taskAppAffinity)) { | |
+ intent.addFlags(Intent.FLAG_FLOATING_WINDOW); | |
+ // Flag the activity as sub-task | |
+ topIntent = false; | |
+ floatingWindow = true; | |
+ } | |
+ } | |
+ // If this is a multiwindow activity we prevent it from messing up the history stack, | |
+ // like jumping back home, killing the current activity or polluting recents | |
+ if (floatingWindow) { | |
+ intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_TASK_ON_HOME); | |
+ intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_SINGLE_TOP); | |
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION); | |
+ // If this is the mother-intent we make it volatile | |
+ if (topIntent) { | |
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); | |
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); | |
+ } | |
+ // Change theme | |
+ realTheme = com.android.internal.R.style.Theme_DeviceDefault_FloatingWindow; | |
+ } else { | |
+ intent.setFlags(intent.getFlags() & ~Intent.FLAG_FLOATING_WINDOW); | |
+ } | |
+ | |
if ((aInfo.flags&ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) { | |
windowFlags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; | |
} | |
@@ -466,8 +512,8 @@ | |
processName = aInfo.processName; | |
} | |
- if (intent != null && (aInfo.flags & ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS) != 0) { | |
- intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); | |
+ if ((intent != null && (aInfo.flags & ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS) != 0) | |
+ || floatingWindow) { | |
} | |
packageName = aInfo.applicationInfo.packageName; | |
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java | |
index 48e5b32..70efae6 100644 | |
--- a/services/core/java/com/android/server/am/ActivityStack.java | |
+++ b/services/core/java/com/android/server/am/ActivityStack.java | |
@@ -46,6 +46,7 @@ | |
import com.android.internal.app.IVoiceInteractor; | |
import com.android.internal.content.ReferrerIntent; | |
import com.android.internal.os.BatteryStatsImpl; | |
+import com.android.server.AttributeCache; | |
import com.android.server.Watchdog; | |
import com.android.server.am.ActivityManagerService.ItemMatcher; | |
import com.android.server.am.ActivityStackSupervisor.ActivityContainer; | |
@@ -83,6 +84,7 @@ | |
import android.service.voice.IVoiceInteractionSession; | |
import android.util.EventLog; | |
import android.util.Slog; | |
+import android.view.ContextThemeWrapper; | |
import android.view.Display; | |
import java.io.FileDescriptor; | |
@@ -152,7 +154,7 @@ | |
* The back history of all previous (and possibly still | |
* running) activities. It contains #TaskRecord objects. | |
*/ | |
- private ArrayList<TaskRecord> mTaskHistory = new ArrayList<TaskRecord>(); | |
+ ArrayList<TaskRecord> mTaskHistory = new ArrayList<TaskRecord>(); | |
/** | |
* Used for validating app tokens with window manager. | |
@@ -1313,13 +1315,13 @@ | |
// Aggregate current change flags. | |
configChanges |= r.configChangeFlags; | |
- if (r.fullscreen) { | |
+ if (r.fullscreen && !r.floatingWindow) { | |
// At this point, nothing else needs to be shown | |
if (DEBUG_VISBILITY) Slog.v(TAG, "Fullscreen: at " + r); | |
behindFullscreen = true; | |
} else if (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()) { | |
if (DEBUG_VISBILITY) Slog.v(TAG, "Showing home: at " + r); | |
- behindFullscreen = true; | |
+ behindFullscreen = !isHomeStack() && r.frontOfTask && task.isOverHomeStack(); | |
} | |
} else { | |
if (DEBUG_VISBILITY) Slog.v( | |
@@ -1507,6 +1509,7 @@ | |
mStackSupervisor.mUserLeaving = false; | |
final TaskRecord prevTask = prev != null ? prev.task : null; | |
+ | |
if (next == null) { | |
// There are no more activities! Let's just start up the | |
// Launcher... | |
@@ -1536,12 +1539,25 @@ | |
} | |
final TaskRecord nextTask = next.task; | |
+ boolean isFloatingWindow = prev != null ? prev.floatingWindow : false; | |
if (prevTask != null && prevTask.stack == this && | |
prevTask.isOverHomeStack() && prev.finishing && prev.frontOfTask) { | |
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); | |
if (prevTask == nextTask) { | |
prevTask.setFrontOfTask(); | |
- } else if (prevTask != topTask()) { | |
+ } else if (prevTask != topTask() && !isFloatingWindow) { | |
+ ArrayList<ActivityRecord> activities = prevTask.mActivities; | |
+ final int numActivities = activities.size(); | |
+ for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { | |
+ final ActivityRecord r = activities.get(activityNdx); | |
+ // r is usually the same as next, but what if two activities were launched | |
+ // before prev finished? | |
+ if (!r.finishing) { | |
+ r.frontOfTask = true; | |
+ break; | |
+ } | |
+ } | |
+ } else if (prevTask != topTask() && !prev.floatingWindow) { | |
// This task is going away but it was supposed to return to the home stack. | |
// Now the task above it has to return to the home task instead. | |
final int taskNdx = mTaskHistory.indexOf(prevTask) + 1; | |
@@ -1637,7 +1653,7 @@ | |
// can be resumed... | |
boolean dontWaitForPause = (next.info.flags&ActivityInfo.FLAG_RESUME_WHILE_PAUSING) != 0; | |
boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause); | |
- if (mResumedActivity != null) { | |
+ if (mResumedActivity != null && !next.floatingWindow) { | |
if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Pausing " + mResumedActivity); | |
pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause); | |
} | |
@@ -1718,7 +1734,7 @@ | |
if (prev.finishing) { | |
if (DEBUG_TRANSITION) Slog.v(TAG, | |
"Prepare close transition: prev=" + prev); | |
- if (mNoAnimActivities.contains(prev)) { | |
+ if (mNoAnimActivities.contains(prev) || next.floatingWindow) { | |
anim = false; | |
mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false); | |
} else { | |
@@ -1733,7 +1749,7 @@ | |
mWindowManager.setAppVisibility(prev.appToken, false); | |
} else { | |
if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare open transition: prev=" + prev); | |
- if (mNoAnimActivities.contains(next)) { | |
+ if (mNoAnimActivities.contains(next) || next.floatingWindow) { | |
anim = false; | |
mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false); | |
} else { | |
@@ -1876,7 +1892,7 @@ | |
if (!next.hasBeenLaunched) { | |
next.hasBeenLaunched = true; | |
} else if (SHOW_APP_STARTING_PREVIEW && lastStack != null && | |
- mStackSupervisor.isFrontStack(lastStack)) { | |
+ mStackSupervisor.isFrontStack(lastStack) && !next.floatingWindow) { | |
mWindowManager.setAppStartingWindow( | |
next.appToken, next.packageName, next.theme, | |
mService.compatibilityInfoForPackageLocked(next.info.applicationInfo), | |
@@ -1909,7 +1925,7 @@ | |
if (!next.hasBeenLaunched) { | |
next.hasBeenLaunched = true; | |
} else { | |
- if (SHOW_APP_STARTING_PREVIEW) { | |
+ if (SHOW_APP_STARTING_PREVIEW && !next.floatingWindow) { | |
mWindowManager.setAppStartingWindow( | |
next.appToken, next.packageName, next.theme, | |
mService.compatibilityInfoForPackageLocked( | |
@@ -2090,11 +2106,11 @@ | |
} | |
if (DEBUG_TRANSITION) Slog.v(TAG, | |
"Prepare open transition: starting " + r); | |
- if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { | |
+ if (((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) || (r.floatingWindow && !r.topIntent)) { | |
mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, keepCurTransition); | |
mNoAnimActivities.add(r); | |
} else { | |
- mWindowManager.prepareAppTransition(newTask | |
+ mWindowManager.prepareAppTransition(newTask && !r.floatingWindow | |
? r.mLaunchTaskBehind | |
? AppTransition.TRANSIT_TASK_OPEN_BEHIND | |
: AppTransition.TRANSIT_TASK_OPEN | |
@@ -2125,7 +2141,7 @@ | |
// tell WindowManager that r is visible even though it is at the back of the stack. | |
mWindowManager.setAppVisibility(r.appToken, true); | |
ensureActivitiesVisibleLocked(null, 0); | |
- } else if (SHOW_APP_STARTING_PREVIEW && doShow) { | |
+ } else if (SHOW_APP_STARTING_PREVIEW && doShow && !r.floatingWindow) { | |
// Figure out if we are transitioning from another activity that is | |
// "has the same starting icon" as the next one. This allows the | |
// window manager to keep the previous window it had previously | |
@@ -2808,7 +2824,7 @@ | |
boolean endTask = index <= 0; | |
if (DEBUG_VISBILITY || DEBUG_TRANSITION) Slog.v(TAG, | |
"Prepare close transition: finishing " + r); | |
- mWindowManager.prepareAppTransition(endTask | |
+ mWindowManager.prepareAppTransition(endTask && !r.floatingWindow | |
? AppTransition.TRANSIT_TASK_CLOSE | |
: AppTransition.TRANSIT_ACTIVITY_CLOSE, false); | |
@@ -3572,7 +3588,7 @@ | |
if (numTasks == 0 || index < 0) { | |
// nothing to do! | |
if (source != null && | |
- (source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { | |
+ ((source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0 || source.floatingWindow)) { | |
ActivityOptions.abort(options); | |
} else { | |
updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options); | |
@@ -3587,7 +3603,7 @@ | |
if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to front transition: task=" + tr); | |
if (source != null && | |
- (source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { | |
+ ((source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION)) != 0 ) { | |
mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false); | |
ActivityRecord r = topRunningActivityLocked(null); | |
if (r != null) { | |
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java | |
index d47ec40..d5a0d0c 100644 | |
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java | |
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java | |
@@ -1907,7 +1907,7 @@ | |
} | |
options = null; | |
movedToFront = true; | |
- } | |
+ } | |
} | |
// If the caller has requested that the target task be | |
// reset, then do so. | |
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java | |
index 584f014..13d73aa 100644 | |
--- a/services/core/java/com/android/server/wm/WindowManagerService.java | |
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java | |
@@ -157,6 +157,7 @@ | |
import java.util.Date; | |
import java.util.HashMap; | |
import java.util.Iterator; | |
+import java.util.Map; | |
import java.util.List; | |
/** {@hide} */ | |
@@ -5689,6 +5690,120 @@ | |
mLastStatusBarVisibility |= flags; | |
} | |
+ private void moveTaskAndActivityToFront(int taskId) { | |
+ try { | |
+ moveTaskToTop(taskId); | |
+ mActivityManager.moveTaskToFront(taskId, 0, null); | |
+ } catch (RemoteException e) { | |
+ Log.e(TAG, "Cannot move the activity to front", e); | |
+ } | |
+ } | |
+ | |
+ @Override | |
+ public void notifyFloatActivityTouched(IBinder token, boolean force) { | |
+ synchronized(mWindowMap) { | |
+ boolean changed = false; | |
+ if (token != null) { | |
+ AppWindowToken newFocus = findAppWindowToken(token); | |
+ if (newFocus == null) { | |
+ Slog.w(TAG, "Attempted to set focus to non-existing app token: " + token); | |
+ return; | |
+ } | |
+ changed = mFocusedApp != newFocus; | |
+ mFocusedApp = newFocus; | |
+ if (changed || force) { | |
+ if (DEBUG_FOCUS) Slog.v(TAG, "Changed app focus to " + token); | |
+ mInputMonitor.setFocusedAppLw(newFocus); | |
+ } | |
+ } | |
+ | |
+ if (changed || force) { | |
+ final long origId = Binder.clearCallingIdentity(); | |
+ updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true); | |
+ mH.removeMessages(H.REPORT_FOCUS_CHANGE); | |
+ mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE); | |
+ Binder.restoreCallingIdentity(origId); | |
+ } | |
+ } | |
+ | |
+ if (!force) { | |
+ final long origId = Binder.clearCallingIdentity(); | |
+ try { | |
+ int taskId = mActivityManager.getTaskForActivity(token, false); | |
+ moveTaskAndActivityToFront(taskId); | |
+ } catch (RemoteException e) { | |
+ Log.e(TAG, "Cannot move the activity to front", e); | |
+ } | |
+ Binder.restoreCallingIdentity(origId); | |
+ } | |
+ } | |
+ | |
+ @Override | |
+ public Rect getAppFullscreenViewRect() { | |
+ final DisplayContent displayContent = getDefaultDisplayContentLocked(); | |
+ final boolean rotated = (mRotation == Surface.ROTATION_90 | |
+ || mRotation == Surface.ROTATION_270); | |
+ final int realdw = rotated ? | |
+ displayContent.mBaseDisplayHeight : displayContent.mBaseDisplayWidth; | |
+ final int realdh = rotated ? | |
+ displayContent.mBaseDisplayWidth : displayContent.mBaseDisplayHeight; | |
+ | |
+ int dw = realdw; | |
+ int dh = realdh; | |
+ | |
+ // Get application display metrics. | |
+ int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation); | |
+ int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation); | |
+ | |
+ return new Rect(0, 0, appWidth, appHeight); | |
+ } | |
+ | |
+ @Override | |
+ public Rect getAppMinimumViewRect() { | |
+ final DisplayContent displayContent = getDefaultDisplayContentLocked(); | |
+ final boolean rotated = (mRotation == Surface.ROTATION_90 | |
+ || mRotation == Surface.ROTATION_270); | |
+ final int realdw = rotated ? | |
+ displayContent.mBaseDisplayHeight : displayContent.mBaseDisplayWidth; | |
+ final int realdh = rotated ? | |
+ displayContent.mBaseDisplayWidth : displayContent.mBaseDisplayHeight; | |
+ | |
+ int dw = realdw; | |
+ int dh = realdh; | |
+ | |
+ // Get application display metrics. | |
+ int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation); | |
+ int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation); | |
+ | |
+ return new Rect(0, 0, (int)(appWidth * 0.5f) , (int)(appHeight * 0.5f)); | |
+ } | |
+ | |
+ @Override | |
+ public Rect getFloatViewRect() { | |
+ final DisplayContent displayContent = getDefaultDisplayContentLocked(); | |
+ final boolean rotated = (mRotation == Surface.ROTATION_90 | |
+ || mRotation == Surface.ROTATION_270); | |
+ final int realdw = rotated ? | |
+ displayContent.mBaseDisplayHeight : displayContent.mBaseDisplayWidth; | |
+ final int realdh = rotated ? | |
+ displayContent.mBaseDisplayWidth : displayContent.mBaseDisplayHeight; | |
+ final boolean nativeLandscape = | |
+ (displayContent.mBaseDisplayHeight < displayContent.mBaseDisplayWidth); | |
+ | |
+ int dw = realdw; | |
+ int dh = realdh; | |
+ | |
+ // Get application display metrics. | |
+ int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation); | |
+ int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation); | |
+ | |
+ if (nativeLandscape ^ rotated) { | |
+ return new Rect(0, 0, (int)(appWidth * 0.7f), (int)(appHeight * 0.9f)); | |
+ } else { | |
+ return new Rect(0, 0, (int)(appWidth * 0.9f) , (int)(appHeight * 0.7f)); | |
+ } | |
+ } | |
+ | |
// Called by window manager policy. Not exposed externally. | |
@Override | |
public int getLidState() { | |
@@ -10843,8 +10958,16 @@ | |
if (DEBUG_FOCUS_LIGHT) Slog.v(TAG, "findFocusedWindow: Found new focus @ " + i + | |
" = " + win); | |
- return win; | |
- } | |
+ | |
+ // Dispatch to this window if it is wants key events. | |
+ if (win.canReceiveKeys()) { | |
+ if (mFocusedApp != null) { | |
+ return win; | |
+ } else { | |
+ return win; | |
+ } | |
+ } | |
+ } | |
if (DEBUG_FOCUS_LIGHT) Slog.v(TAG, "findFocusedWindow: No focusable windows."); | |
return null; | |
@@ -11178,7 +11301,7 @@ | |
return mPolicy.hasPermanentMenuKey(); | |
} | |
- @Override | |
+ @Override | |
public boolean needsNavigationBar() { | |
return mPolicy.needsNavigationBar(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment