安卓12悬浮窗功能修改:RawWindow在有无障碍权限时挂载在无障碍WindowManager 避免setTouchable(false) 失效

This commit is contained in:
TonyJiangWJ 2022-01-16 17:23:41 +08:00
parent b486d0cbff
commit e1009c5003
27 changed files with 1383 additions and 79 deletions

View File

@ -145,6 +145,7 @@ dependencies {
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.activity:activity-ktx:1.4.0'
// Personal libraries
implementation 'com.github.hyb1996:MutableTheme:1.0.0'
// Material Dialogs

View File

@ -22,7 +22,6 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
import com.stardust.app.FragmentPagerAdapterBuilder;
import com.stardust.app.OnActivityResultDelegate;
import com.stardust.autojs.core.floaty.AccessibilityFloatyService;
import com.stardust.autojs.core.permission.OnRequestPermissionsResultCallback;
import com.stardust.autojs.core.permission.PermissionRequestProxyActivity;
import com.stardust.autojs.core.permission.RequestPermissionCallbacks;
@ -273,7 +272,6 @@ public class MainActivity extends BaseActivity implements OnActivityResultDelega
FloatyWindowManger.hideCircularMenu();
ForegroundService.stop(this);
stopService(new Intent(this, FloatyService.class));
stopService(new Intent(this, AccessibilityFloatyService.class));
AutoJs.getInstance().getScriptEngineService().stopAll();
}

View File

@ -53,7 +53,6 @@ dependencies {
exclude group: 'com.android.support'
})
api 'com.google.android.material:material:1.4.0'
api 'com.github.hyb1996:EnhancedFloaty:0.31'
api 'com.makeramen:roundedimageview:2.3.0'
// OpenCv
api project(path: ':autojs-aar:opencv')

View File

@ -50,11 +50,9 @@
android:resource="@xml/accessibility_service_config"/>
</service>
<service android:name="com.stardust.autojs.core.floaty.AccessibilityFloatyService"
android:exported="false"/>
<service android:name="com.stardust.enhancedfloaty.FloatyService"
android:exported="false" tools:node="merge"/>
</application>
</manifest>

View File

@ -2,11 +2,15 @@ package com.stardust.autojs.core.accessibility
import android.accessibilityservice.AccessibilityServiceInfo
import android.os.Build
import android.view.WindowManager
import com.stardust.autojs.core.pref.Pref
import com.stardust.enhancedfloaty.FloatyService
import com.stardust.view.accessibility.AccessibilityService
class AccessibilityService : AccessibilityService() {
private lateinit var windowManager: WindowManager
override fun onServiceConnected() {
val serviceInfo = serviceInfo
if (Pref.isStableModeEnabled) {
@ -21,7 +25,16 @@ class AccessibilityService : AccessibilityService() {
serviceInfo.flags = serviceInfo.flags and AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE.inv()
}
}
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
setServiceInfo(serviceInfo)
super.onServiceConnected()
}
override fun getWindowManager() : WindowManager {
return windowManager
}
override fun refreshFloatyService() {
FloatyService.getInstance()?.refreshAccessWindowManager()
}
}

View File

@ -1,66 +0,0 @@
package com.stardust.autojs.core.floaty;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import com.stardust.enhancedfloaty.FloatyService;
import com.stardust.enhancedfloaty.FloatyWindow;
import com.taobao.idlefish.AccessibilityService;
import java.util.concurrent.CopyOnWriteArraySet;
public class AccessibilityFloatyService extends FloatyService {
private static final CopyOnWriteArraySet<FloatyWindow> windows = new CopyOnWriteArraySet();
private static AccessibilityFloatyService instance;
private WindowManager mWindowManager;
public static void addWindow(FloatyWindow window) {
if (windows.add(window) && instance != null) {
window.onCreate(instance, instance.mWindowManager);
}
}
public static void removeWindow(FloatyWindow window) {
windows.remove(window);
}
public void onCreate() {
super.onCreate();
if (AccessibilityService.Companion.getInstance() != null) {
this.mWindowManager = AccessibilityService.Companion.getInstance().getWindowManager();
}
if (this.mWindowManager == null) {
this.mWindowManager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
}
for (FloatyWindow delegate : windows) {
delegate.onCreate(this, this.mWindowManager);
}
instance = this;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
if (AccessibilityService.Companion.getInstance() == null) {
return null;
}
return AccessibilityService.Companion.getInstance().onBind(intent);
}
public void onDestroy() {
super.onDestroy();
instance = null;
for (FloatyWindow delegate : windows) {
delegate.onServiceDestroy(this);
}
}
}

View File

@ -84,7 +84,7 @@ public class RawWindow extends FloatyWindow {
private int getWindowType() {
if (AccessibilityService.Companion.getInstance()!= null
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
return WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
}
return WindowTypeCompat.getWindowType();
@ -116,6 +116,9 @@ public class RawWindow extends FloatyWindow {
@Override
public void close() {
super.close();
if (mContentView == null) {
return;
}
ViewExtras.recycle(mContentView);
ViewParent parent = mContentView.getParent();
if (parent instanceof View) {

View File

@ -7,7 +7,6 @@ import android.view.ContextThemeWrapper;
import android.view.View;
import com.stardust.autojs.R;
import com.stardust.autojs.core.floaty.AccessibilityFloatyService;
import com.stardust.autojs.core.floaty.BaseResizableFloatyWindow;
import com.stardust.autojs.core.floaty.RawWindow;
import com.stardust.autojs.core.ui.JsViewHelper;
@ -112,8 +111,8 @@ public class Floaty {
public JsRawWindow(RawWindow.RawFloaty floaty) {
mWindow = new RawWindow(floaty);
mUiHandler.post(() -> {
mUiHandler.getContext().startService(new Intent(mUiHandler.getContext(), AccessibilityFloatyService.class));
AccessibilityFloatyService.addWindow(mWindow);
mUiHandler.getContext().startService(new Intent(mUiHandler.getContext(), FloatyService.class));
FloatyService.addWindow(mWindow);
});
RuntimeException exception = mWindow.waitForCreation();
if (exception != Exceptions.NO_EXCEPTION && exception != null) {

View File

@ -0,0 +1,95 @@
package com.stardust.enhancedfloaty;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import com.stardust.autojs.core.floaty.RawWindow;
import com.taobao.idlefish.AccessibilityService;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Created by Stardust on 2017/5/1.
*/
public class FloatyService extends Service {
private static CopyOnWriteArraySet<FloatyWindow> windows = new CopyOnWriteArraySet<>();
private static FloatyService instance;
private WindowManager mWindowManager;
private WindowManager accessibilityWindowManager;
public static void addWindow(FloatyWindow window) {
if (windows.add(window) && instance != null) {
instance.appendWindow(window);
}
}
public static void removeWindow(FloatyWindow window) {
windows.remove(window);
}
public static FloatyService getInstance() {
return instance;
}
/**
* 根据无障碍链接情况刷新windowManager
*/
public void refreshAccessWindowManager() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
// 仅仅针对安卓12优化
return;
}
if (AccessibilityService.Companion.getInstance() != null) {
accessibilityWindowManager = AccessibilityService.Companion.getInstance().getWindowManager();
} else {
accessibilityWindowManager = null;
}
// TODO 目前设计当无障碍服务丢失后 RawWindow会自动移除 后续考虑将window重新添加 但是目前没有完美的方法
}
@Override
public void onCreate() {
super.onCreate();
mWindowManager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
refreshAccessWindowManager();
instance = this;
for (FloatyWindow delegate : windows) {
appendWindow(delegate);
}
}
private void appendWindow(FloatyWindow window) {
if (instance == null) {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
instance.accessibilityWindowManager != null && window instanceof RawWindow) {
window.onCreate(instance, instance.accessibilityWindowManager);
} else {
window.onCreate(instance, instance.mWindowManager);
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
instance = null;
for (FloatyWindow delegate : windows) {
delegate.onServiceDestroy(this);
}
}
}

View File

@ -0,0 +1,116 @@
package com.stardust.enhancedfloaty;
import android.view.View;
import android.view.WindowManager;
import androidx.annotation.CallSuper;
/**
* Created by Stardust on 2017/5/1.
*/
public abstract class FloatyWindow {
private WindowManager mWindowManager;
private FloatyService mFloatyService;
private WindowBridge mWindowBridge;
private WindowManager.LayoutParams mWindowLayoutParams;
private View mWindowView;
@CallSuper
public void onCreate(FloatyService service, WindowManager manager) {
mFloatyService = service;
mWindowManager = manager;
onCreateWindow(service, manager);
}
protected void onCreateWindow(FloatyService service, WindowManager manager) {
setWindowLayoutParams(onCreateWindowLayoutParams());
setWindowView(onCreateView(service));
setWindowBridge(onCreateWindowBridge(getWindowLayoutParams()));
onViewCreated(getWindowView());
//attach to window
attachToWindow(getWindowView(), getWindowManager());
}
protected void onViewCreated(View view) {
}
protected void attachToWindow(View view, WindowManager manager){
getWindowManager().addView(view, getWindowLayoutParams());
onAttachToWindow(view, manager);
}
protected void onAttachToWindow(View view, WindowManager manager) {
}
protected abstract View onCreateView(FloatyService service);
protected WindowBridge onCreateWindowBridge(WindowManager.LayoutParams params) {
return new WindowBridge.DefaultImpl(params, getWindowManager(), getWindowView());
}
protected abstract WindowManager.LayoutParams onCreateWindowLayoutParams();
public void updateWindowLayoutParams(WindowManager.LayoutParams params) {
setWindowLayoutParams(params);
mWindowManager.updateViewLayout(getWindowView(), getWindowLayoutParams());
}
protected void setWindowManager(WindowManager windowManager) {
mWindowManager = windowManager;
}
public WindowManager.LayoutParams getWindowLayoutParams() {
return mWindowLayoutParams;
}
protected void setWindowLayoutParams(WindowManager.LayoutParams windowLayoutParams) {
mWindowLayoutParams = windowLayoutParams;
}
public View getWindowView() {
return mWindowView;
}
protected void setWindowView(View windowView) {
mWindowView = windowView;
}
public FloatyService getFloatyService() {
return mFloatyService;
}
public WindowManager getWindowManager() {
return mWindowManager;
}
public WindowBridge getWindowBridge() {
return mWindowBridge;
}
protected void setWindowBridge(WindowBridge windowBridge) {
mWindowBridge = windowBridge;
}
public void onServiceDestroy(FloatyService service) {
close();
}
public void close() {
try {
getWindowManager().removeView(getWindowView());
FloatyService.removeWindow(this);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,141 @@
package com.stardust.enhancedfloaty;
import android.view.View;
import androidx.annotation.Nullable;
/**
* Created by Stardust on 2017/4/19.
*/
public interface ResizableExpandableFloaty {
View inflateCollapsedView(FloatyService service, ResizableExpandableFloatyWindow window);
View inflateExpandedView(FloatyService service, ResizableExpandableFloatyWindow window);
@Nullable
View getResizerView(View expandedView);
@Nullable
View getMoveCursorView(View expandedView);
float getCollapsedHiddenWidthRadio();
float getCollapsedViewUnpressedAlpha();
float getCollapsedViewPressedAlpha();
boolean shouldRequestFocusWhenExpand();
int getInitialX();
int getInitialY();
int getInitialHeight();
int getInitialWidth();
boolean isInitialExpanded();
abstract class AbstractResizableExpandableFloaty implements ResizableExpandableFloaty {
private float mCollapsedHiddenWidthRadio = 0f;
private float mCollapsedViewUnpressedAlpha = 0.7f;
private float mCollapsedViewPressedAlpha = 1.0f;
private boolean mShouldRequestFocusWhenExpand = true;
private int mInitialX;
private int mInitialY;
private boolean mInitialExpanded = false;
private int mInitialHeight;
private int mInitialWidth;
@Nullable
public View getResizerView(View expandedView) {
return null;
}
@Nullable
public View getMoveCursorView(View expandedView) {
return null;
}
public float getCollapsedHiddenWidthRadio() {
return mCollapsedHiddenWidthRadio;
}
public void setCollapsedHiddenWidthRadio(float collapsedHiddenWidthRadio) {
this.mCollapsedHiddenWidthRadio = collapsedHiddenWidthRadio;
}
public float getCollapsedViewUnpressedAlpha() {
return mCollapsedViewUnpressedAlpha;
}
public void setCollapsedViewUnpressedAlpha(float collapsedViewUnpressedAlpha) {
mCollapsedViewUnpressedAlpha = collapsedViewUnpressedAlpha;
}
public float getCollapsedViewPressedAlpha() {
return mCollapsedViewPressedAlpha;
}
public void setCollapsedViewPressedAlpha(float collapsedViewPressedAlpha) {
mCollapsedViewPressedAlpha = collapsedViewPressedAlpha;
}
public boolean shouldRequestFocusWhenExpand() {
return mShouldRequestFocusWhenExpand;
}
public void setShouldRequestFocusWhenExpand(boolean requestFocusWhenExpand) {
mShouldRequestFocusWhenExpand = requestFocusWhenExpand;
}
public int getInitialX() {
return mInitialX;
}
public void setInitialX(int initialX) {
mInitialX = initialX;
}
public int getInitialY() {
return mInitialY;
}
public void setInitialY(int initialY) {
mInitialY = initialY;
}
public boolean isInitialExpanded() {
return mInitialExpanded;
}
public void setInitialExpanded(boolean initialExpanded) {
mInitialExpanded = initialExpanded;
}
@Override
public int getInitialHeight() {
return mInitialHeight;
}
public void setInitialHeight(int initialHeight) {
mInitialHeight = initialHeight;
}
@Override
public int getInitialWidth() {
return mInitialWidth;
}
public void setInitialWidth(int initialWidth) {
mInitialWidth = initialWidth;
}
}
}

View File

@ -0,0 +1,302 @@
package com.stardust.enhancedfloaty;
import android.graphics.PixelFormat;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import com.stardust.autojs.R;
import com.stardust.enhancedfloaty.gesture.DragGesture;
import com.stardust.enhancedfloaty.gesture.ResizeGesture;
import com.stardust.enhancedfloaty.util.WindowTypeCompat;
import com.stardust.widget.ViewSwitcher;
/**
* Created by Stardust on 2017/4/18.
*/
public class ResizableExpandableFloatyWindow extends FloatyWindow {
private static final int INITIAL_WINDOW_PARAM_FLAG = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
private static final String TAG = "ExpandableFloatyService";
private ResizableExpandableFloaty mFloaty;
private ViewSwitcher mCollapseExpandViewSwitcher;
private View mCollapsedView;
private View mExpandedView;
private View mResizer;
private View mMoveCursor;
private DragGesture mDragGesture;
private int mCollapsedViewX, mCollapsedViewY;
private int mExpandedViewX, mExpandedViewY;
private ViewStack mViewStack = new ViewStack(new ViewStack.CurrentViewSetter() {
@Override
public void setCurrentView(View v) {
mCollapseExpandViewSwitcher.setSecondView(v);
}
});
public ResizableExpandableFloatyWindow(ResizableExpandableFloaty floaty) {
if (floaty == null) {
throw new NullPointerException("floaty == null");
}
mFloaty = floaty;
}
@Override
protected View onCreateView(FloatyService service) {
inflateWindowViews(service);
View windowView = View.inflate(service, R.layout.ef_expandable_floaty_container, null);
windowView.setFocusableInTouchMode(true);
mCollapseExpandViewSwitcher = windowView.findViewById(R.id.container);
mCollapseExpandViewSwitcher.setMeasureAllChildren(false);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mCollapseExpandViewSwitcher.addView(mCollapsedView, params);
mCollapseExpandViewSwitcher.addView(mExpandedView, params);
mViewStack.setRootView(mExpandedView);
return windowView;
}
@Override
protected void onAttachToWindow(View view, WindowManager manager) {
super.onAttachToWindow(view, manager);
initGesture();
setKeyListener();
setInitialState();
}
public View getCollapsedView() {
return mCollapsedView;
}
public View getExpandedView() {
return mExpandedView;
}
public View getResizer() {
return mResizer;
}
public View getMoveCursor() {
return mMoveCursor;
}
protected ViewStack getViewStack() {
return mViewStack;
}
protected int getCollapsedViewX() {
return mCollapsedViewX;
}
protected void setCollapsedViewX(int collapsedViewX) {
mCollapsedViewX = collapsedViewX;
}
protected int getCollapsedViewY() {
return mCollapsedViewY;
}
protected void setCollapsedViewY(int collapsedViewY) {
mCollapsedViewY = collapsedViewY;
}
protected int getExpandedViewX() {
return mExpandedViewX;
}
protected void setExpandedViewX(int expandedViewX) {
mExpandedViewX = expandedViewX;
}
protected int getExpandedViewY() {
return mExpandedViewY;
}
protected void setExpandedViewY(int expandedViewY) {
mExpandedViewY = expandedViewY;
}
protected void setInitialState() {
ResizableExpandableFloaty floaty = getFloaty();
boolean expand = floaty.isInitialExpanded();
if (expand) {
setExpandedViewX(floaty.getInitialX());
setExpandedViewY(floaty.getInitialY());
expand();
} else {
setCollapsedViewX(floaty.getInitialX());
setCollapsedViewX(floaty.getInitialY());
getWindowBridge().updatePosition(getCollapsedViewX(), getCollapsedViewY());
}
}
@Override
protected WindowBridge onCreateWindowBridge(WindowManager.LayoutParams params) {
return new WindowBridge.DefaultImpl(params, getWindowManager(), getWindowView()) {
@Override
public void updatePosition(int x, int y) {
super.updatePosition(x, y);
if (getViewSwitcher().getCurrentView() == getExpandedView()) {
setExpandedViewX(x);
setExpandedViewY(y);
} else {
setCollapsedViewX(x);
setCollapsedViewY(y);
}
}
};
}
protected void inflateWindowViews(FloatyService service) {
ResizableExpandableFloaty floaty = getFloaty();
mExpandedView = floaty.inflateExpandedView(service, this);
mCollapsedView = floaty.inflateCollapsedView(service, this);
mResizer = floaty.getResizerView(getExpandedView());
mMoveCursor = floaty.getMoveCursorView(getExpandedView());
}
protected WindowManager.LayoutParams onCreateWindowLayoutParams() {
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowTypeCompat.getPhoneWindowType(),
INITIAL_WINDOW_PARAM_FLAG,
PixelFormat.TRANSLUCENT);
layoutParams.gravity = Gravity.TOP | Gravity.START;
return layoutParams;
}
protected void initGesture() {
enableResize();
enableMove();
}
protected void enableResize() {
if (getResizer() != null) {
ResizeGesture.enableResize(getResizer(), getExpandedView(), getWindowBridge());
}
}
public ResizableExpandableFloaty getFloaty() {
return mFloaty;
}
protected void enableMove() {
if (getMoveCursor() != null) {
DragGesture gesture = new DragGesture(getWindowBridge(), getMoveCursor());
gesture.setPressedAlpha(1.0f);
}
DragGesture dragGesture = new DragGesture(getWindowBridge(), getCollapsedView());
dragGesture.setUnpressedAlpha(getFloaty().getCollapsedViewUnpressedAlpha());
dragGesture.setPressedAlpha(getFloaty().getCollapsedViewPressedAlpha());
dragGesture.setKeepToSide(true);
dragGesture.setKeepToSideHiddenWidthRadio(getFloaty().getCollapsedHiddenWidthRadio());
dragGesture.setOnDraggedViewClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
expand();
}
});
setDragGesture(dragGesture);
}
protected void setDragGesture(DragGesture dragGesture) {
mDragGesture = dragGesture;
}
protected DragGesture getDragGesture() {
return mDragGesture;
}
public void expand() {
getViewSwitcher().showSecond();
//enableWindowLimit();
if (getFloaty().shouldRequestFocusWhenExpand()) {
requestWindowFocus();
}
getDragGesture().setKeepToSide(false);
getWindowBridge().updatePosition(getExpandedViewX(), getExpandedViewY());
}
protected ViewSwitcher getViewSwitcher() {
return mCollapseExpandViewSwitcher;
}
public void collapse() {
getViewSwitcher().showFirst();
disableWindowFocus();
setWindowLayoutNoLimit();
getDragGesture().setKeepToSide(true);
getWindowBridge().updatePosition(getCollapsedViewX(), getCollapsedViewY());
}
protected void setKeyListener() {
getWindowView().setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
onBackPressed();
return true;
}
if (keyCode == KeyEvent.KEYCODE_HOME) {
onHomePressed();
return true;
}
return false;
}
});
}
private void onBackPressed() {
ViewStack viewStack = getViewStack();
if (viewStack.canGoBack()) {
viewStack.goBack();
} else {
collapse();
}
}
private void onHomePressed() {
getViewStack().goBackToFirst();
collapse();
}
public void disableWindowFocus() {
WindowManager.LayoutParams windowLayoutParams = getWindowLayoutParams();
windowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
updateWindowLayoutParams(windowLayoutParams);
}
public void setWindowLayoutInScreen() {
WindowManager.LayoutParams windowLayoutParams = getWindowLayoutParams();
windowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
updateWindowLayoutParams(windowLayoutParams);
}
public void requestWindowFocus() {
WindowManager.LayoutParams windowLayoutParams = getWindowLayoutParams();
windowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
updateWindowLayoutParams(windowLayoutParams);
getWindowView().requestFocus();
}
public void setWindowLayoutNoLimit() {
WindowManager.LayoutParams windowLayoutParams = getWindowLayoutParams();
windowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
updateWindowLayoutParams(windowLayoutParams);
}
}

View File

@ -0,0 +1,34 @@
package com.stardust.enhancedfloaty;
import android.view.View;
import androidx.annotation.Nullable;
/**
* Created by Stardust on 2017/4/30.
*/
public interface ResizableFloaty {
View inflateView(FloatyService floatyService, ResizableFloatyWindow service);
@Nullable
View getResizerView(View view);
@Nullable
View getMoveCursorView(View view);
abstract class AbstractResizableFloaty implements ResizableFloaty {
@Nullable
public View getResizerView(View view) {
return null;
}
@Nullable
public View getMoveCursorView(View view) {
return null;
}
}
}

View File

@ -0,0 +1,92 @@
package com.stardust.enhancedfloaty;
import android.content.Context;
import android.graphics.PixelFormat;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import com.stardust.autojs.R;
import com.stardust.enhancedfloaty.gesture.DragGesture;
import com.stardust.enhancedfloaty.gesture.ResizeGesture;
import com.stardust.enhancedfloaty.util.WindowTypeCompat;
/**
* Created by Stardust on 2017/4/30.
*/
public class ResizableFloatyWindow extends FloatyWindow {
private static final String TAG = "ResizableFloatyWindow";
private View mView;
private View mResizer;
private View mMoveCursor;
private ResizableFloaty mFloaty;
public ResizableFloatyWindow(ResizableFloaty floaty) {
if (floaty == null) {
throw new NullPointerException("floaty == null");
}
mFloaty = floaty;
}
@Override
public void onCreate(FloatyService service, WindowManager manager) {
super.onCreate(service, manager);
}
@Override
protected View onCreateView(FloatyService service) {
Context context = service.getApplicationContext();
ViewGroup windowView = (ViewGroup) View.inflate(context, R.layout.ef_floaty_container, null);
mView = mFloaty.inflateView(service, this);
mResizer = mFloaty.getResizerView(mView);
mMoveCursor = mFloaty.getMoveCursorView(mView);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
windowView.addView(mView, params);
windowView.setFocusableInTouchMode(true);
return windowView;
}
@Override
protected void onViewCreated(View view) {
super.onViewCreated(view);
initGesture();
}
protected WindowManager.LayoutParams onCreateWindowLayoutParams() {
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowTypeCompat.getPhoneWindowType(),
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT);
layoutParams.gravity = Gravity.TOP | Gravity.START;
return layoutParams;
}
public View getRootView() {
return mView;
}
public View getResizer() {
return mResizer;
}
public View getMoveCursor() {
return mMoveCursor;
}
private void initGesture() {
if (mResizer != null) {
ResizeGesture.enableResize(mResizer, mView, getWindowBridge());
}
if (mMoveCursor != null) {
DragGesture gesture = new DragGesture(getWindowBridge(), mMoveCursor);
gesture.setPressedAlpha(1.0f);
}
}
}

View File

@ -0,0 +1,54 @@
package com.stardust.enhancedfloaty;
import android.view.View;
import java.util.Stack;
/**
* Created by Stardust on 2017/3/11.
*/
public class ViewStack {
public interface CurrentViewSetter {
void setCurrentView(View v);
}
public interface NavigableView {
void goBack();
}
private Stack<View> mStack = new Stack<>();
private CurrentViewSetter mCurrentViewSetter;
public ViewStack(CurrentViewSetter currentViewSetter) {
mCurrentViewSetter = currentViewSetter;
}
public void navigateTo(View v) {
mStack.push(v);
mCurrentViewSetter.setCurrentView(v);
}
public boolean canGoBack() {
return mStack.size() > 1;
}
public void goBack() {
mCurrentViewSetter.setCurrentView(mStack.pop());
}
public void goBackToFirst() {
while (mStack.size() > 1) {
mStack.pop();
}
mCurrentViewSetter.setCurrentView(mStack.peek());
}
public void setRootView(View view) {
mStack.clear();
mStack.push(view);
}
}

View File

@ -0,0 +1,94 @@
package com.stardust.enhancedfloaty;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.WindowManager;
/**
* Created by Stardust on 2017/4/18.
*/
public interface WindowBridge {
int getX();
int getY();
void updatePosition(int x, int y);
int getWidth();
int getHeight();
void updateMeasure(int width, int height);
int getScreenWidth();
int getScreenHeight();
class DefaultImpl implements WindowBridge {
DisplayMetrics mDisplayMetrics;
private WindowManager.LayoutParams mWindowLayoutParams;
private WindowManager mWindowManager;
private View mWindowView;
public DefaultImpl(WindowManager.LayoutParams windowLayoutParams, WindowManager windowManager, View windowView) {
mWindowLayoutParams = windowLayoutParams;
mWindowManager = windowManager;
mWindowView = windowView;
}
@Override
public int getX() {
return mWindowLayoutParams.x;
}
@Override
public int getY() {
return mWindowLayoutParams.y;
}
@Override
public void updatePosition(int x, int y) {
mWindowLayoutParams.x = x;
mWindowLayoutParams.y = y;
mWindowManager.updateViewLayout(mWindowView, mWindowLayoutParams);
}
@Override
public int getWidth() {
return mWindowView.getWidth();
}
@Override
public int getHeight() {
return mWindowView.getHeight();
}
@Override
public void updateMeasure(int width, int height) {
mWindowLayoutParams.width = width;
mWindowLayoutParams.height = height;
mWindowManager.updateViewLayout(mWindowView, mWindowLayoutParams);
}
@Override
public int getScreenWidth() {
ensureDisplayMetrics();
return mDisplayMetrics.widthPixels;
}
@Override
public int getScreenHeight() {
ensureDisplayMetrics();
return mDisplayMetrics.heightPixels;
}
private void ensureDisplayMetrics() {
if (mDisplayMetrics == null) {
mDisplayMetrics = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getMetrics(mDisplayMetrics);
}
}
}
}

View File

@ -0,0 +1,132 @@
package com.stardust.enhancedfloaty.gesture;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import androidx.core.view.GestureDetectorCompat;
import com.stardust.enhancedfloaty.WindowBridge;
/**
* Created by Stardust on 2017/4/18.
*/
public class DragGesture extends GestureDetector.SimpleOnGestureListener {
protected WindowBridge mWindowBridge;
protected View mView;
private float mKeepToSideHiddenWidthRadio = 0.5f;
private int mInitialX;
private int mInitialY;
private float mInitialTouchX;
private float mInitialTouchY;
private View.OnClickListener mOnClickListener;
private boolean mFlung = false;
private boolean mKeepToSide;
private float mPressedAlpha = 0.7f;
private float mUnpressedAlpha = 1.0f;
public DragGesture(WindowBridge windowBridge, View view) {
mWindowBridge = windowBridge;
mView = view;
setupView();
}
private void setupView() {
final GestureDetectorCompat gestureDetector = new GestureDetectorCompat(mView.getContext(), this);
mView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
gestureDetector.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_UP) {
mView.setAlpha(mUnpressedAlpha);
if (!mFlung && isKeepToSide()) {
keepToSide();
}
}
return true;
}
});
}
public float getPressedAlpha() {
return mPressedAlpha;
}
public void setPressedAlpha(float pressedAlpha) {
mPressedAlpha = pressedAlpha;
}
public float getUnpressedAlpha() {
return mUnpressedAlpha;
}
public void setUnpressedAlpha(float unpressedAlpha) {
mUnpressedAlpha = unpressedAlpha;
}
public void setKeepToSide(boolean keepToSide) {
mKeepToSide = keepToSide;
}
public boolean isKeepToSide() {
return mKeepToSide;
}
public void setKeepToSideHiddenWidthRadio(float keepToSideHiddenWidthRadio) {
mKeepToSideHiddenWidthRadio = keepToSideHiddenWidthRadio;
}
public float getKeepToSideHiddenWidthRadio() {
return mKeepToSideHiddenWidthRadio;
}
@Override
public boolean onDown(MotionEvent event) {
mInitialX = mWindowBridge.getX();
mInitialY = mWindowBridge.getY();
mInitialTouchX = event.getRawX();
mInitialTouchY = event.getRawY();
mFlung = false;
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
mWindowBridge.updatePosition(mInitialX + (int) ((e2.getRawX() - mInitialTouchX)),
mInitialY + (int) ((e2.getRawY() - mInitialTouchY)));
mView.setAlpha(mPressedAlpha);
return false;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
mFlung = true;
if (mKeepToSide)
keepToSide();
return false;
}
public void keepToSide() {
int x = mWindowBridge.getX();
int hiddenWidth = (int) (mKeepToSideHiddenWidthRadio * mView.getWidth());
if (x > mWindowBridge.getScreenWidth() / 2)
mWindowBridge.updatePosition(mWindowBridge.getScreenWidth() - mView.getWidth() + hiddenWidth, mWindowBridge.getY());
else
mWindowBridge.updatePosition(-hiddenWidth, mWindowBridge.getY());
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
if (mOnClickListener != null)
mOnClickListener.onClick(mView);
return super.onSingleTapConfirmed(e);
}
public void setOnDraggedViewClickListener(View.OnClickListener onClickListener) {
mOnClickListener = onClickListener;
}
}

View File

@ -0,0 +1,109 @@
package com.stardust.enhancedfloaty.gesture;
import android.content.Context;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import com.stardust.enhancedfloaty.WindowBridge;
/**
* Created by Stardust on 2017/4/18.
*/
public class ResizeGesture extends GestureDetector.SimpleOnGestureListener {
public static ResizeGesture enableResize(View resizer, @Nullable View resizableView, WindowBridge windowBridge) {
ResizeGesture resizeGesture = new ResizeGesture(windowBridge, resizer, resizableView);
final GestureDetector detector = new GestureDetector(resizer.getContext(), resizeGesture);
resizer.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
detector.onTouchEvent(event);
return true;
}
});
return resizeGesture;
}
public static ResizeGesture enableResize(View resizer, WindowBridge windowBridge) {
return enableResize(resizer, null, windowBridge);
}
private WindowBridge mWindowBridge;
private float initialTouchX;
private float initialTouchY;
private int mInitialWidth, mInitialHeight;
private View mResizerView;
private int mMinHeight = 200, mMinWidth = 200;
private final int mStatusBarHeight;
private View mResizableView;
public ResizeGesture(WindowBridge windowBridge, View resizerView, @Nullable View resizableView) {
mWindowBridge = windowBridge;
mResizerView = resizerView;
mResizableView = resizableView;
mStatusBarHeight = getStatusBarHeight(resizerView.getContext());
}
private int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
public void setMinHeight(int minHeight) {
mMinHeight = minHeight;
}
public void setMinWidth(int minWidth) {
mMinWidth = minWidth;
}
@Override
public boolean onDown(MotionEvent event) {
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
mInitialWidth = mResizableView != null ? mResizableView.getWidth() : mWindowBridge.getWidth();
mInitialHeight = mResizableView != null ? mResizableView.getHeight() : mWindowBridge.getHeight();
return false;
}
@Override
public boolean onScroll(MotionEvent e1, final MotionEvent e2, float distanceX, float distanceY) {
int newWidth = mInitialWidth + (int) ((e2.getRawX() - initialTouchX));
int newHeight = mInitialHeight + (int) ((e2.getRawY() - initialTouchY));
newWidth = Math.max(mMinWidth, newWidth);
newHeight = Math.max(mMinHeight, newHeight);
newWidth = Math.min(mWindowBridge.getScreenWidth() - getX() - mResizerView.getWidth(), newWidth);
newHeight = Math.min(mWindowBridge.getScreenHeight() - getY() - mResizerView.getHeight() - mStatusBarHeight, newHeight);
updateMeasure(newWidth, newHeight);
return true;
}
private void updateMeasure(int newWidth, int newHeight) {
if (mResizableView == null) {
mWindowBridge.updateMeasure(newWidth, newHeight);
} else {
ViewGroup.LayoutParams params = mResizableView.getLayoutParams();
params.width = newWidth;
params.height = newHeight;
mResizableView.setLayoutParams(params);
}
}
private int getY() {
return mResizableView != null ? (int) mResizableView.getY() : mWindowBridge.getY();
}
private int getX() {
return mResizableView != null ? (int) mResizableView.getX() : mWindowBridge.getX();
}
}

View File

@ -0,0 +1,57 @@
package com.stardust.enhancedfloaty.util;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
/**
* Created by Stardust on 2017/3/10.
*/
public class FloatingWindowPermissionUtil {
public static void goToFloatingWindowPermissionSettingIfNeeded(Context context) {
if (!hasFloatingWindowPermission(context)) {
goToFloatingWindowPermissionSetting(context);
}
}
public static void goToFloatingWindowPermissionSetting(Context context) {
String packageName = context.getPackageName();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
try {
context.startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + packageName))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
} catch (Exception e) {
goToAppDetailSettings(context, packageName);
}
} else {
goToAppDetailSettings(context, packageName);
}
}
public static boolean hasFloatingWindowPermission(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return Settings.canDrawOverlays(context);
}
return true;
}
public static boolean goToAppDetailSettings(Context context, String packageName) {
try {
Intent i = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
i.addCategory(Intent.CATEGORY_DEFAULT);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.setData(Uri.parse("package:" + packageName));
context.startActivity(i);
return true;
} catch (ActivityNotFoundException ignored) {
return false;
}
}
}

View File

@ -0,0 +1,25 @@
package com.stardust.enhancedfloaty.util;
import android.os.Build;
import android.view.WindowManager;
public class WindowTypeCompat {
public static int getWindowType() {
return getWindowType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
}
public static int getPhoneWindowType() {
return getWindowType(WindowManager.LayoutParams.TYPE_PHONE);
}
public static int getWindowType(int type) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
return type;
}
}
}

View File

@ -0,0 +1,20 @@
package com.stardust.widget;
import android.content.Context;
import android.view.View;
import java.io.Serializable;
/**
* Created by Stardust on 2017/4/18.
* <p>
* The interface and its implementations must be serializable.
* Only in this way it can be set to intent extra and be used as service argument.
* So, implementations should not has any non-serializable fields.
*/
public interface ViewSupplier extends Serializable {
View inflateView(Context context);
}

View File

@ -0,0 +1,63 @@
package com.stardust.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by Stardust on 2017/3/11.
*/
public class ViewSwitcher extends android.widget.ViewSwitcher {
private View mCurrentView;
public ViewSwitcher(Context context) {
super(context);
}
public ViewSwitcher(Context context, View first, View second) {
super(context);
addView(first);
addView(second);
}
public ViewSwitcher(Context context, AttributeSet attrs) {
super(context, attrs);
}
public View getCurrentView() {
return mCurrentView;
}
public int getCurrentViewIndex() {
return mCurrentView == getChildAt(0) ? 0 : 1;
}
public void showFirst() {
ensureCurrentView();
if (mCurrentView != getChildAt(0)) {
showPrevious();
mCurrentView = getChildAt(0);
}
}
private void ensureCurrentView() {
if (mCurrentView == null) {
mCurrentView = getChildAt(0);
}
}
public void showSecond() {
ensureCurrentView();
if (mCurrentView != getChildAt(1)) {
showNext();
mCurrentView = getChildAt(1);
}
}
public void setSecondView(View v) {
removeViewAt(1);
addView(v);
}
}

View File

@ -4,6 +4,7 @@ import android.accessibilityservice.AccessibilityServiceInfo
import android.os.Build
import android.view.WindowManager
import com.stardust.autojs.core.pref.Pref
import com.stardust.enhancedfloaty.FloatyService
import com.stardust.view.accessibility.AccessibilityService
class AccessibilityService: AccessibilityService() {
@ -30,6 +31,10 @@ class AccessibilityService: AccessibilityService() {
}
override fun getWindowManager() : WindowManager {
return windowManager;
return windowManager
}
override fun refreshFloatyService() {
FloatyService.getInstance()?.refreshAccessWindowManager()
}
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<com.stardust.widget.ViewSwitcher xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</RelativeLayout>

View File

@ -100,6 +100,7 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ
Log.v(TAG, "onDestroy: $instance")
instance = null
mEventExecutor?.shutdownNow()
refreshFloatyService()
super.onDestroy()
}
@ -107,6 +108,7 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ
override fun onServiceConnected() {
Log.v(TAG, "onServiceConnected: " + serviceInfo.toString())
instance = this
refreshFloatyService()
super.onServiceConnected()
LOCK.lock()
ENABLED.signalAll()
@ -119,6 +121,13 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ
return mFastRootInActiveWindow
}
/**
* 刷新悬浮窗的windowManager
*/
open fun refreshFloatyService() {
// do refresh on sub class
}
open fun getWindowManager() : WindowManager? {
return null;
}

View File

@ -7,8 +7,6 @@ import android.content.Intent
import android.provider.Settings
import android.text.TextUtils
import java.util.Locale
/**
* Created by Stardust on 2017/1/26.