add jumping between layout hierarchy and layout bounds view

This commit is contained in:
hyb1996
2017-06-27 22:43:11 +08:00
parent 59a1b5dffb
commit 97ff7e49a1
28 changed files with 745 additions and 125 deletions

15
.idea/misc.xml generated
View File

@@ -2,15 +2,16 @@
<project version="4"> <project version="4">
<component name="EntryPointsManager"> <component name="EntryPointsManager">
<entry_points version="2.0" /> <entry_points version="2.0" />
<list size="8"> <list size="9">
<item index="0" class="java.lang.String" itemvalue="android.support.annotation.Keep" /> <item index="0" class="java.lang.String" itemvalue="android.support.annotation.Keep" />
<item index="1" class="java.lang.String" itemvalue="android.webkit.JavascriptInterface" /> <item index="1" class="java.lang.String" itemvalue="android.webkit.JavascriptInterface" />
<item index="2" class="java.lang.String" itemvalue="com.stardust.autojs.runtime.ScriptClass" /> <item index="2" class="java.lang.String" itemvalue="butterknife.OnClick" />
<item index="3" class="java.lang.String" itemvalue="com.stardust.autojs.runtime.ScriptInterface" /> <item index="3" class="java.lang.String" itemvalue="com.stardust.autojs.runtime.ScriptClass" />
<item index="4" class="java.lang.String" itemvalue="com.stardust.view.ViewBind.Click" /> <item index="4" class="java.lang.String" itemvalue="com.stardust.autojs.runtime.ScriptInterface" />
<item index="5" class="java.lang.String" itemvalue="com.stardust.view.ViewBinding.Check" /> <item index="5" class="java.lang.String" itemvalue="com.stardust.view.ViewBind.Click" />
<item index="6" class="java.lang.String" itemvalue="com.stardust.view.ViewBinding.Click" /> <item index="6" class="java.lang.String" itemvalue="com.stardust.view.ViewBinding.Check" />
<item index="7" class="java.lang.String" itemvalue="org.greenrobot.eventbus.Subscribe" /> <item index="7" class="java.lang.String" itemvalue="com.stardust.view.ViewBinding.Click" />
<item index="8" class="java.lang.String" itemvalue="org.greenrobot.eventbus.Subscribe" />
</list> </list>
</component> </component>
<component name="NullableNotNullManager"> <component name="NullableNotNullManager">

View File

