Email and Password Validation in Open Event Android

The Open Event API Server exposes a well documented JSONAPI compliant REST API that can be used in The Open Even Android and Frontend. The Open Event API Server enables the Android and web clients to add the user authentication (sign up/login) in the project. In the process of signing up or logging in user it is needed to validate email and password entered by the user and show the error to give better user experience. In this post I explain how to validate email and password entered by the user using TextInputLayout.

1. Add TextInputLayout

TextInputLayout wraps an EditText (or descendant) to show a floating label when the hint is hidden due to the user inputting text. Add TextInputLayout for email field in the layout as given below.

<android.support.design.widget.TextInputLayout
            android:id="@+id/text_input_layout_email"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <android.support.v7.widget.AppCompatEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="@string/email"
                android:inputType="textEmailAddress" />
</android.support.design.widget.TextInputLayout>

Here the hint attribute is used to display hint in the floating label. Specify the input type so the system displays the appropriate soft input method (such as an on-screen keyboard) for the field. For email EditText we are using textEmailAddress input type. Similarly add TextInputLayout for the password field. The input type for the password is textPassword.

2.  Create and initialize object

Now in the activity create and initialize TextInputLayout and EditText objects for email and password.

@BindView(R.id.text_input_layout_email)
TextInputLayout mTextInputLayoutEmail;
@BindView(R.id.text_input_layout_password)
TextInputLayout mTextInputLayoutPassword;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ButterKnife.bind(this);

    private AppCompatEditText mEditTextEmail = (AppCompatEditText) mTextInputLayoutEmail.getEditText();
    private AppCompatEditText mEditTextPassword = (AppCompatEditText) mTextInputLayoutPassword.getEditText();
}

Here we are using ButterKnife for binding views with fields. The getEditText() method returns the EditText view used for text input inside the TextInputLayout.

3.  Create validate() method

Create validate() method which takes two arguments. The first is email and the second password. It will return true if the email and password are valid else false.

private boolean validate(String email, String password) {

        // Reset errors.
        mTextInputLayoutEmail.setError(null);
        mTextInputLayoutPassword.setError(null);

        if (Utils.isEmpty(email)) {
            mTextInputLayoutEmail.setError("Email is required");
            return false;
        } else if (!Utils.isEmailValid(email)) {
            mTextInputLayoutEmail.setError("Enter a valid email");
            return false;
        }

        if (Utils.isEmpty(password)) {
            mTextInputLayoutPassword.setError("Password is required");
            return false;
        } else if (!Utils.isPasswordValid(password)) {
            mTextInputLayoutPassword.setError("Password must contain at least 6 characters");
            return false;
        }

        return true;
}

Here it first resets the error for the TextInputLayout by setting it to null. Then it checks email string if it is empty then it will show “Email is required” error using setError() method.

4.  Create isEmailValid() and isPasswordValid() method

Now create isEmailValid() and isPasswordvalid() method which is used by validate() method. The isEmailValid() method should take email string as an argument and return boolean indicating whether the email is valid or not. The isEmailValid() method uses Pattern and Matcher class to determine if the pattern of input is email or not. The isPasswordValid() method should take password string as an argument and return true if the password is satisfying minimum condition. Here in our case length of the password should be minimum 6.

public static boolean isEmailValid(String email){
        Pattern pattern = Patterns.EMAIL_ADDRESS;
        Matcher matcher = pattern.matcher(email);
        return matcher.matches();
}

//Check password with minimum requirement here(it should be minimum 6 characters)
public static boolean isPasswordValid(String password){
        return password.length() >= 6;
}

5.  Use validate() method

Now we are ready to use validate() method when signing up or logging in the user. The getText() method of EditText will return text input.

String email = mEditTextEmail.getText().toString();
String password = mEditTextPassword.getText().toString();

if (validate(email, password)) {
    //Sign up or login User
}

Conclusion

Using TextInputLayout with floating hint label and error handling gives awesome UI and UX.

Continue ReadingEmail and Password Validation in Open Event Android

Using Lombok to Reduce Boilerplate Code in Open Event Android App

The Open Even App Android project contains data/model classes like Event, Track, Session, Speaker etc which are used to fetch data from the server and store data in the database. Each model contains private fields, getters, setters and toString() method which are used to change data of the object, access data of the object, logging and debugging. Adding all these methods manually makes the code boilerplate.

In this blog post I explain how to use Lombok to reduce boilerplate code in the model class.

Add dependency

To set up Lombok for your application you have to add the dependency in your app module’s build.gradle file.

dependencies {
	provided   "org.projectlombok:lombok:1.16.18"
}

Install Lombok plugin

In addition to setting up your gradle project correctly, you need to add the Lombok IntelliJ plugin to add Lombok support to Android Studio

  1. Go to File > Settings > Plugins
  2. Click on Browse repositories
  3. Search for Lombok Plugin
  4. Click on Install plugin
  5. Restart Android Studio

Write model class

Lombok has annotations to generate Getters, Setters, Constructors, toString(), Equal() and hashCode() methods.

@Getter,  @Setter, @ToString, @EqualsAndHashCode

@Data is a shortcut annotation that bundles the features of @Getter, @Setter, @ToString and @EqualsAndHashCode

Here I am only defining track model because of its simplicity and less complexity.

@Data
public class Track {

    private int id;
    private String name;
    private String description;
    private String color;
    private String fontColor;
    private RealmList<Session> sessions;
}

Create and use object

After defining models you can create an instance of the object and you will notice that you can access all the getters and setters.

Track track = new Track();
track.setName("Android");

String name = track.getName(); // here value of name will be "Android" 

You can also specify which fields to include and exclude in the toString(), equals() and hashCode() methods using @ToString, @EqualsAndHashCode annotation.

@ToString(of={"id", "name"})

@ToString(exclude="color")

@EqualsAndHashCode(of={"id", "name"})

@EqualsAndHashCode(exclude={"color", "fontColor"})

Constructors

Lombok has three methods to generator constructors

  • @NoArgsConstructor: It generates constructor with no parameters
  • @RequiredArgsConstructor: It generates a constructor with 1 parameter for each field that requires special handling.
  • @AllArgsConstructor: It generates a constructor with 1 parameter for each field in your class.

Conclusion

As you can see, Lombok uses succinct annotations to generate methods such as getters, setters, and constructors. It can easily help you get rid of hundreds of lines of boilerplate code. Lombok also allows you to make your code more expressive, concise and can help you avoid some bugs. To learn more about Lombok project follow the links given below.

Continue ReadingUsing Lombok to Reduce Boilerplate Code in Open Event Android App

Creating SharedPreferences Util in Open Event Android

In the Open Event Android we have the fragment for schedule, speakers which has the option to sort the list. Schedule Fragment have the option to sort by Title, Tracks and  Start Time. Speakers Fragment has the option to sort by Name, Organization and Country. If the user preferred to sort by name then it should always sort the list by name whenever the user uses the app. For this we need to store user preference for sorting list. Another part of the app like Live feed, About fragment also needs to store event id, facebook page id/name etc.

In Android there is a SharedPreferences class to store key value pair in the App specific storage. To store data in SharedPreferences we need to create SharedPreferences Object in different activities and fragment. In this post I explain how to create SharedPreferences Util which can be used to store key value pairs from all over the App.

1. Create SharedPreferencesUtil Class

The first step is to create SharedPreferncesUtil.java file which will contain static SharedPreferences object.

public class SharedPreferencesUtil {
    ...
}

2. Create static objects

Create static SharedPreferences and SharedPreferences.Editor object in the SharedPreferncesUtil.java file.

private static SharedPreferences sharedPreferences;
private static SharedPreferences.Editor editor;

3. Initialize objects

Now after creating objects initialize them in the static block. The code inside static block is executed only once: The first time you make an object of that class or the first time you access a static member of that class.

static {
        sharedPreferences = OpenEventApp.getAppContext().getSharedPreferences(ConstantStrings.FOSS_PREFS, Context.MODE_PRIVATE);
        editor = sharedPreferences.edit();
}

 

Here make sure to use the Application context to avoid a memory leak. The getSharedPreferences() method takes two arguments name of the shared preference and mode. Here we are using Context.MODE_PRIVATE File creation mode where the created file can only be accessed by the calling application.

4. Add methods

Now create static methods to store data so that we can use these methods directly from the other activities or classes. Here I am only adding methods for integer you can add more methods for String, long, boolean etc.

public static void putInt(String key, int value) {
        editor.putInt(key, value).apply();
}

public static int getInt(String key, int defaultValue) {
        return sharedPreferences.getInt(key, defaultValue);
}

5. Use SharedPreferencesUtil class

Now we are ready to use this Util class to store key value pair in SharedPreferences.

SharedPreferencesUtil.putInt(ConstantStrings.PREF_SORT, sortType);

Here the putInt() methods take two arguments one the key and second the value. To get the stored value use getInt() method.

SharedPreferencesUtil.getInt(ConstantStrings.PREF_SORT, 0);

To know more how I solved this issue in Open Event Project visit this link.

Continue ReadingCreating SharedPreferences Util in Open Event Android

Caching Elasticsearch Aggregation Results in loklak Server

To provide aggregated data for various classifiers, loklak uses Elasticsearch aggregations. Aggregated data speaks a lot more than a few instances from it can say. But performing aggregations on each request can be very resource consuming. So we needed to come up with a way to reduce this load.

In this post, I will be discussing how I came up with a caching model for the aggregated data from the Elasticsearch index.

Fields to Consider while Caching

At the classifier endpoint, aggregations can be requested based on the following fields –

  • Classifier Name
  • Classifier Classes
  • Countries
  • Start Date
  • End Date

But to cache results, we can ignore cases where we just require a few classes or countries and store aggregations for all of them instead. So the fields that will define the cache to look for will be –

  • Classifier Name
  • Start Date
  • End Date

Type of Cache

The data structure used for caching was Java’s HashMap. It would be used to map a special string key to a special object discussed in a later section.

Key

The key is built using the fields mentioned previously –

private static String getKey(String index, String classifier, String sinceDate, String untilDate) {
    return index + "::::"
        + classifier + "::::"
        + (sinceDate == null ? "" : sinceDate) + "::::"
        + (untilDate == null ? "" : untilDate);
}

[SOURCE]

In this way, we can handle requests where a user makes a request for every class there is without running the expensive aggregation job every time. This is because the key for such requests will be same as we are not considering country and class for this purpose.

Value

The object used as key in the HashMap is a wrapper containing the following –

  1. json – It is a JSONObject containing the actual data.
  2. expiry – It is the expiry of the object in milliseconds.

class JSONObjectWrapper {
    private JSONObject json; 
    private long expiry;
    ... 
}

Timeout

The timeout associated with a cache is defined in the configuration file of the project as “classifierservlet.cache.timeout”. It defaults to 5 minutes and is used to set the eexpiryof a cached JSONObject –

class JSONObjectWrapper {
    ...
    private static long timeout = DAO.getConfig("classifierservlet.cache.timeout", 300000);

    JSONObjectWrapper(JSONObject json) {
        this.json = json;
        this.expiry = System.currentTimeMillis() + timeout;
    }
    ...
}

 

Cache Hit

For searching in the cache, the previously mentioned string is composed from the parameters requested by the user. Checking for a cache hit can be done in the following manner –

String key = getKey(index, classifier, sinceDate, untilDate);
if (cacheMap.keySet().contains(key)) {
    JSONObjectWrapper jw = cacheMap.get(key);
    if (!jw.isExpired()) {
        // Do something with jw
    }
}
// Calculate the aggregations
...

But since jw here would contain all the data, we would need to filter out the classes and countries which are not needed.

Filtering results

For filtering out the parts which do not contain the information requested by the user, we can perform a simple pass and exclude the results that are not needed.

Since the number of fields to filter out, i.e. classes and countries, would not be that high, this process would not be that resource intensive. And at the same time, would save us from requesting heavy aggregation tasks from the user.

Since the data about classes is nested inside the respective country field, we need to perform two level of filtering –

JSONObject retJson = new JSONObject(true);
for (String key : json.keySet()) {
    JSONArray value = filterInnerClasses(json.getJSONArray(key), classes);
    if ("GLOBAL".equals(key) || countries.contains(key)) {
        retJson.put(key, value);
    }
}

Cache Miss

In the case of a cache miss, the helper functions are called from ElasticsearchClient.java to get results. These results are then parsed from HashMap to JSONObject and stored in the cache for future usages.

JSONObject freshCache = getFromElasticsearch(index, classifier, sinceDate, untilDate);
cacheMap.put(key, new JSONObjectWrapper(freshCache));

The getFromElasticsearch method finds all the possible classes and makes a request to the appropriate method in ElasticsearchClient, getting data for all classifiers and all countries.

Conclusion

In this blog post, I discussed the need for caching of aggregations and the way it is achieved in the loklak server. This feature was introduced in pull request loklak/loklak_server#1333 by @singhpratyush (me).

Resources

Continue ReadingCaching Elasticsearch Aggregation Results in loklak Server

Making loklak Server’s Kaizen Harvester Extendable

Harvesting strategies in loklak are something that the end users can’t see, but they play a vital role in deciding the way in which messages are collected with loklak. One of the strategies in loklak is defined by the Kaizen Harvester, which generates queries from collected messages.

The original strategy used a simple hash queue which drops queries once it is full. This effect is not desirable as we tend to lose important queries in this process if they come up late while harvesting. To overcome this behaviour without losing important search queries, we needed to come up with new harvesting strategy(ies) that would provide a better approach for harvesting. In this blog post, I am discussing the changes made in the kaizen harvester so it can be extended to create different flavors of harvesters.

What can be different in extended harvesters?

To make the Kaizen harvester extendable, we first needed to decide that what are the parts in the original Kaizen harvester that can be changed to make the strategy different (and probably better).

Since one of the most crucial part of the Kaizen harvester was the way it stores queries to be processed, it was one of the most obvious things to change. Another thing that should be allowed to configure across various strategies was the decision of whether to go for harvesting the queries from the query list.

Query storage with KaizenQueries

To allow different methods of storing the queries, KaizenQueries class was introduced in loklak. It was configured to provide basic methods that would be required for a query storing technique to work. A query storing technique can be any data structure that we can use to store search queries for Kaizen harvester.

public abstract class KaizenQueries {

     public abstract boolean addQuery(String query);

     public abstract String getQuery();

     public abstract int getSize();

     public abstract int getMaxSize();

     public boolean isEmpty() {
         return this.getSize() == 0;
     }
}

[SOURCE]

Also, a default type of KaizenQueries was introduced to use in the original Kaizen harvester. This allowed the same interface as the original queue which was used in the harvester.

Another constructor was introduced in Kaizen harvester which allowed setting the KaizenQueries for an instance of its derived classes. It solved the problem of providing an interface of KaizenQueries inside the Kaizen harvester which can be used by any inherited strategy –

private KaizenQueries queries = null;

public KaizenHarvester(KaizenQueries queries) {
    ...
    this.queries = queries;
    ...
}

public void someMethod() {
    ...
    this.queries.addQuery(someQuery);
    ...
}

[SOURCE]

This being added, getting new queries or adding new queries was a simple. We just need to use getQuery() and addQuery() methods without worrying about the internal implementations.

Configurable decision for harvesting

As mentioned earlier, the decision taken for harvesting should also be configurable. For this, a protected method was implemented and used in harvest() method –

protected boolean shallHarvest() {
    float targetProb = random.nextFloat();
    float prob = 0.5F;
    if (this.queries.getMaxSize() > 0) {
        prob = queries.getSize() / (float)queries.getMaxSize();
    }
    return !this.queries.isEmpty() && targetProb < prob;
}

@Override
public int harvest() {
    if (this.shallHarvest()) {
        return harvestMessages();
    }

    grabSuggestions();

    return 0;
}

[SOURCE]

The shallHarvest() method can be overridden by derived classes to allow any type of harvesting decision that they want. For example, we can configure it in such a way that it harvests if there are any queries in the queue, or to use a different probability distribution instead of linear (maybe gaussian around 1).

Conclusion

This blog post explained about the changes made in loklak’s Kaizen harvester which allowed other harvesting strategies to be built on its top. It discussed the two components changed and how they allowed ease of inheriting the original Kaizen harvester. These changes were proposed in PR loklak/loklak_server#1203 by @singhpratyush (me).

Resources

Continue ReadingMaking loklak Server’s Kaizen Harvester Extendable

Dynamic Base URL Support in the Open Event Organizer App

Open Event API Server acts as a backend for Open Event Organizer Android App. The server has a development instance running on the web for developers. Developers use this instance to try out new feature additions, bug fixings and other such changes in the source code. And when confirmed working, these changes are updated to the main running instance which is kept live throughout for the users. Similarly for Android app developers, to test the app with both the instances, we have implemented the dynamic base URL support in the app. The app has a default base URL set to development instance or main instance dependent on the debug mode. That means the app will use a server on developer instance when used under debug mode and will use a main instance server if used under release mode. The app also provides an option to enter an alternate URL while login in the app which replaces default base URL in the app for the session.

In the organizer app, we are using Retrofit + Okhttp for handling network requests and dagger for dependency injection. The OkhttpClient provider in NetworkModule class looks like:

@Provides
@Singleton
OkHttpClient providesOkHttpClient(HostSelectionInterceptor interceptor) {
   return new OkHttpClient.Builder()
       .addNetworkInterceptor(new StethoInterceptor())
       .build();
}

 

Retrofit had a support for mutable base URL in the earlier versions but the feature is no longer available in the recent versions. We are using Interceptor class for changing base URL. The class has a method named intercept, which gets called at each network request. In this method, base URL is reset to the new URL.

So first you have to extend Interceptor class and reset base URL in the intercept method. The Interceptor class in the app looks like:

public final class HostSelectionInterceptor implements Interceptor {
   private String host;
   private String scheme;

   public HostSelectionInterceptor(){
       //Intentionally left blank
   }

   public void setInterceptor(String url) {
       HttpUrl httpUrl = HttpUrl.parse(url);
       scheme = httpUrl.scheme();
       host = httpUrl.host();
   }

   @Override
   public Response intercept(Chain chain) throws IOException {
       Request original = chain.request();

       // If new Base URL is properly formatted then replace the old one
       if (scheme != null && host != null) {
           HttpUrl newUrl = original.url().newBuilder()
               .scheme(scheme)
               .host(host)
               .build();
           original = original.newBuilder()
               .url(newUrl)
               .build();
       }
       return chain.proceed(original);
   }
}

 

The class has a private string field host to save base URL. The method setInterceptor is used to change the base URL. Once the base URL is changed, thereafter all the network requests use changed URL to call. So now our interceptor is ready which can be used to support dynamic base URL in the app. This interceptor is added to Okhttp builder using its method addInterceptor.

@Provides
@Singleton
HostSelectionInterceptor providesHostSelectionInterceptor() {
   return new HostSelectionInterceptor();
}

@Provides
@Singleton
OkHttpClient providesOkHttpClient(HostSelectionInterceptor interceptor) {
   return new OkHttpClient.Builder()
       .addInterceptor(interceptor)
       .addNetworkInterceptor(new StethoInterceptor())
       .build();
}

 

