This commit is contained in:
hyb1996
2017-04-26 13:48:59 +08:00
parent 38fa3b3988
commit 6829c0659e
92 changed files with 3439 additions and 679 deletions

View File

@@ -0,0 +1,68 @@
package com.stardust.mi666;
import com.stardust.util.ScreenMetrics;
import java.util.List;
/**
* Created by Stardust on 2017/4/24.
*/
public class Cracker {
private OCR mOCR;
private Solver mSolver;
private int mX, mY, mIntervalX, mIntervalY, mWidth, mHeight;
private void init(int x, int y, int intervalX, int intervalY, int w, int h) {
mOCR = new OCR(x, y, intervalX, intervalY, w, h);
mSolver = new Solver();
mX = x;
mY = y;
mIntervalY = intervalY;
mIntervalX = intervalX;
mWidth = w;
mHeight = h;
}
public Cracker() {
if (ScreenMetrics.getScreenWidth() == 1080 && ScreenMetrics.getScreenHeight() == 1920) {
init(68, 724, 238, 236, 228, 68);
} else {
double scaleX = 1080 / ScreenMetrics.getScreenWidth();
double scaleY = 1920 / ScreenMetrics.getScreenHeight();
init((int) (68 * scaleX), (int) (724 * scaleY), (int) (238 * scaleX), (int) (236 * scaleY), (int) (228 * scaleX), (int) (68 * scaleY));
}
}
public int[][] crack(String path) {
List<Integer> numbers = mOCR.detect(path);
mSolver.solve(numbers);
if (!mSolver.isSolved()) {
return new int[0][];
}
List<Integer> result = mSolver.getResult();
boolean[] used = new boolean[numbers.size()];
int[][] coordinates = new int[result.size()][2];
for (int i = 0; i < coordinates.length; i++) {
int index = indexOf(numbers, used, result.get(i));
int row = index / 4;
int col = index % 4;
int x = mX + mIntervalX * col + mWidth / 2;
int y = mY + mIntervalY * row + mHeight / 2;
coordinates[i][0] = x;
coordinates[i][1] = y;
}
return coordinates;
}
private int indexOf(List<Integer> numbers, boolean[] used, int integer) {
for (int i = 0; i < numbers.size(); i++) {
if (numbers.get(i) == integer && !used[i]) {
used[i] = true;
return i;
}
}
return -1;
}
}

View File

@@ -0,0 +1,81 @@
package com.stardust.mi666;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import com.stardust.mi666.ocr.ColorDetector;
import com.stardust.mi666.ocr.SimpleTextSplitter;
import com.stardust.mi666.ocr.TemplateMatching;
import com.stardust.pio.UncheckedIOException;
import com.stardust.util.ViewUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Stardust on 2017/4/24.
*/
public class OCR {
private static com.stardust.mi666.ocr.OCR ocr;
private static SimpleTextSplitter textSplitter;
static {
ColorDetector colorDetector = new ColorDetector.SimpleColorDetector(0xff9f826f);
ocr = new TemplateMatching.TemplateMatchingOCR(1, colorDetector);
textSplitter = new SimpleTextSplitter(colorDetector);
}
private int mX, mY, mIntervalX, mIntervalY, mWidth, mHeight;
public OCR(int x, int y, int intervalX, int intervalY, int w, int h) {
mX = x;
mY = y;
mIntervalY = intervalY;
mIntervalX = intervalX;
mWidth = w;
mHeight = h;
}
public static void init(Context context) {
try {
for (int i = 0; i < 10; i++) {
Bitmap number = BitmapFactory.decodeStream(context.getAssets().open("numbers/" + i + ".png"));
ocr.addChar((char) ('0' + i), number);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public List<Integer> detect(String path) {
Bitmap screenshot = BitmapFactory.decodeFile(path);
try {
List<Integer> numbers = new ArrayList<>(16);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
Bitmap block = getBlockAt(screenshot, i, j);
List<Bitmap> bitmaps = textSplitter.split(block);
StringBuilder sb = new StringBuilder();
for (Bitmap num : bitmaps) {
sb.append(ocr.detect(num));
}
numbers.add(Integer.parseInt(sb.toString()));
Log.i("TestOCR", "result = " + sb);
}
}
return numbers;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private Bitmap getBlockAt(Bitmap bitmap, int row, int col) {
return Bitmap.createBitmap(bitmap, mX + col * mIntervalX, mY + row * mIntervalY, mWidth, mHeight);
}
}

View File

@@ -0,0 +1,55 @@
package com.stardust.mi666;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Stardust on 2017/4/24.
*/
public class Solver {
private boolean mSolved = false;
private List<Integer> mResult = new ArrayList<>();
private List<Integer> mTmpList = new ArrayList<>();
private int[] value;
void solve(int sum, int index) {
if (mSolved || index < 0) return;
if (sum == value[index] && mTmpList.size() == 5) {
mSolved = true;
mTmpList.add(value[index]);
mResult.addAll(mTmpList);
return;
}
mTmpList.add(value[index]);
solve(sum - value[index], index - 1);
mTmpList.remove(mTmpList.size() - 1);
solve(sum, index - 1);
}
public void solve(int[] num) {
mResult.clear();
mTmpList.clear();
mSolved = false;
value = num;
solve(6666, 15);
}
public boolean isSolved() {
return mSolved;
}
public List<Integer> getResult() {
return mResult;
}
public void solve(List<Integer> numbers) {
int[] num = new int[numbers.size()];
for (int i = 0; i < num.length; i++) {
num[i] = numbers.get(i);
}
solve(num);
}
}

View File

@@ -0,0 +1,53 @@
package com.stardust.mi666.ocr;
import android.graphics.Color;
/**
* Created by Stardust on 2017/4/23.
*/
public interface ColorDetector {
boolean isCharPixel(int color);
class ColorDistanceDetector implements ColorDetector {
private int mColor;
private double mThreshold;
public ColorDistanceDetector(int color, double threshold) {
mColor = color;
mThreshold = threshold;
}
public static double distance(int c1, int c2) {
double meanR = (Color.red(c1) + Color.red(c2)) / 2;
int r = Color.red(c1) - Color.red(c2);
int g = Color.green(c1) - Color.green(c2);
int b = Color.blue(c1) - Color.blue(c2);
double weightR = 2 + meanR / 256;
double weightG = 4.0;
double weightB = 2 + (255 - meanR) / 256;
return Math.sqrt(weightR * r * r + weightG * g * g + weightB * b * b);
}
@Override
public boolean isCharPixel(int color) {
return distance(color, mColor) < mThreshold;
}
}
class SimpleColorDetector implements ColorDetector {
private int mColor;
public SimpleColorDetector(int color) {
mColor = color;
}
@Override
public boolean isCharPixel(int color) {
return mColor == color;
}
}
}

View File

@@ -0,0 +1,15 @@
package com.stardust.mi666.ocr;
import android.graphics.Bitmap;
/**
* Created by Stardust on 2017/4/23.
*/
public interface OCR {
void addChar(char ch, Bitmap bitmap);
char detect(Bitmap bitmap);
}

View File

@@ -0,0 +1,142 @@
package com.stardust.mi666.ocr;
import android.graphics.Bitmap;
import android.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by Stardust on 2017/4/23.
*/
public class SimpleOCR implements OCR {
private static class PixelLine {
int start;
int end;
PixelLine(int start, int end) {
this.start = start;
this.end = end;
}
}
private static class PixelLines {
int start = -1;
List<PixelLine> lines = new ArrayList<>();
void addLine(PixelLine line) {
lines.add(line);
if (start == -1 || start > line.start) {
start = line.start;
}
}
int distanceTo(PixelLines another) {
if (lines.size() != another.lines.size()) {
return lineWidthSum() + another.lineWidthSum();
}
int offset = another.start - start;
int sum = 0;
for (int i = 0; i < lines.size(); i++) {
sum += Math.abs(lines.get(i).start + offset - another.lines.get(i).start);
sum += Math.abs(lines.get(i).end + offset - another.lines.get(i).end);
}
return sum;
}
private int lineWidthSum() {
int sum = 0;
for (PixelLine line : lines) {
sum += line.end - line.start;
}
return sum;
}
}
public static class CharFeature {
private List<PixelLines> mPixelLinesPerRow;
public CharFeature(int rowNumber) {
mPixelLinesPerRow = new ArrayList<>(rowNumber);
for (int i = 0; i < rowNumber; i++) {
mPixelLinesPerRow.add(new PixelLines());
}
}
void addLine(int row, PixelLine line) {
mPixelLinesPerRow.get(row).addLine(line);
}
int distanceTo(CharFeature feature) {
int sum = 0;
if (feature.mPixelLinesPerRow.size() != mPixelLinesPerRow.size()) {
for (PixelLines lines : mPixelLinesPerRow) {
sum += lines.lines.size();
}
return sum;
}
for (int i = 0; i < mPixelLinesPerRow.size(); i++) {
sum += mPixelLinesPerRow.get(i).distanceTo(feature.mPixelLinesPerRow.get(i));
}
return sum;
}
}
private static final String TAG = "SimpleOCR";
private Map<Character, CharFeature> mChars = new HashMap<>();
private ColorDetector mColorDetector;
public SimpleOCR(ColorDetector colorDetector) {
mColorDetector = colorDetector;
}
public void addChar(char c, Bitmap ch) {
mChars.put(c, getCharFeature(ch));
}
public CharFeature getCharFeature(Bitmap bitmap) {
CharFeature feature = new CharFeature(bitmap.getHeight());
for (int y = 0; y < bitmap.getHeight(); y++) {
int mPixelStart = -1;
for (int x = 0; x < bitmap.getWidth(); x++) {
if (mColorDetector.isCharPixel(bitmap.getPixel(x, y))) {
if (mPixelStart == -1) {
mPixelStart = x;
}
} else if (mPixelStart >= 0) {
feature.addLine(y, new PixelLine(mPixelStart, x));
mPixelStart = -1;
}
}
if (mPixelStart >= 0) {
feature.addLine(y, new PixelLine(mPixelStart, bitmap.getWidth()));
}
}
return feature;
}
public char detect(Bitmap bitmap) {
CharFeature feature = getCharFeature(bitmap);
int min = Integer.MAX_VALUE;
char detectedChar = ' ';
for (Map.Entry<Character, CharFeature> ch : mChars.entrySet()) {
int distance = ch.getValue().distanceTo(feature);
Log.d(TAG, "ch=" + ch.getKey() + " distance=" + distance);
if (distance < min) {
min = distance;
detectedChar = ch.getKey();
}
}
return detectedChar;
}
}

View File

@@ -0,0 +1,34 @@
package com.stardust.mi666.ocr;
import android.graphics.Bitmap;
/**
* Created by Stardust on 2017/4/23.
*/
public class SimpleTextDetector {
private ColorDetector mColorDetector;
public SimpleTextDetector(ColorDetector colorDetector) {
mColorDetector = colorDetector;
}
public Bitmap detect(Bitmap bitmap) {
int minX = bitmap.getWidth();
int minY = bitmap.getHeight();
int maxX = 0;
int maxY = 0;
for (int i = 0; i < bitmap.getWidth(); i++) {
for (int j = 0; j < bitmap.getHeight(); j++) {
if (mColorDetector.isCharPixel(bitmap.getPixel(i, j))) {
minX = Math.min(minX, i);
minY = Math.min(minY, j);
maxX = Math.max(maxX, i);
maxY = Math.max(maxY, j);
}
}
}
return Bitmap.createBitmap(bitmap, minX, minY, maxX - minX, maxY - minY);
}
}

View File

@@ -0,0 +1,52 @@
package com.stardust.mi666.ocr;
import android.graphics.Bitmap;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Stardust on 2017/4/24.
*/
public class SimpleTextSplitter {
private ColorDetector mColorDetector;
private SimpleTextDetector mSimpleTextDetector;
public SimpleTextSplitter(ColorDetector colorDetector) {
mColorDetector = colorDetector;
mSimpleTextDetector = new SimpleTextDetector(colorDetector);
}
public List<Bitmap> split(Bitmap bitmap) {
Bitmap text = mSimpleTextDetector.detect(bitmap);
int[] projection = new int[text.getWidth()];
for (int i = 0; i < text.getWidth(); i++) {
for (int j = 0; j < text.getHeight(); j++) {
if (mColorDetector.isCharPixel(text.getPixel(i, j))) {
projection[i]++;
}
}
}
int start = -1;
List<Bitmap> numbers = new ArrayList<>();
for (int i = 0; i < text.getWidth(); i++) {
if (projection[i] > 0) {
if (start == -1) {
start = i;
}
} else {
if (start >= 0) {
numbers.add(Bitmap.createBitmap(text, start, 0, i - start, text.getHeight()));
start = -1;
}
}
}
if (start >= 0) {
numbers.add(Bitmap.createBitmap(text, start, 0, text.getWidth() - start, text.getHeight()));
}
return numbers;
}
}

View File

@@ -0,0 +1,109 @@
package com.stardust.mi666.ocr;
import android.graphics.Bitmap;
import android.util.Log;
import com.stardust.scriptdroid.tool.BitmapTool;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Stardust on 2017/4/23.
*/
public class TemplateMatching {
private static final String LOG_TAG = "TemplateMatching";
private int mBlockSize;
private ColorDetector mColorDetector;
public TemplateMatching(int blockSize, ColorDetector colorDetector) {
mBlockSize = blockSize;
mColorDetector = colorDetector;
}
public int[][] getFeature(Bitmap bitmap) {
int w = bitmap.getWidth() / mBlockSize;
int h = bitmap.getHeight() / mBlockSize;
int[][] mat = new int[w][h];
for (int i = 0; i < bitmap.getWidth(); i++) {
for (int j = 0; j < bitmap.getHeight(); j++) {
if (mColorDetector.isCharPixel(bitmap.getPixel(i, j))) {
mat[i * w / bitmap.getWidth()][j * h / bitmap.getHeight()]++;
}
}
}
return mat;
}
public int distance(int[][] f, int[][] template, int offsetX, int offsetY) {
int sum = 0;
for (int i = 0; i < template.length; i++) {
for (int j = 0; j < template[i].length; j++) {
if (i >= f.length || j >= f[i].length) {
sum += template[i].length - j;
break;
}
sum += Math.abs(template[i][j] - f[i + offsetX][j + offsetY]);
}
}
return sum;
}
public int minDistance(int[][] f, int[][] template) {
int min = Integer.MAX_VALUE;
int i = 0;
while (i < template.length && i + template.length - 1 < f.length) {
int j = 0;
while (j < template[i].length && j + template[i].length - 1 < f[i].length) {
int d = distance(f, template, i, j);
if (d < min) {
min = d;
}
j++;
}
i++;
}
return min;
}
public static class TemplateMatchingOCR implements OCR {
private Map<Character, int[][]> mChars = new HashMap<>();
private TemplateMatching mTemplateMatching;
private SimpleTextDetector mSimpleTextDetector;
public TemplateMatchingOCR(int blockSize, ColorDetector colorDetector) {
mTemplateMatching = new TemplateMatching(blockSize, colorDetector);
mSimpleTextDetector = new SimpleTextDetector(colorDetector);
}
@Override
public void addChar(char ch, Bitmap bitmap) {
mChars.put(ch, mTemplateMatching.getFeature(mSimpleTextDetector.detect(bitmap)));
}
@Override
public char detect(Bitmap bitmap) {
int min = Integer.MAX_VALUE;
char ch = ' ';
for (Map.Entry<Character, int[][]> entry : mChars.entrySet()) {
int[][] f = mTemplateMatching.getFeature(BitmapTool.scaleBitmap(bitmap, entry.getValue().length, entry.getValue()[0].length));
int d = mTemplateMatching.distance(f, entry.getValue(), 0, 0);
Log.d(LOG_TAG, "char: " + entry.getKey() + " distance: " + d);
if (d < min) {
if (entry.getKey() == '1' && d > 30) {
continue;
}
min = d;
ch = entry.getKey();
}
}
return ch;
}
}
}

View File

@@ -10,13 +10,17 @@ import android.support.annotation.Keep;
import com.squareup.leakcanary.LeakCanary;
import com.stardust.app.SimpleActivityLifecycleCallbacks;
import com.stardust.app.VolumeChangeObserver;
import com.stardust.mi666.OCR;
import com.stardust.scriptdroid.autojs.AutoJs;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.tool.CrashHandler;
import com.stardust.scriptdroid.tool.UpdateChecker;
import com.stardust.scriptdroid.tool.JsBeautifierFactory;
import com.stardust.scriptdroid.ui.error.ErrorReportActivity;
import com.stardust.theme.ThemeColor;
import com.stardust.theme.ThemeColorManager;
import com.stardust.util.ScreenMetrics;
import java.lang.ref.WeakReference;
/**
* Created by Stardust on 2017/1/27.
@@ -26,23 +30,25 @@ public class App extends Application {
private static final String TAG = "App";
private static App instance;
private static Activity currentActivity;
private static WeakReference<App> instance;
private static WeakReference<Activity> currentActivity;
public static App getApp() {
return instance;
return instance.get();
}
private VolumeChangeObserver mVolumeChangeObserver = new VolumeChangeObserver();
public void onCreate() {
super.onCreate();
instance = this;
instance = new WeakReference<>(this);
setUpDebugEnvironment();
init();
registerActivityLifecycleCallback();
}
private void setUpDebugEnvironment() {
if (LeakCanary.isInAnalyzerProcess(this)) {
return;
@@ -56,8 +62,10 @@ public class App extends Application {
ThemeColorManager.setDefaultThemeColor(new ThemeColor(getResources().getColor(R.color.colorPrimary), getResources().getColor(R.color.colorPrimaryDark), getResources().getColor(R.color.colorAccent)));
ThemeColorManager.init(this);
AutoJs.initInstance(this);
JsBeautifierFactory.initJsBeautify(this, "js/jsbeautify.js");
initVolumeChangeObserver();
startService(new Intent(this, AccessibilityWatchDogService.class));
OCR.init(this);
}
private void initVolumeChangeObserver() {
@@ -74,10 +82,6 @@ public class App extends Application {
private void registerActivityLifecycleCallback() {
registerActivityLifecycleCallbacks(new SimpleActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
currentActivity = activity;
}
@Override
public void onActivityPaused(Activity activity) {
@@ -86,20 +90,17 @@ public class App extends Application {
@Override
public void onActivityResumed(Activity activity) {
currentActivity = activity;
ScreenMetrics.initIfNeeded(activity);
currentActivity = new WeakReference<>(activity);
}
@Override
public void onActivityStopped(Activity activity) {
currentActivity = null;
}
});
}
@Keep
public static Activity currentActivity() {
return currentActivity;
return currentActivity.get();
}
public static String getResString(int id) {

View File

@@ -46,7 +46,7 @@ public class Pref {
}
public static boolean isFirstGoToAccessibilitySetting() {
return getDisposableBoolean("isFirstGoToAccessibilitySetting", true);
return getDisposableBoolean("I miss you so much ...", true);
}
public static int oldVersion() {
@@ -77,7 +77,7 @@ public class Pref {
return App.getResString(id);
}
public static int MaxTextLengthForCodeCompletion() {
public static int getMaxTextLengthForCodeCompletion() {
try {
return Integer.parseInt(def().getString(App.getApp().getString(R.string.key_max_length_for_code_completion), "2000"));
} catch (NumberFormatException e) {

View File

@@ -0,0 +1,77 @@
package com.stardust.scriptdroid.external.floating_window;
import android.content.Context;
import android.content.ContextWrapper;
import android.support.annotation.Nullable;
import android.view.ContextThemeWrapper;
import android.view.View;
import com.stardust.enhancedfloaty.ResizableFloaty;
import com.stardust.enhancedfloaty.ResizableFloatyService;
import com.stardust.scriptdroid.R;
/**
* Created by Stardust on 2017/4/20.
*/
public class ConsoleFloaty extends ResizableFloaty {
private ContextWrapper mContextWrapper;
private View mResizer, mMoveCursor;
@Override
public View inflateCollapsedView(ResizableFloatyService service) {
ensureContextWrapper(service);
return View.inflate(mContextWrapper, R.layout.floating_window_collapse, null);
}
private void ensureContextWrapper(Context context) {
if (mContextWrapper == null) {
mContextWrapper = new ContextThemeWrapper(context, R.style.AppTheme);
}
}
@Override
public View inflateExpandedView(ResizableFloatyService service) {
ensureContextWrapper(service);
View view = View.inflate(mContextWrapper, R.layout.floating_console_expand, null);
setListeners(view, service);
return view;
}
private void setListeners(final View view, final ResizableFloatyService service) {
view.findViewById(R.id.close).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
service.stopSelf();
service.stopForeground(true);
}
});
view.findViewById(R.id.move_or_resize).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mMoveCursor.getVisibility() == View.VISIBLE){
mMoveCursor.setVisibility(View.GONE);
mResizer.setVisibility(View.GONE);
}else {
mMoveCursor.setVisibility(View.VISIBLE);
mResizer.setVisibility(View.VISIBLE);
}
}
});
}
@Nullable
@Override
public View getResizerView(View expandedView) {
mResizer = expandedView.findViewById(R.id.resizer);
return mResizer;
}
@Nullable
@Override
public View getMoveCursorView(View expandedView) {
mMoveCursor = expandedView.findViewById(R.id.move_cursor);
return mMoveCursor;
}
}

View File

@@ -0,0 +1,16 @@
package com.stardust.scriptdroid.external.floating_window;
import android.content.Context;
import com.stardust.enhancedfloaty.ResizableFloatyService;
/**
* Created by Stardust on 2017/4/18.
*/
public class FloatingConsole {
public static void startFloatingService(Context context) {
ResizableFloatyService.startService(context, new ConsoleFloaty());
}
}

View File

@@ -186,7 +186,7 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat
@Override
public void onStop() {
MainActivity.onActionRecordStopped(mContext, mRecorder.getCode());
MainActivity.onRecordStop(mContext, mRecorder.getCode());
mRecorder = null;
}

View File

@@ -96,6 +96,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));
}
});

View File

@@ -62,14 +62,15 @@ public class FloatingLayoutHierarchyView extends LayoutHierarchyView {
.customView(mNodeInfoView, false)
.theme(Theme.LIGHT)
.build();
mNodeInfoDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
if (mNodeInfoDialog.getWindow() != null)
mNodeInfoDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
}
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
EventBus.getDefault().post(new MessageEvent(HoverMenuService.MESSAGE_SHOW_AND_EXPAND_MENU));
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_SHOW_AND_EXPAND_MENU));
setVisibility(GONE);
return true;
}

View File

@@ -0,0 +1,71 @@
package com.stardust.scriptdroid.tool;
import android.graphics.Bitmap;
import android.graphics.Matrix;
/**
* Created by Stardust on 2017/4/22.
*/
public class BitmapTool {
public static Bitmap scaleBitmap(Bitmap origin, int newWidth, int newHeight) {
if (origin == null) {
return null;
}
int height = origin.getHeight();
int width = origin.getWidth();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);// 使用后乘
return Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
}
/*
public static MatOfDMatch matchesBitmaps(Bitmap bitmap1, Bitmap bitmap2) {
FeatureDetector detector = FeatureDetector.create(FeatureDetector.BRISK);
DescriptorExtractor descriptor = DescriptorExtractor.create(DescriptorExtractor.ORB);
DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);
Mat descriptors1 = computeDescriptors(bitmap1, detector, descriptor);
Mat descriptors2 = computeDescriptors(bitmap2, detector, descriptor);
MatOfDMatch matches = new MatOfDMatch();
matcher.match(descriptors1, descriptors2, matches);
return matches;
}
private static Mat computeDescriptors(Bitmap bitmap, FeatureDetector detector, DescriptorExtractor descriptor) {
Mat mat = bitmapToMat(bitmap.copy(bitmap.getConfig(), true));
Mat descriptors = new Mat();
MatOfKeyPoint keyPoints1 = new MatOfKeyPoint();
detector.detect(mat, keyPoints1);
descriptor.compute(mat, keyPoints1, descriptors);
return descriptors;
}
private static Mat bitmapToMat(Bitmap bmp) {
Mat mat = new Mat(bmp.getWidth(), bmp.getHeight(), CvType.CV_8UC1);
Utils.bitmapToMat(bmp, mat);
return mat;
}
public static Core.MinMaxLocResult templateMatching(Bitmap bitmap, Bitmap tmp, int matchMethod) {
Mat img = bitmapToMat(bitmap);
Mat template = bitmapToMat(tmp);
// / Create the result matrix
int result_cols = img.cols() - template.cols() + 1;
int result_rows = img.rows() - template.rows() + 1;
Mat result = new Mat(result_rows, result_cols, CvType.CV_32FC1);
// / Do the Matching and Normalize
Imgproc.matchTemplate(img, template, result, matchMethod);
Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
// / Localizing the best match with minMaxLoc
Core.MinMaxLocResult mmr = Core.minMaxLoc(result);
return mmr;
}
*/
}

View File

@@ -14,7 +14,6 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.Callable;
/**
* Created by Stardust on 2017/4/3.
@@ -24,14 +23,14 @@ public abstract class DrawableSaver {
private static final String PREFIX = "saved_drawable_";
protected Drawable mOriginalDrawble;
protected Drawable mOriginalDrawable;
private Context mContext;
private String mName;
public DrawableSaver(Context context, String name, Drawable originalDrawble) {
public DrawableSaver(Context context, String name, Drawable originalDrawable) {
mContext = context;
mName = PREFIX + name;
mOriginalDrawble = originalDrawble;
mOriginalDrawable = originalDrawable;
readImageAndApply();
}
@@ -89,7 +88,7 @@ public abstract class DrawableSaver {
}
public void reset() {
applyDrawableToView(mOriginalDrawble);
applyDrawableToView(mOriginalDrawable);
mContext.deleteFile(mName);
}

View File

@@ -0,0 +1,162 @@
package com.stardust.scriptdroid.tool;
/**
* Created by Stardust on 2017/4/23.
*/
import android.graphics.Bitmap;
import com.stardust.mi666.ocr.ColorDetector;
import com.stardust.mi666.ocr.SimpleTextDetector;
import java.util.HashMap;
import java.util.Map;
public class ImagePHash {
private static final String LOG_TAG = "ImagePHash";
private int width = 32;
private int height;
private int smallerWidth = 8;
private int smallerHeight;
public ImagePHash() {
this(32, 8);
}
public ImagePHash(int width, int smallerWidth) {
this(width, width, smallerWidth, smallerWidth);
}
public ImagePHash(int width, int height, int smallerWidth, int smallerHeight) {
this.width = width;
this.height = height;
this.smallerWidth = smallerWidth;
this.smallerHeight = smallerHeight;
initCoefficients();
}
public int distance(boolean[] s1, boolean[] s2) {
int counter = 0;
for (int k = 0; k < s1.length; k++) {
if (s1[k] != s2[k]) {
counter++;
}
}
return counter;
}
public boolean[] getHash(Bitmap img) {
double[][] vals = new double[width][height];
img = BitmapTool.scaleBitmap(img, width, height);
for (int x = 0; x < img.getWidth(); x++) {
for (int y = 0; y < img.getHeight(); y++) {
vals[x][y] = getBlue(img, x, y);
}
}
double[][] dctVals = applyDCT(vals);
double total = 0;
for (int x = 0; x < smallerWidth; x++) {
for (int y = 0; y < smallerHeight; y++) {
total += dctVals[x][y];
}
}
total -= dctVals[0][0];
double avg = total / (double) ((smallerWidth * smallerHeight) - 1);
boolean[] hash = new boolean[smallerWidth * smallerHeight];
int i = 0;
for (int x = 0; x < smallerWidth; x++) {
for (int y = 0; y < smallerHeight; y++) {
if (x != 0 && y != 0) {
hash[i++] = dctVals[x][y] > avg;
}
}
}
return hash;
}
private static int getBlue(Bitmap img, int x, int y) {
return img.getPixel(x, y) & 0xff;
}
private double[] c;
private double[] c2;
private void initCoefficients() {
c = new double[width];
c2 = new double[height];
for (int i = 1; i < width; i++) {
c[i] = 1;
}
for (int i = 1; i < height; i++) {
c2[i] = 1;
}
c[0] = 1 / Math.sqrt(2.0);
c2[0] = 1 / Math.sqrt(2.0);
}
private double[][] applyDCT(double[][] f) {
double[][] F = new double[width][height];
for (int u = 0; u < width; u++) {
for (int v = 0; v < height; v++) {
double sum = 0.0;
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
sum += Math.cos(((2 * i + 1) / (2.0 * width)) * u * Math.PI) * Math.cos(((2 * j + 1) / (2.0 * height)) * v * Math.PI) * (f[i][j]);
}
}
sum *= ((c[u] * c2[v]) / 4.0);
F[u][v] = sum;
}
}
return F;
}
public static class OCR implements com.stardust.mi666.ocr.OCR {
private SimpleTextDetector mSimpleTextDetector;
private Map<Character, boolean[]> mChars = new HashMap<>();
private ImagePHash mImagePHash;
public OCR(ColorDetector colorDetector, int size, int smallerSize) {
mSimpleTextDetector = new SimpleTextDetector(colorDetector);
mImagePHash = new ImagePHash(size, smallerSize);
}
public OCR(ColorDetector colorDetector, ImagePHash pHash) {
mSimpleTextDetector = new SimpleTextDetector(colorDetector);
mImagePHash = pHash;
}
@Override
public void addChar(char ch, Bitmap bitmap) {
mChars.put(ch, mImagePHash.getHash(mSimpleTextDetector.detect(bitmap)));
}
@Override
public char detect(Bitmap bitmap) {
bitmap = mSimpleTextDetector.detect(bitmap);
int min = Integer.MAX_VALUE;
boolean[] h = mImagePHash.getHash(bitmap);
char ch = ' ';
for (Map.Entry<Character, boolean[]> entry : mChars.entrySet()) {
int d = mImagePHash.distance(h, entry.getValue());
//Log.i(LOG_TAG, "char: " + entry.getKey() + " distance: " + d);
if (d < min) {
min = d;
ch = entry.getKey();
}
}
return ch;
}
}
}

View File

@@ -2,9 +2,6 @@ package com.stardust.scriptdroid.tool;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import com.stardust.app.OnActivityResultDelegate;
import com.stardust.scriptdroid.R;
@@ -22,6 +19,8 @@ public class ImageSelector implements OnActivityResultDelegate {
void onImageSelected(ImageSelector selector, InputStream path);
}
private static final String TAG = ImageSelector.class.getSimpleName();
private static final int REQUEST_CODE = "LOVE EATING".hashCode() >> 16;
private Activity mActivity;
private ImageSelectorCallback mCallback;

View File

@@ -0,0 +1,20 @@
package com.stardust.scriptdroid.tool;
import android.content.Context;
import android.widget.Toast;
import com.stardust.scriptdroid.R;
import com.stardust.util.IntentUtil;
/**
* Created by Stardust on 2017/4/12.
*/
public class IntentTool {
public static void browse(Context context, String url){
if (!IntentUtil.browse(context, url)) {
Toast.makeText(context, R.string.text_no_brower, Toast.LENGTH_SHORT).show();
}
}
}

View File

@@ -0,0 +1,25 @@
package com.stardust.scriptdroid.tool;
import android.annotation.SuppressLint;
import android.app.Application;
import com.stardust.autojs.script.JsBeautifier;
/**
* Created by Stardust on 2017/4/18.
*/
public class JsBeautifierFactory {
@SuppressLint("StaticFieldLeak")
private static JsBeautifier jsBeautifier;
public static JsBeautifier getJsBeautify() {
return jsBeautifier;
}
public static void initJsBeautify(Application context, String path) {
jsBeautifier = new JsBeautifier(context, path);
jsBeautifier.prepare();
}
}

View File

@@ -0,0 +1,26 @@
package com.stardust.scriptdroid.tool;
import android.content.Context;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.R;
/**
* Created by Stardust on 2017/4/18.
*/
public class MaterialDialogFactory {
public static MaterialDialog createProgress(Context context) {
return new MaterialDialog.Builder(context)
.progress(true, 0)
.cancelable(false)
.content(R.string.text_processing)
.build();
}
public static MaterialDialog showProgress(Context context) {
MaterialDialog dialog = createProgress(context);
dialog.show();
return dialog;
}
}

View File

