mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-10 12:09:27 +01:00
Merge pull request 'fossil-q-hr-background-image' (#1833) from fossil-q-hr-background-image into master
This commit is contained in:
commit
e5560fb182
@ -507,6 +507,9 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".devices.qhybrid.HRConfigActivity"
|
android:name=".devices.qhybrid.HRConfigActivity"
|
||||||
android:exported="true" />
|
android:exported="true" />
|
||||||
|
<activity
|
||||||
|
android:name=".devices.qhybrid.ImageEditActivity"
|
||||||
|
android:exported="true" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@ -5,7 +5,10 @@ import android.app.AlertDialog;
|
|||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.provider.MediaStore;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -27,6 +30,7 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -54,6 +58,9 @@ public class HRConfigActivity extends AbstractGBActivity implements View.OnClick
|
|||||||
SparseArray<String> widgetButtonsMapping = new SparseArray<>(4);
|
SparseArray<String> widgetButtonsMapping = new SparseArray<>(4);
|
||||||
|
|
||||||
static public final String CONFIG_KEY_Q_ACTIONS = "Q_ACTIONS";
|
static public final String CONFIG_KEY_Q_ACTIONS = "Q_ACTIONS";
|
||||||
|
private static final int REQUEST_CODE_WIDGET_EDIT = 0;
|
||||||
|
private static final int REQUEST_CODE_IMAGE_PICK = 1;
|
||||||
|
private static final int REQUEST_CODE_IMAGE_EDIT = 2;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -85,7 +92,7 @@ public class HRConfigActivity extends AbstractGBActivity implements View.OnClick
|
|||||||
startIntent.putExtra("EXTRA_WIDGET", widget);
|
startIntent.putExtra("EXTRA_WIDGET", widget);
|
||||||
startIntent.putExtra("EXTRA_WIDGET_IDNEX", position);
|
startIntent.putExtra("EXTRA_WIDGET_IDNEX", position);
|
||||||
|
|
||||||
startActivityForResult(startIntent, 0);
|
startActivityForResult(startIntent, REQUEST_CODE_WIDGET_EDIT);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
loadCustomWidgetList();
|
loadCustomWidgetList();
|
||||||
@ -95,7 +102,36 @@ public class HRConfigActivity extends AbstractGBActivity implements View.OnClick
|
|||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Intent startIntent = new Intent(HRConfigActivity.this, WidgetSettingsActivity.class);
|
Intent startIntent = new Intent(HRConfigActivity.this, WidgetSettingsActivity.class);
|
||||||
|
|
||||||
startActivityForResult(startIntent, 0);
|
startActivityForResult(startIntent, REQUEST_CODE_WIDGET_EDIT);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
findViewById(R.id.qhybrid_set_background).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
new AlertDialog.Builder(HRConfigActivity.this)
|
||||||
|
.setTitle("whoop whoop")
|
||||||
|
.setMessage("background has to be pushed every time a custom widget changes, causing traffic and battery drain. Consider that when using custom widgets.")
|
||||||
|
.setPositiveButton("ok", new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
Intent pickIntent = new Intent(Intent.ACTION_PICK);
|
||||||
|
pickIntent.setType("image/*");
|
||||||
|
|
||||||
|
startActivityForResult(pickIntent, REQUEST_CODE_IMAGE_PICK);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton("nah", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
findViewById(R.id.qhybrid_unset_background).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
Intent intent = new Intent(QHybridSupport.QHYBRID_COMMAND_SET_BACKGROUND_IMAGE);
|
||||||
|
intent.putIntegerArrayListExtra("EXTRA_PIXELS", null);
|
||||||
|
LocalBroadcastManager.getInstance(HRConfigActivity.this).sendBroadcast(intent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -139,34 +175,50 @@ public class HRConfigActivity extends AbstractGBActivity implements View.OnClick
|
|||||||
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
if(data == null) return;
|
if(data == null) return;
|
||||||
if (resultCode == WidgetSettingsActivity.RESULT_CODE_WIDGET_CREATED) {
|
if(requestCode == REQUEST_CODE_WIDGET_EDIT) {
|
||||||
CustomWidget widget = (CustomWidget) data.getExtras().get("EXTRA_WIDGET");
|
if (resultCode == WidgetSettingsActivity.RESULT_CODE_WIDGET_CREATED) {
|
||||||
this.customWidgets.add(widget);
|
CustomWidget widget = (CustomWidget) data.getExtras().get("EXTRA_WIDGET");
|
||||||
refreshWidgetList();
|
this.customWidgets.add(widget);
|
||||||
saveCustomWidgetList();
|
refreshWidgetList();
|
||||||
|
saveCustomWidgetList();
|
||||||
|
|
||||||
LocalBroadcastManager.getInstance(HRConfigActivity.this).sendBroadcast(new Intent(QHYBRID_COMMAND_UPDATE_WIDGETS));
|
LocalBroadcastManager.getInstance(HRConfigActivity.this).sendBroadcast(new Intent(QHYBRID_COMMAND_UPDATE_WIDGETS));
|
||||||
} else if (resultCode == WidgetSettingsActivity.RESULT_CODE_WIDGET_UPDATED) {
|
} else if (resultCode == WidgetSettingsActivity.RESULT_CODE_WIDGET_UPDATED) {
|
||||||
CustomWidget widget = (CustomWidget) data.getExtras().get("EXTRA_WIDGET");
|
CustomWidget widget = (CustomWidget) data.getExtras().get("EXTRA_WIDGET");
|
||||||
int updateIndex = data.getIntExtra("EXTRA_WIDGET_IDNEX", -1);
|
int updateIndex = data.getIntExtra("EXTRA_WIDGET_IDNEX", -1);
|
||||||
|
|
||||||
this.customWidgets.set(updateIndex, widget);
|
this.customWidgets.set(updateIndex, widget);
|
||||||
|
|
||||||
refreshWidgetList();
|
refreshWidgetList();
|
||||||
saveCustomWidgetList();
|
saveCustomWidgetList();
|
||||||
|
|
||||||
LocalBroadcastManager.getInstance(HRConfigActivity.this).sendBroadcast(new Intent(QHYBRID_COMMAND_UPDATE_WIDGETS));
|
LocalBroadcastManager.getInstance(HRConfigActivity.this).sendBroadcast(new Intent(QHYBRID_COMMAND_UPDATE_WIDGETS));
|
||||||
} else if (resultCode == WidgetSettingsActivity.RESULT_CODE_WIDGET_DELETED){
|
} else if (resultCode == WidgetSettingsActivity.RESULT_CODE_WIDGET_DELETED) {
|
||||||
int updateIndex = data.getIntExtra("EXTRA_WIDGET_IDNEX", -1);
|
int updateIndex = data.getIntExtra("EXTRA_WIDGET_IDNEX", -1);
|
||||||
|
|
||||||
this.customWidgets.remove(updateIndex);
|
this.customWidgets.remove(updateIndex);
|
||||||
|
|
||||||
refreshWidgetList();
|
refreshWidgetList();
|
||||||
saveCustomWidgetList();
|
saveCustomWidgetList();
|
||||||
|
|
||||||
LocalBroadcastManager.getInstance(HRConfigActivity.this).sendBroadcast(new Intent(QHYBRID_COMMAND_UPDATE_WIDGETS));
|
LocalBroadcastManager.getInstance(HRConfigActivity.this).sendBroadcast(new Intent(QHYBRID_COMMAND_UPDATE_WIDGETS));
|
||||||
|
}
|
||||||
|
}else if(requestCode == REQUEST_CODE_IMAGE_PICK){
|
||||||
|
if (resultCode == RESULT_OK)
|
||||||
|
{
|
||||||
|
Uri imageUri = data.getData();
|
||||||
|
Intent activityIntent = new Intent();
|
||||||
|
activityIntent.setClass(this, ImageEditActivity.class);
|
||||||
|
activityIntent.setData(imageUri);
|
||||||
|
|
||||||
|
startActivityForResult(activityIntent, REQUEST_CODE_IMAGE_EDIT);
|
||||||
|
}
|
||||||
|
}else if(requestCode == REQUEST_CODE_IMAGE_EDIT){
|
||||||
|
if(resultCode == ImageEditActivity.RESULT_CODE_EDIT_SUCCESS){
|
||||||
|
data.setAction(QHybridSupport.QHYBRID_COMMAND_SET_BACKGROUND_IMAGE);
|
||||||
|
LocalBroadcastManager.getInstance(this).sendBroadcast(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveCustomWidgetList() {
|
private void saveCustomWidgetList() {
|
||||||
|
@ -0,0 +1,260 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Matrix;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.image.AssetImage;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.image.AssetImageFactory;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
|
||||||
|
public class ImageEditActivity extends AbstractGBActivity implements View.OnTouchListener {
|
||||||
|
static final public int RESULT_CODE_EDIT_SUCCESS = 0;
|
||||||
|
|
||||||
|
ImageView overlay;
|
||||||
|
Canvas overlayCanvas;
|
||||||
|
Paint overlayPaint;
|
||||||
|
Bitmap overlayBitmap, mainBitmap;
|
||||||
|
|
||||||
|
float x = 0, y = 0, diameter = 0, imageDimension = 0;
|
||||||
|
int imageWidth, imageHeight;
|
||||||
|
|
||||||
|
private enum MovementState {
|
||||||
|
MOVE_UPPER_LEFT,
|
||||||
|
MOVE_LOWER_RIGHT,
|
||||||
|
MOVE_FRAME
|
||||||
|
}
|
||||||
|
private MovementState movementState;
|
||||||
|
float movementStartX, movementStartY, movementStartFrameX, movementStartFrameY, movementStartDiameter, leftUpperDeltaX, leftUpperDeltaY;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_qhybrid_image_edit);
|
||||||
|
|
||||||
|
final RelativeLayout mainLayout = findViewById(R.id.qhybrid_image_edit_container);
|
||||||
|
overlay = findViewById(R.id.qhybrid_image_edit_image_overlay);
|
||||||
|
overlay.setOnTouchListener(this);
|
||||||
|
|
||||||
|
findViewById(R.id.qhybrid_image_edit_okay).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
finalizeImage();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mainLayout.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
fitImageToLayout(mainLayout);
|
||||||
|
} catch (IOException | RuntimeException e) {
|
||||||
|
GB.log("Error formatting image", GB.ERROR, e);
|
||||||
|
GB.toast("Error formatting image", Toast.LENGTH_LONG, GB.ERROR);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void finalizeImage(){
|
||||||
|
Bitmap cropped = Bitmap.createBitmap(this.mainBitmap, (int) this.x, (int) this.y, (int) this.diameter, (int) this.diameter);
|
||||||
|
Bitmap scaled = Bitmap.createScaledBitmap(cropped, 400, 400, false);
|
||||||
|
cropped.recycle();
|
||||||
|
|
||||||
|
try {
|
||||||
|
AssetImage image = AssetImageFactory.createAssetImage(scaled, false, 0, 0, 0);
|
||||||
|
|
||||||
|
Intent resultIntent = new Intent();
|
||||||
|
resultIntent.putExtra("EXTRA_PIXELS_ENCODED", image.getFileData());
|
||||||
|
setResult(RESULT_CODE_EDIT_SUCCESS, resultIntent);
|
||||||
|
finish();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
|
||||||
|
scaled.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fitImageToLayout(RelativeLayout mainLayout) throws IOException, RuntimeException {
|
||||||
|
float containerHeight = mainLayout.getHeight();
|
||||||
|
float containerWidth = mainLayout.getWidth();
|
||||||
|
float containerRelation = containerHeight / containerWidth;
|
||||||
|
|
||||||
|
Bitmap bitmap = this.createImageFromURI();
|
||||||
|
float imageHeight = bitmap.getHeight();
|
||||||
|
float imageWidth = bitmap.getWidth();
|
||||||
|
float imageRelation = imageHeight / imageWidth;
|
||||||
|
|
||||||
|
float scaleRatio;
|
||||||
|
if(imageRelation > containerRelation){
|
||||||
|
scaleRatio = containerHeight / imageHeight;
|
||||||
|
}else{
|
||||||
|
scaleRatio = containerWidth / imageWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
int scaledHeight = (int)(imageHeight * scaleRatio);
|
||||||
|
int scaledWidth = (int)(imageWidth * scaleRatio);
|
||||||
|
|
||||||
|
this.imageHeight = scaledHeight;
|
||||||
|
this.imageWidth = scaledWidth;
|
||||||
|
|
||||||
|
this.imageDimension = this.diameter = Math.min(scaledHeight, scaledWidth);
|
||||||
|
|
||||||
|
mainBitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, false);
|
||||||
|
|
||||||
|
ImageView mainImageView = findViewById(R.id.qhybrid_image_edit_image);
|
||||||
|
mainImageView.setImageBitmap(mainBitmap);
|
||||||
|
createOverlay(scaledWidth, scaledHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createOverlay(int width, int height){
|
||||||
|
this.overlayBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
|
||||||
|
this.overlayCanvas = new Canvas(this.overlayBitmap);
|
||||||
|
|
||||||
|
this.overlayPaint = new Paint();
|
||||||
|
this.overlayPaint.setColor(Color.BLACK);
|
||||||
|
this.overlayPaint.setStyle(Paint.Style.STROKE);
|
||||||
|
this.overlayPaint.setStrokeWidth(imageDimension / 100);
|
||||||
|
|
||||||
|
renderOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bitmap createImageFromURI() throws IOException, RuntimeException {
|
||||||
|
Uri imageURI = getIntent().getData();
|
||||||
|
if (imageURI == null) {
|
||||||
|
throw new RuntimeException("no image attached");
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentResolver resolver = getContentResolver();
|
||||||
|
Cursor c = resolver.query(imageURI, new String[]{MediaStore.Images.ImageColumns.ORIENTATION}, null, null, null);
|
||||||
|
c.moveToFirst();
|
||||||
|
int orientation = c.getInt(c.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION));
|
||||||
|
c.close();
|
||||||
|
Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageURI);
|
||||||
|
if (orientation != 0) {
|
||||||
|
Matrix matrix = new Matrix();
|
||||||
|
matrix.postRotate(90);
|
||||||
|
|
||||||
|
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderOverlay() {
|
||||||
|
overlayCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
|
||||||
|
|
||||||
|
overlayCanvas.drawCircle(x, y, imageDimension / 15, overlayPaint);
|
||||||
|
overlayCanvas.drawCircle(x + diameter, y + diameter, imageDimension / 15, overlayPaint);
|
||||||
|
|
||||||
|
overlayCanvas.drawCircle(x + diameter / 2, y + diameter / 2, diameter / 2, overlayPaint);
|
||||||
|
|
||||||
|
overlay.setImageBitmap(overlayBitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void forceImageBoundaries(){
|
||||||
|
this.x = Math.max(this.x, 0);
|
||||||
|
this.y = Math.max(this.y, 0);
|
||||||
|
|
||||||
|
this.x = Math.min(this.x, this.imageWidth - diameter);
|
||||||
|
this.y = Math.min(this.y, this.imageHeight - diameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouch(View view, MotionEvent motionEvent) {
|
||||||
|
if(motionEvent.getAction() == MotionEvent.ACTION_DOWN){
|
||||||
|
handleTouchDown(motionEvent);
|
||||||
|
}else if(motionEvent.getAction() == MotionEvent.ACTION_MOVE){
|
||||||
|
handleTouchMove(motionEvent);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleTouchMove(MotionEvent motionEvent){
|
||||||
|
if(movementState == MovementState.MOVE_UPPER_LEFT){
|
||||||
|
float moveDeltaX = motionEvent.getX() - this.movementStartX;
|
||||||
|
float moveDeltaY = motionEvent.getY() - this.movementStartY;
|
||||||
|
|
||||||
|
float mid = (moveDeltaX + moveDeltaY) / 2;
|
||||||
|
|
||||||
|
this.diameter = this.movementStartDiameter - mid;
|
||||||
|
this.x = this.movementStartX + mid + this.leftUpperDeltaX;
|
||||||
|
this.y = this.movementStartY + mid + this.leftUpperDeltaY;
|
||||||
|
}else if(movementState == MovementState.MOVE_LOWER_RIGHT) {
|
||||||
|
float moveDeltaX = motionEvent.getX() - this.movementStartX;
|
||||||
|
float moveDeltaY = motionEvent.getY() - this.movementStartY;
|
||||||
|
|
||||||
|
float mid = (moveDeltaX + moveDeltaY) / 2;
|
||||||
|
|
||||||
|
this.diameter = this.movementStartDiameter + mid;
|
||||||
|
}else if(movementState == MovementState.MOVE_FRAME){
|
||||||
|
this.x = this.movementStartFrameX + (motionEvent.getX() - this.movementStartX);
|
||||||
|
this.y = this.movementStartFrameY + (motionEvent.getY() - this.movementStartY);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.forceImageBoundaries();
|
||||||
|
renderOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleTouchDown(MotionEvent motionEvent) {
|
||||||
|
this.movementStartX = motionEvent.getX();
|
||||||
|
this.movementStartY = motionEvent.getY();
|
||||||
|
this.movementStartFrameX = this.x;
|
||||||
|
this.movementStartFrameY = this.y;
|
||||||
|
this.movementStartDiameter = this.diameter;
|
||||||
|
|
||||||
|
final float threshold = imageDimension / 15;
|
||||||
|
|
||||||
|
float upperLeftDeltaX = this.x - motionEvent.getX();
|
||||||
|
float upperLeftDeltaY = this.y - motionEvent.getY();
|
||||||
|
|
||||||
|
float upperLeftDistance = (float) Math.sqrt(upperLeftDeltaX * upperLeftDeltaX + upperLeftDeltaY * upperLeftDeltaY);
|
||||||
|
|
||||||
|
if(upperLeftDistance < threshold){
|
||||||
|
// Toast.makeText(this, "upper left", 0).show();
|
||||||
|
this.leftUpperDeltaX = upperLeftDeltaX;
|
||||||
|
this.leftUpperDeltaY = upperLeftDeltaY;
|
||||||
|
this.movementState = MovementState.MOVE_UPPER_LEFT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float lowerLeftX = this.x + diameter;
|
||||||
|
float lowerLeftY = this.y + diameter;
|
||||||
|
|
||||||
|
float lowerRightDeltaX = lowerLeftX - motionEvent.getX();
|
||||||
|
float lowerRightDeltaY = lowerLeftY - motionEvent.getY();
|
||||||
|
|
||||||
|
float lowerRightDistance = (float) Math.sqrt(lowerRightDeltaX * lowerRightDeltaX + lowerRightDeltaY * lowerRightDeltaY);
|
||||||
|
|
||||||
|
if(lowerRightDistance < threshold){
|
||||||
|
// Toast.makeText(this, "lower right", 0).show();
|
||||||
|
this.movementState = MovementState.MOVE_LOWER_RIGHT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toast.makeText(this, "anywhere else", 0).show();
|
||||||
|
this.movementState = MovementState.MOVE_FRAME;
|
||||||
|
}
|
||||||
|
}
|
@ -89,6 +89,7 @@ public class QHybridSupport extends QHybridBaseSupport {
|
|||||||
public static final String QHYBRID_COMMAND_SET_MENU_MESSAGE = "nodomain.freeyourgadget.gadgetbridge.Q_SET_MENU_MESSAGE";
|
public static final String QHYBRID_COMMAND_SET_MENU_MESSAGE = "nodomain.freeyourgadget.gadgetbridge.Q_SET_MENU_MESSAGE";
|
||||||
public static final String QHYBRID_COMMAND_SEND_MENU_ITEMS = "nodomain.freeyourgadget.gadgetbridge.Q_SEND_MENU_ITEMS";
|
public static final String QHYBRID_COMMAND_SEND_MENU_ITEMS = "nodomain.freeyourgadget.gadgetbridge.Q_SEND_MENU_ITEMS";
|
||||||
public static final String QHYBRID_COMMAND_SET_WIDGET_CONTENT = "nodomain.freeyourgadget.gadgetbridge.Q_SET_WIDGET_CONTENT";
|
public static final String QHYBRID_COMMAND_SET_WIDGET_CONTENT = "nodomain.freeyourgadget.gadgetbridge.Q_SET_WIDGET_CONTENT";
|
||||||
|
public static final String QHYBRID_COMMAND_SET_BACKGROUND_IMAGE = "nodomain.freeyourgadget.gadgetbridge.Q_SET_BACKGROUND_IMAGE";
|
||||||
|
|
||||||
private static final String QHYBRID_ACTION_SET_ACTIVITY_HAND = "nodomain.freeyourgadget.gadgetbridge.Q_SET_ACTIVITY_HAND";
|
private static final String QHYBRID_ACTION_SET_ACTIVITY_HAND = "nodomain.freeyourgadget.gadgetbridge.Q_SET_ACTIVITY_HAND";
|
||||||
|
|
||||||
@ -144,6 +145,7 @@ public class QHybridSupport extends QHybridBaseSupport {
|
|||||||
commandFilter.addAction(QHYBRID_COMMAND_NOTIFICATION_CONFIG_CHANGED);
|
commandFilter.addAction(QHYBRID_COMMAND_NOTIFICATION_CONFIG_CHANGED);
|
||||||
commandFilter.addAction(QHYBRID_COMMAND_UPDATE_WIDGETS);
|
commandFilter.addAction(QHYBRID_COMMAND_UPDATE_WIDGETS);
|
||||||
commandFilter.addAction(QHYBRID_COMMAND_SEND_MENU_ITEMS);
|
commandFilter.addAction(QHYBRID_COMMAND_SEND_MENU_ITEMS);
|
||||||
|
commandFilter.addAction(QHYBRID_COMMAND_SET_BACKGROUND_IMAGE);
|
||||||
commandReceiver = new BroadcastReceiver() {
|
commandReceiver = new BroadcastReceiver() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -230,6 +232,11 @@ public class QHybridSupport extends QHybridBaseSupport {
|
|||||||
watchAdapter.updateWidgets();
|
watchAdapter.updateWidgets();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case QHYBRID_COMMAND_SET_BACKGROUND_IMAGE:{
|
||||||
|
byte[] pixels = intent.getByteArrayExtra("EXTRA_PIXELS_ENCODED");
|
||||||
|
watchAdapter.setBackgroundImage(pixels);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -130,4 +130,7 @@ public abstract class WatchAdapter {
|
|||||||
|
|
||||||
public void onSendWeather(WeatherSpec weatherSpec) {
|
public void onSendWeather(WeatherSpec weatherSpec) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setBackgroundImage(byte[] pixels) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil_hr;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil_hr;
|
||||||
|
|
||||||
import android.bluetooth.BluetoothGattCharacteristic;
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
@ -9,12 +10,16 @@ import android.graphics.Canvas;
|
|||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.BufferOverflowException;
|
import java.nio.BufferOverflowException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -26,6 +31,7 @@ import java.util.TimeZone;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.GBActivity;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
|
||||||
@ -145,19 +151,56 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadBackground(){
|
private File getBackgroundFile(){
|
||||||
Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(getDeviceSupport().getDevice().getAddress()));
|
return new File(getContext().getExternalFilesDir(null), "hr_background.bin");
|
||||||
boolean forceWhiteBackground = prefs.getBoolean("force_white_color_scheme", false);
|
}
|
||||||
if (forceWhiteBackground) {
|
|
||||||
Bitmap whiteBitmap = Bitmap.createBitmap(239, 239, Bitmap.Config.ARGB_8888);
|
|
||||||
new Canvas(whiteBitmap).drawColor(Color.WHITE);
|
|
||||||
|
|
||||||
|
private void loadBackground(){
|
||||||
|
try {
|
||||||
|
FileInputStream fis = new FileInputStream(getBackgroundFile());
|
||||||
|
int count = fis.available();
|
||||||
|
if(count != 14400){
|
||||||
|
throw new RuntimeException("wrong background file length");
|
||||||
|
}
|
||||||
|
byte[] file = new byte[14400];
|
||||||
|
fis.read(file);
|
||||||
|
fis.close();
|
||||||
|
this.backGroundImage = AssetImageFactory.createAssetImage(file, 0, 0, 0);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
SharedPreferences preferences = getDeviceSpecificPreferences();
|
||||||
|
if (preferences.getBoolean("force_white_color_scheme", false)) {
|
||||||
|
Bitmap whiteBitmap = Bitmap.createBitmap(239, 239, Bitmap.Config.ARGB_8888);
|
||||||
|
new Canvas(whiteBitmap).drawColor(Color.WHITE);
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.backGroundImage = AssetImageFactory.createAssetImage(whiteBitmap, true, 0, 1, 0);
|
||||||
|
queueWrite(new AssetFilePutRequest(this.backGroundImage, (byte) 0x00, this));
|
||||||
|
} catch (IOException e2) {
|
||||||
|
logger.error("Backgroundimage error", e2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException | RuntimeException e) {
|
||||||
|
GB.log("error opening background file", GB.ERROR, e);
|
||||||
|
GB.toast("error opening background file", Toast.LENGTH_LONG, GB.ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBackgroundImage(byte[] pixels) {
|
||||||
|
if(pixels == null){
|
||||||
|
getBackgroundFile().delete();
|
||||||
|
this.backGroundImage = null;
|
||||||
|
}else{
|
||||||
|
this.backGroundImage = AssetImageFactory.createAssetImage(pixels, 0, 0, 0);
|
||||||
try {
|
try {
|
||||||
this.backGroundImage = AssetImageFactory.createAssetImage(whiteBitmap, true, 0, 1, 0);
|
FileOutputStream fos = new FileOutputStream(getBackgroundFile(), false);
|
||||||
|
fos.write(pixels);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Backgroundimage error", e);
|
GB.log("error saving background", GB.ERROR, e);
|
||||||
|
GB.toast("error persistent saving background", Toast.LENGTH_LONG, GB.ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
renderWidgets();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadWidgets() {
|
private void loadWidgets() {
|
||||||
@ -267,9 +310,6 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
|
|||||||
try {
|
try {
|
||||||
ArrayList<AssetImage> widgetImages = new ArrayList<>();
|
ArrayList<AssetImage> widgetImages = new ArrayList<>();
|
||||||
|
|
||||||
if(this.backGroundImage != null){
|
|
||||||
widgetImages.add(this.backGroundImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < this.widgets.size(); i++) {
|
for (int i = 0; i < this.widgets.size(); i++) {
|
||||||
Widget w = widgets.get(i);
|
Widget w = widgets.get(i);
|
||||||
@ -344,15 +384,18 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(this.backGroundImage != null){
|
||||||
|
widgetImages.add(this.backGroundImage);
|
||||||
|
}
|
||||||
|
|
||||||
AssetImage[] images = widgetImages.toArray(new AssetImage[0]);
|
AssetImage[] images = widgetImages.toArray(new AssetImage[0]);
|
||||||
|
|
||||||
// queueWrite(new FileDeleteRequest((short) 0x0700));
|
|
||||||
queueWrite(new AssetFilePutRequest(
|
queueWrite(new AssetFilePutRequest(
|
||||||
images,
|
images,
|
||||||
(byte) 0x00,
|
(byte) 0x00,
|
||||||
this
|
this
|
||||||
));
|
));
|
||||||
|
|
||||||
// queueWrite(new FileDeleteRequest((short) 0x0503));
|
// queueWrite(new FileDeleteRequest((short) 0x0503));
|
||||||
queueWrite(new ImagesSetRequest(
|
queueWrite(new ImagesSetRequest(
|
||||||
images,
|
images,
|
||||||
|
@ -17,6 +17,13 @@ public class AssetImage extends AssetFile {
|
|||||||
this.indexZ = indexZ;
|
this.indexZ = indexZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected AssetImage(byte[] fileData, String fileName, int angle, int distance, int indexZ) {
|
||||||
|
super(fileName, fileData);
|
||||||
|
this.angle = angle;
|
||||||
|
this.distance = distance;
|
||||||
|
this.indexZ = indexZ;
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
@ -16,7 +16,13 @@ public class AssetImageFactory {
|
|||||||
return new AssetImage(fileData, angle, distance, indexZ);
|
return new AssetImage(fileData, angle, distance, indexZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// method created for creating empty files, which turned out not to work anyway
|
||||||
|
private static AssetImage createAssetImage(byte[] fileData, String fileName, int angle, int distance, int indexZ){
|
||||||
|
return new AssetImage(fileData, fileName, angle, distance, indexZ);
|
||||||
|
}
|
||||||
|
|
||||||
public static AssetImage createAssetImage(Bitmap fileData, boolean RLEencode, int angle, int distance, int indexZ) throws IOException {
|
public static AssetImage createAssetImage(Bitmap fileData, boolean RLEencode, int angle, int distance, int indexZ) throws IOException {
|
||||||
|
if(RLEencode == (distance == 0)) throw new RuntimeException("when RLEencoding distance must be 0, image must be at center of screen");
|
||||||
if(RLEencode){
|
if(RLEencode){
|
||||||
int height = fileData.getHeight();
|
int height = fileData.getHeight();
|
||||||
int width = fileData.getWidth();
|
int width = fileData.getWidth();
|
||||||
|
@ -96,4 +96,26 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="add widget" />
|
android:text="add widget" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:weightSum="2"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/qhybrid_unset_background"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="delete background"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/qhybrid_set_background"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="set background"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
35
app/src/main/res/layout/activity_qhybrid_image_edit.xml
Normal file
35
app/src/main/res/layout/activity_qhybrid_image_edit.xml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:weightSum="1">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:id="@+id/qhybrid_image_edit_container"
|
||||||
|
android:layout_above="@id/qhybrid_image_edit_okay"
|
||||||
|
android:layout_alignParentTop="true">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/qhybrid_image_edit_image"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@android:color/holo_red_dark"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/qhybrid_image_edit_image_overlay"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="ok"
|
||||||
|
android:id="@+id/qhybrid_image_edit_okay"
|
||||||
|
android:layout_alignParentBottom="true"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
Loading…
Reference in New Issue
Block a user