And now you are able to change base URL just by using the setInterceptor method of Interceptor class from anywhere in the app. And by then all the network calls use the updated base URL.

Application

I will show you here, how exactly this works in the Open Event Organizer app. On the login page, we have provided an option to enter an alternate base URL.

                                

We have kept a default URL checked. The default URL is set as per debug mode. This is done by setting the fields in the build.gradle. The code looks like:

buildTypes {
       release {
           ...
           buildConfigField "String", "DEFAULT_BASE_URL", '"https://www.eventyay.com/api/v1/"'
       }
       debug {
           buildConfigField "String", "DEFAULT_BASE_URL", '"https://open-event-dev.herokuapp.com/api/v1/"'
       }
   }

 

The field is used in the app as:

private final String DEFAULT_BASE_URL = BuildConfig.DEFAULT_BASE_URL;

 

On login, the loginPresenter calls setInterceptor method of the Interceptor to update the URL according to the user’s input. And the base URL is changed in the app for further network requests.

Links:
1. Gist link for Interceptor implementation code – https://gist.github.com/swankjesse/8571a8207a5815cca1fb
2. Google dagger dependency injector Github Repo
3. Retrofit http client Github Repo
4. Okhttp client Github Repo

Continue ReadingDynamic Base URL Support in the Open Event Organizer App

Presenter Abstraction Layer in Open Event Organizer Android App

Open Event Organizer App design follows Model View Presenter (MVP) architecture which enables heavy unit testing. MVP is a trending architecture design followed these days. If you are not aware of MVP architecture, then please refer any of the tutorial (few links are given at the end of this blog) about it before reading this. In the design, the code becomes little repetitive as the application size increases due to so many presenters and views, which degrades the code readability. So to avoid this and keep the functionality code clean in the App, we have created a Presenter Abstraction Layer which contains the repetitive code and the layer is extended wherever required in the app. I will be talking about the Presenter Abstraction Layer implementation through the App in this blog.

First of all, create a base interface. The base interface contains methods which every presenter will have. The base interface for presenter in the App looks like:

public interface IBasePresenter {
   void start();
   void detach();
}

 

In the method start, presenter loads all the required data from the model and sends it to the view. And releases all the resources in detach. These two methods are required in all the presenters. This interface is extended by other two interfaces which will be actually used by the views. The relevant code is:

public interface IPresenter<V> extends IBasePresenter {
   void attach(V view);
}

public interface IDetailPresenter<K, V> extends IBasePresenter {
   void attach(K key, V view);
}

 

Method attach is used to attach view and the data id (if required) to the presenter. In the app, most of the presenters require an extra data which is used in loading data from the model. Hence two interfaces are extended from the base interface. Now comes the implementation part.

public abstract class BasePresenter<V> implements IPresenter<V> {
   private V view;
   private CompositeDisposable compositeDisposable;

   @Override
   @CallSuper
   public void attach(V view) {
       this.view = view;
       this.compositeDisposable = new CompositeDisposable();
   }

   @Override
   @CallSuper
   public void detach() {
       view = null;
       compositeDisposable.dispose();
   }

   protected V getView() {
       return view;
   }

   protected CompositeDisposable getDisposable() {
       return compositeDisposable;
   }
}

 

The App uses ReactiveX Observables for async operations which contain fragment/activity context hence these need to be disposed at some lifecycle of fragment/activity. detach nulls the view and disposes the compositeDisposable. This method is called at the onStop lifecycle of fragment/activity. The observable subscriptions are one of the major reasons for memory leaks if not disposed at correct lifecycle in Android. So the detach method is called at onStop lifecycle when activity goes into background or fragment is switched by FragmentTransaction. Another base presenter class looks like:

public  abstract class BaseDetailPresenter<K, V> extends BasePresenter<V> implements IDetailPresenter<K, V> {
   private K id;

   @Override
   @CallSuper
   public void attach(K id, V view) {
       super.attach(view);
       this.id = id;
   }

   protected K getId() {
       return id;
   }
}

 

This class extends the previous one except for the attach method. As the presenters extending this, require an extra data id which is passed through this method. So the id can be used in the presenter extending this class using getId. The presenters in the app extend one of these two classes. This helps in making a firm app structure and the development process easier. Abstraction layer should be used wherever same code is repeated. This increases code readability and decreases the chances of creating bugs especially when a team is working on the same project.

Links:
1. MVP for Android: how to organize the presentation layer, by Antonio Leiva
2. Android Code That Scales, With MVP, by Nathan Barraille
3. Ted Mosby – Software Architect, by Hannes Dorfmann

Continue ReadingPresenter Abstraction Layer in Open Event Organizer Android App

Handling High Resolution Images in Phimpme Android