@@ -0,0 +1,161 @@
package com.stardust.scriptdroid.tool;
import android.content.Context;
import android.preference.PreferenceManager;
import com.stardust.pio.UncheckedIOException;
import com.stardust.scriptdroid.App;
import java.io.IOException;
import jackpal.androidterm.ShellTermSession;
import jackpal.androidterm.emulatorview.TermSession;
import jackpal.androidterm.util.TermSettings;
/**
* Created by Stardust on 2017/4/24.
*/
public class Shell {
private TermSession mTermSession;
private String mOutput;
public Shell(boolean root) {
this(App.getApp(), root ? "su\n" : "sh\n");
}
public Shell(Context context, String initialCommand) {
TermSettings settings = new TermSettings(context.getResources(), PreferenceManager.getDefaultSharedPreferences(context));
try {
mTermSession = new MyShellTermSession(settings, initialCommand);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public String execAndWaitFor(String command) {
mTermSession.write(command + "\n");
mOutput = null;
synchronized (this) {
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
return mOutput;
}
public String execAndWaitFor(String command, int millis) {
mTermSession.write(command + "\n");
mOutput = null;
synchronized (this) {
try {
wait(millis);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
return mOutput;
}
public void exec(String command) {
mTermSession.write(command);
}
public void execute(String command) {
mTermSession.write(command);
}
public void Tap(int x, int y) {
execute("input tap " + x + " " + y);
}
public void Swipe(int x1, int y1, int x2, int y2) {
execute("input swipe " + x1 + " " + y1 + " " + x2 + " " + y2);
}
public void Swipe(int x1, int y1, int x2, int y2, long duration) {
execute("input swipe " + x1 + " " + y1 + " " + x2 + " " + y2 + " " + duration);
}
public void KeyCode(int keyCode) {
execute("input keyevent " + keyCode);
}
public void KeyCode(String keyCode) {
execute("input keyevent " + keyCode);
}
public void Home() {
KeyCode(3);
}
public void Back() {
KeyCode(4);
}
public void Power() {
KeyCode(26);
}
public void Up() {
KeyCode(19);
}
public void Down() {
KeyCode(20);
}
public void Left() {
KeyCode(21);
}
public void Right() {
KeyCode(22);
}
public void OK() {
KeyCode(23);
}
public void VolumeUp() {
KeyCode(24);
}
public void VolumeDown() {
KeyCode(25);
}
public void Menu() {
KeyCode(1);
}
public void Camera() {
KeyCode(27);
}
public void Text(String text) {
execute("input text " + text);
}
private class MyShellTermSession extends ShellTermSession {
public MyShellTermSession(TermSettings settings, String initialCommand) throws IOException {
super(settings, initialCommand);
}
@Override
protected void processInput(byte[] data, int offset, int count) {
mOutput = new String(data, offset, count);
synchronized (Shell.this) {
Shell.this.notifyAll();
}
appendToEmulator(data, offset, count);
}
}
}

View File

@@ -13,7 +13,7 @@ import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.stardust.pio.UncheckedIOException;
import com.stardust.scriptdroid.BuildConfig;
import java.lang.reflect.Type;
import java.util.List;
@@ -28,37 +28,40 @@ public class UpdateChecker implements Response.Listener<String>, Response.ErrorL
private RequestQueue mRequestQueue;
private static final String UPDATE_URL = "https://raw.githubusercontent.com/hyb1996/NoRootScriptDroid/master/version.json";
public interface Callback {
void onSuccess(CheckResult result);
void onSuccess(UpdateInfo result);
void onError(Exception exception);
}
private Callback mCallback;
private final int mTimeOut = 3000;
public static UpdateInfo savedResult;
public UpdateChecker(Context context, Callback callback) {
mCallback = callback;
private final int mTimeOut = 3000;
private Callback mCallback;
public UpdateChecker(Context context) {
mRequestQueue = Volley.newRequestQueue(context);
}
private CheckResult parse(String json) {
private UpdateInfo parse(String json) {
Gson gson = new Gson();
Type type = new TypeToken<CheckResult>() {
Type type = new TypeToken<UpdateInfo>() {
}.getType();
CheckResult result = gson.fromJson(json, type);
UpdateInfo result = gson.fromJson(json, type);
Log.i(LOG_TAG, result.toString());
return result;
}
public void check() {
public void check(final Callback callback) {
mCallback = callback;
StringRequest request = new StringRequest(Request.Method.GET, UPDATE_URL, this, this);
request.setRetryPolicy(new DefaultRetryPolicy(mTimeOut, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
request.setTag("update-check");
request.setShouldCache(false);
mRequestQueue.add(request);
}
@@ -66,12 +69,16 @@ public class UpdateChecker implements Response.Listener<String>, Response.ErrorL
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
mCallback.onError(error);
mCallback = null;
}
@Override
public void onResponse(String response) {
try {
CheckResult result = parse(response);
UpdateInfo result = parse(response);
savedResult = result;
if (mCallback == null)
return;
if (result.isValid()) {
mCallback.onSuccess(result);
} else {
@@ -81,13 +88,14 @@ public class UpdateChecker implements Response.Listener<String>, Response.ErrorL
e.printStackTrace();
mCallback.onError(e);
}
mCallback = null;
}
public void cancel() {
mRequestQueue.cancelAll("update-check");
}
public static class CheckResult {
public static class UpdateInfo {
public int versionCode;
public String releaseNotes;
@@ -95,25 +103,34 @@ public class UpdateChecker implements Response.Listener<String>, Response.ErrorL
public List<Download> downloads;
public List<OldVersion> oldVersions;
public int deprecated;
public String downloadUrl;
public boolean isValid() {
return downloads != null && !downloads.isEmpty() && versionCode > 0
&& !TextUtils.isEmpty(versionName) && !TextUtils.isEmpty(releaseNotes);
}
public OldVersion getOldVersion(int versionCode) {
for (OldVersion oldVersion : oldVersions) {
if (oldVersion.versionCode == versionCode) {
return oldVersion;
}
}
return null;
}
@Override
public String toString() {
return "CheckResult{" +
return "UpdateInfo{" +
"versionCode=" + versionCode +
", releaseNotes='" + releaseNotes + '\'' +
", versionName='" + versionName + '\'' +
", downloads=" + downloads +
", oldVersions=" + oldVersions +
", deprecated=" + deprecated +
", downloadUrl='" + downloadUrl + '\'' +
'}';
}
}
public static class OldVersion {

View File

@@ -0,0 +1,129 @@
package com.stardust.scriptdroid.tool;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.stardust.scriptdroid.BuildConfig;
import com.stardust.util.NetworkUtils;
/**
* Created by Stardust on 2017/4/9.
*/
public class VersionInfo {
private static final String KEY_DEPRECATED = "Still loving you...Can we go back...";
private static final String KEY_DEPRECATED_VERSION_CODE = "I miss you so much tonight...Baby don't let me cry...";
public interface OnReceiveUpdateResultCallback {
void onReceive(UpdateChecker.UpdateInfo info, boolean isCurrentVersionDeprecated);
}
private static VersionInfo instance = new VersionInfo();
public static VersionInfo getInstance() {
return instance;
}
private boolean mDeprecated = false;
private UpdateChecker.UpdateInfo mUpdateInfo;
private final int mReconnectTimes = 2;
private int mReconnectCount = 0;
private OnReceiveUpdateResultCallback mOnReceiveUpdateResultCallback;
private SharedPreferences mSharedPreferences;
public void readDeprecatedFromPref(Context context) {
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
if (mSharedPreferences.getInt(KEY_DEPRECATED_VERSION_CODE, 0) < BuildConfig.VERSION_CODE) {
mSharedPreferences.edit().remove(KEY_DEPRECATED_VERSION_CODE)
.putBoolean(KEY_DEPRECATED, false)
.apply();
}
mDeprecated = mSharedPreferences.getBoolean(KEY_DEPRECATED, false);
}
public void readDeprecatedFromPrefIfNeeded(Context context) {
if (mSharedPreferences == null) {
readDeprecatedFromPref(context);
}
}
public boolean isCurrentVersionDeprecated() {
return mDeprecated;
}
public UpdateChecker.UpdateInfo getUpdateInfo() {
return mUpdateInfo;
}
public String getCurrentVersionIssues() {
if (mUpdateInfo == null)
return null;
UpdateChecker.OldVersion oldVersion = mUpdateInfo.getOldVersion(BuildConfig.VERSION_CODE);
if (oldVersion == null)
return null;
return oldVersion.issues;
}
public void checkUpdateIfNeeded(Context context) {
if (mUpdateInfo == null) {
checkUpdateIfUsingWifi(context);
}
}
public void setOnReceiveUpdateResultCallback(OnReceiveUpdateResultCallback onReceiveUpdateResultCallback) {
mOnReceiveUpdateResultCallback = onReceiveUpdateResultCallback;
}
private void checkUpdateIfUsingWifi(Context context) {
if (NetworkUtils.isWifiAvailable(context)) {
checkUpdate(context);
}
}
public void checkUpdate(Context context) {
mReconnectCount = 0;
checkUpdateInner(context);
}
private void checkUpdateInner(final Context context) {
mReconnectCount++;
new UpdateChecker(context).check(new UpdateChecker.Callback() {
@Override
public void onSuccess(UpdateChecker.UpdateInfo result) {
if (result.isValid()) {
setUpdateInfo(result);
} else if (mReconnectCount < mReconnectTimes) {
checkUpdate(context);
}
}
@Override
public void onError(Exception exception) {
if (mReconnectCount < mReconnectTimes) {
checkUpdate(context);
}
}
});
}
private void setUpdateInfo(UpdateChecker.UpdateInfo result) {
mDeprecated = BuildConfig.VERSION_CODE <= result.deprecated;
mUpdateInfo = result;
if (mDeprecated) {
mSharedPreferences.edit().putBoolean(KEY_DEPRECATED, mDeprecated)
.putInt(KEY_DEPRECATED_VERSION_CODE, BuildConfig.VERSION_CODE)
.apply();
}
if (mOnReceiveUpdateResultCallback != null) {
mOnReceiveUpdateResultCallback.onReceive(result, mDeprecated);
}
}
}

View File

@@ -1,8 +1,10 @@
package com.stardust.scriptdroid.ui;
import android.app.Activity;
import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
@@ -10,8 +12,14 @@ import android.support.v7.widget.Toolbar;
import android.view.View;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.pio.PFile;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.external.tasker.TaskPrefEditActivity;
import com.stardust.scriptdroid.tool.UpdateChecker;
import com.stardust.scriptdroid.tool.VersionInfo;
import com.stardust.scriptdroid.ui.update.UpdateCheckDialog;
import com.stardust.theme.ThemeColorManager;
import com.stardust.util.BackPressedHandler;
@@ -42,6 +50,20 @@ public abstract class BaseActivity extends AppCompatActivity {
if ((getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0) {
ThemeColorManager.addActivityStatusBar(this);
}
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onPause() {
super.onPause();
VersionInfo.getInstance().setOnReceiveUpdateResultCallback(null);
}
@SuppressWarnings("unchecked")

View File

@@ -22,15 +22,19 @@ import timber.log.Timber;
public class TimberConsole implements Console {
static {
Timber.plant(new ConsoleTree.Builder()
.minPriority(Log.VERBOSE)
.verboseColor(0xff909090)
.debugColor(0xffc88b48)
.infoColor(0xffc9c9c9)
.warnColor(0xffa97db6)
.errorColor(0xffff534e)
.assertColor(0xffff5540)
.build());
try {
Timber.plant(new ConsoleTree.Builder()
.minPriority(Log.VERBOSE)
.verboseColor(0xff909090)
.debugColor(0xffc88b48)
.infoColor(0xffc9c9c9)
.warnColor(0xffa97db6)
.errorColor(0xffff534e)
.assertColor(0xffff5540)
.build());
} catch (Exception e) {
// FIXME: 2017/4/26 java.lang.NoClassDefFoundError: com.jraska.console.timber.ConsoleTree at android4.4
}
}
@Override

View File

@@ -11,6 +11,8 @@ import android.support.design.widget.Snackbar;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethod;
@@ -19,17 +21,24 @@ import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.autojs.engine.JavaScriptEngine;
import com.stardust.autojs.ScriptExecutionListener;
import com.stardust.autojs.script.FileScriptSource;
import com.stardust.autojs.script.JsBeautifier;
import com.stardust.autojs.script.ScriptSource;
import com.stardust.autojs.script.StringScriptSource;
import com.stardust.enhancedfloaty.ResizableFloatyService;
import com.stardust.pio.PFile;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.autojs.AutoJs;
import com.stardust.scriptdroid.external.floating_window.FloatingConsole;
import com.stardust.scriptdroid.scripts.ScriptFile;
import com.stardust.scriptdroid.tool.JsBeautifierFactory;
import com.stardust.scriptdroid.tool.MaterialDialogFactory;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.ui.console.ConsoleActivity;
import com.stardust.scriptdroid.ui.edit.editor920.Editor920Activity;
import com.stardust.scriptdroid.ui.edit.editor920.Editor920Utils;
import com.stardust.scriptdroid.ui.edit.sidemenu.EditSideMenuFragment;
import com.stardust.scriptdroid.ui.edit.sidemenu.FunctionListRecyclerView;
import com.stardust.scriptdroid.ui.help.HelpCatalogueActivity;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.util.SparseArrayEntries;
import com.stardust.view.ViewBinding;
@@ -56,6 +65,7 @@ import timber.log.Timber;
public class EditActivity extends Editor920Activity {
public static class InputMethodEnhanceBarBridge implements InputMethodEnhanceBar.EditTextBridge {
private Editor920Activity mEditor920Activity;
@@ -152,6 +162,7 @@ public class EditActivity extends Editor920Activity {
}
}
};
private JsBeautifier mJsBeautifier = JsBeautifierFactory.getJsBeautify();
public void onCreate(Bundle b) {
super.onCreate(b);
@@ -314,6 +325,51 @@ public class EditActivity extends Editor920Activity {
menuItem.setEnabled(!disabled);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_editor, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_console:
startActivity(new Intent(getContext(), ConsoleActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
return true;
case R.id.action_help:
HelpCatalogueActivity.showMainCatalogue(this);
return true;
case R.id.action_beautify:
beautifyCode();
return true;
}
return super.onOptionsItemSelected(item);
}
private void beautifyCode() {
final MaterialDialog dialog = MaterialDialogFactory.showProgress(this);
mJsBeautifier.beautify(mEditorDelegate.getText(), new JsBeautifier.Callback() {
@Override
public void onSuccess(final String beautifiedCode) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mEditorDelegate.mEditText.setText(beautifiedCode);
dialog.dismiss();
}
});
}
@Override
public void onException(Exception e) {
e.printStackTrace();
}
});
}
@Override
public void finish() {
if (!mReadOnly && mEditorDelegate.isChanged()) {

View File

@@ -122,7 +122,7 @@ public class CodeCompletion implements TextWatcher {
}
private Collection<CodeCompletionItem> searchWordCompletion(String str) {
if (mEditText.getEditableText().length() < Pref.MaxTextLengthForCodeCompletion()) {
if (mEditText.getEditableText().length() < Pref.getMaxTextLengthForCodeCompletion()) {
return searchCodeCompletion(str, splitWord(mEditText.getEditableText().toString()));
}
return new TreeSet<>();

View File

@@ -0,0 +1,24 @@
package com.stardust.scriptdroid.ui.edit.sidemenu;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.stardust.app.Fragment;
import com.stardust.scriptdroid.R;
/**
* Created by Stardust on 2017/4/18.
*/
public class HelpSideMenuFragment extends Fragment {
@Nullable
@Override
public View createView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_help_side_menu, container, false);
}
}

View File

@@ -25,26 +25,27 @@ import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.app.FragmentPagerAdapterBuilder;
import com.stardust.app.NotAskAgainDialog;
import com.stardust.app.OnActivityResultDelegate;
import com.stardust.scriptdroid.BuildConfig;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.scripts.ScriptFile;
import com.stardust.scriptdroid.scripts.StorageScriptProvider;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
import com.stardust.scriptdroid.tool.DrawableSaver;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.ui.main.script_list.MyScriptListFragment;
import com.stardust.scriptdroid.ui.main.sample_list.SampleScriptListFragment;
import com.stardust.scriptdroid.ui.main.script_list.MyScriptListFragment;
import com.stardust.scriptdroid.ui.main.script_list.ScriptFileChooserDialogBuilder;
import com.stardust.scriptdroid.ui.main.task.TaskManagerFragment;
import com.stardust.scriptdroid.ui.settings.SettingsActivity;
import com.stardust.scriptdroid.ui.update.VersionGuard;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.util.BackPressedHandler;
import com.stardust.util.MessageEvent;
import com.stardust.view.ViewBinding;
import com.stardust.widget.SlidingUpPanel;
import com.stardust.scriptdroid.BuildConfig;
import com.stardust.scriptdroid.R;
import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;
import com.stardust.view.accessibility.AccessibilityServiceUtils;
import com.stardust.widget.SlidingUpPanel;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@@ -68,6 +69,7 @@ public class MainActivity extends BaseActivity {
private OnActivityResultDelegate.Intermediary mActivityResultIntermediary = new OnActivityResultDelegate.Intermediary();
private DrawableSaver mDrawerHeaderBackgroundSaver, mAppbarBackgroundSaver;
private VersionGuard mVersionGuard;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -77,8 +79,11 @@ public class MainActivity extends BaseActivity {
registerBackPressHandlers();
handleIntent(getIntent());
EventBus.getDefault().register(this);
mVersionGuard = new VersionGuard(this);
}
private void registerBackPressHandlers() {
registerBackPressedHandler(new BackPressedHandler() {
@Override
@@ -101,7 +106,7 @@ public class MainActivity extends BaseActivity {
private void showAccessibilitySettingPromptIfDisabled() {
if (!AccessibilityServiceUtils.isAccessibilityServiceEnabled(this, AccessibilityWatchDogService.class)) {
new NotAskAgainDialog.Builder(this, "showAccessibilitySettingPromptIfDisabled")
new NotAskAgainDialog.Builder(this, "Eating...love you...miss you...17.4.12")
.title(R.string.text_need_to_enable_accessibility_service)
.content(R.string.explain_accessibility_permission)
.positiveText(R.string.text_go_to_setting)
@@ -279,24 +284,6 @@ public class MainActivity extends BaseActivity {
return ((MyScriptListFragment) mPagerAdapter.getStoredFragment(0));
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
mActivityResultIntermediary.onActivityResult(requestCode, resultCode, data);
}
public static void onActionRecordStopped(Context context, String script) {
Intent intent = new Intent(context, MainActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(EXTRA_ACTION, ACTION_ON_ACTION_RECORD_STOPPED)
.putExtra(ARGUMENT_SCRIPT, script);
context.startActivity(intent);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
StorageScriptProvider.getDefault().notifyStoragePermissionGranted();
}
@ViewBinding.Click(R.id.toolbar)
public void OnToolbarClick() {
mAppbarBackgroundSaver.select(this, mActivityResultIntermediary);
@@ -310,9 +297,36 @@ public class MainActivity extends BaseActivity {
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
mActivityResultIntermediary.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
StorageScriptProvider.getDefault().notifyStoragePermissionGranted();
}
@Override
protected void onResume() {
super.onResume();
mVersionGuard.checkDeprecateAndUpdate();
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
public static void onRecordStop(Context context, String script) {
Intent intent = new Intent(context, MainActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(EXTRA_ACTION, ACTION_ON_ACTION_RECORD_STOPPED)
.putExtra(ARGUMENT_SCRIPT, script);
context.startActivity(intent);
}
}

View File

@@ -12,6 +12,7 @@ import android.widget.Toast;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.tool.IntentTool;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.util.IntentUtil;
@@ -51,9 +52,7 @@ public class AboutActivity extends BaseActivity {
@ViewBinding.Click(R.id.github)
private void openGitHub() {
if (!IntentUtil.browse(this, getString(R.string.my_github))) {
Toast.makeText(this, R.string.text_no_brower, Toast.LENGTH_SHORT).show();
}
IntentTool.browse(this, getString(R.string.my_github));
}
@ViewBinding.Click(R.id.qq)

View File

@@ -1,7 +1,6 @@
package com.stardust.scriptdroid.ui.update;
import android.content.Context;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.widget.Toast;
@@ -12,8 +11,7 @@ import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.tool.UpdateChecker;
import com.stardust.util.IntentUtil;
import java.util.Timer;
import java.util.TimerTask;
import java.util.LinkedList;
/**
* Created by Stardust on 2017/4/9.
@@ -21,7 +19,6 @@ import java.util.TimerTask;
public class UpdateCheckDialog implements UpdateChecker.Callback {
private MaterialDialog mResultDialog;
private MaterialDialog mProgressDialog;
private Context mContext;
private UpdateChecker mUpdateChecker;
@@ -29,70 +26,42 @@ public class UpdateCheckDialog implements UpdateChecker.Callback {
public UpdateCheckDialog(Context context) {
mContext = context;
mUpdateChecker = new UpdateChecker(context, this);
mUpdateChecker = new UpdateChecker(context);
}
public void show() {
if (mProgressDialog != null)
throw new IllegalStateException();
mProgressDialog = new MaterialDialog.Builder(mContext)
.content(mContext.getString(R.string.text_checking_update))
.progress(true, 0)
.cancelable(false)
.show();
mUpdateChecker.check();
mUpdateChecker.check(this);
}
@Override
public void onSuccess(UpdateChecker.CheckResult result) {
public void onSuccess(UpdateChecker.UpdateInfo result) {
mProgressDialog.dismiss();
if (BuildConfig.VERSION_CODE >= result.versionCode) {
Toast.makeText(mContext, R.string.text_is_latest_version, Toast.LENGTH_SHORT).show();
return;
}
mResultDialog = buildDialog(result);
mResultDialog.show();
String updateSite = result.downloads.get(0).name;
final String updateUrl = result.downloads.get(0).url;
mResultDialog = new MaterialDialog.Builder(mContext)
.title(mContext.getString(R.string.text_new_version) + " " + result.versionName)
.content(result.releaseNotes)
.positiveText(updateSite)
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
IntentUtil.browse(mContext, updateUrl);
}
})
new UpdateInfoDialogBuilder(mContext, result)
.show();
}
private MaterialDialog buildDialog(final UpdateChecker.CheckResult result) {
String updateSite = result.downloads.get(0).name;
final String updateUrl = result.downloads.get(0).url;
MaterialDialog.Builder builder = new MaterialDialog.Builder(mContext)
.title(mContext.getString(R.string.text_new_version) + " " + result.versionName)
.content(result.releaseNotes)
.positiveText(updateSite)
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
IntentUtil.browse(mContext, updateUrl);
}
});
if (result.downloads.size() > 1) {
builder.negativeText(result.downloads.get(1).name)
.onNegative(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
IntentUtil.browse(mContext, result.downloads.get(1).url);
}
});
}
return builder.build();
clear();
}
@Override
public void onError(Exception exception) {
mProgressDialog.dismiss();
Toast.makeText(mContext, R.string.text_check_update_error, Toast.LENGTH_SHORT).show();
clear();
}
private void clear() {
mProgressDialog = null;
mContext = null;
}
}

View File

@@ -0,0 +1,146 @@
package com.stardust.scriptdroid.ui.update;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.BuildConfig;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.scripts.StorageScriptProvider;
import com.stardust.scriptdroid.tool.IntentTool;
import com.stardust.scriptdroid.tool.UpdateChecker;
import com.stardust.util.DownloadTask;
import com.stardust.util.IntentUtil;
import com.stardust.widget.CommonMarkdownView;
/**
* Created by Stardust on 2017/4/9.
*/
public class UpdateInfoDialogBuilder extends MaterialDialog.Builder {
private static final String KEY_DO_NOT_ASK_AGAIN_FOR_VERSION = "I cannot forget you...cannot help missing you...";
private View mView;
private SharedPreferences mSharedPreferences;
private UpdateChecker.UpdateInfo mUpdateInfo;
public UpdateInfoDialogBuilder(@NonNull Context context, UpdateChecker.UpdateInfo info) {
super(context);
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
updateInfo(info);
}
public UpdateInfoDialogBuilder updateInfo(UpdateChecker.UpdateInfo info) {
mUpdateInfo = info;
mView = View.inflate(context, R.layout.dialog_update_info, null);
setReleaseNotes(mView, info);
setCurrentVersionIssues(mView, info);
setUpdateDownloadButtons(mView, info);
title(context.getString(R.string.text_new_version) + " " + info.versionName);
customView(mView, false);
return this;
}
public UpdateInfoDialogBuilder showDoNotAskAgain() {
mView.findViewById(R.id.do_not_ask_again_container).setVisibility(View.VISIBLE);
CheckBox checkBox = (CheckBox) mView.findViewById(R.id.do_not_ask_again);
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mSharedPreferences.edit().putBoolean(KEY_DO_NOT_ASK_AGAIN_FOR_VERSION + mUpdateInfo.versionCode, isChecked).apply();
}
});
return this;
}
@Override
public MaterialDialog show() {
if (mSharedPreferences.getBoolean(KEY_DO_NOT_ASK_AGAIN_FOR_VERSION + mUpdateInfo.versionCode, false)) {
return null;
}
return super.show();
}
private void setCurrentVersionIssues(View view, UpdateChecker.UpdateInfo info) {
TextView issues = (TextView) view.findViewById(R.id.issues);
UpdateChecker.OldVersion currentVersion = info.getOldVersion(BuildConfig.VERSION_CODE);
if (currentVersion == null) {
issues.setVisibility(View.GONE);
} else {
issues.setText(currentVersion.issues);
}
}
private void setUpdateDownloadButtons(View view, UpdateChecker.UpdateInfo info) {
LinearLayout downloads = (LinearLayout) view.findViewById(R.id.downloads);
setDirectlyDownloadButton(downloads, info);
for (final UpdateChecker.Download download : info.downloads) {
Button button = (Button) View.inflate(getContext(), R.layout.dialog_update_info_btn, null);
button.setText(download.name);
downloads.addView(button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
IntentTool.browse(v.getContext(), download.url);
}
});
}
}
private void setDirectlyDownloadButton(LinearLayout container, final UpdateChecker.UpdateInfo info) {
if (TextUtils.isEmpty(info.downloadUrl)) {
return;
}
Button button = (Button) View.inflate(getContext(), R.layout.dialog_update_info_btn, null);
button.setText(R.string.text_directly_download);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
directlyDownload(info.downloadUrl);
}
});
container.addView(button);
}
private void directlyDownload(String downloadUrl) {
final MaterialDialog dialog = new MaterialDialog.Builder(getContext())
.title(R.string.text_downloading)
.progress(false, 100)
.show();
final String path = StorageScriptProvider.DEFAULT_DIRECTORY_PATH + "AutoJs.apk";
final DownloadTask task = new DownloadTask() {
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
dialog.setProgress(values[0]);
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
dialog.dismiss();
if (!result) {
Toast.makeText(getContext(), R.string.text_download_failed, Toast.LENGTH_SHORT).show();
} else {
IntentUtil.installApk(getContext(), path);
}
}
};
task.execute(downloadUrl, path);
}
private void setReleaseNotes(View view, UpdateChecker.UpdateInfo info) {
CommonMarkdownView markdownView = (CommonMarkdownView) view.findViewById(R.id.release_notes);
markdownView.loadMarkdown(info.releaseNotes);
}
}

View File

@@ -0,0 +1,94 @@
package com.stardust.scriptdroid.ui.update;
import android.app.Activity;
import android.content.DialogInterface;
import android.support.annotation.NonNull;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.tool.UpdateChecker;
import com.stardust.scriptdroid.tool.VersionInfo;
/**
* Created by Stardust on 2017/4/12.
*/
public class VersionGuard {
private Activity mActivity;
private MaterialDialog mDeprecatedDialog;
private VersionInfo mVersionInfo = VersionInfo.getInstance();
public VersionGuard(Activity activity) {
mActivity = activity;
}
public void checkDeprecateAndUpdate() {
mVersionInfo.readDeprecatedFromPrefIfNeeded(mActivity);
if (mVersionInfo.isCurrentVersionDeprecated()) {
showDeprecatedDialogIfNeeded();
} else {
checkUpdateIfNeeded();
}
}
private void checkUpdateIfNeeded() {
if (mVersionInfo.getUpdateInfo() == null) {
mVersionInfo.setOnReceiveUpdateResultCallback(new VersionInfo.OnReceiveUpdateResultCallback() {
@Override
public void onReceive(UpdateChecker.UpdateInfo info, boolean isCurrentVersionDeprecated) {
mVersionInfo.setOnReceiveUpdateResultCallback(null);
if (isCurrentVersionDeprecated) {
showDeprecatedDialogIfNeeded();
} else {
showUpdateInfo(info);
}
}
});
mVersionInfo.checkUpdateIfNeeded(mActivity);
}
}
private void showUpdateInfo(UpdateChecker.UpdateInfo info) {
new UpdateInfoDialogBuilder(mActivity, info)
.showDoNotAskAgain()
.show();
}
private void showDeprecatedDialogIfNeeded() {
if (mDeprecatedDialog != null && mDeprecatedDialog.isShowing())
return;
String content = mActivity.getString(R.string.warning_version_too_old);
String issues = mVersionInfo.getCurrentVersionIssues();
if (issues != null) {
content += "\n" + issues;
}
mDeprecatedDialog = new MaterialDialog.Builder(mActivity)
.title(R.string.text_version_too_old)
.content(content)
.positiveText(R.string.text_update)
.negativeText(R.string.text_exit)
.cancelable(false)
.autoDismiss(false)
.onAny(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
if (which == DialogAction.POSITIVE) {
new UpdateCheckDialog(mActivity)
.show();
} else {
mActivity.finish();
}
}
})
.dismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
mDeprecatedDialog = null;
}
})
.show();
}
}

View File

@@ -1,410 +0,0 @@
package com.stardust.view;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.view.GestureDetectorCompat;
import android.util.DisplayMetrics;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.RelativeLayout;
import com.stardust.scriptdroid.R;
import com.stardust.util.ViewUtil;
import com.stardust.widget.ViewSwitcher;
/**
* Created by Stardust on 2017/3/11.
*/
public class ResizableFloaty {
private static final String TAG = "ResizableFloaty";
private View mCollapsedView, mExpandedView;
private static ResizableFloaty floaty;
public ResizableFloaty(View collapsedView, View expandedView) {
mExpandedView = expandedView;
mCollapsedView = collapsedView;
}
public static void startService(Context context, View collapsedView, View expandedView) {
floaty = new ResizableFloaty(collapsedView, expandedView);
context.startService(new Intent(context, FloatingWindowService.class));
}
interface WindowBridge {
int getX();
int getY();
void updatePosition(int x, int y);
int getWidth();
int getHeight();
void updateMeasure(int width, int height);
int getScreenWidth();
int getScreenHeight();
}
public static class FloatingWindowService extends Service {
private WindowManager mWindowManager;
private WindowManager.LayoutParams mWindowLayoutParams;
private RelativeLayout mWindowView;
private ViewSwitcher mCollapseExpandViewSwitcher;
private View mResizer;
private ResizableFloaty mFloaty = floaty;
private ResizeGesture mResizeGesture;
private DragGesture mDragGesture;
private ViewStack mViewStack = new ViewStack(new ViewStack.CurrentViewSetter() {
@Override
public void setCurrentView(View v) {
mCollapseExpandViewSwitcher.setSecondView(v);
}
});
private WindowBridge mWindowBridge = new WindowBridge() {
DisplayMetrics mDisplayMetrics;
@Override
public int getX() {
return mWindowLayoutParams.x;
}
@Override
public int getY() {
return mWindowLayoutParams.y;
}
@Override
public void updatePosition(int x, int y) {
mWindowLayoutParams.x = x;
mWindowLayoutParams.y = y;
mWindowManager.updateViewLayout(mWindowView, mWindowLayoutParams);
}
@Override
public int getWidth() {
return mWindowView.getWidth();
}
@Override
public int getHeight() {
return mWindowView.getHeight();
}
@Override
public void updateMeasure(int width, int height) {
mWindowLayoutParams.width = width;
mWindowLayoutParams.height = height;
mWindowManager.updateViewLayout(mWindowView, mWindowLayoutParams);
}
@Override
public int getScreenWidth() {
ensureDisplayMetrics();
return mDisplayMetrics.widthPixels;
}
@Override
public int getScreenHeight() {
ensureDisplayMetrics();
return mDisplayMetrics.heightPixels;
}
private void ensureDisplayMetrics() {
if (mDisplayMetrics == null) {
mDisplayMetrics = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getMetrics(mDisplayMetrics);
}
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
mWindowLayoutParams = createWindowLayoutParams();
initWindowView();
initGesture();
setUpListeners();
}
private void setUpListeners() {
mDragGesture.setOnDraggedViewClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
enableWindowFocus();
expand();
}
});
mWindowView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
onBackPressed();
return true;
}
if (keyCode == KeyEvent.KEYCODE_HOME) {
onHomePressed();
return true;
}
return false;
}
});
}
private void expand() {
mCollapseExpandViewSwitcher.showSecond();
mResizeGesture.setResizeEnabled(true);
mDragGesture.setKeepToSide(false);
}
private void onBackPressed() {
if (mViewStack.canGoBack()) {
mViewStack.goBack();
} else {
collapse();
}
}
private void onHomePressed() {
mViewStack.goBackToFirst();
collapse();
}
private void collapse() {
mCollapseExpandViewSwitcher.showFirst();
disableWindowFocus();
mResizeGesture.setResizeEnabled(false);
mDragGesture.setKeepToSide(true);
}
private void disableWindowFocus() {
mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mWindowManager.updateViewLayout(mWindowView, mWindowLayoutParams);
}
private void initGesture() {
mResizeGesture = ResizeGesture.enableResize(mResizer, mWindowBridge);
mDragGesture = DragGesture.enableDrag(mWindowView, mWindowBridge);
mResizeGesture.setResizeEnabled(false);
mDragGesture.setKeepToSide(true);
}
private void enableWindowFocus() {
mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mWindowManager.updateViewLayout(mWindowView, mWindowLayoutParams);
mWindowView.requestFocus();
}
private void initWindowView() {
mWindowView = (RelativeLayout) View.inflate(getApplicationContext(), R.layout.resizable_floaty_container, null);
mWindowView.setFocusableInTouchMode(true);
mCollapseExpandViewSwitcher = (ViewSwitcher) mWindowView.findViewById(R.id.container);
mResizer = mWindowView.findViewById(R.id.resizer);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mCollapseExpandViewSwitcher.addView(floaty.mCollapsedView, params);
mCollapseExpandViewSwitcher.addView(floaty.mExpandedView, params);
mViewStack.setRootView(floaty.mExpandedView);
mWindowManager.addView(mWindowView, mWindowLayoutParams);
}
private WindowManager.LayoutParams createWindowLayoutParams() {
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
layoutParams.gravity = Gravity.TOP | Gravity.START;
return layoutParams;
}
}
public static class DragGesture extends GestureDetector.SimpleOnGestureListener {
public static DragGesture enableDrag(final View view, WindowBridge bridge) {
final DragGesture gestureListener = new DragGesture(bridge, view) {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
view.setAlpha(0.8f);
return super.onScroll(e1, e2, distanceX, distanceY);
}
};
final GestureDetectorCompat gestureDetector = new GestureDetectorCompat(view.getContext(), gestureListener);
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
gestureDetector.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_UP) {
view.setAlpha(1.0f);
if (!gestureListener.mFlung && gestureListener.isKeepToSide()) {
gestureListener.keepToSide();
}
}
return true;
}
});
return gestureListener;
}
private WindowBridge mWindowBridge;
private boolean mKeepToSide;
private View.OnClickListener mOnClickListener;
private View mView;
private int initialX;
private int initialY;
private float initialTouchX;
private float initialTouchY;
private boolean mFlung = false;
public DragGesture(WindowBridge windowBridge, View view) {
mWindowBridge = windowBridge;
mView = view;
}
public void setKeepToSide(boolean keepToSide) {
mKeepToSide = keepToSide;
}
public boolean isKeepToSide() {
return mKeepToSide;
}
@Override
public boolean onDown(MotionEvent event) {
initialX = mWindowBridge.getX();
initialY = mWindowBridge.getY();
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
mFlung = false;
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
mWindowBridge.updatePosition(initialX + (int) ((e2.getRawX() - initialTouchX)),
initialY + (int) ((e2.getRawY() - initialTouchY)));
return false;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
mFlung = true;
if (mKeepToSide)
keepToSide();
return false;
}
public void keepToSide() {
int newX = mWindowBridge.getX();
if (newX > mWindowBridge.getScreenWidth() / 2)
mWindowBridge.updatePosition(mWindowBridge.getScreenWidth(), mWindowBridge.getY());
else
mWindowBridge.updatePosition(0, mWindowBridge.getY());
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
if (mOnClickListener != null)
mOnClickListener.onClick(mView);
return super.onSingleTapConfirmed(e);
}
public void setOnDraggedViewClickListener(View.OnClickListener onClickListener) {
mOnClickListener = onClickListener;
}
}
public static class ResizeGesture extends GestureDetector.SimpleOnGestureListener {
public static ResizeGesture enableResize(View resizer, WindowBridge windowBridge) {
ResizeGesture resizeGesture = new ResizeGesture(windowBridge, resizer);
final GestureDetector detector = new GestureDetector(resizer.getContext(), resizeGesture);
resizer.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
detector.onTouchEvent(event);
return true;
}
});
return resizeGesture;
}
private WindowBridge mWindowBridge;
private float initialTouchX;
private float initialTouchY;
private int mInitialWidth, mInitialHeight;
private View mResizerView;
private int mMinHeight = 200, mMinWidth = 200;
private final int mStatusBarHeight;
public ResizeGesture(WindowBridge windowBridge, View resizerView) {
mWindowBridge = windowBridge;
mResizerView = resizerView;
mStatusBarHeight = ViewUtil.getStatusBarHeight(resizerView.getContext());
}
public void setMinHeight(int minHeight) {
mMinHeight = minHeight;
}
public void setMinWidth(int minWidth) {
mMinWidth = minWidth;
}
public void setResizeEnabled(boolean enabled) {
mResizerView.setVisibility(enabled ? View.VISIBLE : View.GONE);
}
@Override
public boolean onDown(MotionEvent event) {
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
mInitialWidth = mWindowBridge.getWidth();
mInitialHeight = mWindowBridge.getHeight();
return false;
}
@Override
public boolean onScroll(MotionEvent e1, final MotionEvent e2, float distanceX, float distanceY) {
int newWidth = mInitialWidth + (int) ((e2.getRawX() - initialTouchX));
int newHeight = mInitialHeight + (int) ((e2.getRawY() - initialTouchY));
newWidth = Math.max(mMinWidth, newWidth);
newHeight = Math.max(mMinHeight, newHeight);
newWidth = Math.min(mWindowBridge.getScreenWidth() - mWindowBridge.getX() - mResizerView.getWidth(), newWidth);
newHeight = Math.min(mWindowBridge.getScreenHeight() - mWindowBridge.getY() - mResizerView.getHeight() - mStatusBarHeight, newHeight);
mWindowBridge.updateMeasure(newWidth, newHeight);
return true;
}
}
}

View File

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