Python / JS Full Stack Developer for Open Event Project

STATUS: ACTIVE

As a Full Stack Engineer at FOSSASIA, you’ll work together with a global community to develop the Open Event project and to maintain our web service at eventyay.com. Our services are entirely Free/Open Source. We use Flask as a backend and Ember.js (transitioning to React) as a frontend technology. The system is deployed on a Hetzner cloud server. You’ll own what you build, making technical decisions and applying our best practices to every piece of code. The team uses scrum emails for the daily standup and Gitter for continuous chat communication.

Candidates who have started contributing code to one of our projects and to the Open Event backend and frontend specifically will have an advantage.

Number of Positions: Several

[Apply Here]

About the team

  • We are a team working with a community of FOSS developers
  • We work remotely in different timezones
  • Our system is built using Ember (frontend) and we need someone who can help us to transition to ReactJS
  • We use Flask/Python for the backend
  • We have an informal and collaborative environment
  • We embrace Continuous Integration

Responsibilities

  • Assume leadership and responsibility for the technical tasks and deliverables
  • Become project owner of Open Event development and provide daily code commits
  • Deploy project on the server using Docker
  • Write unit tests for all portions of our applications
  • Automate the software deployment process
  • Support community developers and review PRs
  • Work according to FOSSASIA Best Practices
  • Provide daily scrums, code contributions and communicate on chat
  • Participate and lead weekly developer meeting
  • Ensure related apps (Android/iOS) work with APIs and review PRs
  • Take on responsibilities in other FOSSASIA projects as needed

Requirements

  • Experience working in a remote setting, or with a remote team and across multiple time zones
  • Professional experience using HTML, CSS, and Javascript
  • Experience with Flask/Python
  • Experience with Ember.js and ReactJS (or the desire to learn it)
  • Experience with Continuous Integration and Docker
  • Understanding of best practices for web development and software design
  • Not afraid of going deep on backend code to understand features
  • Enjoy writing tested and modular code
  • Working time overlaps with European and Singapore time
  • You have good spoken/written English
  • Self-motivated and independent

Code

Please check out the project on GitHub before applying.

Open Event Server: https://github.com/fossasia/open-event-server

Open Event Frontend: https://github.com/fossasia/open-event-frontend

Salary

Depending on position or freelance agreement.

Benefits

  • Visit Singapore and participate in annual FOSSASIA Summit
  • Participate in local Open Source meetups and conferences
  • Work with a community of enthusiastic software developers

Location

Worldwide

Contact

Please apply through our online form.

[Submit Application Here]

Links

Continue ReadingPython / JS Full Stack Developer for Open Event Project

FOSSASIA Virtual Summit 2021 Schedule and Highlights

We are very happy to share with you the schedule of the FOSSASIA Virtual Summit 2021!

  • 190+ speakers from 42 countries and 6 continents are joining us.
  • We are covering a diverse range of topics at the event such as Quantum computing, machine learning, open robotics, CAD and hardware development, search, databases, Covid-19 and open health, Blockchain, digital privacy, microservices, cloud, Kubernetes, kernel development, licensing and many more.
  • More than 180 sessions, talks, panels and workshops are taking place online. 
  • Plus, trainers conduct 28 hours of hands-on onsite sessions at the Lifelong Learning Institute in Singapore.

Get your free ticket here!

The summit will spread out over the week of March 13-21 and will run on our open source virtual event platform eventyay.

In the virtual exhibition you can meet our sponsors and partners like Microsoft, Oracle, MySQL, OpenTAP Keysight, Linux Professional Institute, Elasticsearch, OpnTec, Cloud Native Computing Foundation, FreeBSD, UI-licious, Lifelong Learning Institute Singapore and the UNESCO.

And you can get together with developers and contributors from Free and Open Source projects, makerspaces, developer clubs and university IT groups. At summit hubs across Asia we are connecting to participants online and locally.