In Android, loading heavy and high resolution images is a difficult task. For instance, if we try to load a photo clicked at a resolution four times that of the screen and try to load it in an imageview, it may result in an app’s crash due to the OutOFMemory exception. It happens because at the run time of our application some limited memory is allocated to our application and if we exceed that by loading a high quality images. To make a perfect gallery application, one must take care of all the possible causes for application crashes. In Phimpme Android, we have done this with the help of Glide library with some other tweaks to help catch all possible causes for the OutOfMemory exceptions. In this tutorial, I will be discussing how we have displayed heavy images with the help of Glide library and other tweaks to avoid the above mentioned exception.

Step 1:

To avoid the OutOFMemory exception, first we have to add the below line of code in the AndroidManifest.xml file.

android:largeHeap=”true”

What this piece of code does is that it increases the amount of heap memory that is allocated at the time of run time of the application. Hence, more heap memory, less chance of running out of memory.

Step 2:

To load the images into the image view we can make use of the Glide library as it is the most recommended way to do it according to the Google’s  Android developer page to cache bitmaps. The below code helps us to load the image in the imageView using a pager adapter.

Glide.with(getContext())
          .load(img.getUri())
          .asBitmap().format(DecodeFormat.PREFER_RGB_565)
          .signature(useCache ? img.getSignature(): new StringSignature(new Date().getTime()+""))
          .diskCacheStrategy(DiskCacheStrategy.SOURCE)
          .thumbnail(0.5f)
          .transform(new RotateTransformation(getContext(), img.getOrientation(), false))
          .animate(R.anim.fade_in)
          .into(new SimpleTarget<Bitmap>() {
              @Override
              public void onResourceReady(Bitmap bitmap, GlideAnimation<? super Bitmap> glideAnimation) {
                  photoView.setImageBitmap(bitmap);
              }
          });

This is the way we have done it in the Phimpme Android application using the Glide library. We are loading the image as a bitmap and by preferring the bitmap RGB565 as it consumes 50% less memory than the RGB8888 model which may be the type of the original image. Of course the image quality will seem bit less but it is not noticeable until we zoom in to full extent.

The next thing we are doing is caching the image in the memory using the below line of code using Glide library.

.diskCacheStrategy(DiskCacheStrategy.SOURCE)

As caching images offers faster access to the images. It also helps in avoiding the OutOfMemory crashes when we are using a large list of images. Link to cache images without using the Glide library is mentioned in the resources section below. After this, we are loading the image in the PhotoView which is a module we are using in the Phimpme Android application, which extends to the imageView and comes with many zooming images functionalities.

This is how we have implemented the loading of images in the gallery view in Phimpme Android application so that it can handle resolution of any sizes without running out of memory. To get the full source code on how to load high resolution images, please refer to the Phimpme Android repository.

Resource

  1. Introduction to glide image loader: https://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en
  2. Google developer guide to cache bitmaps without using glide library : https://developer.android.com/topic/performance/graphics/cache-bitmap.html.
  3. Google developer guide to OutOfMemory Exceptions: https://developer.android.com/reference/java/lang/OutOfMemoryError.html
  4. LeafPic GitHub repository: https://github.com/HoraApps/LeafPic
Continue ReadingHandling High Resolution Images in Phimpme Android

Displaying Upcoming Sessions at a Microlocation Open Event Android

When I am attending a session in a room, I don’t get information on what is coming up.”

The issue that the user expressed was that he wanted to know what were the upcoming sessions at a microlocation. While I took up this issue in Open Event Android a few days back, I was thinking of ways about how this can be implemented. The app should be easy-to-use even for non-developers and thus, any new feature shouldn’t be too complex in its implementation. We decided upon doing the following:

  • Adding an “upcoming” option in the options menu of the Location activity.
  • This option’s purpose was to trigger the app to show information about the upcoming session in that microlocation.

Initial changes in LocationActivity.java

First of all, we added a new icon in the options menu of LocationActivity.java. One of the things that we learnt there was to use ifRoom|collapseActionView option for the app:showAsAction  

attribute as frequently as possible. This option ensures that the title in the option’s menu is visible at all times irrespective of the options being visible along with their icons.

So in case, the title is too big and there is very little room for the options to appear individually, then instead of squeezing down the title, the “ifRoom” attribute will collapse the option icons and insert a 3-dotted drop-down option list with all the options appearing in the drop-down.

Something like this:

The icon’s XML element and UI looked something like this:

<item
       android:id="@+id/upcoming_sessions"
       android:icon="@drawable/ic_timeline_white_24dp"
       android:title="@string/upcoming"
       app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.support.v7.widget.Button"/>

About the drawable icon that you see in the screenshot above, it was a tough find. Before I talk about how I came across this icon, I will talk about adding an icon in Android Studio.

How to add an icon in Android Studio?

Adding an item in Android studio means adding a drawable at a basic level. You can find all drawables under the app/src/main/res/drawable folder.

To add a new drawable, right-click on the drawable folder and go to new –>Vector asset. A window similar to what is shown below will appear.

Now, on selecting the “icon” option you will be taken to a huge list of icons that you can add in your app and then use them subsequently. But the problem here is that it is tough at times to find the icon that will be fit for your purpose. Like in my case, there was no direct icon for “upcoming”. This is when we had to do something more. We had to browse to this amazing site by Google: https://material.io/icons/ This site shows all the available icons in a much more interactive way and it was a lot more easier for me to come across the icon we wanted using this site.

The vector drawable file for the icon we chose looks like this:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
       android:width="24dp"
       android:height="24dp"
       android:viewportWidth="24.0"
       android:viewportHeight="24.0">
   <path
       android:fillColor="#FFFFFF"
       android:pathData="M23,8c0,1.1 -0.9,2 -2,2 -0.18,0 -0.35,-0.02 -0.51,-0.07l-3.56,3.55c0.05,0.16 0.07,0.34 0.07,0.52 0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2c0,-0.18 0.02,-0.36 0.07,-0.52l-2.55,-2.55c-0.16,0.05 -0.34,0.07 -0.52,0.07s-0.36,-0.02 -0.52,-0.07l-4.55,4.56c0.05,0.16 0.07,0.33 0.07,0.51 0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2 0.9,-2 2,-2c0.18,0 0.35,0.02 0.51,0.07l4.56,-4.55C8.02,9.36 8,9.18 8,9c0,-1.1 0.9,-2 2,-2s2,0.9 2,2c0,0.18 -0.02,0.36 -0.07,0.52l2.55,2.55c0.16,-0.05 0.34,-0.07 0.52,-0.07s0.36,0.02 0.52,0.07l3.55,-3.56C19.02,8.35 19,8.18 19,8c0,-1.1 0.9,-2 2,-2s2,0.9 2,2z"/>
</vector>

What would the upcoming icon do?

Keeping in mind the necessity for the feature to be less complex, I decided that the upcoming icon will lead the user to a dialog box that shows the status of upcoming sessions in that micro location. The implementation for this feature involved 2 main things:

  1. Finding out the upcoming session from the list of sessions in the microlocation.
  2. Generate a dialog box that shows information about that session.

Finding position of upcoming session in Recycler View

Upcoming session will be a session whose starting time comes after the current time. So the approach was simple.

  1. Run a loop on a sorted list of all sessions in a microlocation.
  2. Find out every session’s start time.
  3. Compare the start time of every session with the current time.
  4. Find the first session whose start time comes after the current time.
  5. Store that session’s position, name, ID and other stuff like track name and track color.
  6. Break out of the loop.

This was the basic logic or algorithm, so to say. Here’s the implementation in the upcomingSession() function:

public void upcomingSession(){
   String upcomingTitle = "";
   String track = "";
   String color = null;
   Date current = new Date();
   for (Session sess:sortedSessions){
       try {
           Date start = DateUtils.getDate(sess.getStartsAt());
           if (start.after(current)){
               upcomingTitle = sess.getTitle();
               track = sess.getTrack().getName();
               color = sess.getTrack().getColor();
               break;
           }
       } catch (ParseException e) {
           e.printStackTrace();
       }
   }

Now, displaying a dialog box consisting of all the necessary information is an easy thing to do once you have the required information. So, I’ll just provide some code for it here without explaining much about it.

The initialisations:

public void upcomingSessionsInitial(){
   upcomingDialogBox = new Dialog(this);
           upcomingDialogBox.setContentView(R.layout.upcoming_dialogbox);
           trackImageIcon = (ImageView)upcomingDialogBox.findViewById(R.id.track_image_drawable);
           upcomingSessionText = (TextView) upcomingDialogBox.findViewById(R.id.upcoming_session_textview);
           upcomingSessionTitle = (TextView) upcomingDialogBox.findViewById(R.id.upcoming_Session_title);
           Button dialogButton = (Button) upcomingDialogBox.findViewById(R.id.upcoming_button);
           dialogButton.setOnClickListener(new View.OnClickListener() {
               @Override
               public void onClick(View view) {
                   upcomingDialogBox.dismiss();
               }
           });
}

The calling:

switch (item.getItemId()){
       case R.id.action_map_location:
           FragmentManager fragmentManager = getSupportFragmentManager();
           FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

           Bundle bundle = new Bundle();
           bundle.putBoolean(ConstantStrings.IS_MAP_FRAGMENT_FROM_MAIN_ACTIVITY, false);
           bundle.putString(ConstantStrings.LOCATION_NAME, location);

           Fragment mapFragment = ((OpenEventApp)getApplication())
                   .getMapModuleFactory()
                   .provideMapModule()
                   .provideMapFragment();
           mapFragment.setArguments(bundle);
           fragmentTransaction.replace(R.id.content_frame_location, mapFragment, FRAGMENT_TAG_LOCATION).addToBackStack(null).commit();

           sessionRecyclerView.setVisibility(View.GONE);
           noSessionsView.setVisibility(View.GONE);
           menu.setGroupVisible(R.id.menu_group_location_activity, false);
           return true;
       case android.R.id.home:
           onBackPressed();
           getSupportFragmentManager().popBackStack();
           sessionRecyclerView.setVisibility(View.VISIBLE);
           return true;
       case R.id.upcoming_sessions:
           upcomingDialogBox.show();
           return true;
       default:
           return true;
   }
}

Final result:

This is the final result or solution that we generated for the issue that was addressed by one of the users:

Some useful links are:

Continue ReadingDisplaying Upcoming Sessions at a Microlocation Open Event Android

Shifting from Java to Kotlin in SUSI Android

SUSI Android (https://github.com/fossasia/susi_android) is written in Java. After the announcement of Google to officially support Kotlin as a first class language for Android development we decided to shift to Kotlin as it is more robust and code friendly than Java.

Advantages of Kotlin over Java

  1. Kotlin is a null safe language. It changes all the instances used in the code to non nullable type thus it ensures that the developer don’t get any nullPointerException.
  2. Kotlin provides the way to declare Extensive function similar to that of C#. We can use this function in the same way as we use the member functions of our class.
  3. Kotlin also provides support for Lambda function and other high order functions.

For more details refer to this link.

After seeing the above points it is now clear that Kotlin is much more effective than Java and there is harm in switching the code from Java to Kotlin. Lets now see the implementation in Susi Android.

Implementation in Susi Android

In the Susi Android App we are implementing the MVP design with Kotlin. We are converting the code by one activity each time from java to Kotlin. The advantage here with Kotlin is that it is totally compatible with java at any time. Thus allowing the developer to change the code bit by bit instead of all at once.Let’s now look at SignUp Activity implementation in Susi Android.

The SignUpView interface contains all the function related to the view.

interface ISignUpView {


  fun alertSuccess()

  fun alertFailure()

  fun alertError(message: String)

  fun setErrorEmail()

  fun setErrorPass()

  fun setErrorConpass(msg: String)

  fun setErrorUrl()

  fun enableSignUp(bool: Boolean)

  fun clearField()

  fun showProgress()

  fun hideProgress()

  fun passwordInvalid()

  fun emptyEmailError()

  fun emptyPasswordError()

  fun emptyConPassError()


}

The SignUpActivity implements the view interface in the following way. The view is responsible for all the interaction of user with the UI elements of the app. It does not contain any business logic related to the app.

class SignUpActivity : AppCompatActivity(), ISignUpView {


  var signUpPresenter: ISignUpPresenter? = null

  var progressDialog: ProgressDialog? = null


  override fun onCreate(savedInstanceState: Bundle?) {

      super.onCreate(savedInstanceState)

      setContentView(R.layout.activity_sign_up)

      addListeners()

      setupPasswordWatcher()


      progressDialog = ProgressDialog(this@SignUpActivity)

      progressDialog?.setCancelable(false)

      progressDialog?.setMessage(this.getString(R.string.signing_up))


      signUpPresenter = SignUpPresenter()

      signUpPresenter?.onAttach(this)

  }


  fun addListeners() {

      showURL()

      hideURL()

      signUp()

  }


  override fun onOptionsItemSelected(item: MenuItem): Boolean {

      if (item.itemId == android.R.id.home) {

          finish()

          return true

      }

      return super.onOptionsItemSelected(item)

  }

Now we will see the implementation of models in Susi Android in Kotlin and compare it with Java.

Lets First see the implementation in Java

public class WebSearchModel extends RealmObject {

  private String url;

  private String headline;

  private String body;

  private String imageURL;


  public WebSearchModel() {

  }


  public WebSearchModel(String url, String headline, String body, String imageUrl) {

      this.url = url;

      this.headline = headline;

      this.body = body;

      this.imageURL = imageUrl;

  }


  public void setUrl(String url) {

      this.url = url;

  }


  public void setHeadline(String headline) {

      this.headline = headline;

  }


  public void setBody(String body) {

      this.body = body;

  }


  public void setImageURL(String imageURL) {

      this.imageURL = imageURL;

  }


  public String getUrl() {

      return url;

  }


  public String getHeadline() {

      return headline;

  }


  public String getBody() {

      return body;

  }


  public String getImageURL() {

      return imageURL;

  }

}
open class WebSearchModel : RealmObject {


  var url: String? = null


  var headline: String? = null


  var body: String? = null


  var imageURL: String? = null


  constructor() {}


  constructor(url: String, headline: String, body: String, imageUrl: String) {

      this.url = url

      this.headline = headline

      this.body = body

      this.imageURL = imageUrl

  }

}

You can yourself see the difference and how easily with the help of Kotlin we can reduce the code drastically.

For diving more into the code, we can refer to the GitHub repo of Susi Android (https://github.com/fossasia/susi_android).

Resources

Continue ReadingShifting from Java to Kotlin in SUSI Android