Adding an Edit Route for Access Codes in Open Event Frontend

This blog post will illustrate how to add edit route to access code to allow organizers editing an access code. Editing access codes deals with the following API on Open Event Server.

PATCH          /v1/access-codes/{id}

First of all we need to add a route to allow editing of access codes. Our route will be /events/{event_id}/tickets/access-codes/edit/{access_code_id}. To generate a new route we need to run the command

ember g route events/view/tickets/access-codes/edit

This will generate new routes files and templates. Also this will add this route in router.js. In router.js we need to specify what we are passing as a parameter to route. For this we specify access_code_id to let ember know that this parameter will be passed with the URL. This is done so as to know which access code to update. Our route should look something like this /events/{event_id}/tickets/access-codes/edit/{access_code_id}. Final router.js file for access-codes part is given below:

//  app/router.js
 
this.route('access-codes', function() {
   this.route('list', { path: '/:access_status' });
   this.route('create');
   this.route('edit', { path: '/edit/:access_code_id' });
});

 

Next we need to pass model (data) to our edit route template. In our model we will be looking for a particular access code with an id and event tickets. After we get our event tickets, we then look for the event tickets which are already present in that access code. This is done so as to check the tickets in the template which are already present in that access code. So for this in afterModel hook we loop over all event tickets and whichever ticket is included in the access code tickets array we mark its isChecked property as true. This helps us to mark those tickets as checked in multiple select checkboxes of template.

// app/routes/events/view/tickets/access-codes/edit.js