FOSSASIA Summit Highlights

  • 5 keynotes from Hong Phuc Dang (Founder, FOSSASIA) and Mario Behling (Co-Founder FOSSASIA, CEO OpnTec), Frank Karlitschek (CEO, Nextcloud), Greg Kroah-Hartman (Linux Kernel Maintainer), Brian Behlendorf (Executive Director, Hyperledger) and Bunnie Huang (CEO, Precursor)
  • 150+ sessions covering tech areas of Quantum computing, PyTorch, Scikit-learn, pocket science, open robotics, FreeCAD, hardware development, search, databases, Debian packaging, Covid-19, Hyperledger, Open Source event solutions, digital privacy, microservices, cloud, Kubernetes, and Linux Kernel development.
  • 30+ hands-on workshops focusing on getting started with Python, creating MySQL shell utilities and custom plugins, setting up PostgreSQL databases, creating test automation, machine learning, and science measurements.
  • Panel discussions about Open Source licensing vs. available source licenses, firmware development, digital sovereignty, Blockchain and diversity in tech.
  • Virtual exhibition with video rooms and exhibitor tours where participants can connect with companies and active tech communities from across Asia.

FOSSASIA Summit Exhibition and Hubs

Apart from company partners you can meet communities, students and developers from Asia and around the world in our virtual exhibition. Groups include OpenFIESTA Tsinghua University, Shenzhen DIY Community, Open Source Hong Kong, OpenStack Indonesia, Mozilla Philippines, Ubuntu Korea, FOSS Myanmar, KDE, BuildingBloCS Singapore, SUTD, Python Software Foundation, LibreOffice, KiwiTCMS, Nextcloud Sourcefabric, LambdaChip, and more.

Hubs bring attendees together locally or online. Participating hubs include the International Organisation of Software Developers, Open Source Club at Saintgits Engineering College Kerala, Biohacking Space Peshawar Pakistan, Society for Data Science at BIT Mesra India, Mar Athanasius College of Engineering India, Developer Student Community in BHILAI Institute of Technology Durg and Team Aveon Racing of BIT Mesra India.

FOSSASIA Summit Daily Focus Topics

Each day has different focus topics and tracks.

Sat, 13 Mar: Robotics, Open Hardware, Pocket Science Lab, Python/Web development, Digital Sovereignty

Sun, 14 Mar: Open Hardware, open science, lightning talks, Python/Web development

Mon, 15 Mar: Blockchain, hardware, design, learn how to solder

Tue, 16 Mar: DevOps, cloud, containers, Kernel & Platform, Quantum computers, Kubernetes

Wed, 17 Mar: Databases, MySQL, PostgreSQL, Monitoring

Thu, 18 Mar: Security, Privacy,  Digital Sovereignty, IoT, cloud, Blockchain, Open Source voice assistants

Fri, 19 Mar: Artificial Intelligence, PyTorch, Visdom, search, scikit, NLP

Sat, 20 Mar: DevOps, Python bot programming, Container Regisitries, OSS Licenses vs. available source, FOSS Community in Asia, OpenStreetMap, Python/Web development

Sun, 21 Mar: Lightning talks, Open Health, Covid-19 apps around the world, Fdroid, platform, smart devices

Continue ReadingFOSSASIA Virtual Summit 2021 Schedule and Highlights

Adding an option to hide map in browse events.

Open Event provides filtering while browsing events. These filters are present in a sidebar which also consists of a map. In this blog, I will describe how I implemented the feature to toggle the visibility of the map present in sidebar for mobile devices.

About the issue

This issue was part of improvements decided for the browse events section. Earlier the map in sidebar was shown irrespective of the user’s device. It is essentially not required to always show the map in mobile device and is a better choice to provide user with an option to view or hide the map.

Sidebar as viewed from an android device before the fix was merged.

The Solution

A button is introduced in the mobile view which controls if the map should be visible or hidden. In the sidebar component (app/components/explore/side-bar.js), a variable “isMapVisible” which decides if the map should be visible (if it is true) or not (if it is false) at a particular instant. A new action “toggleMap” is written which changes the value of “isMapVisible” whenever the button is clicked.

isMapVisible = true;
@action
  toggleMap() {
    this.toggleProperty(‘isMapVisible’);
  }

In the handlebar file (app/templates/components/explore/side-bar.hbs), the map and associated text is changed as per the truthy or falsy value of the “isMapVisible” in the component.

<div class=”map item {{if (not isMapVisible) ‘mobile hidden’}}”>