@@ -9,8 +9,8 @@ android {
applicationId "com.stardust.scriptdroid" applicationId "com.stardust.scriptdroid"
minSdkVersion 19 minSdkVersion 19
targetSdkVersion 23 targetSdkVersion 23
versionCode 137 versionCode 139
versionName "2.0.12 Beta" versionName "2.0.13 Alpha2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
ndk { ndk {
@@ -111,7 +111,7 @@ dependencies {
// Android issue reporter (a github issue reporter) // Android issue reporter (a github issue reporter)
compile 'com.heinrichreimersoftware:android-issue-reporter:1.3.1' compile 'com.heinrichreimersoftware:android-issue-reporter:1.3.1'
// //
compile group: "pl.openrnd.android", name: "multi-level-listview", version: "1.0.1" compile 'com.github.hyb1996:android-multi-level-listview:1.1'
compile 'de.psdev.licensesdialog:licensesdialog:1.8.1' compile 'de.psdev.licensesdialog:licensesdialog:1.8.1'
compile 'io.mattcarroll.hover:hover:0.9.7' compile 'io.mattcarroll.hover:hover:0.9.7'
compile 'com.bignerdranch.android:expandablerecyclerview:3.0.0-RC1' compile 'com.bignerdranch.android:expandablerecyclerview:3.0.0-RC1'
@@ -129,6 +129,7 @@ dependencies {
compile 'com.afollestad.material-dialogs:commons:0.9.2.3' compile 'com.afollestad.material-dialogs:commons:0.9.2.3'
compile 'com.makeramen:roundedimageview:2.2.1' compile 'com.makeramen:roundedimageview:2.2.1'
compile 'com.rengwuxian.materialedittext:library:2.0.3' compile 'com.rengwuxian.materialedittext:library:2.0.3'
compile 'org.msgpack:msgpack-core:0.8.11'
compile(name: '920-common-release', ext: 'aar') compile(name: '920-common-release', ext: 'aar')
compile(name: '920-styles-release', ext: 'aar') compile(name: '920-styles-release', ext: 'aar')
compile(name: '920-file_explorer-release', ext: 'aar') compile(name: '920-file_explorer-release', ext: 'aar')

View File

@@ -0,0 +1,89 @@
package com.stardust.app;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.R;
import java.util.ArrayList;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* Created by Stardust on 2017/6/26.
*/
public class OperationDialogBuilder extends MaterialDialog.Builder {
private RecyclerView mOperations;
private ArrayList<Integer> mIds = new ArrayList<>();
private ArrayList<Integer> mIcons = new ArrayList<>();
private ArrayList<Integer> mTexts = new ArrayList<>();
private Object mOnItemClickTarget;
public OperationDialogBuilder(@NonNull Context context) {
super(context);
mOperations = new RecyclerView(context);
mOperations.setLayoutManager(new LinearLayoutManager(context));
mOperations.setAdapter(new RecyclerView.Adapter<ViewHolder>() {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.operation_dialog_item, parent, false));
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.itemView.setId(mIds.get(position));
holder.text.setText(mTexts.get(position));
holder.icon.setImageResource(mIcons.get(position));
if (mOnItemClickTarget != null) {
//// TODO: 2017/6/26 效率
ButterKnife.bind(mOnItemClickTarget, holder.itemView);
}
}
@Override
public int getItemCount() {
return mIds.size();
}
});
customView(mOperations, false);
}
public OperationDialogBuilder item(int id, int iconRes, int textRes) {
mIds.add(id);
mIcons.add(iconRes);
mTexts.add(textRes);
return this;
}
public OperationDialogBuilder bindItemClick(Object target) {
mOnItemClickTarget = target;
return this;
}
class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.icon)
ImageView icon;
@BindView(R.id.text)
TextView text;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}

View File

@@ -18,11 +18,13 @@ import com.stardust.hover.WindowHoverMenu;
import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.autojs.AutoJs; import com.stardust.scriptdroid.autojs.AutoJs;
import com.stardust.scriptdroid.external.floatingwindow.FloatingWindowManger; import com.stardust.scriptdroid.external.floatingwindow.FloatingWindowManger;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo;
import com.stardust.scriptdroid.external.floatingwindow.menu.view.FloatingLayoutBoundsView; import com.stardust.scriptdroid.external.floatingwindow.menu.view.FloatingLayoutBoundsView;
import com.stardust.scriptdroid.external.floatingwindow.menu.view.FloatingLayoutHierarchyView; import com.stardust.scriptdroid.external.floatingwindow.menu.view.FloatingLayoutHierarchyView;
import com.stardust.scriptdroid.tool.AccessibilityServiceTool; import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
import com.stardust.theme.ThemeColorManagerCompat; import com.stardust.theme.ThemeColorManagerCompat;
import com.stardust.util.MessageEvent; import com.stardust.util.MessageEvent;
import com.stardust.util.MessageIntent;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.Subscribe;
@@ -45,13 +47,18 @@ public class HoverMenuService extends Service {
public boolean state; public boolean state;
} }
public static final String MESSAGE_SHOW_AND_EXPAND_MENU = "MESSAGE_SHOW_AND_EXPAND_MENU"; public static final String ACTION_SHOW_AND_EXPAND_MENU = "ACTION_SHOW_AND_EXPAND_MENU";
public static final String MESSAGE_SHOW_LAYOUT_HIERARCHY = "MESSAGE_SHOW_LAYOUT_HIERARCHY"; public static final String ACTION_SHOW_LAYOUT_HIERARCHY = "ACTION_SHOW_LAYOUT_HIERARCHY";
public static final String MESSAGE_SHOW_LAYOUT_BOUNDS = "MESSAGE_SHOW_LAYOUT_BOUNDS"; public static final String ACTION_SHOW_LAYOUT_BOUNDS = "ACTION_SHOW_LAYOUT_BOUNDS";
public static final String MESSAGE_COLLAPSE_MENU = "MESSAGE_COLLAPSE_MENU"; public static final String ACTION_COLLAPSE_MENU = "ACTION_COLLAPSE_MENU";
public static final String MESSAGE_MENU_COLLAPSING = "MESSAGE_MENU_COLLAPSING"; public static final String ACTION_MENU_COLLAPSING = "ACTION_MENU_COLLAPSING";
public static final String MESSAGE_MENU_EXPANDING = "MESSAGE_MENU_EXPANDING"; public static final String ACTION_MENU_EXPANDING = "ACTION_MENU_EXPANDING";
public static final String MESSAGE_MENU_EXIT = "MESSAGE_MENU_EXIT"; public static final String ACTION_MENU_EXIT = "ACTION_MENU_EXIT";
public static final String ACTION_SHOW_NODE_LAYOUT_HIERARCHY = "ACTION_SHOW_NODE_LAYOUT_HIERARCHY";
public static final String ACTION_SHOW_NODE_LAYOUT_BOUNDS = "ACTION_SHOW_NODE_LAYOUT_BOUNDS";
public static final String EXTRA_NODE_INFO = "EXTRA_NODE_INFO";
private static boolean sIsRunning; private static boolean sIsRunning;
private static EventBus eventBus = new EventBus(); private static EventBus eventBus = new EventBus();
@@ -70,8 +77,12 @@ public class HoverMenuService extends Service {
eventBus.post(new ServiceStateChangedEvent(sIsRunning)); eventBus.post(new ServiceStateChangedEvent(sIsRunning));
} }
public static void postEvent(MessageEvent event) { public static void postIntent(Intent intent) {
eventBus.post(event); eventBus.post(new MessageIntent(intent));
}
public static void postMessageIntent(MessageIntent intent) {
eventBus.post(intent);
} }
@@ -96,7 +107,7 @@ public class HoverMenuService extends Service {
private HoverMenu.OnExitListener mWindowHoverMenuMenuExitListener = new HoverMenu.OnExitListener() { private HoverMenu.OnExitListener mWindowHoverMenuMenuExitListener = new HoverMenu.OnExitListener() {
@Override @Override
public void onExitByUserRequest() { public void onExitByUserRequest() {
eventBus.post(new MessageEvent(MESSAGE_MENU_EXIT)); eventBus.post(new MessageEvent(ACTION_MENU_EXIT));
savePreferredLocation(); savePreferredLocation();
mWindowHoverMenu.hide(); mWindowHoverMenu.hide();
stopSelf(); stopSelf();
@@ -142,14 +153,14 @@ public class HoverMenuService extends Service {
mWindowHoverMenu.setHoverMenuTransitionListener(new SimpleHoverMenuTransitionListener() { mWindowHoverMenu.setHoverMenuTransitionListener(new SimpleHoverMenuTransitionListener() {
@Override @Override
public void onExpanding() { public void onExpanding() {
eventBus.post(new MessageEvent(MESSAGE_MENU_EXPANDING)); eventBus.post(new MessageEvent(ACTION_MENU_EXPANDING));
captureCurrentWindow(); captureCurrentWindow();
} }
@Override @Override
public void onCollapsing() { public void onCollapsing() {
AutoJs.getInstance().getLayoutInspector().clearCapture(); AutoJs.getInstance().getLayoutInspector().clearCapture();
eventBus.post(new MessageEvent(MESSAGE_MENU_COLLAPSING)); eventBus.post(new MessageEvent(ACTION_MENU_COLLAPSING));
} }
}); });
} }
@@ -197,20 +208,30 @@ public class HoverMenuService extends Service {
} }
@Subscribe @Subscribe
public void onMessageEvent(MessageEvent event) { public void handleMessageIntent(MessageIntent intent) {
switch (event.message) { switch (intent.getAction()) {
case MESSAGE_SHOW_AND_EXPAND_MENU: case ACTION_SHOW_AND_EXPAND_MENU:
showAndExpandMenu(); showAndExpandMenu();
break; break;
case MESSAGE_SHOW_LAYOUT_HIERARCHY: case ACTION_SHOW_LAYOUT_HIERARCHY:
showLayoutHierarchy(); showLayoutHierarchy();
break; break;
case MESSAGE_SHOW_LAYOUT_BOUNDS: case ACTION_SHOW_LAYOUT_BOUNDS:
showLayoutBounds(); showLayoutBounds();
break; break;
case MESSAGE_COLLAPSE_MENU: case ACTION_COLLAPSE_MENU:
mWindowHoverMenu.collapseMenu(); mWindowHoverMenu.collapseMenu();
break; break;
case ACTION_SHOW_NODE_LAYOUT_BOUNDS:
mFloatingLayoutHierarchyView.setVisibility(View.GONE);
showLayoutBounds();
mFloatingLayoutBoundsView.setSelectedNode((NodeInfo) intent.getObjectExtra(EXTRA_NODE_INFO));
break;
case ACTION_SHOW_NODE_LAYOUT_HIERARCHY:
mFloatingLayoutBoundsView.setVisibility(View.GONE);
showLayoutHierarchy();
mFloatingLayoutHierarchyView.setSelectedNode((NodeInfo) intent.getObjectExtra(EXTRA_NODE_INFO));
break;
} }
} }

View File

@@ -51,7 +51,7 @@ public class MainMenuNavigatorContent implements NavigatorContent {
if (!ensureCapture()) { if (!ensureCapture()) {
return; return;
} }
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_SHOW_LAYOUT_HIERARCHY)); HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_SHOW_LAYOUT_HIERARCHY));
} }
private boolean ensureCapture() { private boolean ensureCapture() {
@@ -72,7 +72,7 @@ public class MainMenuNavigatorContent implements NavigatorContent {
if (!ensureCapture()) { if (!ensureCapture()) {
return; return;
} }
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_SHOW_LAYOUT_BOUNDS)); HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_SHOW_LAYOUT_BOUNDS));
} }
@ViewBinding.Click(R.id.stop_all_running_scripts) @ViewBinding.Click(R.id.stop_all_running_scripts)
@@ -84,7 +84,7 @@ public class MainMenuNavigatorContent implements NavigatorContent {
private void openMainActivity() { private void openMainActivity() {
App.getApp().startActivity(new Intent(App.getApp(), MainActivity.class) App.getApp().startActivity(new Intent(App.getApp(), MainActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK)); .addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK));
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU)); HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_COLLAPSE_MENU));
} }
@NonNull @NonNull
@@ -125,9 +125,9 @@ public class MainMenuNavigatorContent implements NavigatorContent {
@Subscribe @Subscribe
public void onMessageEvent(MessageEvent event) { public void onMessageEvent(MessageEvent event) {
if (event.message.equals(HoverMenuService.MESSAGE_MENU_EXPANDING)) { if (event.message.equals(HoverMenuService.ACTION_MENU_EXPANDING)) {
syncCurrentInfo(); syncCurrentInfo();
} else if (event.message.equals(HoverMenuService.MESSAGE_MENU_EXIT)) { } else if (event.message.equals(HoverMenuService.ACTION_MENU_EXIT)) {
HoverMenuService.getEventBus().unregister(this); HoverMenuService.getEventBus().unregister(this);
} }
} }

View File

@@ -1,6 +1,7 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.content; package com.stardust.scriptdroid.external.floatingwindow.menu.content;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v7.widget.SwitchCompat; import android.support.v7.widget.SwitchCompat;
import android.view.View; import android.view.View;
@@ -124,7 +125,7 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat
private void resumeRecord() { private void resumeRecord() {
mRecorder.resume(); mRecorder.resume();
setState(Recorder.STATE_RECORDING); setState(Recorder.STATE_RECORDING);
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU)); HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_COLLAPSE_MENU));
} }
private void pauseRecord() { private void pauseRecord() {
@@ -137,7 +138,7 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat
mRecorder.setOnStateChangedListener(this); mRecorder.setOnStateChangedListener(this);
mRecorder.start(); mRecorder.start();
setState(Recorder.STATE_RECORDING); setState(Recorder.STATE_RECORDING);
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU)); HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_COLLAPSE_MENU));
} }
private void setState(int state) { private void setState(int state) {
@@ -153,7 +154,7 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat
private void stopRecord() { private void stopRecord() {
mRecorder.stop(); mRecorder.stop();
setState(Recorder.STATE_STOPPED); setState(Recorder.STATE_STOPPED);
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU)); HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_COLLAPSE_MENU));
} }
@@ -163,10 +164,10 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat
@Subscribe @Subscribe
public void onMessageEvent(MessageEvent event) { public void onMessageEvent(MessageEvent event) {
if (event.message.equals(HoverMenuService.MESSAGE_MENU_EXPANDING)) { if (event.message.equals(HoverMenuService.ACTION_MENU_EXPANDING)) {
if (mRecorder != null && mRecorder.getState() == Recorder.STATE_RECORDING) if (mRecorder != null && mRecorder.getState() == Recorder.STATE_RECORDING)
pauseRecord(); pauseRecord();
} else if (event.message.equals(HoverMenuService.MESSAGE_MENU_EXIT)) { } else if (event.message.equals(HoverMenuService.ACTION_MENU_EXIT)) {
onMenuExit(); onMenuExit();
} }
} }

View File

@@ -1,6 +1,7 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.content; package com.stardust.scriptdroid.external.floatingwindow.menu.content;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.view.ContextThemeWrapper; import android.view.ContextThemeWrapper;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@@ -15,7 +16,6 @@ import com.stardust.scriptdroid.script.StorageScriptProvider;
import com.stardust.scriptdroid.ui.edit.EditActivity; import com.stardust.scriptdroid.ui.edit.EditActivity;
import com.stardust.scriptdroid.ui.main.script_list.ScriptAndFolderListRecyclerView; import com.stardust.scriptdroid.ui.main.script_list.ScriptAndFolderListRecyclerView;
import com.stardust.scriptdroid.ui.main.script_list.ScriptListWithProgressBarView; import com.stardust.scriptdroid.ui.main.script_list.ScriptListWithProgressBarView;
import com.stardust.util.MessageEvent;
import com.stardust.widget.ViewHolderSupplier; import com.stardust.widget.ViewHolderSupplier;
import io.mattcarroll.hover.Navigator; import io.mattcarroll.hover.Navigator;
@@ -53,7 +53,7 @@ public class ScriptListNavigatorContent implements NavigatorContent {
@Override @Override
public void onClick(ScriptFile file, int position) { public void onClick(ScriptFile file, int position) {
Scripts.run(file); Scripts.run(file);
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU)); HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_COLLAPSE_MENU));
} }
}); });
@@ -94,7 +94,7 @@ public class ScriptListNavigatorContent implements NavigatorContent {
int position = mFloatingScriptFileListView.getChildViewHolder((View) v.getParent()).getAdapterPosition(); int position = mFloatingScriptFileListView.getChildViewHolder((View) v.getParent()).getAdapterPosition();
ScriptFile file = mFloatingScriptFileListView.getAdapter().getScriptFileAt(position); ScriptFile file = mFloatingScriptFileListView.getAdapter().getScriptFileAt(position);
EditActivity.editFile(v.getContext(), file); EditActivity.editFile(v.getContext(), file);
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU)); HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_COLLAPSE_MENU));
} }
}); });

View File

@@ -10,11 +10,8 @@ import java.util.concurrent.Executor;
/** /**
* Created by Stardust on 2017/3/10. * Created by Stardust on 2017/3/10.
*
*/ */
// TODO: 2017/5/8
public class LayoutInspector { public class LayoutInspector {
private volatile NodeInfo mCapture; private volatile NodeInfo mCapture;
@@ -22,7 +19,7 @@ public class LayoutInspector {
private Executor mExecutor = UnderuseExecutors.getExecutor(); private Executor mExecutor = UnderuseExecutors.getExecutor();
public void captureCurrentWindow() { public void captureCurrentWindow() {
AccessibilityService service = AccessibilityWatchDogService.getInstance(); AccessibilityWatchDogService service = AccessibilityWatchDogService.getInstance();
if (service == null) { if (service == null) {
mCapture = null; mCapture = null;
} else { } else {

View File

@@ -21,7 +21,7 @@ public class NodeInfo {
private Rect mBoundsInScreen; private Rect mBoundsInScreen;
public String id; public String id;
public CharSequence contentDesc; public CharSequence desc;
public CharSequence className; public CharSequence className;
public CharSequence packageName; public CharSequence packageName;
public CharSequence text; public CharSequence text;
@@ -42,7 +42,7 @@ public class NodeInfo {
public NodeInfo(AccessibilityNodeInfoCompat node) { public NodeInfo(AccessibilityNodeInfoCompat node) {
id = simplifyId(node.getViewIdResourceName()); id = simplifyId(node.getViewIdResourceName());
contentDesc = node.getContentDescription(); desc = node.getContentDescription();
className = node.getClassName(); className = node.getClassName();
packageName = node.getPackageName(); packageName = node.getPackageName();
text = node.getText(); text = node.getText();
@@ -102,6 +102,7 @@ public class NodeInfo {
return capture(r); return capture(r);
} }
@NonNull
public List<NodeInfo> getChildren() { public List<NodeInfo> getChildren() {
return children; return children;
} }

View File

@@ -5,6 +5,7 @@ import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.Region;
import android.os.Build; import android.os.Build;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi; import android.support.annotation.RequiresApi;
@@ -16,6 +17,8 @@ import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.No
import com.stardust.util.ViewUtil; import com.stardust.util.ViewUtil;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
/** /**
@@ -24,12 +27,16 @@ import java.util.List;
public class LayoutBoundsView extends View { public class LayoutBoundsView extends View {
private static final int COLOR_SHADOW = 0x6a000000;
private NodeInfo mRootNode; private NodeInfo mRootNode;
private NodeInfo mTouchedNode;
private Paint mBoundsPaint;
private Paint mPaint; private Paint mFillingPaint;
private int mStatusBarHeight; private int mStatusBarHeight;
private OnNodeInfoSelectListener mOnNodeInfoSelectListener; private OnNodeInfoSelectListener mOnNodeInfoSelectListener;
private int mTouchedNodeBoundsColor = Color.RED;
private int mNormalNodeBoundsColor = Color.GREEN;
private Rect mTouchedNodeBounds;
public LayoutBoundsView(Context context) { public LayoutBoundsView(Context context) {
super(context); super(context);
@@ -59,32 +66,68 @@ public class LayoutBoundsView extends View {
public void setRootNode(NodeInfo rootNode) { public void setRootNode(NodeInfo rootNode) {
mRootNode = rootNode; mRootNode = rootNode;
mTouchedNode = null;
} }
private void init() { private void init() {
mPaint = new Paint(); mBoundsPaint = new Paint();
mPaint.setColor(Color.GREEN); mBoundsPaint.setStyle(Paint.Style.STROKE);
mPaint.setStyle(Paint.Style.STROKE); mFillingPaint = new Paint();
mFillingPaint.setStyle(Paint.Style.FILL);
mFillingPaint.setColor(COLOR_SHADOW);
mStatusBarHeight = ViewUtil.getStatusBarHeight(getContext()); mStatusBarHeight = ViewUtil.getStatusBarHeight(getContext());
setWillNotDraw(false); setWillNotDraw(false);
} }
@Override @Override
protected void onDraw(Canvas canvas) { protected void onDraw(Canvas canvas) {
super.onDraw(canvas); super.onDraw(canvas);
if (mTouchedNode != null) {
canvas.save();
if (mTouchedNodeBounds == null) {
mTouchedNodeBounds = new Rect(mTouchedNode.getBoundsInScreen());
mTouchedNodeBounds.offset(0, -mStatusBarHeight);
}
canvas.clipRect(mTouchedNodeBounds, Region.Op.DIFFERENCE);
}
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mFillingPaint);
if (mTouchedNode != null) {
canvas.restore();
}
mBoundsPaint.setColor(mNormalNodeBoundsColor);
draw(canvas, mRootNode); draw(canvas, mRootNode);
if (mTouchedNode != null) {
mBoundsPaint.setColor(mTouchedNodeBoundsColor);
drawRect(canvas, mTouchedNode.getBoundsInScreen(), mStatusBarHeight, mBoundsPaint);
}
} }
public Paint getPaint() { public Paint getBoundsPaint() {
return mPaint; return mBoundsPaint;
} }
public Paint getFillingPaint() {
return mFillingPaint;
}
public int getStatusBarHeight() {
return mStatusBarHeight;
}
public void setTouchedNodeBoundsColor(int touchedNodeBoundsColor) {
mTouchedNodeBoundsColor = touchedNodeBoundsColor;
}
public void setNormalNodeBoundsColor(int normalNodeBoundsColor) {
mNormalNodeBoundsColor = normalNodeBoundsColor;
}
private void draw(Canvas canvas, NodeInfo node) { private void draw(Canvas canvas, NodeInfo node) {
if (node == null) if (node == null)
return; return;
drawRect(canvas, node.getBoundsInScreen(), mStatusBarHeight, mPaint); drawRect(canvas, node.getBoundsInScreen(), mStatusBarHeight, mBoundsPaint);
for (NodeInfo child : node.getChildren()) { for (NodeInfo child : node.getChildren()) {
draw(canvas, child); draw(canvas, child);
} }
@@ -98,25 +141,39 @@ public class LayoutBoundsView extends View {
@Override @Override
public boolean onTouchEvent(MotionEvent event) { public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN && mRootNode != null) { if (mRootNode != null) {
ArrayList<NodeInfo> list = new ArrayList<>(); setSelectedNode(findNodeAt(mRootNode, (int) event.getRawX(), (int) event.getRawY()));
findNodeAt(mRootNode, (int) event.getRawX(), (int) event.getRawY(), list); }
showNodeInfoList(list); if (event.getAction() == MotionEvent.ACTION_UP && mTouchedNode != null) {
onNodeInfoClick(mTouchedNode);
return true;
} }
return super.onTouchEvent(event); return super.onTouchEvent(event);
} }
private void showNodeInfoList(ArrayList<NodeInfo> list) {
onNodeInfoClick(list.get(list.size() - 1));
}
private void onNodeInfoClick(NodeInfo nodeInfo) { private void onNodeInfoClick(NodeInfo nodeInfo) {
if (mOnNodeInfoSelectListener != null) { if (mOnNodeInfoSelectListener != null) {
mOnNodeInfoSelectListener.onNodeSelect(nodeInfo); mOnNodeInfoSelectListener.onNodeSelect(nodeInfo);
} }
} }
private NodeInfo findNodeAt(NodeInfo node, int x, int y) {
ArrayList<NodeInfo> list = new ArrayList<>();
findNodeAt(node, x, y, list);
if (list.isEmpty()) {
return null;
}
return Collections.min(list, new Comparator<NodeInfo>() {
@Override
public int compare(NodeInfo o1, NodeInfo o2) {
return o1.getBoundsInScreen().width() * o1.getBoundsInScreen().height() -
o2.getBoundsInScreen().width() * o2.getBoundsInScreen().height();
}
});
}
private void findNodeAt(NodeInfo node, int x, int y, List<NodeInfo> list) { private void findNodeAt(NodeInfo node, int x, int y, List<NodeInfo> list) {
for (NodeInfo child : node.getChildren()) { for (NodeInfo child : node.getChildren()) {
if (child != null && child.getBoundsInScreen().contains(x, y)) { if (child != null && child.getBoundsInScreen().contains(x, y)) {
@@ -126,4 +183,9 @@ public class LayoutBoundsView extends View {
} }
} }
public void setSelectedNode(NodeInfo selectedNode) {
mTouchedNode = selectedNode;
mTouchedNodeBounds = null;
invalidate();
}
} }

View File

@@ -19,8 +19,11 @@ import com.stardust.util.ViewUtil;
import com.stardust.widget.LevelBeamView; import com.stardust.widget.LevelBeamView;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set;
import java.util.Stack;
import pl.openrnd.multilevellistview.ItemInfo; import pl.openrnd.multilevellistview.ItemInfo;
import pl.openrnd.multilevellistview.MultiLevelListAdapter; import pl.openrnd.multilevellistview.MultiLevelListAdapter;
@@ -34,13 +37,17 @@ import pl.openrnd.multilevellistview.OnItemClickListener;
public class LayoutHierarchyView extends MultiLevelListView { public class LayoutHierarchyView extends MultiLevelListView {
public interface OnItemLongClickListener {
void onItemLongClick(View view, NodeInfo nodeInfo);
}
private Adapter mAdapter; private Adapter mAdapter;
private OnNodeInfoSelectListener mOnNodeInfoSelectListener; private OnItemLongClickListener mOnItemLongClickListener;
private AdapterView.OnItemLongClickListener mOnItemLongClickListenerProxy = new AdapterView.OnItemLongClickListener() { private AdapterView.OnItemLongClickListener mOnItemLongClickListenerProxy = new AdapterView.OnItemLongClickListener() {
@Override @Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (mOnNodeInfoSelectListener != null) { if (mOnItemLongClickListener != null) {
mOnNodeInfoSelectListener.onNodeSelect(((ViewHolder) view.getTag()).nodeInfo); mOnItemLongClickListener.onItemLongClick(view, ((ViewHolder) view.getTag()).nodeInfo);
return true; return true;
} }
return false; return false;
@@ -55,7 +62,9 @@ public class LayoutHierarchyView extends MultiLevelListView {
private Drawable mOriginalBackground; private Drawable mOriginalBackground;
private boolean mShowClickedNodeBounds; private boolean mShowClickedNodeBounds;
private int mClickedColor = 0x77c4c4c4; private int mClickedColor = 0x99b2b3b7;
private NodeInfo mRootNode;
private Set<NodeInfo> mInitiallyExpandedNodes = new HashSet<>();
public LayoutHierarchyView(Context context) { public LayoutHierarchyView(Context context) {
super(context); super(context);
@@ -80,6 +89,9 @@ public class LayoutHierarchyView extends MultiLevelListView {
mClickedColor = clickedColor; mClickedColor = clickedColor;
} }
public int getStatusBarHeight() {
return mStatusBarHeight;
}
private void init() { private void init() {
mAdapter = new Adapter(); mAdapter = new Adapter();
@@ -127,12 +139,14 @@ public class LayoutHierarchyView extends MultiLevelListView {
} }
public void setRootNode(NodeInfo rootNodeInfo) { public void setRootNode(NodeInfo rootNodeInfo) {
mRootNode = rootNodeInfo;
mAdapter.setDataItems(Collections.singletonList(rootNodeInfo)); mAdapter.setDataItems(Collections.singletonList(rootNodeInfo));
mClickedNodeInfo = null; mClickedNodeInfo = null;
mInitiallyExpandedNodes.clear();
} }
public void setOnNodeInfoLongClickListener(final OnNodeInfoSelectListener onNodeInfoSelectListener) { public void setOnItemLongClickListener(final OnItemLongClickListener onNodeInfoSelectListener) {
mOnNodeInfoSelectListener = onNodeInfoSelectListener; mOnItemLongClickListener = onNodeInfoSelectListener;
} }
@Override @Override
@@ -143,6 +157,35 @@ public class LayoutHierarchyView extends MultiLevelListView {
} }
} }
public void setSelectedNode(NodeInfo selectedNode) {
mInitiallyExpandedNodes.clear();
Stack<NodeInfo> parents = new Stack<>();
searchNodeParents(selectedNode, mRootNode, parents);
mClickedNodeInfo = parents.peek();
for (NodeInfo nodeInfo : parents) {
mInitiallyExpandedNodes.add(nodeInfo);
}
mAdapter.reloadData();
}
private boolean searchNodeParents(NodeInfo nodeInfo, NodeInfo rootNode, Stack<NodeInfo> stack) {
stack.push(rootNode);
if (nodeInfo == rootNode) {
return true;
}
boolean found = false;
for (NodeInfo child : rootNode.getChildren()) {
if (searchNodeParents(nodeInfo, child, stack)) {
found = true;
break;
}
}
if (!found) {
stack.pop();
}
return found;
}
private class ViewHolder { private class ViewHolder {
TextView nameView; TextView nameView;
TextView infoView; TextView infoView;
@@ -163,15 +206,20 @@ public class LayoutHierarchyView extends MultiLevelListView {
@Override @Override
public List<?> getSubObjects(Object object) { protected List<?> getSubObjects(Object object) {
return ((NodeInfo) object).getChildren(); return ((NodeInfo) object).getChildren();
} }
@Override @Override
public boolean isExpandable(Object object) { protected boolean isExpandable(Object object) {
return !((NodeInfo) object).getChildren().isEmpty(); return !((NodeInfo) object).getChildren().isEmpty();
} }
@Override
protected boolean isInitiallyExpanded(Object object) {
return mInitiallyExpandedNodes.contains((NodeInfo) object);
}
@Override @Override
public View getViewForObject(Object object, View convertView, ItemInfo itemInfo) { public View getViewForObject(Object object, View convertView, ItemInfo itemInfo) {
NodeInfo nodeInfo = (NodeInfo) object; NodeInfo nodeInfo = (NodeInfo) object;
@@ -199,6 +247,9 @@ public class LayoutHierarchyView extends MultiLevelListView {
viewHolder.levelBeamView.setLevel(itemInfo.getLevel()); viewHolder.levelBeamView.setLevel(itemInfo.getLevel());
if (nodeInfo == mClickedNodeInfo) {
setClickedItem(convertView, nodeInfo);
}
return convertView; return convertView;
} }

View File

@@ -31,9 +31,10 @@ import butterknife.Optional;
public class NodeInfoView extends RecyclerView { public class NodeInfoView extends RecyclerView {
private static final String[] FIELD_NAMES = {"id", private static final String[] FIELD_NAMES = {
"id",
"bounds", "bounds",
"contentDesc", "desc",
"className", "className",
"packageName", "packageName",
"text", "text",
@@ -56,7 +57,7 @@ public class NodeInfoView extends RecyclerView {
Arrays.sort(FIELD_NAMES); Arrays.sort(FIELD_NAMES);
for (int i = 0; i < FIELD_NAMES.length; i++) { for (int i = 0; i < FIELD_NAMES.length; i++) {
try { try {
FIELDS[i] = NodeInfo.class.getField(FIELD_NAMES[i]); FIELDS[i] = NodeInfo.class.getDeclaredField(FIELD_NAMES[i]);
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -92,10 +93,10 @@ public class NodeInfoView extends RecyclerView {
} }
public void setNodeInfo(NodeInfo nodeInfo) { public void setNodeInfo(NodeInfo nodeInfo) {
for (int i = 1; i < FIELD_NAMES.length; i++) { for (int i = 0; i < FIELDS.length; i++) {
try { try {
Object value = FIELDS[i - 1].get(nodeInfo); Object value = FIELDS[i].get(nodeInfo);
mData[i][1] = value == null ? "" : value.toString(); mData[i + 1][1] = value == null ? "" : value.toString();
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -1,17 +1,24 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.view; package com.stardust.scriptdroid.external.floatingwindow.menu.view;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager; import android.view.WindowManager;
import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.Theme; import com.afollestad.materialdialogs.Theme;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.external.floatingwindow.menu.HoverMenuService; import com.stardust.scriptdroid.external.floatingwindow.menu.HoverMenuService;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo; import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.LayoutBoundsView; import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.LayoutBoundsView;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.NodeInfoView; import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.NodeInfoView;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.OnNodeInfoSelectListener; import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.OnNodeInfoSelectListener;
import com.stardust.util.MessageEvent; import com.stardust.util.MessageIntent;
import com.stardust.widget.BubblePopupMenu;
import java.util.Arrays;
/** /**
* Created by Stardust on 2017/3/12. * Created by Stardust on 2017/3/12.
@@ -20,7 +27,9 @@ import com.stardust.util.MessageEvent;
public class FloatingLayoutBoundsView extends LayoutBoundsView { public class FloatingLayoutBoundsView extends LayoutBoundsView {
private MaterialDialog mNodeInfoDialog; private MaterialDialog mNodeInfoDialog;
private BubblePopupMenu mBubblePopMenu;
private NodeInfoView mNodeInfoView; private NodeInfoView mNodeInfoView;
private NodeInfo mSelectedNode;
public FloatingLayoutBoundsView(Context context) { public FloatingLayoutBoundsView(Context context) {
super(context); super(context);
@@ -31,27 +40,51 @@ public class FloatingLayoutBoundsView extends LayoutBoundsView {
setOnNodeInfoSelectListener(new OnNodeInfoSelectListener() { setOnNodeInfoSelectListener(new OnNodeInfoSelectListener() {
@Override @Override
public void onNodeSelect(NodeInfo info) { public void onNodeSelect(NodeInfo info) {
showNodeInfo(info); mSelectedNode = info;
ensureOperationPopMenu();
if (mBubblePopMenu.getContentView().getMeasuredWidth() <= 0)
mBubblePopMenu.preMeasure();
mBubblePopMenu.showAsDropDownAtLocation(FloatingLayoutBoundsView.this, info.getBoundsInScreen().height(), info.getBoundsInScreen().centerX() - mBubblePopMenu.getContentView().getMeasuredWidth() / 2, info.getBoundsInScreen().bottom - getStatusBarHeight());
} }
}); });
setVisibility(GONE); setVisibility(GONE);
getPaint().setColor(0xFF222222); getBoundsPaint().setStrokeWidth(2f);
getPaint().setStrokeWidth(2f);
} }
private void showNodeInfo(NodeInfo info) { private void showNodeInfo() {
ensureDialog(); ensureDialog();
mNodeInfoView.setNodeInfo(info); mNodeInfoView.setNodeInfo(mSelectedNode);
mNodeInfoDialog.show(); mNodeInfoDialog.show();
} }
private void ensureOperationPopMenu() {
if (mBubblePopMenu != null)
return;
mBubblePopMenu = new BubblePopupMenu(getContext(), Arrays.asList(
getResources().getString(R.string.text_show_widget_infomation),
getResources().getString(R.string.text_show_layout_hierarchy)));
mBubblePopMenu.setOnItemClickListener(new BubblePopupMenu.OnItemClickListener() {
@Override
public void onClick(View view, int position) {
mBubblePopMenu.dismiss();
if (position == 0) {
showNodeInfo();
} else {
HoverMenuService.postMessageIntent(new MessageIntent(HoverMenuService.ACTION_SHOW_NODE_LAYOUT_HIERARCHY)
.putExtra(HoverMenuService.EXTRA_NODE_INFO, mSelectedNode));
}
}
});
mBubblePopMenu.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
mBubblePopMenu.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
}
private void ensureDialog() { private void ensureDialog() {
if (mNodeInfoDialog == null) { if (mNodeInfoDialog == null) {
mNodeInfoView = new NodeInfoView(getContext()); mNodeInfoView = new NodeInfoView(getContext());
mNodeInfoDialog = new MaterialDialog.Builder(getContext()) mNodeInfoDialog = new MaterialDialog.Builder(getContext())
.customView(mNodeInfoView, false) .customView(mNodeInfoView, false)
.backgroundColor(0xddffffff)
.theme(Theme.LIGHT) .theme(Theme.LIGHT)
.build(); .build();
mNodeInfoDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_PHONE); mNodeInfoDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_PHONE);
@@ -61,7 +94,7 @@ public class FloatingLayoutBoundsView extends LayoutBoundsView {
@Override @Override
public boolean dispatchKeyEvent(KeyEvent event) { public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_SHOW_AND_EXPAND_MENU)); HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_SHOW_AND_EXPAND_MENU));
setVisibility(GONE); setVisibility(GONE);
return true; return true;
} }

View File

@@ -1,17 +1,23 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.view; package com.stardust.scriptdroid.external.floatingwindow.menu.view;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager; import android.view.WindowManager;
import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.Theme; import com.afollestad.materialdialogs.Theme;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.external.floatingwindow.menu.HoverMenuService; import com.stardust.scriptdroid.external.floatingwindow.menu.HoverMenuService;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo; import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.LayoutHierarchyView; import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.LayoutHierarchyView;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.NodeInfoView; import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.NodeInfoView;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.OnNodeInfoSelectListener; import com.stardust.util.MessageIntent;
import com.stardust.util.MessageEvent; import com.stardust.widget.BubblePopupMenu;
import java.util.Arrays;
/** /**
* Created by Stardust on 2017/3/12. * Created by Stardust on 2017/3/12.
@@ -20,9 +26,12 @@ import com.stardust.util.MessageEvent;
public class FloatingLayoutHierarchyView extends LayoutHierarchyView { public class FloatingLayoutHierarchyView extends LayoutHierarchyView {
private static final String TAG = "FloatingHierarchyView"; private static final String TAG = "FloatingHierarchyView";
private static final int COLOR_SHADOW = 0xddffffff;
private MaterialDialog mNodeInfoDialog; private MaterialDialog mNodeInfoDialog;
private BubblePopupMenu mBubblePopMenu;
private NodeInfoView mNodeInfoView; private NodeInfoView mNodeInfoView;
private NodeInfo mSelectedNodeInfo;
public FloatingLayoutHierarchyView(Context context) { public FloatingLayoutHierarchyView(Context context) {
super(context); super(context);
@@ -30,26 +39,53 @@ public class FloatingLayoutHierarchyView extends LayoutHierarchyView {
} }
private void init() { private void init() {
setBackgroundColor(0x99ffffff); setBackgroundColor(COLOR_SHADOW);
setVisibility(GONE); setVisibility(GONE);
setShowClickedNodeBounds(true); setShowClickedNodeBounds(true);
getBoundsPaint().setStrokeWidth(3); getBoundsPaint().setStrokeWidth(3);
getBoundsPaint().setColor(0xFFD32F2F); getBoundsPaint().setColor(0xFFD32F2F);
setOnNodeInfoLongClickListener(new OnNodeInfoSelectListener() { setOnItemLongClickListener(new OnItemLongClickListener() {
@Override @Override
public void onNodeSelect(NodeInfo info) { public void onItemLongClick(View view, NodeInfo nodeInfo) {
showNodeInfo(info); mSelectedNodeInfo = nodeInfo;
ensureOperationPopMenu();
if (mBubblePopMenu.getContentView().getMeasuredWidth() <= 0)
mBubblePopMenu.preMeasure();
mBubblePopMenu.showAsDropDown(view, view.getWidth() / 2 - mBubblePopMenu.getContentView().getMeasuredWidth() / 2, 0);
} }
}); });
} }
private void showNodeInfo(NodeInfo info) { private void ensureOperationPopMenu() {
ensureDialog(); if (mBubblePopMenu != null)
mNodeInfoView.setNodeInfo(info); return;
mBubblePopMenu = new BubblePopupMenu(getContext(), Arrays.asList(
getResources().getString(R.string.text_show_widget_infomation),
getResources().getString(R.string.text_show_layout_bounds)));
mBubblePopMenu.setOnItemClickListener(new BubblePopupMenu.OnItemClickListener() {
@Override
public void onClick(View view, int position) {
mBubblePopMenu.dismiss();
if (position == 0) {
showNodeInfo();
} else {
HoverMenuService.postMessageIntent(new MessageIntent(HoverMenuService.ACTION_SHOW_NODE_LAYOUT_BOUNDS)
.putExtra(HoverMenuService.EXTRA_NODE_INFO, mSelectedNodeInfo));
}
}
});
mBubblePopMenu.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
mBubblePopMenu.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
}
void showNodeInfo() {
ensureNodeInfoDialog();
mNodeInfoView.setNodeInfo(mSelectedNodeInfo);
mNodeInfoDialog.show(); mNodeInfoDialog.show();
} }
private void ensureDialog() { private void ensureNodeInfoDialog() {
if (mNodeInfoDialog == null) { if (mNodeInfoDialog == null) {
mNodeInfoView = new NodeInfoView(getContext()); mNodeInfoView = new NodeInfoView(getContext());
mNodeInfoDialog = new MaterialDialog.Builder(getContext()) mNodeInfoDialog = new MaterialDialog.Builder(getContext())
@@ -64,11 +100,12 @@ public class FloatingLayoutHierarchyView extends LayoutHierarchyView {
@Override @Override
public boolean dispatchKeyEvent(KeyEvent event) { public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_SHOW_AND_EXPAND_MENU)); HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_SHOW_AND_EXPAND_MENU));
setVisibility(GONE); setVisibility(GONE);
return true; return true;
} }
return super.dispatchKeyEvent(event); return super.dispatchKeyEvent(event);
} }
} }

View File

@@ -1,5 +1,6 @@
package com.stardust.scriptdroid.ui.main.script_list; package com.stardust.scriptdroid.ui.main.script_list;
import android.content.DialogInterface;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@@ -13,6 +14,7 @@ import android.widget.EditText;
import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.app.Fragment; import com.stardust.app.Fragment;
import com.stardust.app.OperationDialogBuilder;
import com.stardust.scriptdroid.script.ScriptFile; import com.stardust.scriptdroid.script.ScriptFile;
import com.stardust.pio.PFile; import com.stardust.pio.PFile;
import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.R;
@@ -20,6 +22,7 @@ import com.stardust.scriptdroid.script.Scripts;
import com.stardust.scriptdroid.script.StorageScriptProvider; import com.stardust.scriptdroid.script.StorageScriptProvider;
import com.stardust.scriptdroid.ui.edit.EditActivity; import com.stardust.scriptdroid.ui.edit.EditActivity;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.util.UnderuseExecutors;
import com.stardust.view.ViewBinder; import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding; import com.stardust.view.ViewBinding;
import com.stardust.widget.SimpleAdapterDataObserver; import com.stardust.widget.SimpleAdapterDataObserver;
@@ -29,6 +32,9 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.InputStream; import java.io.InputStream;
import butterknife.OnClick;
import butterknife.Optional;
/** /**
* Created by Stardust on 2017/3/13. * Created by Stardust on 2017/3/13.
*/ */
@@ -109,15 +115,17 @@ public class MyScriptListFragment extends Fragment {
private void initDialogs() { private void initDialogs() {
mScriptFileOperationDialog = buildDialog(R.layout.dialog_script_file_operations); mScriptFileOperationDialog = new OperationDialogBuilder(getContext())
mDirectoryOperationDialog = buildDialog(R.layout.dialog_directory_operations); .item(R.id.rename, R.drawable.ic_ali_rename, R.string.text_rename)
} .item(R.id.open_by_other_apps, R.drawable.ic_ali_open, R.string.text_open_by_other_apps)
.item(R.id.create_shortcut, R.drawable.ic_ali_shortcut, R.string.text_send_shortcut)
private MaterialDialog buildDialog(int layout) { .item(R.id.delete, R.drawable.ic_ali_delete, R.string.text_delete)
View view = View.inflate(getActivity(), layout, null); .bindItemClick(this)
ViewBinder.bind(this, view); .build();
return new MaterialDialog.Builder(getActivity()) mDirectoryOperationDialog = new OperationDialogBuilder(getContext())
.customView(view, false) .item(R.id.rename, R.drawable.ic_ali_rename, R.string.text_rename)
.item(R.id.delete, R.drawable.ic_ali_delete, R.string.text_delete)
.bindItemClick(this)
.build(); .build();
} }
@@ -230,8 +238,9 @@ public class MyScriptListFragment extends Fragment {
}); });
} }
@ViewBinding.Click(R.id.rename) @Optional
private void renameScriptFile() { @OnClick(R.id.rename)
void renameScriptFile() {
dismissDialogs(); dismissDialogs();
String originalName = mSelectedScriptFile.getSimplifiedName(); String originalName = mSelectedScriptFile.getSimplifiedName();
showNameInputDialog(originalName, new InputCallback(mSelectedScriptFile.isDirectory(), originalName), new MaterialDialog.InputCallback() { showNameInputDialog(originalName, new InputCallback(mSelectedScriptFile.isDirectory(), originalName), new MaterialDialog.InputCallback() {
@@ -252,8 +261,9 @@ public class MyScriptListFragment extends Fragment {
} }
@ViewBinding.Click(R.id.open_by_other_apps) @Optional
private void openByOtherApps() { @OnClick(R.id.open_by_other_apps)
void openByOtherApps() {
dismissDialogs(); dismissDialogs();
Scripts.openByOtherApps(mSelectedScriptFile); Scripts.openByOtherApps(mSelectedScriptFile);
onScriptFileOperated(); onScriptFileOperated();
@@ -269,16 +279,18 @@ public class MyScriptListFragment extends Fragment {
}); });
} }
@ViewBinding.Click(R.id.create_shortcut) @Optional
private void createShortcut() { @OnClick(R.id.create_shortcut)
void createShortcut() {
dismissDialogs(); dismissDialogs();
Scripts.createShortcut(mSelectedScriptFile); Scripts.createShortcut(mSelectedScriptFile);
Snackbar.make(getView(), R.string.text_already_create, Snackbar.LENGTH_SHORT).show(); Snackbar.make(getView(), R.string.text_already_create, Snackbar.LENGTH_SHORT).show();
onScriptFileOperated(); onScriptFileOperated();
} }
@ViewBinding.Click(R.id.delete) @Optional
private void deleteScriptFile() { @OnClick(R.id.delete)
void deleteScriptFile() {
dismissDialogs(); dismissDialogs();
new MaterialDialog.Builder(getActivity()) new MaterialDialog.Builder(getActivity())
.title(R.string.delete_confirm) .title(R.string.delete_confirm)
@@ -295,7 +307,7 @@ public class MyScriptListFragment extends Fragment {
private void doDeletingScriptFile() { private void doDeletingScriptFile() {
mScriptListWithProgressBarView.showProgressBar(); mScriptListWithProgressBarView.showProgressBar();
new Thread(new Runnable() { UnderuseExecutors.execute(new Runnable() {
@Override @Override
public void run() { public void run() {
if (PFile.deleteRecursively(mSelectedScriptFile)) { if (PFile.deleteRecursively(mSelectedScriptFile)) {
@@ -306,7 +318,7 @@ public class MyScriptListFragment extends Fragment {
} }
onScriptFileOperated(); onScriptFileOperated();
} }
}).start(); });
} }
private void showMessage(final int resId) { private void showMessage(final int resId) {

View File

@@ -14,5 +14,5 @@ public abstract class BindableViewHolder<DataType> extends RecyclerView.ViewHold
super(itemView); super(itemView);
} }
public abstract void bind(DataType data); public abstract void bind(DataType data, int position);
} }

View File

@@ -0,0 +1,109 @@
package com.stardust.widget;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.support.v7.widget.RecyclerView;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;
import com.stardust.scriptdroid.R;
import java.util.List;
/**
* Created by Stardust on 2017/5/23.
*/
public class BubblePopupMenu extends PopupWindow {
public interface OnItemClickListener {
void onClick(View view, int position);
}
private RecyclerView mRecyclerView;
private OnItemClickListener mOnItemClickListener;
private View mLittleTriangle;
public BubblePopupMenu(Context context, List<String> options) {
super(context);
View view = View.inflate(context, R.layout.bubble_popup_menu, null);
mLittleTriangle = view.findViewById(R.id.little_triangle);
mRecyclerView = (RecyclerView) view.findViewById(R.id.list);
mRecyclerView.setAdapter(new SimpleRecyclerViewAdapter<>(R.layout.bubble_popup_menu_item, options, new SimpleRecyclerViewAdapter.ViewHolderFactory<MenuItemViewHolder>() {
@Override
public MenuItemViewHolder create(View itemView) {
return new MenuItemViewHolder(itemView);
}
}));
setContentView(view);
setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
setOutsideTouchable(true);
setFocusable(true);
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
}
public void showAsDropDownAtLocation(View parent, int contentHeight, int x, int y) {
int screenWidth = getContentView().getResources().getDisplayMetrics().widthPixels;
int screenHeight = getContentView().getResources().getDisplayMetrics().heightPixels;
int width = getContentView().getWidth();
int height = getContentView().getHeight();
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mLittleTriangle.getLayoutParams();
if (x + width > screenWidth) {
params.leftMargin = x + width - screenWidth;
} else if (x < 0) {
params.leftMargin = x;
} else {
params.leftMargin = 0;
}
if (y + height > screenHeight) {
getContentView().setRotation(180);
mRecyclerView.setRotation(180);
params.leftMargin = -params.leftMargin;
y -= contentHeight + height;
} else {
getContentView().setRotation(0);
mRecyclerView.setRotation(0);
}
mLittleTriangle.setLayoutParams(params);
super.showAtLocation(parent, Gravity.NO_GRAVITY, x, y);
}
public void preMeasure() {
getContentView().measure(getWidth(), getHeight());
}
private class MenuItemViewHolder extends BindableViewHolder<String> {
private TextView mOption;
public MenuItemViewHolder(View itemView) {
super(itemView);
mOption = (TextView) itemView.findViewById(R.id.option);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
int i = mRecyclerView.getChildAdapterPosition(v);
mOnItemClickListener.onClick(v, i);
}
}
});
}
@Override
public void bind(String s, int position) {
mOption.setText(s);
}
}
}

View File

@@ -0,0 +1,56 @@
package com.stardust.widget;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Created by Stardust on 2017/5/24.
*/
public class SimpleRecyclerViewAdapter<M, VH extends BindableViewHolder<M>> extends RecyclerView.Adapter<VH> {
public interface ViewHolderFactory<VH> {
VH create(View itemView);
}
private List<M> mDataList = new ArrayList<>();
private int mLayoutResource;
private ViewHolderFactory<VH> mVHViewHolderFactory;
public SimpleRecyclerViewAdapter(int layoutResource, List<M> dataList, ViewHolderFactory<VH> VHViewHolderFactory) {
mLayoutResource = layoutResource;
mVHViewHolderFactory = VHViewHolderFactory;
mDataList.addAll(dataList);
}
public SimpleRecyclerViewAdapter(int layoutResource, ViewHolderFactory<VH> VHViewHolderFactory) {
this(layoutResource, Collections.<M>emptyList(), VHViewHolderFactory);
}
@Override
public VH onCreateViewHolder(ViewGroup parent, int viewType) {
return mVHViewHolderFactory.create(LayoutInflater.from(parent.getContext()).inflate(mLayoutResource, parent, false));
}
@Override
public void onBindViewHolder(VH holder, int position) {
M m = mDataList.get(position);
holder.bind(m, position);
}
@Override
public int getItemCount() {
return mDataList.size();
}
public void add(M m) {
mDataList.add(m);
notifyItemInserted(mDataList.size() - 1);
}
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/bubble_bg"/>
<corners android:radius="16dp"/>
</shape>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<rotate
android:fromDegrees="45"
android:pivotX="0"
android:pivotY="100%"
android:toDegrees="45">
<shape android:shape="rectangle">
<size
android:width="12dp"
android:height="12dp"/>
<solid android:color="@color/bubble_bg"/>
</shape>
</rotate>
</item>
</layer-list>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:orientation="vertical">
<View
android:id="@+id/little_triangle"
android:layout_width="17dp"
android:layout_height="8.5dp"
android:layout_gravity="center_horizontal"
android:background="@drawable/little_triangle"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bubble"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"/>
</LinearLayout>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:orientation="vertical">
<TextView
android:id="@+id/option"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="12dp"
android:textColor="@android:color/primary_text_light"
android:textSize="15sp"
tools:text="OOXX"/>
</LinearLayout>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingBottom="16dp"
android:paddingLeft="24dp"
android:paddingTop="16dp">
<com.stardust.theme.widget.ThemeColorImageView
android:id="@+id/icon"
android:layout_width="20dp"
android:layout_height="20dp"/>
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:gravity="center_vertical"
android:textColor="@android:color/primary_text_light"
android:textSize="16sp"/>
</LinearLayout>

View File

@@ -4,4 +4,5 @@
<color name="colorPrimaryDark">#00796B</color> <color name="colorPrimaryDark">#00796B</color>
<color name="colorAccent">#536DFE</color> <color name="colorAccent">#536DFE</color>
<color name="sliding_up_panel_shadow_color">#99444444</color> <color name="sliding_up_panel_shadow_color">#99444444</color>
<color name="bubble_bg">#eef2f3f9</color>
</resources> </resources>

View File

@@ -186,6 +186,9 @@
<string name="text_about_me_and_repo">关于项目与开发者</string> <string name="text_about_me_and_repo">关于项目与开发者</string>
<string name="text_attribute">属性</string> <string name="text_attribute">属性</string>
<string name="text_value"></string> <string name="text_value"></string>
<string name="text_show_widget_infomation">查看控件信息</string>
<string name="text_show_layout_hierarchy">在布局层次中查看</string>
<string name="text_show_layout_bounds">在布局范围中查看</string>
<string-array name="record_control_keys"> <string-array name="record_control_keys">
<item></item> <item></item>

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeNotificationStateChanged|typeAnnouncement|typeAssistReadingContext|typeContextClicked|typeGestureDetectionEnd|typeGestureDetectionStart|typeTouchExplorationGestureEnd|typeTouchExplorationGestureStart|typeTouchInteractionEnd|typeTouchInteractionStart|typeViewAccessibilityFocusCleared|typeViewAccessibilityFocused|typeViewClicked|typeViewHoverEnter|typeViewHoverExit|typeViewFocused|typeViewLongClicked|typeViewScrolled|typeViewSelected|typeViewTextChanged|typeViewTextSelectionChanged|typeViewTextTraversedAtMovementGranularity|typeWindowContentChanged|typeWindowsChanged|typeWindowStateChanged" android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagIncludeNotImportantViews|flagReportViewIds|flagRetrieveInteractiveWindows|flagRequestEnhancedWebAccessibility|flagRequestFilterKeyEvents" android:accessibilityFlags="flagReportViewIds|flagRequestEnhancedWebAccessibility|flagRequestFilterKeyEvents"
android:canPerformGestures="true" android:canPerformGestures="true"
android:canRequestEnhancedWebAccessibility="true" android:canRequestEnhancedWebAccessibility="true"
android:canRetrieveWindowContent="true" android:canRetrieveWindowContent="true"

View File

@@ -13,11 +13,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
public abstract class AccessibilityService extends android.accessibilityservice.AccessibilityService { public abstract class AccessibilityService extends android.accessibilityservice.AccessibilityService {
public interface NotificationCallback {
void onNotification();
}
private CopyOnWriteArrayList<NotificationCallback> mNotificationCallbacks = new CopyOnWriteArrayList<>();
private AccessibilityNodeInfo mRootInActiveWindow; private AccessibilityNodeInfo mRootInActiveWindow;
@CallSuper @CallSuper
@@ -32,7 +27,12 @@ public abstract class AccessibilityService extends android.accessibilityservice.
@Override @Override
public AccessibilityNodeInfo getRootInActiveWindow() { public AccessibilityNodeInfo getRootInActiveWindow() {
return mRootInActiveWindow; try {
return super.getRootInActiveWindow();
} catch (IllegalStateException ignored) {
return mRootInActiveWindow;
}
} }
} }

View File

@@ -0,0 +1,51 @@
package com.stardust.util;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import java.util.HashMap;
/**
* Created by Stardust on 2017/6/27.
*/
public class MessageIntent extends Intent {
private HashMap<String, Object> mObjectExtras;
public MessageIntent() {
}
public MessageIntent(Intent o) {
super(o);
}
public MessageIntent(String action) {
super(action);
}
public MessageIntent(String action, Uri uri) {
super(action, uri);
}
public MessageIntent(Context packageContext, Class<?> cls) {
super(packageContext, cls);
}
public MessageIntent(String action, Uri uri, Context packageContext, Class<?> cls) {
super(action, uri, packageContext, cls);
}
public MessageIntent putExtra(String key, Object value) {
if (mObjectExtras == null) {
mObjectExtras = new HashMap<>();
}
mObjectExtras.put(key, value);
return this;
}
public Object getObjectExtra(String key) {
return mObjectExtras.get(key);
}
}