Posts by Hani

Happy android developer :P

Kotlin – Extension Functions

kotlin_800x320

What is Extension Functions?

Kotlin, provides the ability to extend a class with a new functionality without having to inherit from the class or use any type of design pattern such as Decorator (Annotation in Java).

What it looks like?

We want to add sayHello function to all String objects in our project. We can do it by Inheritance and Extension Function.

Inheritance: We should inherit from String class and add sayHello function to it then we should use our new custom String class instead of original String class.

class CustomString : String{
   fun sayHello(): String {
      return "Hello ".plus(this)
   }
}

// usage
val myString: CustomString = "Hani";
println(myString.sayHello())

// output: Hello Hani

Extension Function: MAGIC IS HERE, below function can do it for us 🙂

fun String.sayHello(): String {
   return "Hello ".plus(this)
}

// usage
val myString = "Hani"
println(myString.sayHello())

// output: Hello Hani

Some Useful Examples

Hiding soft keyboard in Android

fun Activity.hideKeyboard() {
   val view = this.currentFocus
   if (view != null) {
      val imm = this.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager imm.hideSoftInputFromWindow(view.windowToken, 0)
   }
}

// usage
activity.hidekeyboard()

Convert DP to PX

fun Context.convertDpToPx(dps: Float): Int = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dps, resources.displayMetrics).toInt()

// usage:
context.convertDpToPx(10)

Trim text of EditText

fun EditText.trim(): String = this.text.toString().trim()

// usage
myEditText.trim()

Data-binding Pros and Cons, Choose wisely

android

I have used data-binding in two projects, I would say data-binding is too bad for your project, it can be the cause of spaghetti code. Let’s see the pros and cons of data-binding.

Pros

1- Easy to use
2- Less boilerplate code (eliminating findViewById and more)
3- Fit to MVVM pattern
4- Synchronize data between sources and UI elements

Cons

1- Showing unrelated errors

Showing unrelated errors when compiler can’t compile the project for any problem, (e.g. Dagger2 or Room or Realm) so compiler can’t generate data-binding related classes so it will show unrelated errors like below:
data-binding_unrelated_errors

2- Business logic belongs in code

It can be more complex if you have different layouts for different screens.

3- Updating view after changing data can be cause of blink

4- Auto-rename for package names doesn’t work on XML files

5- The auto-generated .class files increases app size

it will matter if you have tons of it.

Other Solutions

  • Use databinding but do not use events !! (It can fix the cons #2)
  • Using ButterKnife
  • Using traditional way, findViewById()
  • Using Kotlin Android Extensions

Learn Dagger2 with simple example

unnamed

I have read and watched a lot of different Dagger2 tutorials but most of them are too long or hard to understand so I decided to write a new simple and short tutorial for Dagger2, I hope you like it.

Why we need it?

  • Simplifies access to shared instances: It provides a simple way to obtain references to shared instances, for example once we declare in Dagger our singleton instances such as `SharedPrefrences` then we can declare fields with a simple `@Inject` annotation.
  • Easier unit and integration testing: We can easily swap out modules that make network responses and mock out this behavior.

Lest’s start with a simple example

Full source of example is available on my GitHub account.

Add Dagger2 dependencies

First of all we need to add Dagger2 dependencies, Put below code to your module-level build.gradle file.

add-Dagger2-dependencies

If you are getting an error like Error:Conflict with dependency ‘com.google.code.findbugs:jsr305’ in project ‘:app’ you should add the following to your main app/build.gradle file.

conflict-error

Two simple class

We have two classes (Vehicle and Motor), Vehicle class needs Motor class to run and MainActivity needs Vehicle class. We will use Dagger2 to provide these instances.

vehicle-class

motor-class

Module class

Module class is responsible for providing objects which can be injected, In this example we want to inject Motor class to Vehicle class and inject Vehicle class to MainActivity so  we should create MyModule to provide these instances.

mymodule.png

@Provide annotation: returned object from this method is available for dependency injection.

@Component interface

Dagger2 needs component class to know how should it create instances from our classes.

MyComponent.png

@Component interface: connection between the provider of object and the objects which express a dependency.

Inject Dependency in Constructor

By adding @Inject annotation, dagger2 can automatically create an instance from that object like our example Motor object in Vehicle class.

Inject dependency in MainClass

Dagger2 can automatically inject dependencies in constructors, but Android components (activities, fragments, etc.) are instantiated by Android framework which makes it difficult to use dependency injection on them, so we should inject them manually like below code:

MainActivity

Little more

Read this article : Dependency Injection with Dagger2
Watch these videos:

Change text color of MenuItem in Navigation Drawer

ezgif-3-5ca8deec47

Navigation drawer style is my worst nightmare. In my navigation drawer I have different items with different text colors. In this post I will show you how we can change MenuItems text color programmatically.

For doing this we need two methods:

  1. Set text color for menu item:
  2.   private void setTextColorForMenuItem(MenuItem menuItem, @ColorRes int color) {
        SpannableString spanString = new SpannableString(menuItem.getTitle().toString());
        spanString.setSpan(new ForegroundColorSpan(ContextCompat.getColor(this, color)), 0, spanString.length(), 0);
        menuItem.setTitle(spanString);
      }
    
  3. Reset all menu items text color:
  private void resetAllMenuItemsTextColor(NavigationView navigationView) {
    for (int i = 0; i < navigationView.getMenu().size(); i++)
      setTextColorForMenuItem(navigationView.getMenu().getItem(i), R.color.textPrimary);
  }

We are almost done, we should just set text color for each menu item in onNavigationItemSelected method like below:

  @Override
  public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    resetAllMenuItemsTextColor(navigationView);
    setTextColorForMenuItem(item, R.color.colorPrimary);

    switch (item.getItemId()) {
      case R.id.nav_search_jobs:
        setTextColorForMenuItem(item, R.color.nav_search);
        // do other stuff
        break;
      case R.id.nav_job_recommended:
        setTextColorForMenuItem(item, R.color.nav_recommendation);
        // do other stuff
        break;
    }
    ...
  }