{{#if device.isMobile}}
  <div class=”ui bottom attached button” role=”button” {{action ‘toggleMap’}}> {{if (not isMapVisible) ‘Show’ ‘Hide’}} Map </div>
{{/if}}

After making the changes, the sidebar looks as follows on the mobile devices

The above images are from an Android device.

Resources:

Issue: https://github.com/fossasia/open-event-frontend/issues/3122
Pull Request: https://github.com/fossasia/open-event-frontend/pull/3444

Ember Docs:https://guides.emberjs.com/v2.14.0/tutorial/simple-component/Toggle Component Tutorial: https://www.learnhowtoprogram.com/ember-js/ember-js/components-hide-show-image

Continue ReadingAdding an option to hide map in browse events.

Addition of new filters for event search

Open Event has an event search provided but it lacked two of the userful filters which will make searching for an event for a user easier than the current ecosystem. In this blog post, I describe how I implemented filtering of events on the basis of CFP status and Ticket Type.

About the issue

Addition of these two filters were subparts of improving the browse events page better. The browse events page currently functions in an optimal way but these improvements make it even better.

How the filters are added

For adding these two filters, it was required that the request sent to server to filter the event must contain ticket type and cfp so that the results from the server can be received. To achieve this, the frontend code needed the following changes:

  1. Passing of request variable into sidebar component present in app/templates/explore.hbs and addition of a clear filter for the same in the same file.e startDate=start_date endDate=end_date location=location ticket_type=ticket_type}
{{explore/side-bar model=model category=category sub_category=sub_category event_type=event_type startDate=start_date endDate=end_date location=location ticket_type=ticket_type}}
{{#if filters.ticket_type}}
  <div class=”ui mini label”>
    {{ticket_type}}
    <a role=”button” {{action ‘clearFilter’ ‘ticket_type’}}>
      <i class=”icon close”></i>
    </a>
  </div>
{{/if}}
  1. Adding UI for both the filters in app/templates/components/explore/side-bar.hbs. Accordion UI is used to achieve this.
  <div class=”item”>
    {{#ui-accordion}}
      <span class=”title”>
        <i class=”dropdown icon”></i>
        {{t ‘Ticket Type’ }}
      </span>
      <div class=”content menu”>
        <a href=”#”
          class=”link item {{if (eq ticket_type ‘free’) ‘active’}}”
          {{action ‘selectTicketType’ ‘free’}}>
          {{t ‘Free’}}
        </a>
        <a href=”#”
          class=”link item {{if (eq ticket_type ‘paid’) ‘active’}}”
          {{action ‘selectTicketType’ ‘paid’}}>
          {{t ‘Paid’}}
        </a>
      </div>
    {{/ui-accordion}}
  </div>
  1. Editing the routes (app/routes/explore.js) to make sure if the request has new filter parameter then it should be sent to server.
if (params.ticket_type) {
      filterOptions.push({
        name : ‘tickets’,
        op   : ‘any’,
        val  : {
          name : ‘type’,
          op   : ‘eq’,
          val  : params.ticket_type
        }
      });
    }
  1. Addition of new request parameter in the controller (app/controllers/explore.js)
queryParams  : [‘category’, ‘sub_category’, ‘event_type’, ‘start_date’, ‘end_date’, ‘location’, ‘ticket_type’],

ticket_type  : null,

if (filterType === ‘ticket_type’) {  this.set(‘ticket_type’, null);
}
  1. Editing the sidebar component to set the value of request parameter when the user interacts with the filter.
hideClearFilters: computed(‘category’, ‘sub_category’, ‘event_type’, ‘startDate’, ‘endDate’, ‘location’, ‘ticket_type’, function() {
    return !(this.category || this.sub_category || this.event_type || this.startDate || this.endDate || this.location || this.ticket_type !== null);
}),

selectTicketType(ticketType) {
  this.set(‘ticket_type’, ticketType === this.ticket_type ? null : ticketType);
},

this.set(‘ticket_type’, null);

Sidebar after the addition of filters.

Resources:

Issue: https://github.com/fossasia/open-event-frontend/issues/3098
Pull Requests:

Ticket Type: https://github.com/fossasia/open-event-frontend/pull/3158
CFS: https://github.com/fossasia/open-event-frontend/pull/3144

Specifying Query Params: https://guides.emberjs.com/release/routing/query-params/

UI Resources for the feature: https://semantic-org.github.io/Semantic-UI-Ember/#/modules/accordion

Continue ReadingAddition of new filters for event search

Fixing Notification Services Across EventYay

In this Blog-Post, I will show you, the fixing of multiple notification messages getting sent on multiple clicks or display of multiple messages for a single response.

What Caused the Problem?

In our Open Event Frontend, We are using Ember Notify Services to inject notification across the web app to show the notifications. Due to improper error handling on the client side sometimes we get multiple notification messages for a single click or for a single action. As shown in this picture.

How Did we Tackle It ?

Since the following issue was long pertaining, I solved the issue by adding a unique ID to each notification injected through the webapp. Since a unique ID was associated with each notification message, A single notification was getting displayed on each and every action irrespective of number of clicks or number of actions called. If an actions caused two notification to be simultaneously triggered, One of them on the basis of ID is suppressed so that a unique and understandable notification get shown.

Code Snippet:

Before the changes:

.then(() => {
          if (state === 'draft') {
            this.notify.success(this.l10n.t('Your event has been published successfully.'));
          } else {
            this.notify.success(this.l10n.t('Your event has been unpublished.'));
          }

After the changes:

this.notify.success(this.isNewInvite ? this.l10n.t('Role Invite sent successfully') : this.l10n.t('Role Invite updated successfully'), {
            id: 'man_role'
          });
        })
        .catch(() => {
          this.notify.error(this.l10n.t('Oops something went wrong. Please try again'), {
            id: 'man_role_error'
          });
        })

Pull Request : Open-Event-Frontend-3438

Issue : Open-Event-Frontend-3437

Tags :

OpenEvent, EventYay, Fossasia, Intern-2k19

Continue ReadingFixing Notification Services Across EventYay

Addition Of Landing Pages For Mobile Apps

In this Blog-Post, I will showcase the landing pages on the Android frontend of Eventyay. As the Eventyay Android App has been launched on Play Store as well as F-Droid, We needed to make sure users know that they can access all the details regarding their event on their phones too. Now EventYay features two landing pages for each of its mobile apps :

EventYay Attendee App 

EventYay Orga App

Addition Of Landing Pages was a 3-Step Procedure, which is stated below :

  1. ) Defining The Needed Routes and Making Space in Footer.
  2. ) Writing the Code and Designing the Layout for the web-page.
  3. ) Writing Integration Tests for both of the pages.

I will explain each one of the processes involved in making Landing Pages for Mobile Apps in brief in this Blog-Post.

1. Defining The Needed Routes and Making Space in Footer.

To add any web-page in the Open-Event Frontend , we need to define its route in the Application Router File. The code snippet which adds them is:-

// app/router.js

router.map(function() {
  this.route('login');
  this.route('register');
  this.route('reset-password');
  this.route('attendee-app');
  this.route('organizer-app');

After successfully defining the routes, We need to create the HandleBars for the corresponding routes. To access those routes we need to create a section on EventYay Footer to show to the users.

The following code will result in showing the links to the landing page in footer-section

2. Writing the Code and Designing the Layout for the web-page.

After successfully defining the routes and links, its time we move on to writing the HandleBars for the above pages. The first design for the page would be the pointing menu offered by Semantic-UI v.2.0, which would describe the current tab.

After the designing of Navigation Menu in the AttendeeApp.hbs, Its time we design the main outlet and the body of the page. The Design of the layout page is based on the Landing Pages Created in Other Platforms Like Event.IO, EventBrite etc. A Mobile View on the right side with the Key Features of The App on the left side

3. Writing Integration Tests for both of the Pages.

Since writing test is the most important part of the Development Lifecycle. I preferred to write Acceptance Test instead of Integration Test or Unit Test, As both of the pages created were very simple in functionality. In Acceptance Test, We would be checking the URL For Both Of The Pages in every case After Login or Without Login.

Test For Attendee App :

test('visiting /landing-attendee-app without login', async function(assert) {
    await visit('/attendee-app');
    assert.equal(currentURL(), '/attendee-app');
  });

  test('visiting /landing-attendee-app with login', async function(assert) {
    await login(assert);
    await visit('/attendee-app');
    assert.equal(currentURL(), '/attendee-app');
  });

Resources

Issue: OpenEventFrontend-2929

Pull Request : OpenEventFrontend-2979

Additional Resources: Routing Guide., Templates in EmberJS.

Tags: OpenEvent, EventYay, Fossasia, Android Apps, Intern-2k19

Continue ReadingAddition Of Landing Pages For Mobile Apps

Allow Same Discount/Access Code for Multiple Events in the Open Event Server

In this Blog-Post, I will show how to allow the system to create the same Discount/Access Code for multiple events in the Open Event Server.

What was the issue:

The main problem was that the server used to identify the discount code and access code based on the discount code/access code itself, which did not allow multiple events to have the same discount/access codes.

Can you think of a better solution to this?
Yes, we should have been searching for it based on the discount/access code as well as the event they are associated with.

Changing the endpoint:

Now to do so, we want to pass the id of the event as well as discount/access code itself with the endpoint so that we can search the database based on the event_id and the code itself.  

Changes in Discount/Access Code Endpoint:

'/event/<int:discount_event_id>/discount-code/<code>'
'/event/<int:access_event_id>/access-code/<code>'

Change logic for database search:

Now when searching for discount/access code in the database, we need to pass the event_id along with the discount/access code, so that we can get the column of discount/access code associated with that event, even if we have multiple discount/access code with the same name for a different event. 

Changes in Database search logic:

access = db.session.query(AccessCode).filter_by(code=kwargs.get('code')
event_id = kwargs.get('access_event_id')).first()

discount = db.session.query(DiscountCode).filter_by(code=kwargs.get('code'),
event_id = kwargs.get('discount_event_id')).first()

Change endpoint in API docs and update Dredd hooks:

Now that we have changed the endpoint to get a discount/access code, we need to change API docs as well as Dredd hooks to accommodate the change in API docs.

Changes in API docs:

## Get Discount Code Detail using the code [/v1/event/{event_id}/discount-code/{code}]
## Get Access Code Detail using the code [/v1/event/{event_id}/access-code/{code}]

Changes in Dredd Hooks:

In discount code hook:

discount_code.event_id = 1

In access code hook:

event = EventFactoryBasic()
db.session.add(event)
db.session.commit()

Resources:

Link to Issue: fossasia/open-event-server#6027
Link to PR: fossasia/open-event-server#6208

Continue ReadingAllow Same Discount/Access Code for Multiple Events in the Open Event Server

Creating an awesome ‘About Us’ page for the Open Event Organizer Android App

Open Event Organizer App (Eventyay Organizer App) is an Android app based on the Eventyay platform. It contains various features using which organizers can manage their events.

This article will talk about a library which can help you create great about pages for Android apps without the need of making custom layouts.

It is the Android About Page library.

Let’s go through the process of its implementation in the Eventyay Organizer App.

First add the dependency in the app level build.gradle file:

implementation 'com.github.medyo:android-about-page:1.2.5'

Creating elements to be added:

Element legalElement = new Element();
legalElement.setTitle("Legal");

Element developersElement = new Element();      
developersElement.setTitle(getString(R.string.developers));

Element shareElement = new Element();
shareElement.setTitle(getString(R.string.share));

Element thirdPartyLicenses = new Element();       
thirdPartyLicenses.setTitle(getString(R.string.third_party_licenses));

Setting image, description and adding items in the About Page:

AboutPage aboutPage = new AboutPage(getContext())
            .isRTL(false)
            .setImage(R.mipmap.ic_launcher)            
            .setDescription(getString(R.string.about_us_description))
            .addItem(new Element("Version " + BuildConfig.VERSION_NAME, R.drawable.ic_info))
            .addGroup("Connect with us")
            .addGitHub("fossasia/open-event-organizer-android")
            .addPlayStore(getContext().getPackageName())
            .addWebsite(getString(R.string.FRONTEND_HOST))
            .addFacebook(getString(R.string.FACEBOOK_ID))
            .addTwitter(getString(R.string.TWITTER_ID))
            .addYoutube(getString(R.string.YOUTUBE_ID))
            .addItem(developersElement)
            .addItem(legalElement)
            .addItem(shareElement);

if (BuildConfig.FLAVOR.equals("playStore")) {    
    aboutPage.addItem(thirdPartyLicenses);
}

View aboutPageView = aboutPage.create();

Now add the aboutPageView in the fragment.

To make the values configurable from build.gradle, add this is the defaultConfig:

resValue "string", "FACEBOOK_ID", "eventyay"
resValue "string", "TWITTER_ID", "eventyay"
resValue "string", "YOUTUBE_ID", "UCQprMsG-raCIMlBudm20iLQ"

That’s it! The About Page is now ready.

Resources:

Library used: Android About Page

Pull Request: #1904

Open Event Organizer App: Project repo, Play Store, F-Droid

Continue ReadingCreating an awesome ‘About Us’ page for the Open Event Organizer Android App

Mapbox implementation in Open Event Organizer Android App

Open Event Organizer Android App is used by event organizers to manage events on the Eventyay platform. While creating or updating an event, location is one of the important factors which needs to be added so that the attendees can be informed of the venue.

Here, we’ll go through the process of implementing Mapbox Places Autocomplete for event location in the F-Droid build variant.

The first step is to create an environment variable for the Mapbox Access Token. 

def MAPBOX_ACCESS_TOKEN = System.getenv('MAPBOX_ACCESS_TOKEN') ?: "YOUR_ACCESS_TOKEN"

Add the Mapbox dependency:

fdroidImplementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-places-v8:0.9.0'

Fetching the access token in EventDetailsStepOne as well as UpdateEventFragment:

ApplicationInfo applicationInfo = null;
        try {
            applicationInfo = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA);
        } catch (PackageManager.NameNotFoundException e) {
            Timber.e(e);
        }
        Bundle bundle = applicationInfo.metaData;

        String mapboxAccessToken = bundle.getString(getString(R.string.mapbox_access_token));

The app should not crash if the access token is not available. To ensure this, we need to put a check. Since, the default value of the access token is set to “YOUR_ACCESS_TOKEN”, the following code will check whether a token is available or not:

if (mapboxAccessToken.equals("YOUR_ACCESS_TOKEN")) {
    ViewUtils.showSnackbar(binding.getRoot(),                             R.string.access_token_required);
    return;
}

Initializing the PlacesAutocompleteFragment:

PlaceAutocompleteFragment autocompleteFragment = PlaceAutocompleteFragment.newInstance(
                mapboxAccessToken, PlaceOptions.builder().backgroundColor(Color.WHITE).build());

getFragmentManager().beginTransaction()
    .replace(R.id.fragment, autocompleteFragment)
    .addToBackStack(null)
    .commit();

Now, a listener needs to be set up to get the selected place and set the various fields like latitude, longitude, location name and searchable location name.

autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
                @Override
                public void onPlaceSelected(CarmenFeature carmenFeature) {
                    Event event = binding.getEvent();
                    event.setLatitude(carmenFeature.center().latitude());
                    event.setLongitude(carmenFeature.center().longitude());
                    event.setLocationName(carmenFeature.placeName());
                    event.setSearchableLocationName(carmenFeature.text());
                    binding.form.layoutLocationName.setVisibility(View.VISIBLE);
                    binding.form.locationName.setText(event.getLocationName());
                    getFragmentManager().popBackStack();
                }

                @Override
                public void onCancel() {
                    getFragmentManager().popBackStack();
                }
            });

This brings the process of implementing Mapbox SDK to completion.

GIF showing the working of Mapbox Places Autocomplete

Resources:

Documentation: Mapbox Places Plugin

Open Event Organizer App: Project repo, Play Store, F-Droid

Continue ReadingMapbox implementation in Open Event Organizer Android App

Implementation of Android App Links in Open Event Organizer App

Android App Links are HTTP URLs that bring users directly to specific content in an Android app. They allow the website URLs to immediately open the corresponding content in the related Android app.

Whenever such a URL is clicked, a dialog is opened allowing the user to select a particular app which can handle the given URL.

In this blog post, we will be discussing the implementation of Android App Links for password reset in Open Event Organizer App, the Android app developed for event organizers using the Eventyay platform.

What is the purpose of using App Links?

App Links are used to open the corresponding app when a link is clicked.

  • If the app is installed, then it will open on clicking the link.
  • If app is not installed, then the link will open in the browser.

The first steps involve:

  1. Creating intent filters in the manifest.
  2. Adding code to the app’s activities to handle incoming links.
  3. Associating the app and the website with Digital Asset Links.

Adding Android App Links

First step is to add an intent-filter for the AuthActivity.

<intent-filter>
    <action android:name="android.intent.action.VIEW" />

    <category android:name="android.intent.category.DEFAULT" />
     <category android:name="android.intent.category.BROWSABLE" />

     <data
         android:scheme="https"
         android:host="@string/FRONTEND_HOST"
         android:pathPrefix="/reset-password" />
</intent-filter>

Here, FRONTEND_HOST is the URL for the web frontend of the Eventyay platform.

This needs to be handled in AuthActivity:

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    handleIntent(intent);
}
private void handleIntent(Intent intent) {
    String appLinkAction = intent.getAction();
    Uri appLinkData = intent.getData();

    if (Intent.ACTION_VIEW.equals(appLinkAction) && appLinkData != null) {
        LinkHandler.Destination destination = LinkHandler.getDestinationAndToken(appLinkData.toString()).getDestination();
        String token = LinkHandler.getDestinationAndToken(appLinkData.toString()).getToken();

        if (destination.equals(LinkHandler.Destination.RESET_PASSWORD)) {
            getSupportFragmentManager().beginTransaction()
            .replace(R.id.fragment_container,                     
                         ResetPasswordFragment.newInstance(token))
            .commit();
        }
    }
}

 Call the handleIntent() method in onCreate():

handleIntent(getIntent());

Get the token in onCreate() method of ResetPasswordFragment:

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (getArguments() != null)
        token = getArguments().getString(TOKEN_KEY);
}

Set the token in ViewModel:

if (token != null)
    resetPasswordViewModel.setToken(token);

The setToken() method in ViewModel:

if (token != null)
    resetPasswordViewModel.setToken(token);

LinkHandler class for handling the links:

package com.eventyay.organizer.utils;

public class LinkHandler {

    public Destination destination;
    public String token;

    public LinkHandler(Destination destination, String token) {
        this.destination = destination;
        this.token = token;
    }

    public static LinkHandler getDestinationAndToken(String url) {
        if (url.contains("reset-password")) {
            String token = url.substring(url.indexOf('=') + 1);
            return new LinkHandler(Destination.RESET_PASSWORD, token);
        } else if (url.contains("verify")) {
            String token = url.substring(url.indexOf('=') + 1);
            return new LinkHandler(Destination.VERIFY_EMAIL, token);
        } else
            return null;
    }

    public Destination getDestination() {
        return destination;
    }

    public String getToken() {
        return token;
    }

    public enum Destination {
        VERIFY_EMAIL,
        RESET_PASSWORD
    }
}

enum is used to handle links for both, password reset as well as email verification.

Finally, the unit tests for LinkHandler:

package com.eventyay.organizer.utils;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import static org.junit.Assert.assertEquals;

@RunWith(JUnit4.class)
public class LinkHandlerTest {

    private String resetPassUrl = "https://eventyay.com/reset-password?token=12345678";
    private String verifyEmailUrl = "https://eventyay.com/verify?token=12345678";

    @Test
    public void shouldHaveCorrectDestination() {
        assertEquals(LinkHandler.Destination.RESET_PASSWORD,
            LinkHandler.getDestinationAndToken(resetPassUrl).getDestination());
        assertEquals(LinkHandler.Destination.VERIFY_EMAIL,
            LinkHandler.getDestinationAndToken(verifyEmailUrl).getDestination());
    }

    @Test
    public void shouldGetPasswordResetToken() {
        assertEquals(LinkHandler.Destination.RESET_PASSWORD,
            LinkHandler.getDestinationAndToken(resetPassUrl).getDestination());
        assertEquals("12345678",
            LinkHandler.getDestinationAndToken(resetPassUrl).getToken());
    }

    @Test
    public void shouldGetEmailVerificationToken() {
        assertEquals(LinkHandler.Destination.VERIFY_EMAIL,
            LinkHandler.getDestinationAndToken(verifyEmailUrl).getDestination());
        assertEquals("12345678",
            LinkHandler.getDestinationAndToken(verifyEmailUrl).getToken());
    }
}

Resources:

Documentation: Link

Further reading: Android App Linking

Pull Request: feat: Add app link for password reset

Open Event Organizer App: Project repo, Play Store, F-Droid

Continue ReadingImplementation of Android App Links in Open Event Organizer App