model(params) {
   return RSVP.hash({
     accessCode : this.store.findRecord('access-code', params.access_code_id, {}),
     tickets    : this.modelFor('events.view').query('tickets', {})
   });
 },

 async afterModel(model) {
   let tickets = await model.accessCode.tickets;
   let allTickets = model.tickets;
   allTickets.forEach(ticket => {
     if (tickets.includes(ticket)) {
       ticket.set('isChecked', true);
     } else {
       ticket.set('isChecked', false);
     }
   });

 

The information about multiple select checkboxes in frontend has been discussed in this blog post. Now after we are done setting our edit route we need to redirect user to edit page when he/she clicks on the edit button in access code list. For this we define the necessary actions in template which will be triggered when user clicks on the icon. Code for the edit icon in access code list is given below.

// templates/components/ui-table/cell/events/view/tickets/access-codes/cell-actions.hbs

class="ui vertical compact basic buttons"> {{#ui-popup content=(t 'Edit') click=(action editAccessCode record.id) class='ui icon button' position='left center'}} class="edit icon"> {{/ui-popup}}

 

The editAccessCode action looks something like this.

// controller/events/view/tickets/access-codes/edit.js

editAccessCode(id) {
     this.transitionToRoute('events.view.tickets.access-codes.edit', id);
}

 

After clicking on the edit icon user is redirected to edit route where he/she can edit access code choosen. We use the same component that we chose for creating access code. To know more about create access code template component refer to this blog. Finally when saving the edited access code we call save action. This action is defined in the controllers. The action looks something like this.

// controllers/events/view/tickets/access-codes/edit.js

export default Controller.extend({
 actions: {
   save(accessCode) {
     accessCode.save()
       .then(() => {
         this.get('notify').success(this.get('l10n').t('Access code has been successfully updated.'));
         this.transitionToRoute('events.view.tickets.access-codes');
       })
       .catch(() => {
         this.get('notify').error(this.get('l10n').t('An unexpected error has occured. Discount code cannot be created.'));
       });
   }
 }
});

 

After the access code is saved. We redirect user back to access code list.

Resources

Continue Reading

Modifying Tickets API in Open Event Server to Return Hidden Tickets Only for Organizers and Admins

This blog article will illustrate how we can modify the permissions settings for an API to enable different kind of responses to users with different level of permissions. In this article we will discuss these changes with respect to Tickets API.

Initially we had a query where we were returning only those tickets who were set to be visible by the admin. Query for this was:

class TicketList(ResourceList):
   """
   List Tickets based on different params
   """
   def before_get(self, args, view_kwargs):
       """
       before get method to get the resource id for assigning schema
       :param args:
       :param view_kwargs:
       :return:
       """
       if view_kwargs.get('ticket_tag_id') or view_kwargs.get('access_code_id') or         view_kwargs.get('order_identifier'):
           self.schema = TicketSchemaPublic

   def query(self, view_kwargs):
       """
       query method for resource list
       :param view_kwargs:
       :return:
       """


       query_ = self.session.query(Ticket).filter_by(is_hidden=False)

 

Problem with this query was that this returned same response irrespective of who is logged in. Hence even the organizers were not able to modify hidden tickets because they were not returned by server.

Solution to this problem was to provide hidden tickets only to those who are organizer or are admin/super admins. For this we used the JWT token that was being sent from frontend in request headers for each authenticated request that was being made from frontend.

We modified the code to something like this:

class TicketList(ResourceList):
   """
   List Tickets based on different params
   """
   def before_get(self, args, view_kwargs):
       """
       before get method to get the resource id for assigning schema
       :param args:
       :param view_kwargs:
       :return:
       """
       if view_kwargs.get('ticket_tag_id') or view_kwargs.get('access_code_id') or view_kwargs.get('order_identifier'):
           self.schema = TicketSchemaPublic

   def query(self, view_kwargs):
       """
       query method for resource list
       :param view_kwargs:
       :return:
       """

       if 'Authorization' in request.headers:
           _jwt_required(current_app.config['JWT_DEFAULT_REALM'])
           if current_user.is_super_admin or current_user.is_admin:
               query_ = self.session.query(Ticket)
           elif view_kwargs.get('event_id') and has_access('is_organizer', event_id=view_kwargs['event_id']):
               query_ = self.session.query(Ticket)
           else:
               query_ = self.session.query(Ticket).filter_by(is_hidden=False)
       else:
           query_ = self.session.query(Ticket).filter_by(is_hidden=False)

 

Here we added some conditions which were used to check permission level of logged in user. After picking JWT token from request headers we check if the user was admin or super_admin, then we return all the tickets without any condition. Then we also check if the logged in user was organizer of event then also we send all the tickets without any conditions.

However if request comes from unauthenticated users (without valid token) or users with normal privileges, then we returned tickets whose isHidden property was set to False. The functions such as is_organizer and is_super_admin acted as helpers as they were imported from other helper files where they were defined.

Resources

Continue Reading

Adding support for rich text in Eventyay Organizer App

The Open Event Organizer App provides the users with its one of the core features of the ability to create or update an event. To add this feature, we will use HTML and android’s WebView in order to aid us integrate support for rich text in the app.

The first step is adding the Wasabeef RichText library to your project. Open your build.gradle file and add the support library to the dependency section.

Adding the dependency in build.gradle(app-level) in the project:

dependencies {
   //Other dependencies
   //Rich text editor
   implementation “jp.wasabeef:richeditor-android:1.2.2”
}

What we need is an activity accessible throughout the project for any element that needs to input rich text, which means that this activity goes in the utils package.

Let’s start with building this activity:

We need to first set a hint text or a placeholder text for the editor in case there’s no saved text for the concerned field.

public class RichEditorActivity extends AppCompatActivity {
   

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       

       binding.editor.setPlaceholder(getString(R.string.enter_text));
       Intent intent = getIntent();
       if (intent != null) {
         description = intent.getStringExtra(TAG_RICH_TEXT);
         if (!TextUtils.isEmpty(description) && !description.equals(getString(R.string.describe_event))) {
           binding.editor.setHtml(description);
         }
       }
}

Now let’s see how we add the formatted text to our WebView. Currently we are supporting the options:

  • Undo
  • Redo
  • Bold
  • Italic
  • StrikeThrough
  • Bulleted list
  • Numbered list

as follows:

binding.actionUndo.setOnClickListener(v > binding.editor.undo());
binding.actionRedo.setOnClickListener(v > binding.editor.redo());
binding.actionBold.setOnClickListener(v > binding.editor.setBold());
binding.actionItalic.setOnClickListener(v > binding.editor.setItalic());
binding.actionStrikethrough.setOnClickListener(v > binding.editor.setStrikeThrough());
binding.actionInsertBullets.setOnClickListener(v > binding.editor.setBullets());
binding.actionInsertNumbers.setOnClickListener(v > binding.editor.setNumbers());

To add support for adding links, we need to first setup a click listener and show a dialog on click:

binding.actionInsertLink.setOnClickListener(v -> {
   if (linkDialog == null) {
       createLinkDialog();
   }
   linkDialog.show();
});

The above listener is using a linkDialog, which is initialized in the method below, as follows:

We are first dynamically creating a LinearLayout, and then 2 EditTextViews, and then adding those views to the LinearLayout. Finally we build the AlertDialog as usual and set the Dialog’s view to the LinearLayout we created.

private void createLinkDialog() {
   LinearLayout layout = new LinearLayout(this);
   layout.setOrientation(LinearLayout.VERTICAL);
   final EditText text = new EditText(this);
   text.setHint(getString(R.string.text));
   layout.addView(text);
   final EditText link = new EditText(this);
   link.setHint(getString(R.string.insert_url));
   layout.addView(link);

   linkDialog = new AlertDialog.Builder(this)
      .setPositiveButton(getString(R.string.create), (dialog, which) -> {
           binding.editor.insertLink(link.getText().toString(), text.getText().toString());
      })
      .setNegativeButton(getString(R.string.cancel), (dialog, which) -> {
          dialog.dismiss();
      })
      .setView(layout)
      .create();
}

Here’s how the result looks like:

Resources

Continue Reading

Adding chrome custom tabs support for native browsing in Eventyay Organizer App

In Eventyay Organizer App when a user taps a URL, we face a choice: either launch a browser, or build our own in-app browser using WebViews.

Both options present challenges — launching the browser is a heavy context switch that isn’t customizable, while WebViews don’t share state with the browser and add maintenance overhead.

Chrome Custom Tabs give apps more control over their web experience, and make transitions between native and web content more seamless without having to resort to a WebView.

The first step for a Custom Tabs integration is adding the Custom Tabs Support Library to your project. Open your build.gradle file and add the support library to the dependency section. One must remember that Chrome Custom Tabs is not an Open Source library, so we are here including the  playStoreImplementation  as opposed to implementation, so that our FDroid build doesn’t fail.

Adding the dependency in build.gradle(app-level) in the project:

dependencies {
   //Other dependencies

   // Chrome Custom Tabs
   playStoreImplementation ‘com.android.support:customtabs:${versions.chromeCustomTabs}
}

 

And add this to  versions.gradle  file:

versions.chromeCustomTabs=‘27.1.0’

The UI Customizations are done by using the  CustomTabsIntent  and the CustomTabsIntent.Builder  classes; the performance improvements are achieved by using the  CustomTabsClient  to connect to the Custom Tabs service, warm-up Chrome and let it know which urls will be opened.

Since we need this to feature to be available for each place in the app that redirects to an external link, we must create it in the utility class of the project. Let’s name this class as BrowserUtils and do it as follows:

public final class BrowserUtils {
    private BrowserUtils() {
   }
    public static void launchUrl(Context context, String url) {
       CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
       CustomTabsIntent customTabsIntent = builder.build();
       customTabsIntent.launchUrl(context, Uri.parse(url));
   }
}

Now all we need to do is replace the old method to create an intent:

findPreference(getString(R.string.privacy_policy_key)).setOnPreferenceClickListener(preference -> {
           Intent intent = new Intent(Intent.ACTION_VIEW);
           intent.setData(Uri.parse(PRIVACY_POLICY_URL));
           startActivity(intent);
           return true;
       });

with this call to the utility method:

findPreference(getString(R.string.privacy_policy_key)).setOnPreferenceClickListener(preference -> {
           BrowserUtils.launchUrl(getContext(), PRIVACY_POLICY_URL);
           return true;
       });

We can also add animation or customize the color of the toolbar, add action buttons or add animations like this:

builder.setToolbarColor(colorInt);
builder.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left);

builder.setExitAnimations(this, R.anim.slide_in_left, R.anim.slide_out_right);

Here’s the result:

Resources

Continue Reading

Using Transitions API with Email Validation in Open Event Organizer Android App

Transitions in Material Design apps provide visual continuity. As the user navigates the app, views in the app change state. Motion and transformation reinforce the idea that interfaces are tangible, connecting common elements from one view to the next.

In the Open Event Organizer Android App, we need a transition from the Get Started screen such that, if the user email is registered, we transition to the Login Screen otherwise, we Transition to the Sign Up Screen. And the transition should be such that the email field is continuously visible. One more condition is that, if the email field is even varied by one character, we need to transition back to the Get Started Screen.

To implement this, we need to use Shared Elements from the Android Transitions API.

What are shared elements?

A shared element transition determines how views that are present in two fragments transition between them. For example, an image that is displayed on an ImageView on both Fragment A and Fragment B transitions from A to B when B becomes visible.

Fade transition is used to fade a view and ChangeBounds transition is used to move a view without changing its size.

To make a transition, we use the setupTransitionAnimations() function like this:

(Note that we have created our own Fade and ChangeBounds transitions and not using XML)

   public void setupTransitionAnimation(Fragment fragment) {
       Fade exitFade = new Fade();
       exitFade.setDuration(100);
       this.setExitTransition(exitFade);
       fragment.setReturnTransition(exitFade);

       ChangeBounds changeBoundsTransition = new ChangeBounds();
       fragment.setSharedElementEnterTransition(changeBoundsTransition);

       Fade enterFade = new Fade();
       enterFade.setStartDelay(300);
       enterFade.setDuration(300);
       fragment.setEnterTransition(enterFade);
       this.setReenterTransition(enterFade);
   }

Now, in order to detect, if the email field is touched and even changed by one character, we use the TextWatcher like this:

       binding.emailLayout.getEditText().addTextChangedListener(new TextWatcher() {

           @Override
           public void beforeTextChanged(CharSequence s, int start, int count, int after) {
               //do nothing
           }

           @Override
           public void onTextChanged(CharSequence s, int start, int before, int count) {
               if (start != 0) {
                   sharedViewModel.setEmail(s.toString());
                   getFragmentManager().popBackStack();
               }
           }

           @Override
           public void afterTextChanged(Editable s) {
               //do nothing

           }

       }     

So basically, the moment the text is changed, we add the changed email to the SharedViewModel so that it can be used in the other fragments, and then to start the transition, we pop the back stack using getFragmentManager().popBackStack();

This is what the result looks like:

Resources

  • Google – Android developer blog post:

https://android-developers.googleblog.com/2018/02/continuous-shared-element-transitions.html

Continue Reading

Creating step designs from KiCAD for PSLab

PSLab hardware device is developed using KiCAD. It is an open source PCB designing tool which we can use for free and it has almost all the features needed to build a professional PCB. But it lacks one thing. It cannot generate and export 3D models. In fact there is a 3D viewer in KiCAD but there is no way to export it. When manufacturing PSLab devices, it was required by the manufacturers so that they can have a clear understanding how the components are placed. This step is necessary for them to design the production line.

Before we get started, there are few prerequisites to help us get this done. They are as follows;

  1. FreeCAD: Open source 3D modeling software
  2. KiCAD step up tools: External library to import KiCAD PCB layouts to FreeCAD

You may need to follow installation instructions to install FreeCAD from the link given. Once we are all set, extract the KiCAD Stepup tools. There we can find a set of python libraries and some bash scripts. We can either use the scripts or type commands ourselves. I found scripts having some issues configuring paths.

To fire up FreeCAD with KiCAD stepup tools enabled, type the following command on your console;

$ freecad kicad-StepUp-tools.FCMacro

Make sure the console is pointing to the directory where the FCMacro file is located. This will open up FreeCAD and if you opened it already and saw the opening screen of FreeCAD, you’d notice a whole new toolbar is added.

Here you can see many tools related to import and export step files and 3D models from outside libraries and folders. Each tool is specific;

  • Load-kicad-footprint:

This tool is useful to generate a step file for an individual PCB component, say a resistor into a step file.

  • Export-to-kicad:

There are instances where when we design a custom foot print, and KiCAD doesn’t have the 3D model. In such a case we can export such a model to KiCAD

  • Load-kicad:

This is the tool we are using to export PSLab PCB board to step format. Before we move on to this tool there is one last configuration we have to do. FreeCAD doesn’t know where we have put KiCAD 3D models. This library simply transforms the available 3D models in KiCAD into step files and build the final output combining all of them together as in the featured image of this blog post. To setup the 3D model path, in KiCAD, there is a path configuration option. Copy the path under “KISYS3DMOD”.

Figure 2: Path Configuration dialog box in KiCAD

And paste it into the ini file named “ksu-config.ini” which you can find in home folder.

Figure 3: Place to add 3D model path in ksu-config.ini file

Once that is done, click on the Load-KiCAD tool icon and browse to the repository where the PSLab hardware files are located at. Open the board file and FreeCAD will generate part by part and finally output the complete design. Now we can export the design in plenty of formats such as steps, stl another similar file format and many more.

Reference:

  1. https://www.freecadweb.org/wiki/Download
  2. http://kicad-pcb.org/external-tools/stepup/
Continue Reading

Use Travis to extract testing APKs for PSLab

Travis CI is a popular continuous integration tool built to test software and deployments done at GitHub repositories. They provide free plans to open source projects. PSLab Android is using Travis to ensure that all the pull requests made and the merges are build-bug frees. Travis can do this pretty well, but that is not all it can do. It’s a powerful tool and we can think of it as a virtual machine (with high specs) installed in a server for us to utilize efficiently.

There was a need in PSLab development that, if we want to test the functionalities of code at a branch. To fulfil this need, one has to download the branch to his local machine and use gradle to build a new apk. This is a tedious task, not to mention reviewers, even developers wouldn’t like to do this.

If the apk files were generated and in a branch, we can simply download them using a console command.

$ wget https://raw.github.com/fossasia/<repository>/<branch>/<file with extension>

 

With the help of Travis integration, we can create a simple script to generate these apks for us. This is done as follows;

Once the Travis build is complete for a triggering event such as a pull request, it will run it’s “after_success” block. We are going to execute a script at this point. Simply add the following code snippet.

after_success:
  - 'if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then bash <script-name>.sh; fi'

 

This will run the script you have mentioned using bash. Here we will have the following code snippets in the specified bash script.

First of all we have to define the branch we want to build. This can be done using a variable assignment.

export DEVELOPMENT_BRANCH=${DEVELOPMENT_BRANCH:-development}

 

Once the build is complete, there will be new folders in the virtual machine. One of them is the app folder. Inside this folder contains the build folder where all the apk files are generated. So the next step is to copy these apk files to a place of our preference. I am using a folder named apk to make it much sense.

cd apk
\cp -r ../app/build/outputs/apk/*/**.apk .
\cp -r ../app/build/outputs/apk/debug/output.json debug-output.json
\cp -r ../app/build/outputs/apk/release/output.json release-output.json
\cp -r ../README.md .

 

Usually, the build folder has following apk files

  • app-debug.apk
  • app-release-unsigned.apk
  • app-release.apk

Release apks are usually signed with a key and it would cause issues while installation. So we have to filter out the debug apk that we usually use for debugging and get it as the output apk. This involves simple file handling operations in Linux and a bit of git.

First of all, rename the apk file so that it will be different from other files.

# Rename apks with dev prefixes
mv app-debug.apk app-dev-debug.apk

 

Then add and commit them to a specific branch where we want the output from.

git add -A
git commit -am "Travis build pushed [development]"
git push origin apk --force --quiet> /dev/null

 

Once it is all done, you will have a branch created and updated with the apk files you have defined.

 

Figure 1: UI of pslab-android apk branch

Reference:

  1. https://travis-ci.org/
Continue Reading

I2C communication in PSLab Android

PSLab device is a compact electronic device with a variety of features. One of them is the ability to integrate sensors and get readings from them. One might think that why they should use PSLab device to get sensor readings and they can use some other hardware like Arduino. In those devices, user has to create firmware and need to know how to interface them with correct sampling rates and settings. But with PSLab, they all come in the whole package. User just have to plug the device to his phone and the sensor to device and he’s ready.

The idea of this blog post is to show how this sophisticated process is actually happening. Before that, let me give you a basic introduction on how I2C communication protocol works. I2C protocol is superior to UART and SPI protocols as they are device limited or requires many wires. But with I2C, you can literally connect thousands of sensors with just 4 wires. These wires are labeled as follows;

  • VCC – Power line
  • GND – Ground line
  • SDA – Data line
  • SCL – Signal clock

It is required that the SDA and SCL lines need to be connected to VCC line using two pull up resistors. But that’s just hardware. Let’s move on to learn how I2C communicate.

Here there is this communicating concept called master and slave. To start communication, master issues a global signal like a broadcast to all the devices connected to SCL and SDA lines. This signal contains the address of the slave, master needs to address and get data from. If the slave received this call to him, he will pull down the SDA line to signal the master that he heard him and ready to communicate with him. Here communication means reading or writing data. Then the communication happens and the link between master and slave breaks giving opportunity to other masters and slaves.

One might think this is a slow process. But these signals are transmitted at high frequencies. In PSLab it is at 100 kHz and that is one millisecond.

PSLab library has a special class to handle I2C communication. That is

public class I2C {/**/}

 

Once this class is initiated, one has to call the start function to start communication. This method requires the address we wish to communicate with and the mode of operation stating if it is a read or write operation

public int start(int address, int rw) throws IOException {
   packetHandler.sendByte(commandsProto.I2C_HEADER);
   packetHandler.sendByte(commandsProto.I2C_START);
   packetHandler.sendByte((address << 1) | rw & 0xff);
   return (packetHandler.getAcknowledgement() >> 4);
}

 

Once the address is sent out, protocol requires us to stop and wait for acknowledgement.

public void wait() throws IOException {
   packetHandler.sendByte(commandsProto.I2C_HEADER);
   packetHandler.sendByte(commandsProto.I2C_WAIT);
   packetHandler.getAcknowledgement();
}

 

If there are no congestion in the lines such as reading from multiple devices, the acknowledgement will be instantaneous. Once that is complete, we can start communication either byte-wise or bulk-wise

public int send(int data) throws IOException {
   packetHandler.sendByte(commandsProto.I2C_HEADER);
   packetHandler.sendByte(commandsProto.I2C_SEND);
   packetHandler.sendByte(data);
   return (packetHandler.getAcknowledgement() >> 4);
}

 

As an example, reading sensor values at a given interval can be done using the following method call.

public ArrayList<Byte> read(int length) throws IOException {
   ArrayList<Byte> data = new ArrayList<>();
   for (int i = 0; i < length - 1; i++) {
       packetHandler.sendByte(commandsProto.I2C_HEADER);
       packetHandler.sendByte(commandsProto.I2C_READ_MORE);
       data.add(packetHandler.getByte());
       packetHandler.getAcknowledgement();
   }
   packetHandler.sendByte(commandsProto.I2C_HEADER);
   packetHandler.sendByte(commandsProto.I2C_READ_END);
   data.add(packetHandler.getByte());
   packetHandler.getAcknowledgement();
   return data;
}

 

Once we get the data bundle, either we can save them or display in a graph whatever the way it’s convenient.

Reference:

  1. https://en.wikipedia.org/wiki/I%C2%B2C
Continue Reading

Adding Data Point Markers to OSM

PSLab Android app supports multiple sensors external and internal. Users can view sensor readings and record them into a csv file for later use. They can also have their location embedded into the data set they are recording. Saving the location related to each sensor reading is important. Say in a school experiment teacher can ask the student to measure dust particle concentration in city and inside his house. If the data set didn’t have any reference to the location where it was recorded, it is just incomplete. To facilitate this feature, we have enabled location in data recordings.

Enabling location is just not enough. User should be able to view the locations. Probably on a map itself would be a good idea. With the use of Open Street Maps, we can add markers to specific locations. These markers can be used as a point to show on map where a specific data set had been recorded by the user. This can be implemented further to add additional features such as standardized labels to view which data set is at which location etc.

Figure 1: Markers on Map

Unlike Google Maps API, in Open Street Maps there is no direct implementation to add a marker. But it is not a hard implementation either. We can simply create a class extending map overlays and use that as a base to add markers.

We can start by creating a new class that extends ItemizedOverlay<OverlayItem> class as follows. In this class, it would be wise to have an array list full of markers we are using in the map to modularize the whole markers related tasks into this one class rather than implementing in every place where map is used.

public class MapOverlay extends ItemizedOverlay<OverlayItem> {

    private ArrayList<OverlayItem> overlayItemList = new   ArrayList<OverlayItem>();

}

 

Once the class is initiated, have the following methods implemented. The following method will add markers to the array list we have created at the beginning.

public void addItem(GeoPoint p, String title, String snippet){
  OverlayItem newItem = new OverlayItem(title, snippet, p);
  overlayItemList.add(newItem);
  populate(); 
}

 

This method will be used to handle focusing events related to map markers.

@Override
public boolean onSnapToItem(int arg0, int arg1, Point arg2, IMapView arg3){
  return false;
}

 

Following method is used by the map itself to generate markers from the marker list.

@Override
protected OverlayItem createItem(int arg0) {
  return overlayItemList.get(arg0);
}

 

This method is an overriden method we will have to include in the class body.

@Override
public int size() {
  return overlayItemList.size();
}

 

Once the overlay class is completed, we can move on to actual implementation of a map on Openstreetmap view.

From the main activity where the map is viewed, initiate the marker overlay class and pass the drawable that needs to be shown as the marker to the class constructor as follows:

MapOverlay mapoverlay = null;
Drawable marker=getResources().getDrawable(android.R.drawable.map_hand);
int markerWidth = marker.getIntrinsicWidth();
int markerHeight = marker.getIntrinsicHeight();
marker.setBounds(0, markerHeight, markerWidth, 0);

ResourceProxy resourceProxy = new DefaultResourceProxyImpl(getApplicationContext());
mapoverlay = new MapOverlay(marker, resourceProxy);
mapView.getOverlays().add(mapoverlay);
         
GeoPoint point = new GeoPoint(55.75, 37.616667);
mapoverlay.addItem(point, "Russia", "Russia");

 

We can add as many GeoPoints as we want to markers list and they all will be displayed on the map like this;

Figure 2: Final Output of Markers

Reference:

http://android-coding.blogspot.com/2012/06/example-of-implementing-openstreetmap.html

Continue Reading

Adding Open Street Maps to PSLab Android

PSLab Android app is an open source app that uses fully open libraries and tools so that the community can use all it’s features without any compromises related to pricing or feature constraints. This will brings us to the topic how to implement a map feature in PSLab Android app without using proprietary tools and libraries. This is really important as now the app is available on Fdroid and they don’t allow apps to have proprietary tools in them if they are published there. In other words, it simply says we cannot use Google Maps APIs no matter how powerful they are in usage.

There is a workaround and that is using Open Street Maps (OSM). OSM is an open source project which is supported by a number of developers all around the globe to develop an open source alternative to Google Maps. It supports plenty of features we need in PSLab Android app as well. Starting from displaying a high resolution map along with caching the places user has viewed, we can add markers to show data points and locations in sensor data logging implementations.

All these features can be made available once we add the following dependencies in gradle build file. Make sure to use the latest version as there will be improvements and bug fixes in each newer version

implementation "org.osmdroid:osmdroid-android:$rootProject.osmVersion"
implementation "org.osmdroid:osmdroid-mapsforge:$rootProject.mapsforgeVersion"
implementation "org.osmdroid:osmdroid-geopackage:$rootProject.geoPackageVersion"

 

OSM will be functional only after the following permission states were granted.

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"  />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

 

In a view xml file, add the layout org.osmdroid.views.MapView to initiate the map view. There is a known issue in OSM library. That is during the initiation, if the zoom factor is set to a small value, there will be multiple instances of maps as shown in Fig 1. The solution is to have a higher zoom value when the map is loaded.

Figure 1: Multiple map tiles in OSM

Once we initialize the map view inside an activity, a zoom level can be easily set using a map controller as follows;

map = findViewById(R.id.osmmap);
map.setTileSource(TileSourceFactory.MAPNIK);
map.setBuiltInZoomControls(true);
map.setMultiTouchControls(true);

IMapController mapController = map.getController();
mapController.setZoom((double) 9);
GeoPoint startPoint = new GeoPoint(0.00, 0.00);
mapController.setCenter(startPoint);

 

After successfully implementing the map view, we can develop the business logic to add markers and descriptions to improve the usability of OS Maps. They will be available in the upcoming blog posts.

Reference:

  1. https://github.com/osmdroid/osmdroid/wiki/How-to-add-the-osmdroid-library-via-Gradle
  2. https://www.openstreetmap.org/
Continue Reading
Close Menu
%d bloggers like this: