Commit 43b1f4ab authored by PurkkaKoodari's avatar PurkkaKoodari

Alpha 0.4: HUD working

parent ee0e5f46
......@@ -19,8 +19,8 @@ Wordbase Hacker is licensed under the [MIT license](https://opensource.org/licen
- finding possible words
- scoring words on different categories
- displaying result exactly as in the game
- HUD on top of the game to show the word to play
### To be implemented
- HUD to be placed over game view
- automatic playing (?)
\ No newline at end of file
......@@ -78,15 +78,24 @@
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/builds" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-support" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/restart-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split-apk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
......
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.pietu1998.wordbasehacker"
android:versionCode="3"
android:versionName="alpha 0.3">
android:versionCode="4"
android:versionName="alpha 0.4">
<uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.GET_TASKS"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:name=".HackerApplication"
......
......@@ -51,8 +51,9 @@ public class BoardActivity extends AppCompatActivity {
private boolean changingValues = false;
private Game game;
private List<Possibility> possibilities = new ArrayList<>();
private Possibility best = null;
private char[] tileLetters = null;
private boolean loaded = false;
private boolean loaded = false, playing = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
......@@ -70,10 +71,15 @@ public class BoardActivity extends AppCompatActivity {
play.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (best == null)
return;
playing = true;
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.wordbaseapp", "com.wordbaseapp.BoardActivity"));
intent.putExtra("game_id", (long) game.getId());
startActivity(intent);
((HackerApplication) getApplication()).showSuggestedPath(best.getCoordinates());
Toast.makeText(BoardActivity.this, R.string.hud_close_info, Toast.LENGTH_LONG).show();
}
});
Parcelable extra = getIntent().getParcelableExtra("game");
......@@ -88,21 +94,24 @@ public class BoardActivity extends AppCompatActivity {
actionBar.setTitle(getResources().getString(R.string.title_activity_board, game.getOpponent()));
actionBar.setDisplayHomeAsUpEnabled(true);
}
loaded = false;
}
@Override
protected void onStart() {
super.onStart();
if (loaded)
return;
loaded = true;
ProgressDialog dialog = new ProgressDialog(this);
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.setCancelable(false);
dialog.show();
LoadTask task = new LoadTask(dialog);
task.execute();
if (playing) {
((HackerApplication) getApplication()).hudOperationDone();
playing = false;
}
if (!loaded) {
loaded = true;
ProgressDialog dialog = new ProgressDialog(this);
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.setCancelable(false);
dialog.show();
LoadTask task = new LoadTask(dialog);
task.execute();
}
}
@Override
......@@ -222,7 +231,6 @@ public class BoardActivity extends AppCompatActivity {
private void updateView() {
int max = Integer.MIN_VALUE;
Possibility best = null;
for (Possibility pos : possibilities) {
int score = scoring.calculateScore(pos.getScore());
if (score > max) {
......
......@@ -58,12 +58,6 @@ public class BoardDrawable extends Drawable {
Paint whiteText = new Paint(blackText);
whiteText.setColor(0xFFFFFFFF);
Paint path = new Paint();
path.setStyle(Paint.Style.STROKE);
path.setStrokeWidth(8);
path.setStrokeCap(Paint.Cap.ROUND);
path.setColor(0xFF009900);
int[] tileStates = pos.getResult();
for (int y = 0, index = 0; y < 13; y++) {
for (int x = 0; x < 10; x++, index++) {
......@@ -84,22 +78,31 @@ public class BoardDrawable extends Drawable {
(t & (Tile.MINE | Tile.SUPER_MINE)) != 0 ? whiteText : blackText);
}
}
if (pos.getCoordinates().length > 2) {
byte[] c = pos.getCoordinates();
canvas.drawCircle(80 * c[0] + 40, 80 * c[1] + 40, 32, path);
if (c[0] != c[2] && c[1] != c[3])
canvas.drawLine(c[0] * 80 + (c[2] - c[0]) * SQRT512 + 40, c[1] * 80 + (c[3] - c[1])
* SQRT512 + 40, c[2] * 80 + 40, c[3] * 80 + 40, path);
else if (c[0] == c[2])
canvas.drawLine(80 * c[0] + 40, 48 * c[1] + 32 * c[3] + 40, 80 * c[2] + 40, 80 * c[3] + 40,
path);
else
canvas.drawLine(48 * c[0] + 32 * c[2] + 40, 80 * c[1] + 40, 80 * c[2] + 40, 80 * c[3] + 40,
path);
for (int i = 2; i < c.length - 2; i += 2) {
canvas.drawLine(80 * c[i] + 40, 80 * c[i + 1] + 40, 80 * c[i + 2] + 40, 80 * c[i + 3] + 40, path);
}
}
drawPath(canvas, pos.getCoordinates());
}
public static void drawPath(@NonNull Canvas canvas, byte[] coords) {
if (coords.length <= 2)
return;
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(8);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setColor(0xFF009900);
canvas.drawCircle(80 * coords[0] + 40, 80 * coords[1] + 40, 32, paint);
if (coords[0] != coords[2] && coords[1] != coords[3])
canvas.drawLine(coords[0] * 80 + (coords[2] - coords[0]) * SQRT512 + 40,
coords[1] * 80 + (coords[3] - coords[1]) * SQRT512 + 40,
coords[2] * 80 + 40, coords[3] * 80 + 40, paint);
else if (coords[0] == coords[2])
canvas.drawLine(80 * coords[0] + 40, 48 * coords[1] + 32 * coords[3] + 40,
80 * coords[2] + 40, 80 * coords[3] + 40, paint);
else
canvas.drawLine(48 * coords[0] + 32 * coords[2] + 40, 80 * coords[1] + 40,
80 * coords[2] + 40, 80 * coords[3] + 40, paint);
for (int i = 2; i < coords.length - 2; i += 2)
canvas.drawLine(80 * coords[i] + 40, 80 * coords[i + 1] + 40,
80 * coords[i + 2] + 40, 80 * coords[i + 3] + 40, paint);
}
@Override
......
package net.pietu1998.wordbasehacker;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (!intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED))
return;
if (HudUtils.isHudAutoEnabled(context)) {
Intent service = new Intent(context, HudService.class);
context.startService(service);
}
}
}
......@@ -54,14 +54,19 @@ public class HackerApplication extends Application {
}
}
public void startHudUpdateTimer() {
public void editHudSettings() {
if (service != null)
service.startTimer();
service.editHudSettings();
}
public void stopHudUpdateTimer() {
public void showSuggestedPath(byte[] coords) {
if (service != null)
service.stopTimer();
service.showSuggestedPath(coords);
}
public void hudOperationDone() {
if (service != null)
service.hudOperationDone();
}
}
package net.pietu1998.wordbasehacker;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.Service;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.support.annotation.NonNull;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import java.util.Timer;
import java.util.TimerTask;
public class HudService extends Service {
......@@ -33,125 +27,48 @@ public class HudService extends Service {
return binder;
}
private Timer timer;
private boolean timerStarted = false;
private Handler handler;
@Override
public void onCreate() {
super.onCreate();
handler = new Handler();
createHud();
showHudIfPossible();
if (HudUtils.isHudAutoEnabled(this))
startTimer();
}
public void startTimer() {
if (!timerStarted) {
timerStarted = true;
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
HudService.this.checkWordbaseRunning();
}
});
}
}, 0, 1000);
}
}
public void stopTimer() {
if (timerStarted) {
timerStarted = false;
timer.cancel();
timer = null;
}
WindowManager manager = (WindowManager) getSystemService(WINDOW_SERVICE);
Point screenSize = new Point();
manager.getDefaultDisplay().getSize(screenSize);
view = new HudView(this, screenSize.x);
}
@Override
public void onDestroy() {
stopTimer();
hideHud();
super.onDestroy();
}
private TextView view;
private HudView view;
private boolean windowShown = false;
private boolean wasRunning = false;
private boolean moving = false;
private boolean resizing = false;
private int origX, origY;
private float startX, startY;
private boolean settingMode = false;
private int getOverlayType() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
: WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
: settingMode ? WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
: WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
}
private void showHudIfPossible() {
if (!windowShown && HudUtils.canShowHud(this)) {
windowShown = true;
WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT, getOverlayType(),
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT, getOverlayType(),
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
params.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
params.gravity = Gravity.TOP | Gravity.START;
params.x = origX;
params.y = origY;
params.x = 0;
params.y = 0;
WindowManager manager = (WindowManager) getSystemService(WINDOW_SERVICE);
manager.addView(view, params);
}
}
private void createHud() {
view = new TextView(this);
view.setText("HUD placeholder");
view.setTextColor(0xFF00FF00);
view.setBackgroundColor(0xFFFFFF00);
view.setPadding(5, 5, 5, 5);
origX = 100;
origY = 100;
view.setOnTouchListener(new View.OnTouchListener() {
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View view, MotionEvent event) {
WindowManager.LayoutParams params = (WindowManager.LayoutParams) view.getLayoutParams();
if (params == null)
return false;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
startX = event.getRawX();
startY = event.getRawY();
moving = true;
break;
case MotionEvent.ACTION_UP:
moving = false;
params.x = origX = (int) (origX + event.getRawX() - startX);
params.y = origY = (int) (origY + event.getRawY() - startY);
((WindowManager) getSystemService(WINDOW_SERVICE)).updateViewLayout(view, params);
break;
case MotionEvent.ACTION_MOVE:
if (moving) {
params.x = (int) (origX + event.getRawX() - startX);
params.y = (int) (origY + event.getRawY() - startY);
((WindowManager) getSystemService(WINDOW_SERVICE)).updateViewLayout(view, params);
}
break;
}
return true;
}
});
}
private void hideHud() {
if (windowShown) {
windowShown = false;
......@@ -160,21 +77,26 @@ public class HudService extends Service {
}
}
private boolean wordbaseRunning() {
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
ActivityManager.RunningAppProcessInfo processInfo = manager.getRunningAppProcesses().get(0);
return processInfo.processName.startsWith("com.wordbaseapp");
public void hudOperationDone() {
hideHud();
settingMode = false;
view.setEditMode(false);
view.setCoordinates(null);
}
public void editHudSettings() {
settingMode = true;
view.setEditMode(true);
view.invalidate();
showHudIfPossible();
}
private void checkWordbaseRunning() {
boolean runningNow = wordbaseRunning();
if (runningNow != wasRunning) {
wasRunning = runningNow;
if (runningNow)
showHudIfPossible();
else
hideHud();
}
public void showSuggestedPath(@NonNull byte[] path) {
settingMode = false;
view.setEditMode(false);
view.setCoordinates(path);
view.invalidate();
showHudIfPossible();
}
}
......@@ -19,11 +19,6 @@ public final class HudUtils {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(context.getString(R.string.pref_key_hud), true);
}
public static boolean isHudAutoEnabled(Context context) {
return isHudEnabled(context) &&
PreferenceManager.getDefaultSharedPreferences(context).getBoolean(context.getString(R.string.pref_key_hudauto), true);
}
public static boolean canShowHud(Context context) {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context);
}
......
package net.pietu1998.wordbasehacker;
import android.annotation.SuppressLint;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.preference.PreferenceManager;
import android.support.v4.math.MathUtils;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
@SuppressLint("ViewConstructor")
public class HudView extends View {
private final HudService service;
public HudView(HudService context, int defaultWidth) {
super(context);
this.service = context;
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
x = preferences.getInt(context.getString(R.string.pref_key_hudx), 0);
y = preferences.getInt(context.getString(R.string.pref_key_hudy), 0);
width = preferences.getInt(context.getString(R.string.pref_key_hudwidth), defaultWidth);
setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
int screenWidth = getWidth();
int screenHeight = getHeight();
int buttonSize = screenWidth / 10;
RectF moveButton = new RectF(screenWidth / 2 - buttonSize, screenHeight / 2 - buttonSize,
screenWidth / 2 + buttonSize, screenHeight / 2 + buttonSize);
RectF resizeButton = new RectF(0, screenHeight / 2 - buttonSize,
buttonSize * 2, screenHeight / 2 + buttonSize);
RectF acceptButton = new RectF(screenWidth - buttonSize * 2, screenHeight / 2 - buttonSize,
screenWidth, screenHeight / 2 + buttonSize);
int action = event.getAction() & MotionEvent.ACTION_MASK;
switch (action) {
case MotionEvent.ACTION_DOWN:
if (moving || resizing || accepting)
break;
if (moveButton.contains(event.getX(), event.getY()))
moving = true;
else if (resizeButton.contains(event.getX(), event.getY()))
resizing = true;
else if (acceptButton.contains(event.getX(), event.getY()))
accepting = true;
startX = event.getX();
startY = event.getY();
origX = x;
origY = y;
origWidth = width;
invalidate();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_MOVE:
if (moving) {
x = MathUtils.clamp(origX + (int) ((event.getX() - startX) / 4), 0, Math.max(0, screenWidth - width));
y = MathUtils.clamp(origY + (int) ((event.getY() - startY) / 4), 0, Math.max(0, (int) (screenHeight - 1.3f * width)));
} else if (resizing) {
width = MathUtils.clamp(origWidth + (int) ((startY - event.getY()) / 4), 50, Math.max(50, screenWidth - x));
} else if (accepting && !acceptButton.contains(event.getX(), event.getY())) {
accepting = false;
}
if (action == MotionEvent.ACTION_UP) {
if (accepting) {
PreferenceManager.getDefaultSharedPreferences(service).edit()
.putInt(service.getString(R.string.pref_key_hudx), x)
.putInt(service.getString(R.string.pref_key_hudy), y)
.putInt(service.getString(R.string.pref_key_hudwidth), width)
.apply();
service.hudOperationDone();
}
moving = resizing = accepting = false;
}
invalidate();
break;
}
return true;
}
});
}
private boolean editMode = false;
private int x = 0, y = 0, width = 800;
private byte[] coordinates;
private boolean moving = false;
private boolean resizing = false;
private boolean accepting = false;
private int origX, origY, origWidth;
private float startX, startY;
public void setEditMode(boolean editMode) {
this.editMode = editMode;
}
public void setCoordinates(byte[] coordinates) {
this.coordinates = coordinates;
}
private static final float STROKE = 1;
private static final float CORNER = 5;
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
Log.d("WordbaseHacker", "Drawing view, resize: " + editMode);
if (editMode) {
canvas.save();
canvas.translate(x, y);
canvas.scale(width / 100f, width / 100f);
Paint backPaint = new Paint();