diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml new file mode 100644 index 00000000..44943151 --- /dev/null +++ b/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 9a237764..6ddcda74 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,6 @@ + - + diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460d..00000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index ae66a799..09a88463 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -60,6 +60,7 @@ dependencies { // Room implementation "androidx.room:room-runtime:2.3.0" + implementation 'com.google.android.gms:play-services:11.8.0' annotationProcessor "androidx.room:room-compiler:2.3.0" // Parsing diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1c3392df..40325a72 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,10 +5,13 @@ - + + + + - + + + \ No newline at end of file diff --git a/app/src/main/java/com/schneewittchen/rosandroid/widgets/location/LocationData.java b/app/src/main/java/com/schneewittchen/rosandroid/widgets/location/LocationData.java new file mode 100644 index 00000000..e6a04f30 --- /dev/null +++ b/app/src/main/java/com/schneewittchen/rosandroid/widgets/location/LocationData.java @@ -0,0 +1,46 @@ +package com.schneewittchen.rosandroid.widgets.location; + +import com.schneewittchen.rosandroid.model.repositories.rosRepo.node.BaseData; +import com.schneewittchen.rosandroid.model.entities.widgets.BaseEntity; + +import org.ros.internal.message.Message; +import org.ros.node.topic.Publisher; + +import sensor_msgs.NavSatFix; + + +/** + * TODO: Description + * + * @author Gennaro Raiola + * @version 0.0.1 + * @created on 19.11.22 + */ + +public class LocationData extends BaseData { + + public double latitude; + public double longitude; + public double altitude; + public String provider; + + public LocationData(double latitude, double longitude, double altitude, String provider) { + this.latitude = latitude; + this.longitude = longitude; + this.altitude = altitude; + this.provider = provider; + } + + @Override + public Message toRosMessage(Publisher publisher, BaseEntity widget) { + + sensor_msgs.NavSatFix message = (NavSatFix) publisher.newMessage(); + + message.getHeader().setFrameId(provider); + message.setLatitude(latitude); + message.setLongitude(longitude); + message.setAltitude(altitude); + + return message; + } +} diff --git a/app/src/main/java/com/schneewittchen/rosandroid/widgets/location/LocationDetailVH.java b/app/src/main/java/com/schneewittchen/rosandroid/widgets/location/LocationDetailVH.java new file mode 100644 index 00000000..5d1a020a --- /dev/null +++ b/app/src/main/java/com/schneewittchen/rosandroid/widgets/location/LocationDetailVH.java @@ -0,0 +1,66 @@ +package com.schneewittchen.rosandroid.widgets.location; + +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.Spinner; + +import com.schneewittchen.rosandroid.R; +import com.schneewittchen.rosandroid.model.entities.widgets.BaseEntity; +import com.schneewittchen.rosandroid.ui.views.details.PublisherWidgetViewHolder; +import com.schneewittchen.rosandroid.utility.Utils; + +import java.util.Collections; +import java.util.List; + +import sensor_msgs.NavSatFix; + +/** + * TODO: Description + * + * @author Gennaro Raiola + * @version 0.0.1 + * @created on 19.11.22 + */ +public class LocationDetailVH extends PublisherWidgetViewHolder { + + private EditText textText; + private Spinner rotationSpinner; + private ArrayAdapter rotationAdapter; + + @Override + public void initView(View view) { + textText = view.findViewById(R.id.btnTextTypeText); + rotationSpinner = view.findViewById(R.id.btnTextRotation); + + // Init spinner + rotationAdapter = ArrayAdapter.createFromResource(view.getContext(), + R.array.button_rotation, android.R.layout.simple_spinner_dropdown_item); + + rotationSpinner.setAdapter(rotationAdapter); + } + + @Override + public void bindEntity(BaseEntity entity) { + LocationEntity locationEntity = (LocationEntity) entity; + + textText.setText(locationEntity.text); + String degrees = Utils.numberToDegrees(locationEntity.rotation); + rotationSpinner.setSelection(rotationAdapter.getPosition(degrees)); + } + + @Override + public void updateEntity(BaseEntity entity) { + LocationEntity locationEntity = (LocationEntity) entity; + + locationEntity.text = textText.getText().toString(); + String degrees = rotationSpinner.getSelectedItem().toString(); + locationEntity.rotation = Utils.degreesToNumber(degrees); + } + + @Override + public List getTopicTypes() { + return Collections.singletonList(NavSatFix._TYPE); + } + +} diff --git a/app/src/main/java/com/schneewittchen/rosandroid/widgets/location/LocationEntity.java b/app/src/main/java/com/schneewittchen/rosandroid/widgets/location/LocationEntity.java new file mode 100644 index 00000000..b1adeae3 --- /dev/null +++ b/app/src/main/java/com/schneewittchen/rosandroid/widgets/location/LocationEntity.java @@ -0,0 +1,32 @@ +package com.schneewittchen.rosandroid.widgets.location; + +import com.schneewittchen.rosandroid.model.entities.widgets.PublisherWidgetEntity; +import com.schneewittchen.rosandroid.model.repositories.rosRepo.message.Topic; + +import sensor_msgs.NavSatFix; + +/** + * TODO: Description + * + * @author Gennaro Raiola + * @version 0.0.1 + * @created on 19.11.22 + */ + +public class LocationEntity extends PublisherWidgetEntity { + + public String text; + public int rotation; + public boolean buttonPressed; + + public LocationEntity() { + this.width = 4; + this.height = 4; + this.topic = new Topic("location", NavSatFix._TYPE); + this.immediatePublish = true; + //this.publishRate = 20f; + this.text = "Publish phone's location to ROS"; + this.rotation = 0; + this.buttonPressed = false; + } +} diff --git a/app/src/main/java/com/schneewittchen/rosandroid/widgets/location/LocationView.java b/app/src/main/java/com/schneewittchen/rosandroid/widgets/location/LocationView.java new file mode 100644 index 00000000..6211783f --- /dev/null +++ b/app/src/main/java/com/schneewittchen/rosandroid/widgets/location/LocationView.java @@ -0,0 +1,198 @@ +package com.schneewittchen.rosandroid.widgets.location; + +import static com.google.android.gms.location.LocationServices.getFusedLocationProviderClient; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.os.Looper; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.util.AttributeSet; +import android.view.MotionEvent; + +import androidx.annotation.Nullable; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import com.google.android.gms.location.LocationCallback; +import com.google.android.gms.location.LocationRequest; +import com.google.android.gms.location.LocationResult; +import com.schneewittchen.rosandroid.R; +import com.schneewittchen.rosandroid.ui.views.widgets.PublisherWidgetView; + +import android.Manifest; +import android.content.pm.PackageManager; +import android.util.Log; +import android.view.WindowManager; + +import java.util.ArrayList; + + +/** + * TODO: Description + * + * @author Gennaro Raiola + * @version 0.0.1 + * @created on 19.11.22 + */ +public class LocationView extends PublisherWidgetView { + + public static final String TAG = LocationView.class.getSimpleName(); + + Context context; + + Paint buttonPaint; + TextPaint textPaint; + StaticLayout staticLayout; + + LocationRequest locationRequest; + double altitude; + double latitude; + double longitude; + String provider; + + + public LocationView(Context context) { + super(context); + this.context = context; + init(); + } + + public LocationView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + this.context = context; + init(); + } + + private void init() { + + ((Activity) getContext()).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + LocationEntity entity = (LocationEntity) widgetEntity; + + buttonPaint = new Paint(); + buttonPaint.setColor(getResources().getColor(R.color.colorPrimary)); + buttonPaint.setStyle(Paint.Style.FILL_AND_STROKE); + + textPaint = new TextPaint(); + textPaint.setColor(Color.BLACK); + textPaint.setStyle(Paint.Style.FILL_AND_STROKE); + textPaint.setTextSize(26 * getResources().getDisplayMetrics().density); + + locationRequest = new LocationRequest(); + locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); + //long interval = (long)(1.0/entity.publishRate)*1000; + //Log.i(TAG,"INTERVAL - --------------------" + interval); + long interval = 500; + locationRequest.setInterval(interval); + locationRequest.setFastestInterval(interval); + locationRequest.setSmallestDisplacement(0); + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED ) { + requestPermissionsIfNecessary(new String[]{ + Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION + }); + } + getFusedLocationProviderClient(context).requestLocationUpdates(locationRequest, new LocationCallback() { + @Override + public void onLocationResult(LocationResult locationResult) { + provider = locationResult.getLastLocation().getProvider(); + altitude = locationResult.getLastLocation().getAltitude(); + latitude = locationResult.getLastLocation().getLatitude(); + longitude = locationResult.getLastLocation().getLongitude(); + publishCoordinates(); + } + }, + Looper.myLooper()); + + } + + private void requestPermissionsIfNecessary(String[] permissions) { + ArrayList permissionsToRequest = new ArrayList<>(); + for (String permission : permissions) { + if (ContextCompat.checkSelfPermission(this.getContext(), permission) + != PackageManager.PERMISSION_GRANTED) { + // Permission is not granted + permissionsToRequest.add(permission); + } + } + } + + private void changeState(boolean pressed) { + LocationEntity entity = (LocationEntity) widgetEntity; + entity.buttonPressed = pressed; + invalidate(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (this.editMode) { + return super.onTouchEvent(event); + } + + LocationEntity entity = (LocationEntity) widgetEntity; + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + if(entity.buttonPressed) + changeState(false); + else + changeState(true); + break; + default: + return false; + } + + return true; + } + + @Override + public void onDraw(Canvas canvas) { + float width = getWidth(); + float height = getHeight(); + float textLayoutWidth = width; + + LocationEntity entity = (LocationEntity) widgetEntity; + + if (entity.rotation == 90 || entity.rotation == 270) { + textLayoutWidth = height; + } + + if(!entity.buttonPressed) + buttonPaint.setColor(getResources().getColor(R.color.colorPrimary)); + else + buttonPaint.setColor(getResources().getColor(R.color.color_attention)); + + canvas.drawRect(new Rect(0, 0, (int) width, (int) height), buttonPaint); + + staticLayout = new StaticLayout(entity.text, + textPaint, + (int) textLayoutWidth, + Layout.Alignment.ALIGN_CENTER, + 1.0f, + 0, + false); + canvas.save(); + canvas.rotate(entity.rotation, width / 2, height / 2); + canvas.translate(((width / 2) - staticLayout.getWidth() / 2), height / 2 - staticLayout.getHeight() / 2); + staticLayout.draw(canvas); + canvas.restore(); + } + + public void publishCoordinates() { + + LocationEntity entity = (LocationEntity) widgetEntity; + + if(entity.buttonPressed) { + Log.d(TAG, " Provider: " + provider + " Longitude: " + longitude + " Latitude: " + latitude + " Altitude " + altitude); + this.publishViewData(new LocationData(latitude, longitude, altitude, provider)); + } + } + +} diff --git a/app/src/main/res/layout/widget_detail_location.xml b/app/src/main/res/layout/widget_detail_location.xml new file mode 100644 index 00000000..bddc5e46 --- /dev/null +++ b/app/src/main/res/layout/widget_detail_location.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/widgets.xml b/app/src/main/res/values/widgets.xml index 93278309..67b0fd68 100644 --- a/app/src/main/res/values/widgets.xml +++ b/app/src/main/res/values/widgets.xml @@ -12,6 +12,7 @@ Logger RqtPlot Viz2D + Location @@ -99,6 +100,18 @@ You can zoom in and out by pinching 2 or more fingers together or apart. + + + The Location node is designed to publish the phone GPS and or network coordinates to ROS as a sensor_msgs/NavSatFix message. + The publisher can be started or stopped by pressing its button in the Viz tab. + + + + + 90° + 180° + 270° + @@ -145,4 +158,4 @@ 2D View of multiple layers. - \ No newline at end of file + diff --git a/jcraft/src/main/AndroidManifest.xml b/jcraft/src/main/AndroidManifest.xml index 1438840c..29a0cd81 100644 --- a/jcraft/src/main/AndroidManifest.xml +++ b/jcraft/src/main/AndroidManifest.xml @@ -1,2 +1,8 @@ + package="com.jcraft" > + + + + + +