That’s it 😉

What’s your flavor?

product_flavors

Product Flavor is a very powerful feature. It’s the most useful way to work with different hosts, icons, package names or even makes free and paid versions of your app depending on various versions of the same app.

What is Product Flavors?

According to Google:
“You can customize product flavors to use different code and resources while sharing and reusing the parts that are common to all versions of your app.”

How can you define it?

You must write it on module-level build.gradle file inside the android block.

  productFlavors {
    free {
      applicationId 'com.example.myapp.free'
    }
    paid {
      applicationId 'com.example.myapp.paid'
    }
  }

After you create and configure your product flavars, click on Sync Now. after the sync completes, Gradle automatically creates build variants based on your build types and product flavars, and names them according to (product-flavor)(Build-Type).

screenshot-from-2017-02-13-14-50-02

Let’s see some useful examples

Multiple package names

What if you want to have installed on your phone one app with development state and one for production state. The only thing you have to do is define it like below:

  productFlavors {
    devel {
      applicationId "com.example.myapp.devel"
    }
    prod {
      applicationId "com.example.myapp"
    }
  }

or if you have two different versions like demo version and full version:

  productFlavors {
    demo {
      applicationIdSuffix ".demo"
      versionNameSuffix "-demo"
    }
    full {
      applicationIdSuffix ".full"
      versionNameSuffix "-full"
    }
  }

Use multiple hosts

We should define HOST variable:

  productFlavors {
    devel {
      buildConfigField 'String', 'HOST', '"http://37.61.202.252:8580"'
    }
    prod {
      buildConfigField 'String', 'HOST', '"http://37.61.202.252:8580"'
    }
  }

We will try to show you how you can integrate this with Retrofit to send requests to the appropriate server without handling which server you’re pointing and based on the flavor:

retrofit = new retrofit2.Retrofit.Builder()
        .baseUrl(BuildConfig.HOST)
        .client(okHttp)
        .build();

Like the above example you can define different variables in Gradle and then use it in java code, check the below snippet:

productFlavors {
    devel {
      buildConfigField 'int', 'FOO', '52'
      buildConfigField 'String', 'FOO_STRING', '"bar"'
      buildConfigField 'boolean', 'LOG', 'true'
    }
    prod {
      buildConfigField 'int', 'FOO', '42'
      buildConfigField 'String', 'FOO_STRING', '"foo"'
      buildConfigField 'boolean', 'LOG', 'false'
    }
  }

You can use them in your java code:

    Integer foo = BuildConfig.FOO;
    String fooString = BuildConfig.FOO_STRING;
    boolean log = BuildConfig.LOG;

Bonus: BuildConfig.java

You can find all information and data relating to the Build Variant in BuildConfig.class, This class created automatically for each Build Variant by Gradle. You can find this file in build directory or with Ctrl+N shortcut in AndroidStudio.

buildconfig

Declare Dependencies

You can configure a dependency for a specific build variant or even testing source set.

dependencies {
    freeCompile project(":mylibrary")
    fullReleaseCompile project(path: ':library', configuration: 'release')
    fullDebugCompile project(path: ':library', configuration: 'debug')
    testCompile 'junit:junit:4.12'
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
}

Build with source sets

Do you want to use some mocking objects in your tests? or you don’t want to use full version files or classes in the demo version? Let’s see how we can do it.

First of all, You should add ‘demo’ and ‘full’ product flavors into build.gradle file then click on Sync Now button in notification bar, after syncing you should put the real files for full version into /src/full/ path and put the demo files for demo version into /src/demo/ path. Gradle will find the corresponding files and will add them to the output package. let’s see it in code:

My build.gradle file:

  productFlavors {
    demo {
    }
    full {
    }
  }

Now we have 4 different build variants:

screenshot-from-2017-03-06-20-30-44

We need an interface class like below in /src/main/java/com/hanihashemi/test path:

public interface iApplicationCoreValue {
  void doImportantJob();
}

We should implement it for demo and full version.

For full version we should implement ApplicationCoreValue in /src/full/java/com/hanihashemi/test path

public class ApplicationCoreValue implements iApplicationCoreValue {
  @Override
  public void doImportantJob() {
    // show message ==> Please buy the full version 
  }
}

For demo version we should implement ApplicationCoreValue in /src/demo/java/com/hanihashemi/test path

public class ApplicationCoreValue implements iApplicationCoreValue {
  @Override
  public void doImportantJob() {
    // do it here
  }
}

YESSS it’s done, by choosing releaseFull we can build full version and by chossing releaseDemo we can build it for demo version.

If you think I missed something, please leave me a comment 😉

For a more in-depth and technical description of Build Variants, check out the Android Studio Build Variant user guide:
Configure Build Variants

I encourage you to read this tutorial as well:
Android Testing Codelab

Espresso, Using Intent Extra

Espresso

As you know for creating a new Espresso test in JUnit 4 style you should use ActivityTestRule to reduce the amount of boilerplate code you need to write. By using it, the testing framework launches the activity under test before each test method annotated with @Test and @before.

@RunWith(AndroidJUnit4.class)
public class UserProfileTest {

  @Rule
  public ActivityTestRule<UserProfileActivity> mActivityTestRule = new ActivityTestRule<>(UserProfileActivity.class);

  @Test
  public void signUpActivityTest() {
    ...
  }
}

So what if you want to send Intent to an activity? Here is the example:

@RunWith(AndroidJUnit4.class)
public class UserProfileTest {

  @Rule
  public ActivityTestRule<UserProfileActivity> mActivityTestRule = new ActivityTestRule<>(UserProfileActivity.class) {
    @Override
    protected Intent getActivityIntent() {
      Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
      Intent intent = new Intent(context, UserProfileActivity.class);
      intent.putExtra("ID", "120");
      return intent;
    }
  };

  @Test
  public void signUpActivityTest() {

  }
}

Start Activity from another Application

start_activity

We want to start Notification Activity in FSB app from our app, To doing this we have two different solution:

1. By set component

Intent intent = new Intent();
intent.setComponent(new ComponentName("application-package-name","class-path"));
startActivity(intent);

For example FSB application package name is “de.findsomebuddy.app” and Notification Activity class path is “de.findsomebuddy.app.activity.NotificationActivity” so my ComponentName should be like this:

intent.setComponent(new ComponentName("de.findsomebuddy.app","de.findsomebuddy.app.activity.NotificationActivity"));

2. By action name

You should add “intent-filter” for the target Activity (NotificationActivity) in “AndroidManifest.xml” like this:

<activity android:name=".features.main.NotificationActivity">
   <intent-filter>
      <action android:name="action-name" />
      <category android:name="android.intent.category.DEFAULT" />
   </intent-filter>
</activity>

For action name you define, it’s best to use the package name as a prefix to ensure uniqueness. For example, a NOTIFICATION action might be specified as follows:

<action android:name="de.findsomebuddy.app.activity.NOTIFICATION"/>

Intent should be like this:

Intent intent = new Intent("unique-action-name");
intent.putExtra("title", "my custom title");
startActivity(intent);

You can get extra data in target activity like this:

@Override
protected void onNewIntent(Intent intent) {
   super.onNewIntent(intent);
   String title = intent.getStringExtra("title");
}

Note: Do not use onCreate method because if your activity’s launchMode was set to singleTop the onCreate method will not call if activity is already loaded in memory.