Merge "[SuwLib] Avoid infinite loop in SystemBarHelper"
This commit is contained in:
commit
4b88e9f4d9
|
@ -23,6 +23,7 @@ import android.content.Context;
|
|||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
|
@ -42,6 +43,8 @@ import com.android.setupwizardlib.R;
|
|||
*/
|
||||
public class SystemBarHelper {
|
||||
|
||||
private static final String TAG = "SystemBarHelper";
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
private static final int DEFAULT_IMMERSIVE_FLAGS =
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
|
@ -59,6 +62,12 @@ public class SystemBarHelper {
|
|||
*/
|
||||
private static final int STATUS_BAR_DISABLE_BACK = 0x00400000;
|
||||
|
||||
/**
|
||||
* The maximum number of retries when peeking the decor view. When polling for the decor view,
|
||||
* waiting it to be installed, set a maximum number of retries.
|
||||
*/
|
||||
private static final int PEEK_DECOR_VIEW_RETRIES = 3;
|
||||
|
||||
/**
|
||||
* Hide the navigation bar for a dialog.
|
||||
*
|
||||
|
@ -69,7 +78,8 @@ public class SystemBarHelper {
|
|||
final Window window = dialog.getWindow();
|
||||
temporarilyDisableDialogFocus(window);
|
||||
addImmersiveFlagsToWindow(window, DIALOG_IMMERSIVE_FLAGS);
|
||||
addImmersiveFlagsToDecorView(window, new Handler(), DIALOG_IMMERSIVE_FLAGS);
|
||||
addImmersiveFlagsToDecorView(window, new Handler(), DIALOG_IMMERSIVE_FLAGS,
|
||||
PEEK_DECOR_VIEW_RETRIES);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,7 +94,8 @@ public class SystemBarHelper {
|
|||
public static void hideSystemBars(final Window window) {
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
|
||||
addImmersiveFlagsToWindow(window, DEFAULT_IMMERSIVE_FLAGS);
|
||||
addImmersiveFlagsToDecorView(window, new Handler(), DEFAULT_IMMERSIVE_FLAGS);
|
||||
addImmersiveFlagsToDecorView(window, new Handler(), DEFAULT_IMMERSIVE_FLAGS,
|
||||
PEEK_DECOR_VIEW_RETRIES);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,25 +172,30 @@ public class SystemBarHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN only takes effect when it is added a view instead of
|
||||
* View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN only takes effect when it is added to a view instead of
|
||||
* the window.
|
||||
*/
|
||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
||||
private static void addImmersiveFlagsToDecorView(final Window window, final Handler handler,
|
||||
final int vis) {
|
||||
final int vis, final int retries) {
|
||||
// Use peekDecorView instead of getDecorView so that clients can still set window features
|
||||
// after calling this method.
|
||||
final View decorView = window.peekDecorView();
|
||||
if (decorView != null) {
|
||||
addVisibilityFlag(decorView, vis);
|
||||
} else {
|
||||
// If the decor view is not installed yet, try again in the next loop.
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
addImmersiveFlagsToDecorView(window, handler, vis);
|
||||
}
|
||||
});
|
||||
final int newRetries = retries - 1;
|
||||
if (newRetries >= 0) {
|
||||
// If the decor view is not installed yet, try again in the next loop.
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
addImmersiveFlagsToDecorView(window, handler, vis, newRetries);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Log.w(TAG, "Cannot get decor view of window: " + window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,24 +19,18 @@ package com.android.setupwizardlib.test;
|
|||
import android.annotation.SuppressLint;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.SystemClock;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.view.InputQueue;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceHolder.Callback2;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.setupwizardlib.test.util.MockWindow;
|
||||
import com.android.setupwizardlib.util.SystemBarHelper;
|
||||
|
||||
public class SystemBarHelperTest extends AndroidTestCase {
|
||||
|
@ -112,6 +106,24 @@ public class SystemBarHelperTest extends AndroidTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testHideSystemBarsNoInfiniteLoop() throws InterruptedException {
|
||||
final TestWindow window = new TestWindow(getContext(), null);
|
||||
final HandlerThread thread = new HandlerThread("SystemBarHelperTest");
|
||||
thread.start();
|
||||
final Handler handler = new Handler(thread.getLooper());
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SystemBarHelper.hideSystemBars(window);
|
||||
}
|
||||
});
|
||||
SystemClock.sleep(500); // Wait for the looper to drain all the messages
|
||||
thread.quit();
|
||||
// Initial peek + 3 retries = 4 tries total
|
||||
assertEquals("Peek decor view should give up after 4 tries", 4, window.peekDecorViewCount);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testHideSystemBarsDialog() {
|
||||
final Dialog dialog = new Dialog(mContext);
|
||||
|
@ -167,175 +179,16 @@ public class SystemBarHelperTest extends AndroidTestCase {
|
|||
return window;
|
||||
}
|
||||
|
||||
private static class TestWindow extends Window {
|
||||
private static class TestWindow extends MockWindow {
|
||||
|
||||
private View mDecorView;
|
||||
public int peekDecorViewCount = 0;
|
||||
|
||||
public TestWindow(Context context, View decorView) {
|
||||
super(context);
|
||||
mDecorView = decorView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeSurface(Callback2 callback2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeInputQueue(InputQueue.Callback callback) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFloating() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view, LayoutParams layoutParams) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addContentView(View view, LayoutParams layoutParams) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getCurrentFocus() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayoutInflater getLayoutInflater() {
|
||||
return LayoutInflater.from(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(CharSequence charSequence) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitleColor(int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openPanel(int i, KeyEvent keyEvent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closePanel(int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void togglePanel(int i, KeyEvent keyEvent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidatePanelMenu(int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performPanelShortcut(int i, int i2, KeyEvent keyEvent, int i3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performPanelIdentifierAction(int i, int i2, int i3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeAllPanels() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performContextMenuIdentifierAction(int i, int i2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration configuration) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackgroundDrawable(Drawable drawable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFeatureDrawableResource(int i, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFeatureDrawableUri(int i, Uri uri) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFeatureDrawable(int i, Drawable drawable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFeatureDrawableAlpha(int i, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFeatureInt(int i, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeKeyEvents(boolean b) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean superDispatchKeyEvent(KeyEvent keyEvent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean superDispatchKeyShortcutEvent(KeyEvent keyEvent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean superDispatchTouchEvent(MotionEvent motionEvent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean superDispatchTrackballEvent(MotionEvent motionEvent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean superDispatchGenericMotionEvent(MotionEvent motionEvent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getDecorView() {
|
||||
return mDecorView;
|
||||
|
@ -343,67 +196,16 @@ public class SystemBarHelperTest extends AndroidTestCase {
|
|||
|
||||
@Override
|
||||
public View peekDecorView() {
|
||||
peekDecorViewCount++;
|
||||
return mDecorView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle saveHierarchyState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreHierarchyState(Bundle bundle) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActive() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChildDrawable(int i, Drawable drawable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChildInt(int i, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShortcutKey(int i, KeyEvent keyEvent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVolumeControlStream(int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVolumeControlStream() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStatusBarColor() {
|
||||
return 0;
|
||||
public void setNavigationBarColor(int i) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatusBarColor(int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNavigationBarColor() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigationBarColor(int i) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,252 @@
|
|||
package com.android.setupwizardlib.test.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.InputQueue;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
|
||||
public class MockWindow extends Window {
|
||||
|
||||
public MockWindow(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeSurface(SurfaceHolder.Callback2 callback2) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeInputQueue(InputQueue.Callback callback) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFloating() {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(int i) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view, ViewGroup.LayoutParams layoutParams) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addContentView(View view, ViewGroup.LayoutParams layoutParams) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getCurrentFocus() {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayoutInflater getLayoutInflater() {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(CharSequence charSequence) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitleColor(int i) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openPanel(int i, KeyEvent keyEvent) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closePanel(int i) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void togglePanel(int i, KeyEvent keyEvent) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidatePanelMenu(int i) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performPanelShortcut(int i, int i1, KeyEvent keyEvent, int i2) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performPanelIdentifierAction(int i, int i1, int i2) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeAllPanels() {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performContextMenuIdentifierAction(int i, int i1) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration configuration) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackgroundDrawable(Drawable drawable) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFeatureDrawableResource(int i, int i1) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFeatureDrawableUri(int i, Uri uri) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFeatureDrawable(int i, Drawable drawable) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFeatureDrawableAlpha(int i, int i1) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFeatureInt(int i, int i1) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeKeyEvents(boolean b) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean superDispatchKeyEvent(KeyEvent keyEvent) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean superDispatchKeyShortcutEvent(KeyEvent keyEvent) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean superDispatchTouchEvent(MotionEvent motionEvent) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean superDispatchTrackballEvent(MotionEvent motionEvent) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean superDispatchGenericMotionEvent(MotionEvent motionEvent) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getDecorView() {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public View peekDecorView() {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle saveHierarchyState() {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreHierarchyState(Bundle bundle) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActive() {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChildDrawable(int i, Drawable drawable) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChildInt(int i, int i1) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShortcutKey(int i, KeyEvent keyEvent) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVolumeControlStream(int i) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVolumeControlStream() {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStatusBarColor() {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatusBarColor(int i) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNavigationBarColor() {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigationBarColor(int i) {
|
||||
throw new UnsupportedOperationException("Unexpected method call on mock");
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue