Ok Glass, Treat an incident (2/2)

Share Button

Following our previous article, we are still working on MBAAS and GPS guiding. In this article, we focus on retrieving the information saved in Parse and then talk about the geo-point search.

This time, we will talk about treating an incident already registered. First, we need to choose among the declared incidents, and then go to its location. To do that, we retrieve from Parse the ten closest incidents still in progress. When they are retrieved, the user can choose among them the one he wants to treat. He is then directed to a map displaying his current location and the incident location. On this view he can also access the incident description and a related picture. Then he can go through to a simple guiding screen. At this point, the user can only see the distance as the crow flies and the incident general direction. He can also access a map that shows his current location and the incident location. However, the map won’t actualize as the user moves.

This time the speech recognition understands what I said on the first trial. Oh, again don’t try to search for any car there is none.

Retrieve closest incident with Parse

Incident_retrieved

We begin our flow by searching for the ten closest incidents that are still in progress. Each of them is displayed in its own card as follow: in first its id, then its type and finally its creation date. The cards are bundled in a ScrollView as for the type choosing part in the report application. We also retrieve the first report and the incident picture. Since we cannot display them right away, we pass those to the next screen.

public void onLocationChanged(Location location) {
	// Called when a new location is found by the network location provider.
	mUserLatitude = location.getLatitude();
	mUserLongitude = location.getLongitude();

	mLocation = new ParseGeoPoint(mUserLatitude, mUserLongitude);

	ParseQuery<ParseObject> query = ParseQuery
			.getQuery(Poc_TreatAnIncident_Constants.INCIDENT_LOCATION);

	query.whereNear("location", mLocation);
	query.whereEqualTo(Poc_TreatAnIncident_Constants.INCIDENT_TREATED,
					false);
	query.setLimit(10);

	query.findInBackground(new FindCallback<ParseObject>() {

		@Override
		public void done(List<ParseObject> objects, ParseException e) {
			if (e == null) {
				Card card;
				mCards = new ArrayList<Card>();
				String _footer = "/" + objects.size();
				ParseObject po;

				for (int i = 0; i < objects.size(); i++) {
					card = new Card(IncidentSearchActivity.this);
					po = objects.get(i);
					mClosestIncidents.add(po);

					card.setText(dispalyIncidentInfo(po));
					card.setFootnote((i + 1) + _footer);
					mCards.add(card);
				}

				mCardScrollView = new CardScrollView(
						IncidentSearchActivity.this);
				IncidentTypeCardScrollAdapter adapter = new IncidentTypeCardScrollAdapter();
				mCardScrollView.setAdapter(adapter);
				mCardScrollView.setOnItemClickListener(mClickAdapter);
				mCardScrollView.activate();
				setContentView(mCardScrollView);
				mLocationManager.removeUpdates(mLocationListener);
			} else {
				Toast.makeText(IncidentSearchActivity.this,
						"Failed to retrieve incidents", Toast.LENGTH_SHORT)
						.show();
			}
		}
	});
}

Here, we retrieve the ten nearest incidents with findInBackground() with the query whereNear(). We specify that we only want the ten closest with setLimit().

Additional information: location, picture and description

incident_map

On this activity the user can see where the incident is located and he can consult its description by swiping backward. The picture of the incident is also available by swiping forward when the map is displayed. As in our previous application, the map comes from Google static maps. Here, we didn’t use a card because it doesn’t seem to be in any way able to display only an image, apart in background. On cards you can only have text, a background image or a mosaic of pictures on the left side of the card.

Direction with Glass

To guide the user to the incident scene, we chose to give only a general direction and the distance as the crow flies. To provide this guiding we have a mark that moves depending on the Glass camera orientation regarding the incident.

direction front When the user is heading in the rigth direction the mark moves from one side of the screen to the other in accordance to the incident direction. direction left When the direction to the incident isn’t in front of the user but on his left. direction right When the direction to the incident isn’t in front of the user but on his right.

Caution: the camera is located on the movable arm used to tune the angle of the prism. This might introduce a small difference regarding the real angle.

Provided with only this indicator, it would be pretty hard for the user to direct himself solely with this information. Thus, we provide him with the possibility to check his position. By swiping backward, the user can access a map where his current location and the incident location are displayed. As for all of our maps, this one is also a static one and will not be refreshed as the user is moving. It will only refresh when the user will check again his position.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/orient"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.orientingtest.DirectionActivity$PlaceholderFragment" >    
    <ImageView
        android:id="@+id/incident_on_left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:src="@drawable/mleft"
        android:visibility="gone" />

    <ImageView
        android:id="@+id/incident_on_right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:src="@drawable/mright"
        android:visibility="gone" />

    <ImageView
        android:id="@+id/incident_in_front"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerHorizontal="false"
        android:layout_centerVertical="true"
        android:src="@drawable/mfront" />
    
    <ImageView android:id="@+id/map_current_position"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone" />

    <TextView
        android:id="@+id/information"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="@dimen/card_margin"
        android:ellipsize="end"
        android:singleLine="true"
        android:text="@string/swipe_left_for_info"
        android:textAppearance="?android:attr/textAppearanceSmall" />
    
    <TextView
        android:id="@+id/distance"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="@dimen/card_margin"
        android:ellipsize="end"
        android:singleLine="true"
        android:text="@string/wait_result"
        android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>

At initialization, the default orientation is north. You need to move to refresh your real orientation. There can be some time before it properly refresh.

public void directionTo(double heading) {
	float bearing = MathUtils.getBearing(mUserLatitude, mUserLongitude,
			mIncidentLatitude, mIncidentLongitude);
	float differentialAngle = (float) (bearing - heading);

	if (differentialAngle < 0)
		differentialAngle += 360;

	// The view displays 90 degrees across its width so that one 90 degree head rotation is
	// equal to one full view cycle.
	float pixelsPerDegree = (mTipsContainer.getWidth() - 68) / 90.0f;

	double distancem = 1000 * MathUtils.getDistance(mUserLatitude,
			mUserLongitude, mIncidentLatitude, mIncidentLongitude);

	mTipsView.setText(mDistanceFormat.format(distancem) + " m");

	if ((differentialAngle >= 45 && differentialAngle <= 180)) {
		mLeftMark.setVisibility(View.GONE);
		mRightMark.setVisibility(View.VISIBLE);
		mMarkFront.setVisibility(View.GONE);
	} else if ((differentialAngle > 180 && differentialAngle <= 315)) {
		mLeftMark.setVisibility(View.VISIBLE);
		mRightMark.setVisibility(View.GONE);
		mMarkFront.setVisibility(View.GONE);
	} else if ((differentialAngle >= 0 && differentialAngle < 45)
			|| differentialAngle == 360) {
		mLeftMark.setVisibility(View.GONE);
		mRightMark.setVisibility(View.GONE);
		mMarkFront.setVisibility(View.VISIBLE);

		mMarkFront.setLayoutParams(positionL);
		RelativeLayout.LayoutParams pos = positionL;
		int margin = (int) (((mTipsContainer.getWidth() - 68) / 2) + (pixelsPerDegree * differentialAngle));

		pos.setMargins(margin, 0, 0, 0);
		mMarkFront.setLayoutParams(pos);
	} else if (differentialAngle > 315 && differentialAngle < 360) {
		mLeftMark.setVisibility(View.GONE);
		mRightMark.setVisibility(View.GONE);
		mMarkFront.setVisibility(View.VISIBLE);

		mMarkFront.setLayoutParams(positionL);
		RelativeLayout.LayoutParams pos = positionL;
		int margin = (int) ((pixelsPerDegree * (differentialAngle - 315)));

		pos.setMargins(margin, 0, 0, 0);
		mMarkFront.setLayoutParams(pos);
	}
}

If differentialAngle is between 0 and 180 degrees, the incident is on the left of the user. It is only true once we have normalize differentialAngle between 0 and 360 degrees.

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("google.navigation:q=48.649469,-2.02579"));
startActivity(intent);

Instead of our rough guiding, it also would have been possible to give a turn by turn guiding but we wanted to test how the Glass handles direction and orientation by ourselves. This intent came from people analyzing the log of the Glass embedded command “Get direction”, so it might change in future updates, making this possibly not reliable (see How can I launch Directions from GDK).

Once the user is within ten meters from the incident, he can use a tap interaction to continue with the flow.

Picture and report after the incident is treated

After the incident is treated, the user must take a picture of the solved incident and issue a final report.  The user takes the picture as in the previous applications. He can preview it and retry until he is satisfied. In the same way, we use the speech recognition to record the user final report after the picture has been taken. We use the same implementation as for our previous application. The report and the picture are then saved in Parse with the saveInBackground() function.

Conclusion

With this application we have been able to test out the orientation sensor, alongside with the location providing features of Glass. We have also seen that it is really simple to retrieve data from Parse even with just a location constraint. The complexity is nicely hidden behind a nice and friendly function.

This article concludes our work on location and MBAAS. We will continue to work on Glass and we will come back to you when we have more to share.

Share Button

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *