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

View File

@ -2,15 +2,16 @@
<project version="4">
<component name="EntryPointsManager">
<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="1" class="java.lang.String" itemvalue="android.webkit.JavascriptInterface" />
<item index="2" class="java.lang.String" itemvalue="com.stardust.autojs.runtime.ScriptClass" />
<item index="3" class="java.lang.String" itemvalue="com.stardust.autojs.runtime.ScriptInterface" />
<item index="4" class="java.lang.String" itemvalue="com.stardust.view.ViewBind.Click" />
<item index="5" class="java.lang.String" itemvalue="com.stardust.view.ViewBinding.Check" />
<item index="6" class="java.lang.String" itemvalue="com.stardust.view.ViewBinding.Click" />
<item index="7" class="java.lang.String" itemvalue="org.greenrobot.eventbus.Subscribe" />
<item index="2" class="java.lang.String" itemvalue="butterknife.OnClick" />
<item index="3" class="java.lang.String" itemvalue="com.stardust.autojs.runtime.ScriptClass" />
<item index="4" class="java.lang.String" itemvalue="com.stardust.autojs.runtime.ScriptInterface" />
<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.Check" />
<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>
</component>
<component name="NullableNotNullManager">

View File

@ -9,8 +9,8 @@ android {
applicationId "com.stardust.scriptdroid"
minSdkVersion 19
targetSdkVersion 23
versionCode 137
versionName "2.0.12 Beta"
versionCode 139
versionName "2.0.13 Alpha2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
ndk {
@ -111,7 +111,7 @@ dependencies {
// Android issue reporter (a github issue reporter)
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 'io.mattcarroll.hover:hover:0.9.7'
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.makeramen:roundedimageview:2.2.1'
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-styles-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.autojs.AutoJs;
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.FloatingLayoutHierarchyView;
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
import com.stardust.theme.ThemeColorManagerCompat;
import com.stardust.util.MessageEvent;
import com.stardust.util.MessageIntent;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@ -45,13 +47,18 @@ public class HoverMenuService extends Service {
public boolean state;
}
public static final String MESSAGE_SHOW_AND_EXPAND_MENU = "MESSAGE_SHOW_AND_EXPAND_MENU";
public static final String MESSAGE_SHOW_LAYOUT_HIERARCHY = "MESSAGE_SHOW_LAYOUT_HIERARCHY";
public static final String MESSAGE_SHOW_LAYOUT_BOUNDS = "MESSAGE_SHOW_LAYOUT_BOUNDS";
public static final String MESSAGE_COLLAPSE_MENU = "MESSAGE_COLLAPSE_MENU";
public static final String MESSAGE_MENU_COLLAPSING = "MESSAGE_MENU_COLLAPSING";
public static final String MESSAGE_MENU_EXPANDING = "MESSAGE_MENU_EXPANDING";
public static final String MESSAGE_MENU_EXIT = "MESSAGE_MENU_EXIT";
public static final String ACTION_SHOW_AND_EXPAND_MENU = "ACTION_SHOW_AND_EXPAND_MENU";
public static final String ACTION_SHOW_LAYOUT_HIERARCHY = "ACTION_SHOW_LAYOUT_HIERARCHY";
public static final String ACTION_SHOW_LAYOUT_BOUNDS = "ACTION_SHOW_LAYOUT_BOUNDS";
public static final String ACTION_COLLAPSE_MENU = "ACTION_COLLAPSE_MENU";
public static final String ACTION_MENU_COLLAPSING = "ACTION_MENU_COLLAPSING";
public static final String ACTION_MENU_EXPANDING = "ACTION_MENU_EXPANDING";
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 EventBus eventBus = new EventBus();
@ -70,8 +77,12 @@ public class HoverMenuService extends Service {
eventBus.post(new ServiceStateChangedEvent(sIsRunning));
}
public static void postEvent(MessageEvent event) {
eventBus.post(event);
public static void postIntent(Intent intent) {
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() {
@Override
public void onExitByUserRequest() {
eventBus.post(new MessageEvent(MESSAGE_MENU_EXIT));
eventBus.post(new MessageEvent(ACTION_MENU_EXIT));
savePreferredLocation();
mWindowHoverMenu.hide();
stopSelf();
@ -142,14 +153,14 @@ public class HoverMenuService extends Service {
mWindowHoverMenu.setHoverMenuTransitionListener(new SimpleHoverMenuTransitionListener() {
@Override
public void onExpanding() {
eventBus.post(new MessageEvent(MESSAGE_MENU_EXPANDING));
eventBus.post(new MessageEvent(ACTION_MENU_EXPANDING));
captureCurrentWindow();
}
@Override
public void onCollapsing() {
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
public void onMessageEvent(MessageEvent event) {
switch (event.message) {
case MESSAGE_SHOW_AND_EXPAND_MENU:
public void handleMessageIntent(MessageIntent intent) {
switch (intent.getAction()) {
case ACTION_SHOW_AND_EXPAND_MENU:
showAndExpandMenu();
break;
case MESSAGE_SHOW_LAYOUT_HIERARCHY:
case ACTION_SHOW_LAYOUT_HIERARCHY:
showLayoutHierarchy();
break;
case MESSAGE_SHOW_LAYOUT_BOUNDS:
case ACTION_SHOW_LAYOUT_BOUNDS:
showLayoutBounds();
break;
case MESSAGE_COLLAPSE_MENU:
case ACTION_COLLAPSE_MENU:
mWindowHoverMenu.collapseMenu();
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()) {
return;
}
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_SHOW_LAYOUT_HIERARCHY));
HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_SHOW_LAYOUT_HIERARCHY));
}
private boolean ensureCapture() {
@ -72,7 +72,7 @@ public class MainMenuNavigatorContent implements NavigatorContent {
if (!ensureCapture()) {
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)
@ -84,7 +84,7 @@ public class MainMenuNavigatorContent implements NavigatorContent {
private void openMainActivity() {
App.getApp().startActivity(new Intent(App.getApp(), MainActivity.class)
.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
@ -125,9 +125,9 @@ public class MainMenuNavigatorContent implements NavigatorContent {
@Subscribe
public void onMessageEvent(MessageEvent event) {
if (event.message.equals(HoverMenuService.MESSAGE_MENU_EXPANDING)) {
if (event.message.equals(HoverMenuService.ACTION_MENU_EXPANDING)) {
syncCurrentInfo();
} else if (event.message.equals(HoverMenuService.MESSAGE_MENU_EXIT)) {
} else if (event.message.equals(HoverMenuService.ACTION_MENU_EXIT)) {
HoverMenuService.getEventBus().unregister(this);
}
}

View File

@ -1,6 +1,7 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.content;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v7.widget.SwitchCompat;
import android.view.View;
@ -124,7 +125,7 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat
private void resumeRecord() {
mRecorder.resume();
setState(Recorder.STATE_RECORDING);
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU));
HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_COLLAPSE_MENU));
}
private void pauseRecord() {
@ -137,7 +138,7 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat
mRecorder.setOnStateChangedListener(this);
mRecorder.start();
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) {
@ -153,7 +154,7 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat
private void stopRecord() {
mRecorder.stop();
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
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)
pauseRecord();
} else if (event.message.equals(HoverMenuService.MESSAGE_MENU_EXIT)) {
} else if (event.message.equals(HoverMenuService.ACTION_MENU_EXIT)) {
onMenuExit();
}
}

View File

@ -1,6 +1,7 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.content;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.view.ContextThemeWrapper;
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.main.script_list.ScriptAndFolderListRecyclerView;
import com.stardust.scriptdroid.ui.main.script_list.ScriptListWithProgressBarView;
import com.stardust.util.MessageEvent;
import com.stardust.widget.ViewHolderSupplier;
import io.mattcarroll.hover.Navigator;
@ -53,7 +53,7 @@ public class ScriptListNavigatorContent implements NavigatorContent {
@Override
public void onClick(ScriptFile file, int position) {
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();
ScriptFile file = mFloatingScriptFileListView.getAdapter().getScriptFileAt(position);
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.
*
*/
// TODO: 2017/5/8
public class LayoutInspector {
private volatile NodeInfo mCapture;
@ -22,7 +19,7 @@ public class LayoutInspector {
private Executor mExecutor = UnderuseExecutors.getExecutor();
public void captureCurrentWindow() {
AccessibilityService service = AccessibilityWatchDogService.getInstance();
AccessibilityWatchDogService service = AccessibilityWatchDogService.getInstance();
if (service == null) {
mCapture = null;
} else {

View File

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

View File

@ -5,6 +5,7 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Build;
import android.support.annotation.Nullable;
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 java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
@ -24,12 +27,16 @@ import java.util.List;
public class LayoutBoundsView extends View {
private static final int COLOR_SHADOW = 0x6a000000;
private NodeInfo mRootNode;
private Paint mPaint;
private NodeInfo mTouchedNode;
private Paint mBoundsPaint;
private Paint mFillingPaint;
private int mStatusBarHeight;
private OnNodeInfoSelectListener mOnNodeInfoSelectListener;
private int mTouchedNodeBoundsColor = Color.RED;
private int mNormalNodeBoundsColor = Color.GREEN;
private Rect mTouchedNodeBounds;
public LayoutBoundsView(Context context) {
super(context);
@ -59,32 +66,68 @@ public class LayoutBoundsView extends View {
public void setRootNode(NodeInfo rootNode) {
mRootNode = rootNode;
mTouchedNode = null;
}
private void init() {
mPaint = new Paint();
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.STROKE);
mBoundsPaint = new Paint();
mBoundsPaint.setStyle(Paint.Style.STROKE);
mFillingPaint = new Paint();
mFillingPaint.setStyle(Paint.Style.FILL);
mFillingPaint.setColor(COLOR_SHADOW);
mStatusBarHeight = ViewUtil.getStatusBarHeight(getContext());
setWillNotDraw(false);
}
@Override
protected void onDraw(Canvas 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);
if (mTouchedNode != null) {
mBoundsPaint.setColor(mTouchedNodeBoundsColor);
drawRect(canvas, mTouchedNode.getBoundsInScreen(), mStatusBarHeight, mBoundsPaint);
}
}
public Paint getPaint() {
return mPaint;
public Paint getBoundsPaint() {
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) {
if (node == null)
return;
drawRect(canvas, node.getBoundsInScreen(), mStatusBarHeight, mPaint);
drawRect(canvas, node.getBoundsInScreen(), mStatusBarHeight, mBoundsPaint);
for (NodeInfo child : node.getChildren()) {
draw(canvas, child);
}
@ -98,25 +141,39 @@ public class LayoutBoundsView extends View {
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN && mRootNode != null) {
ArrayList<NodeInfo> list = new ArrayList<>();
findNodeAt(mRootNode, (int) event.getRawX(), (int) event.getRawY(), list);
showNodeInfoList(list);
if (mRootNode != null) {
setSelectedNode(findNodeAt(mRootNode, (int) event.getRawX(), (int) event.getRawY()));
}
if (event.getAction() == MotionEvent.ACTION_UP && mTouchedNode != null) {
onNodeInfoClick(mTouchedNode);
return true;
}
return super.onTouchEvent(event);
}
private void showNodeInfoList(ArrayList<NodeInfo> list) {
onNodeInfoClick(list.get(list.size() - 1));
}
private void onNodeInfoClick(NodeInfo nodeInfo) {
if (mOnNodeInfoSelectListener != null) {
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) {
for (NodeInfo child : node.getChildren()) {
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 java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.Stack;
import pl.openrnd.multilevellistview.ItemInfo;
import pl.openrnd.multilevellistview.MultiLevelListAdapter;
@ -34,13 +37,17 @@ import pl.openrnd.multilevellistview.OnItemClickListener;
public class LayoutHierarchyView extends MultiLevelListView {
public interface OnItemLongClickListener {
void onItemLongClick(View view, NodeInfo nodeInfo);
}
private Adapter mAdapter;
private OnNodeInfoSelectListener mOnNodeInfoSelectListener;
private OnItemLongClickListener mOnItemLongClickListener;
private AdapterView.OnItemLongClickListener mOnItemLongClickListenerProxy = new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (mOnNodeInfoSelectListener != null) {
mOnNodeInfoSelectListener.onNodeSelect(((ViewHolder) view.getTag()).nodeInfo);
if (mOnItemLongClickListener != null) {
mOnItemLongClickListener.onItemLongClick(view, ((ViewHolder) view.getTag()).nodeInfo);
return true;
}
return false;
@ -55,7 +62,9 @@ public class LayoutHierarchyView extends MultiLevelListView {
private Drawable mOriginalBackground;
private boolean mShowClickedNodeBounds;
private int mClickedColor = 0x77c4c4c4;
private int mClickedColor = 0x99b2b3b7;
private NodeInfo mRootNode;
private Set<NodeInfo> mInitiallyExpandedNodes = new HashSet<>();
public LayoutHierarchyView(Context context) {
super(context);
@ -80,6 +89,9 @@ public class LayoutHierarchyView extends MultiLevelListView {
mClickedColor = clickedColor;
}
public int getStatusBarHeight() {
return mStatusBarHeight;
}
private void init() {
mAdapter = new Adapter();
@ -127,12 +139,14 @@ public class LayoutHierarchyView extends MultiLevelListView {
}
public void setRootNode(NodeInfo rootNodeInfo) {
mRootNode = rootNodeInfo;
mAdapter.setDataItems(Collections.singletonList(rootNodeInfo));
mClickedNodeInfo = null;
mInitiallyExpandedNodes.clear();
}
public void setOnNodeInfoLongClickListener(final OnNodeInfoSelectListener onNodeInfoSelectListener) {
mOnNodeInfoSelectListener = onNodeInfoSelectListener;
public void setOnItemLongClickListener(final OnItemLongClickListener onNodeInfoSelectListener) {
mOnItemLongClickListener = onNodeInfoSelectListener;
}
@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 {
TextView nameView;
TextView infoView;
@ -163,15 +206,20 @@ public class LayoutHierarchyView extends MultiLevelListView {
@Override
public List<?> getSubObjects(Object object) {
protected List<?> getSubObjects(Object object) {
return ((NodeInfo) object).getChildren();
}
@Override
public boolean isExpandable(Object object) {
protected boolean isExpandable(Object object) {
return !((NodeInfo) object).getChildren().isEmpty();
}
@Override
protected boolean isInitiallyExpanded(Object object) {
return mInitiallyExpandedNodes.contains((NodeInfo) object);
}
@Override
public View getViewForObject(Object object, View convertView, ItemInfo itemInfo) {
NodeInfo nodeInfo = (NodeInfo) object;
@ -199,6 +247,9 @@ public class LayoutHierarchyView extends MultiLevelListView {
viewHolder.levelBeamView.setLevel(itemInfo.getLevel());
if (nodeInfo == mClickedNodeInfo) {
setClickedItem(convertView, nodeInfo);
}
return convertView;
}

View File

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

View File

@ -1,17 +1,24 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.view;
import android.content.Context;
import android.content.Intent;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import com.afollestad.materialdialogs.MaterialDialog;
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.layout_inspector.NodeInfo;
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.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.
@ -20,7 +27,9 @@ import com.stardust.util.MessageEvent;
public class FloatingLayoutBoundsView extends LayoutBoundsView {
private MaterialDialog mNodeInfoDialog;
private BubblePopupMenu mBubblePopMenu;
private NodeInfoView mNodeInfoView;
private NodeInfo mSelectedNode;
public FloatingLayoutBoundsView(Context context) {
super(context);
@ -31,27 +40,51 @@ public class FloatingLayoutBoundsView extends LayoutBoundsView {
setOnNodeInfoSelectListener(new OnNodeInfoSelectListener() {
@Override
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);
getPaint().setColor(0xFF222222);
getPaint().setStrokeWidth(2f);
getBoundsPaint().setStrokeWidth(2f);
}
private void showNodeInfo(NodeInfo info) {
private void showNodeInfo() {
ensureDialog();
mNodeInfoView.setNodeInfo(info);
mNodeInfoView.setNodeInfo(mSelectedNode);
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() {
if (mNodeInfoDialog == null) {
mNodeInfoView = new NodeInfoView(getContext());
mNodeInfoDialog = new MaterialDialog.Builder(getContext())
.customView(mNodeInfoView, false)
.backgroundColor(0xddffffff)
.theme(Theme.LIGHT)
.build();
mNodeInfoDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_PHONE);
@ -61,7 +94,7 @@ public class FloatingLayoutBoundsView extends LayoutBoundsView {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
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);
return true;
}

View File

@ -1,17 +1,23 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.view;
import android.content.Context;
import android.content.Intent;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import com.afollestad.materialdialogs.MaterialDialog;
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.layout_inspector.NodeInfo;
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.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.
@ -20,9 +26,12 @@ import com.stardust.util.MessageEvent;
public class FloatingLayoutHierarchyView extends LayoutHierarchyView {
private static final String TAG = "FloatingHierarchyView";
private static final int COLOR_SHADOW = 0xddffffff;
private MaterialDialog mNodeInfoDialog;
private BubblePopupMenu mBubblePopMenu;
private NodeInfoView mNodeInfoView;
private NodeInfo mSelectedNodeInfo;
public FloatingLayoutHierarchyView(Context context) {
super(context);
@ -30,26 +39,53 @@ public class FloatingLayoutHierarchyView extends LayoutHierarchyView {
}
private void init() {
setBackgroundColor(0x99ffffff);
setBackgroundColor(COLOR_SHADOW);
setVisibility(GONE);
setShowClickedNodeBounds(true);
getBoundsPaint().setStrokeWidth(3);
getBoundsPaint().setColor(0xFFD32F2F);
setOnNodeInfoLongClickListener(new OnNodeInfoSelectListener() {
setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public void onNodeSelect(NodeInfo info) {
showNodeInfo(info);
public void onItemLongClick(View view, NodeInfo nodeInfo) {
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) {
ensureDialog();
mNodeInfoView.setNodeInfo(info);
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_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();
}
private void ensureDialog() {
private void ensureNodeInfoDialog() {
if (mNodeInfoDialog == null) {
mNodeInfoView = new NodeInfoView(getContext());
mNodeInfoDialog = new MaterialDialog.Builder(getContext())
@ -64,11 +100,12 @@ public class FloatingLayoutHierarchyView extends LayoutHierarchyView {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
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);
return true;
}
return super.dispatchKeyEvent(event);
}
}

View File

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

View File

@ -14,5 +14,5 @@ public abstract class BindableViewHolder<DataType> extends RecyclerView.ViewHold
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="colorAccent">#536DFE</color>
<color name="sliding_up_panel_shadow_color">#99444444</color>
<color name="bubble_bg">#eef2f3f9</color>
</resources>

View File

@ -186,6 +186,9 @@
<string name="text_about_me_and_repo">关于项目与开发者</string>
<string name="text_attribute">属性</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">
<item></item>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<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:accessibilityFlags="flagIncludeNotImportantViews|flagReportViewIds|flagRetrieveInteractiveWindows|flagRequestEnhancedWebAccessibility|flagRequestFilterKeyEvents"
android:accessibilityFlags="flagReportViewIds|flagRequestEnhancedWebAccessibility|flagRequestFilterKeyEvents"
android:canPerformGestures="true"
android:canRequestEnhancedWebAccessibility="true"
android:canRetrieveWindowContent="true"

View File

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