Porting Phimpme Android to Kotlin

As we are going ahead in Phimpme Project we are now on verge to start our account manager which deals with sharing images to many platforms right from the app. The account manager will take care of logging In the user. Saving it’s important credentials such access token, username etc as required by the API.

Google IO ‘17 just passed, and we seen tons of new features, APIs and development tools. One of the them is official support for Kotlin in Android Studio.

As stated by the developers at the conference that one best way to work on Kotlin is add today in your project. Because it is compatible with Java, we can work together on both languages in the same project. It is not mandatory for you to shift your entire code to Kotlin to build a project. So starting with the account manager we decided to bring this to our code. It helps in reducing the boilerplate code for example in Phimpme, I created a model for Realm database.

open class AccountDatabase(

       @PrimaryKey var name: String = "",

       var username: String = "",

       var token: String = ""

) : RealmObject()

That’s all I need to create a model class, no need to create getter and setter property

This helps me to get details from getter methods and show on Account Manager Recycler View like below.

Step 1 : Upgrade to Android Studio Preview 3.0

Android Studio Preview 3.0 comes up with all new features and Kotlin support. We all upgraded to that. It has a great Android Profiler with advance features for debugging and logcat is now moved separately. This step is not mandatory, you can work on older version of Android Studio as well.

Step 2 : Configure Kotlin

It’s easy in Android Studio Preview 3.0 to configure Kotlin. Go to Tools → Kotlin → Configure Kotlin in project.

What in the configuration

  • It added a classpath dependency in project level build.gradle file
classpath"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
  • Added Kotlin plugin
apply plugin: 'kotlin-android'
  • Added kotlin std lib in app level build.gradle
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"

Step 3: How to add Kotlin files

Now your project is ready for Kotlin. In Android Studio Preview 3.0 you can create new Kotlin files from the file menu.

Also by using Activity template you can select the source language as Java or Kotlin as per your preference.

Step 4 : Work with Kotlin

There are a lot new features in Kotlin to explore. Some of them are

  • Null Safety : In Kotlin, the type system distinguishes between references that can hold null (nullable references) and those that can not (non-null references). For example, a regular variable of type String cannot hold null.
var a: String = "abc"
a = null // compilation error

To allow nulls, we can declare a variable as nullable string, written String?:

 var b: String? = "abc"
 b = null // ok
  • Val and Var are two keywords in Kotlin to declare variables. Val gives you read only variable which is same as final modifier in Java, it is not changing. In other words it is immutable Data variables. Var is mutable data variable
  • Semicolons (;) are optional
  • No switch it’s when block in Kotlin. No need to write break and case: below is snippet from phimpme app
override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.action_add_account -> {
                val fragmentManager = fragmentManager
                val accountsPicker = AccountPickerFragment().newInstance("Accounts Picker")
                accountsPicker.show(fragmentManager, "Accounts Picker")
            }
            else -> return super.onOptionsItemSelected(item)
        }
        return true
    }


Source:

Continue ReadingPorting Phimpme Android to Kotlin

Adding Pinterest Integration in Phimpme Android

After establishing Facebook and Twitter share in Phimpme, our next goal was to integrate more social networking sites. Pinterest is an ideal website for us as it is widely used among various photography enthusiasts. Our goal was to share the image on the Pinterest website without the use of any other native android application.

Note: First, we added Phimpme app in the developer section https://developers.pinterest.com/apps/. This step is crucial as it generates APP ID and it is necessary for authentication.

Adding Pinterest option in Accounts Activity in Phimpme

In accounts activity, we have added a list of accounts that we have integrated in a Recyclerview. Currently, we have integrated Facebook and Twitter. We need to add Pinterest App icon and Pinterest option in the Recyclerview.

To add Pinterest app icon I downloaded the icon from iconfinder in SVG format. Using SVG format allows icon to resize itself according to the screen sizes of the users. We saved the icon file name as ic_pinterest_black.xml

Pinterest icon SVG:

<vector android:height="24dp" android:viewportHeight="32.0"
   android:viewportWidth="32.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
   <path android:fillColor="#231F20" android:pathData="M16,0.27C7.16,0.27 0,7.43 0,16.27c0,6.55 3.94,12.18 9.58,14.65c-0.05,-1.12 -0.01,-2.46 0.28,-3.67c0.31,-1.3 2.06,-8.72 2.06,-8.72s-0.51,-1.02 -0.51,-2.53c0,-2.37 1.37,-4.14 3.08,-4.14c1.46,0 2.16,1.09 2.16,2.4c0,1.46 -0.93,3.65 -1.41,5.68c-0.4,1.7 0.85,3.08 2.53,3.08c3.03,0 5.07,-3.89 5.07,-8.51c0,-3.51 -2.36,-6.13 -6.66,-6.13c-4.85,0 -7.88,3.62 -7.88,7.66c0,1.39 0.41,2.38 1.05,3.14c0.29,0.35 0.34,0.49 0.23,0.89C9.51,20.37 9.33,21.08 9.26,21.36c-0.11,0.4 -0.44,0.55 -0.8,0.4c-2.23,-0.91 -3.28,-3.36 -3.28,-6.11c0,-4.55 3.83,-9.99 11.44,-9.99c6.11,0 10.13,4.42 10.13,9.16c0,6.28 -3.49,10.97 -8.63,10.97c-1.73,0 -3.35,-0.93 -3.91,-1.99c0,0 -0.93,3.69 -1.13,4.4c-0.34,1.23 -1,2.46 -1.61,3.43C12.9,32.04 14.43,32.27 16,32.27c8.84,0 16,-7.16 16,-16S24.84,0.27 16,0.27z"/>
</vector>

We made an array of all the accounts list in the Accounts Activity. I added Pinterest in the array list. This array list is inflated in the Accounts Activity.

public static String[] accountName = {"Facebook", "Twitter", "Drupal", "NextCloud", "Wordpress", "Pinterest"};

To add the icon the recyclerview, we have to get the resource id from the drawable folder and then set is in the recyclerview. We have done it dynamically so that we don’t have to fetch the id of every accounts icons.

Integer id = getContext().getResources().getIdentifier(context.getString(R.string.ic_) +
               (accountName[position].toLowerCase()) + "_black"
       , context.getString(R.string.drawable)
       , getContext().getPackageName());

holder.accountAvatar.setImageResource(id);

Importing Pinterest SDK in Phimpme project

To import the Pinterest SDK in the project, please download the Pinterest SDK from the link: http://assets.pinterest.com/sdk/android-pdk.tar. Go to File->import new module->Import Gradle Project. Build the project, if there is any error resolve the error and then build Gradle again.

compile project(‘:pdk’)

Add APP ID in the manifest file of the project

We need to add the APP ID that was generated while making the app the Pinterest in the manifest folder of the Phimpme Android. In the case of Phimpme, we have added the APP ID in the intent filter in the Accounts Activity. This is necessary for the interaction of the Phimpme with the Pinterest website.

<activity
   android:name=".accounts.AccountActivity"
   android:screenOrientation="portrait"
   android:theme="@style/Theme.AppCompat.NoActionBar">
   <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="pdk4910600717739247160" />
   </intent-filter>
</activity>

Establishing Pinterest authentication in Accounts Activity

First, we need to import all the necessary class from the Pinterest SDK. These class includes callback functions, PDKClient which manages the interface between the Phimpme app and the Pinterest website. We need to pass the APP ID in the PDKClient function as an argument and also the view in the onConnect function in PDKClient.

pdkClient = PDKClient.configureInstance(this, getResources().getString(R.string.pinterest_app_id));
pdkClient.onConnect(this);
pdkClient.setDebugMode(true);

Before signing In we need to check if we are already signed in or not. This is how we have done in  Phimpme app. If the app is already signed In a Toast will pop up showing that it already signed In.

if (accountPresenter.checkAlreadyExist(accountName[5])) {
   Toast.makeText(this, R.string.already_signed_in,
           Toast.LENGTH_SHORT).show();
}

Authentication happens in these following steps:

Adding permissions in the List                                                                                These include the following permissions:

  • Read permission
  • Write permission
  • Read relationship
  • Write relationship
List scopes = new ArrayList<String>();
scopes.add(PDKClient.PDKCLIENT_PERMISSION_READ_PUBLIC);
scopes.add(PDKClient.PDKCLIENT_PERMISSION_WRITE_PUBLIC);
scopes.add(PDKClient.PDKCLIENT_PERMISSION_READ_RELATIONSHIPS);
scopes.add(PDKClient.PDKCLIENT_PERMISSION_WRITE_RELATIONSHIPS);

Passing the view, scope array and PDKCallback as arguments in the login function

I have passed the view, scope array list and PDKCallback as arguments in the login function in PDKClient class. This will initiate the authentication process and the user will be redirected to the Pinterest web page after the user has filled up the credentials properly and logged in to the Pinterest the user will be redirected to the Phimpme accounts page. If the user has logged In successfully onSuccess function with being called where PDKResponse will pass an argument. This response object can be used to fetch the username of the logged In user or the Pin and the BoardID of the user.

@Override
public void onSuccess(PDKResponse response) {
   Log.d(getClass().getName(), response.getData().toString());

   // Begin realm transaction
   realm.beginTransaction();

   // Creating Realm object for AccountDatabase Class
   account = realm.createObject(AccountDatabase.class,
           accountName[5]);


   // Writing values in Realm database
   account.setUsername(String.valueOf(response.getUser()));

   // Finally committing the whole data
   realm.commitTransaction();

   Toast.makeText(AccountActivity.this, R.string.success, Toast.LENGTH_SHORT).show();
}

If the user fails to log in to the account onFailure function will be called and a Toast will pop up.

@Override
public void onFailure(PDKException exception) {
   Log.e(getClass().getName(), exception.getDetailMessage());
   Toast.makeText(AccountActivity.this, R.string.fail, Toast.LENGTH_SHORT).show();
}

Inserting the credentials to the Realm Database

We have used the Realm database to store all the accounts credentials in one place. This is necessary for the maintenance and to check if the user is logged or not. AccountName array holds the list of the name which is added to the database.

// Begin realm transaction
realm.beginTransaction();

// Creating Realm object for AccountDatabase Class
account = realm.createObject(AccountDatabase.class,
       accountName[5]);


// Writing values in Realm database
account.setUsername(String.valueOf(response.getUser()));

// Finally committing the whole data
realm.commitTransaction();

Conclusion

Now, users can login into the Pinterest from the Accounts Activity. After authenticating the users, we can use the authentication to share the image from the app to the Pinterest website.

Github

Resources

Continue ReadingAdding Pinterest Integration in Phimpme Android

Uploading images to Dropbox from the Phimpme App

The Phimpme Android application along with the camera, gallery and image editing features offers the option to upload images to many social media and cloud storages without having to install several other applications. As we can see from the screenshot below, Phimpme application contains a user-friendly accounts screen to connect to the accounts using a username and password so that we can upload photos from the share screen to that particular account later on.

One such famous cloud storage is the Dropbox and in this tutorial, I am explaining how I implemented the account authentication and image uploading feature to Dropbox in the Phimpme Android application.

Step 1

The first thing we need to do is to create an application in the Dropbox application console and to get the app key and the API secret key which we will require later on for the authentication purposes. To create an application on the Dropbox application console page,

  1. Go to this link. It will open a page as depicted in the screenshot below:
  2. Now click on the Dropbox API option.
  3. Click on App folder – access to a single folder created specifically for your app.
  4. Write the name of your application and press the create app button.

After this, we will be redirected to the page which will contain all the keys required to authenticate and upload photos.

Step 2

After getting the keys, the next thing we need to do is install the Dropbox SDK. To do this:

  1. Download the Android SDK from this link and extract it.
  2. Copy the dropbox-android-sdk-1.6.3.jar and json_simple-1.1.jar file to the libs folder.
  3. Click on the add as library button by right clicking on the jar files added.
  4. Copy the below-mentioned code in the AndroidManifest.xml file which defines the dropbox login activity within a new activity tag.
android:name="com.dropbox.client2.android.AuthActivity"
android:launchMode="singleTask"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:configChanges="orientation|keyboard">
<intent-filter>
     <!-- Change this to be db- followed by your app key -->
         <data android:scheme="db-app_key" />
         <action android:name="android.intent.action.VIEW" />
         <category android:name="android.intent.category.BROWSABLE"/>
         <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

In the 7th line of the code snippet, replace the app_key with the key you received from following the step 1.

Step 3

After setting up everything, we need to extract the access token for the user to upload the photos in that particular account. To do this, we can make use of the below code snippet, which uses the dropbox SDK we installed in step 2 to create an object named mDBApi and initialises it to authenticate the user.

private DropboxAPI<AndroidAuthSession> mDBApi;AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
AndroidAuthSession session = new AndroidAuthSession(appKeys);
mDBApi = new DropboxAPI<AndroidAuthSession>(session);

After initialisation in the onCreate method of the activity, we can authenticate the user using the following line of code.

mDBApi.getSession().startOAuth2Authentication(MyActivity.this);

This will open up a window where the user will be prompted to login to their dropbox account. After the login is finished, we will be taken back to the activity which made the authentication call, so in the onResume method, we need to get the access token of the user which will be used later on to upload the images using the following code snippet provided below:

mDBApi.getSession().finishAuthentication();
String accessToken = mDBApi.getSession().getOAuth2AccessToken();

After we have stored the access token, we can upload the selected image to the Dropbox using the following line of code.

File file = new File("working-draft.jpg");
FileInputStream inputStream = new FileInputStream(file);
Entry response = mDBApi.putFile("/magnum-opus.jpg", inputStream,
                              file.length(), null, null);

For more information on uploading and retrieving data from the Dropbox account, you can go to the dropbox developer guide and for working example refer to the Phimpme Android repository in the resources below.

Resources

  1. Phimpme Repo : Phimpme Android github repository.
  2. Dropbox official documentation : https://www.dropbox.com/developers-v1/core/start/android
  3. Dropbox application console : https://www.dropbox.com/developers/apps
  4. Stackoverflow example to upload image on Dropbox : https://stackoverflow.com/questions/10827371/upload-photo-to-dropbox-by-android
Continue ReadingUploading images to Dropbox from the Phimpme App

Applying Filters on Images using Native Functions in Phimpme Android

In the Phimpme application, the user can apply multiple colorful filters on images captured from application’s camera or already available images on the device. This application of filters on images is performed using native image processing functions. We implemented many filters for enhancing the image. Implementation of few of the filter functions is shown below.

Filters are applied to an image by modifying the color values of pixels in the Phimpme application. This is similar to the implementation of image enhancing functions in the editor of Phimpme. My post on that is available here.

Black and White filter:

Black and white filter can be called as gray scaling the image. In a gray scale image, there will only be a single color channel. If multiple channels are present, the corresponding pixel values in all channels will be same. Here in Phimpme, we have an RGB image. It has 3 color channels. Every pixel has three values. Black and white filter can be implemented by replacing those three different values with the average of those values. The implementation of the function and the resultant image with the comparison is shown below.

void applyBlackAndWhiteFilter(Bitmap* bitmap) {
  register unsigned int i;
  unsigned int length = (*bitmap).width * (*bitmap).height;
  register unsigned char grey;
  unsigned char* red = (*bitmap).red;
  unsigned char* green = (*bitmap).green;
  unsigned char* blue = (*bitmap).blue;
  for (i = length; i--;) {
     grey = (red[i] + green[i] + blue[i]) / 3;
     red[i] = truncate((int) grey);
     green[i] = truncate((int) grey);
     blue[i] = truncate((int) grey);
  }
}

    

Ansel Filter

This Ansel Filter is a monotone filter present in Phimpme which is similar to black and white. Here in this filter, the contrast will be little high and gives the image artistic look. This is achieved in Phimpme by hard overlaying the gray pixel components of the image. The rest is same as the black and white filter. The implementation of hard overlay blending and the Ansel function is shown below with the resultant images.

static unsigned char hardLightLayerPixelComponents(unsigned char maskComponent, unsigned char imageComponent) {

  return (maskComponent > 128) ? 255 - (( (255 - (2 * (maskComponent-128)) ) * (255-imageComponent) )/256) : (2*maskComponent*imageComponent)/256;
}

void applyAnselFilter(Bitmap* bitmap) {
/*initializations*/
  unsigned char br,bg,bb;
  for (i = length; i--; ) {
       grey = (red[i] + green[i] + blue[i]) / 3;
       int eff = hardLightLayerPixelComponents(grey, grey);
       red[i] = truncate(eff);
       green[i] = truncate(eff);
       blue[i] = truncate(eff);
  }
}

    

Sepia Filter

The Sepia Filter in Phimpme results in a monotone image with orangish yellow tone. Its implementation uses pre-defined look up tables(LUTs) for all the three channels. The luminosity of a particular pixel is found out and then the red, green, blue values are found out from the look up tables(LUTs) corresponding to that luminosity. The look up table arrays we used for the sepia effect in Phimpme are given below and the implementation is also shown below.

const unsigned char sepiaRedLut[256] = {24, 24, 25, 26, 27, 28, 29, 30, 30, 30, 31, 32, 33, 34, 35, 36, 37, 37, 38, 38, 39, 40, 41, 42, 43, 43, 44, 45, 46, 47, 47, 48, 49, 50, 50, 51, 52, 53, 54, 55, 56, 57, 57, 58, 58, 59, 60, 61, 62, 63, 64, 64, 65, 66, 67, 68, 69, 70, 71, 71, 72, 72, 73, 74, 75, 76, 77, 78, 78, 79, 80, 81, 82, 83, 84, 85, 85, 86, 87, 88, 89, 89, 90, 91, 92, 93, 93, 94, 95, 96, 97, 97, 98, 99, 100, 101, 102, 102, 103, 104, 105, 106, 107, 108, 109, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 146, 147, 148, 149, 150, 151, 152, 153, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 178, 180, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, 190, 191, 193, 194, 195, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 255};

const unsigned char sepiaGreenLut[256] = {16, 16, 16, 17, 18, 18, 19, 20, 20, 20, 21, 22, 22, 23, 24, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 31, 32, 33, 33, 34, 35, 36, 36, 36, 37, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45, 46, 47, 47, 48, 48, 49, 50, 51, 51, 52, 53, 54, 54, 55, 55, 56, 57, 58, 59, 60, 61, 61, 61, 62, 63, 64, 65, 66, 67, 67, 68, 68, 69, 70, 72, 73, 74, 75, 75, 76, 77, 78, 78, 79, 80, 81, 81, 82, 83, 84, 85, 86, 87, 88, 90, 90, 91, 92, 93, 94, 95, 96, 97, 97, 98, 99, 100, 101, 103, 104, 105, 106, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 122, 123, 123, 124, 125, 127, 128, 129, 130, 131, 132, 132, 134, 135, 136, 137, 138, 139, 141, 141, 142, 144, 145, 146, 147, 148, 149, 150, 151, 152, 154, 155, 156, 157, 158, 160, 160, 161, 162, 163, 165, 166, 167, 168, 169, 170, 171, 173, 174, 175, 176, 177, 178, 179, 180, 182, 183, 184, 185, 187, 188, 189, 189, 191, 192, 193, 194, 196, 197, 198, 198, 200, 201, 202, 203, 205, 206, 207, 208, 209, 210, 211, 212, 213, 215, 216, 217, 218, 219, 220, 221, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 255};

const unsigned char sepiaBlueLut[256] = {5, 5, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 11, 12, 12, 13, 13, 14, 14, 14, 14, 15, 15, 16, 16, 17, 17, 17, 18, 18, 19, 20, 20, 21, 21, 21, 22, 22, 23, 23, 24, 25, 25, 26, 27, 28, 28, 29, 29, 30, 31, 31, 31, 32, 33, 33, 34, 35, 36, 37, 38, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45, 46, 47, 47, 48, 49, 50, 51, 52, 53, 53, 54, 55, 56, 57, 58, 59, 60, 60, 61, 62, 63, 65, 66, 67, 67, 68, 69, 70, 72, 73, 74, 75, 75, 76, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 90, 91, 92, 93, 93, 95, 97, 98, 99, 100, 101, 102, 104, 104, 106, 107, 108, 109, 111, 112, 114, 115, 115, 117, 118, 120, 121, 122, 123, 124, 125, 127, 128, 129, 131, 132, 133, 135, 136, 137, 138, 139, 141, 142, 144, 145, 147, 147, 149, 150, 151, 153, 154, 156, 157, 159, 159, 161, 162, 164, 165, 167, 168, 169, 170, 172, 173, 174, 176, 177, 178, 180, 181, 182, 184, 185, 186, 188, 189, 191, 192, 193, 194, 196, 197, 198, 200, 201, 203, 204, 205, 206, 207, 209, 210, 211, 213, 214, 215, 216, 218, 219, 220, 221, 223, 224, 225, 226, 227, 229, 230, 231, 232, 234, 235, 236, 237, 238, 239, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 255};
void applySepia(Bitmap* bitmap){
/*bitmap initializations*/
for (i = length; i--; ) {
  register float r = (float) red[i] / 255;
  register float g = (float) green[i] / 255;
  register float b = (float) blue[i] / 255;
  register float luminosity =  (0.21f * r + 0.72f * g + 0.07 * b) * 255;
      red[i] = truncate((int)( sepiaRedLut[(int)luminosity]));
      green[i] = truncate((int)(sepiaGreenLut[(int)luminosity]));
      blue[i] = truncate((int)(sepiaBlueLut[(int)luminosity]));
  }
}

     

Cyano Filter

As the name suggests this filter adds a cyan tone to the image. For implementing this cyano filter, we first found the black and white value of the pixel and then the ceilingComponent value of the pixels of three color channels. Then the ceilingComponent Values and the gray values are overlayed to give the resultant image. Finding the ceilComponent Values and the filter implementation is shown below.

#define componentCeiling(x) ((x > 255) ? 255 : x)

static unsigned char overlayPixelComponents(unsigned int overlayComponent, unsigned char underlayComponent, float alpha) {
  float underlay = underlayComponent * alpha;
  return (unsigned char)((underlay / 255) * (underlay + ((2.0f * overlayComponent) / 255) * (255 - underlay)));

}

void applyCyano(Bitmap* bitmapl) {
  //Cache to local variables
//Bitamp initialization
  register unsigned int i;
  register unsigned char grey, r, g, b;
  for (i = length; i--;) {
     grey = ((red[i] * 0.222f) + (green[i] * 0.222f) + (blue[i] * 0.222f));
     r = componentCeiling(61.0f + grey);
     g = componentCeiling(87.0f + grey);
     b = componentCeiling(136.0f + grey);
     grey = (red[i] + green[i] + blue[i]) / 3;
     red[i] = truncate((int)(overlayPixelComponents(grey, r, 0.9f)));
     green[i] = truncate((int)(overlayPixelComponents(grey, g, 0.9f)));
     blue[i] = truncate((int)(overlayPixelComponents(grey, b, 0.9f)));
  }
}

      

Grain Filter

It is clear from the name that this filter adds grain to the image giving an artistic effect. It can be implemented in a very simple manner by assigning gray values to random pixels of an image. The condition inside the main for loop of the below implementation controls the proportion of added grain with respect to the whole image. For generating a random value, the timer has to be initialized first. The whole implementation of the function is shown below.

void applyGrain(Bitmap* bitmap) {
/*initializations*/
   time_t t;
   srand((unsigned) time(&t));
  for (i = length; i--;) {
       int rval = rand()%255;
       if (rand()%100 < 15)){
           int grey = (red[i] + green[i] + blue[i]) / 3;
           red[i] = truncate(rval);
           green[i] = truncate(rval);
           blue[i] = truncate(rval);
       }
  }
}

      

Threshold Filter

Thresholding an image gives a binary image i.e the pixels of the image will have only two values. One for a value less than the threshold and other for values greater than the threshold. The threshold value is adjusted by seek bar in Phimpme. An image looks very artistic for a particular value on the seek bar. Its implementation is shown below.

void applyThreshold(Bitmap* bitmap, int val) {
/*bitmap initializations*/
  unsigned char grey, color;
  int thres = 220 - (int)((val/100.0) * 190);
  for (i = length; i--;) {
       grey = (red[i] + green[i] + blue[i]) / 3;
       if (grey < thres) color = 0;
       else color = 255;
       red[i] = truncate((int)(color));
       green[i] = truncate((int)(color));
       blue[i] = truncate((int)(color));
  }
}

     

Resources:

Continue ReadingApplying Filters on Images using Native Functions in Phimpme Android

Enhancing Images using Native functions in Phimpme Android

Enhancing the image can be performed by adjusting the brightness, contrast, saturation etc. of that image. In the Phimpme Android Image Application, we implemented many enhancement operations. All these image enhancement operations are performed by the native image processing functions.

An image is made up of color channels. A gray-scale image has a single channel, colored opaque image has three channels and colored image with transparency has four channels. Each color channel of an image represents a two dimensional matrix of integer values. An image of resolution 1920×1080 has 1920 elements in its row and 1080 such rows. The integer values present in the matrices will be ranging from 0 to 255. For a grayscale image there will be a single channel. So, for that image, 0 corresponds to black color and 255 corresponds to white color. By changing the value present in the matrices, the image can be modified.

The implementation of the enhancement functions in Phimpme Application are given below.

Brightness

Brightness adjustment is the easiest of the image processing functions in Phimpme. Brightness can be adjusted by increasing or decreasing the values of all elements in all color channel matrices. Its implementation is given below.

void tuneBrightness(Bitmap* bitmap, int val) {
  register unsigned int i;
  unsigned int length = (*bitmap).width * (*bitmap).height;
  unsigned char* red = (*bitmap).red;
  unsigned char* green = (*bitmap).green;
  unsigned char* blue = (*bitmap).blue;
  signed char bright = (signed char)(((float)(val-50)/100)*127);
  for (i = length; i--; ) {
       red[i] =  truncate(red[i]+bright);
       green[i] = truncate(green[i]+bright);
       blue[i] = truncate(blue[i]+bright);
  }
}

  

low brightness, normal, high brightness(in the order) images are shown above

For the above function, the argument val is given by the seekbar implemented in java activity. Its value ranges from 0 – 100, so a new variable is introduced to change the range of the input argument in the function. You can see that in the for loop there is function named truncate. As the name suggests it truncates the input argument’s value to accepted range. It is added to the top of the c file as below

#define truncate(x) ((x > 255) ? 255 : (x < 0) ? 0 : x)

Contrast

Contrast of an image is adjusted in Phimpme application by increasing the brightness of the brighter pixel and decreasing value of the darker pixel. This is achieved by using the following formula for the adjustment contrast in editor of phimpme application.

pixel[i] = {(259 x (C + 255))/(255 x (259 - C))} x (pixel[i] - 128)

In the above formula, C is the contrast value and pixel[i] is the value of the element in the image matrix that we are modifying for changing the contrast.

 

low contrast, normal, high contrast(in the order) images are shown above

So, after this formula for modifying every pixel value, the function looks like below

void tuneContrast(Bitmap* bitmap, int val) {
  register unsigned int i;
  unsigned int length = (*bitmap).width * (*bitmap).height;
  unsigned char* red = (*bitmap).red;
  unsigned char* green = (*bitmap).green;
  unsigned char* blue = (*bitmap).blue;
  int contrast = (int)(((float)(val-50)/100)*255);
  float factor = (float)(259*(contrast + 255))/(255*(259-contrast));

  for (i = length; i--; ) {
       red[i] = truncate((int)(factor*(red[i]-128))+128);
       green[i] = truncate((int)(factor*(green[i]-128))+128);
       blue[i] = truncate((int)(factor*(blue[i]-128))+128);
  }
}

Hue

The below image explains hue shift by showing what happens when shift in hue takes place over time. The image with hue 0 looks identical with image with hue 360. Hue shift is cyclic. The definition and formulae corresponding hue is found in wikipedia page here. Using that formulae and converting them back, i.e we got rgb values from hue in Phimpme application. Its implementation is shown below.

[img source:wikipedia]

void tuneHue(Bitmap* bitmap, int val) {
  register unsigned int i;
  unsigned int length = (*bitmap).width * (*bitmap).height;
  unsigned char* red = (*bitmap).red;
  unsigned char* green = (*bitmap).green;
  unsigned char* blue = (*bitmap).blue;
  double H = 3.6*val;
  double h_cos = cos(H*PI/180);
  double h_sin = sin(H*PI/180);
  double r,g,b;

  for (i = length; i--; ) {
       r = (double)red[i]/255;
       g = (double)green[i]/255;
       b = (double)blue[i]/255;
       red[i] = truncate((int)(255*((.299+.701*h_cos+.168*h_sin)*r +  (.587-.587*h_cos+.330*h_sin)*g + (.114-.114*h_cos-.497*h_sin)*b)));

       green[i] = truncate((int)(255*((.299-.299*h_cos-.328*h_sin)*r + (.587+.413*h_cos+.035*h_sin)*g + (.114-.114*h_cos+.292*h_sin)*b)));

       blue[i] = truncate((int)(255*((.299-.3*h_cos+1.25*h_sin)*r +  (.587-.588*h_cos-1.05*h_sin)*g + (.114+.886*h_cos-.203*h_sin)*b)));
  }
}

Saturation

Saturation is the colorfulness of the image. You can see the below null saturation, unmodified and high saturated images in the respective order. The technical definition and formulae for getting the saturation value from the rgb value is given in the wikipedia page here. In Phimpme application we used those formulae to get the rgb values from the saturation value.

Its implementation is given below.

  

low saturation, normal, high saturation(in the order) images are shown above

void tuneSaturation(Bitmap* bitmap, int val) {
  register unsigned int i;
  unsigned int length = (*bitmap).width * (*bitmap).height;
  unsigned char* red = (*bitmap).red;
  unsigned char* green = (*bitmap).green
  unsigned char* blue = (*bitmap).blue;
  double sat = 2*((double)val/100);
  double temp;
  double r_val = 0.299, g_val = 0.587, b_val = 0.114;
  double r,g,b;
  for (i = length; i--; ) {
      r = (double)red[i]/255;
      g = (double)green[i]/255;
      b = (double)blue[i]/255;
      temp = sqrt( r * r * r_val +
                     g * g * g_val +
                       b * b * b_val );
      red[i] = truncate((int)(255*(temp + (r - temp) * sat)));
      green[i] = truncate((int)(255*(temp + (g - temp) * sat)));
      blue[i] = truncate((int)(255*(temp + (b - temp) * sat)));
  }
}

Temperature

If the color temperature of the image is high, i.e the image with the warm temperature will be having more reds and less blues. For a cool temperature image reds are less and blues are more. So In Phimpme Application, we implemented this simply by adjusting the brightness of the red channel matrix and blue channel matrix as we did in brightness adjustment. We didn’t modify the green channel here.

  

low temperature, normal, high temperature(in the order) images are shown above

void tuneTemperature(Bitmap* bitmap, int val) {
  register unsigned int i;
  unsigned int length = (*bitmap).width * (*bitmap).height;
  unsigned char* red = (*bitmap).red;
  unsigned char* green = (*bitmap).green;
  unsigned char* blue = (*bitmap).blue;
  int temperature = (int)1.5*(val-50);
  for (i = length; i--; ) {
       red[i] = truncate(red[i] + temperature);
       blue[i] = truncate(blue[i] - temperature);
  }
}

Tint

In Phimpme application, we adjusted the tint of an image in the same way of adjusting the temperature. But in this instead of modifying the red and blue channels, we modified the green channel of the image. An image with more tint will have a tone of magenta color and if it is decreased the image will have a greenish tone. The below shown code shows how we implemented this function in image editor of Phimpme application.

  

low tint, normal, high tint(in the order) images are shown above

void tuneTint(Bitmap* bitmap, int val) {
  register unsigned int i;
  unsigned int length = (*bitmap).width * (*bitmap).height;
  unsigned char* red = (*bitmap).red;
  unsigned char* green = (*bitmap).green;
  unsigned char* blue = (*bitmap).blue;
  int tint = (int)(1.5*(val-50));

  for (i = length; i--; ) {
       green[i] = truncate(green[i] - tint);
  }
}

Vignette

Vignetting is the reduciton in the brightness of the image towards the edges than the center. It is applied to draw the attention of the viewer to the center of the image.

 

normal and vignetted images are shown above

For implementing vignette in Phimpme application, we reduced the brightness of the pixel corresponding to a radial gradient value which is generated based on the pixel’s distance from the corner and center. It’s function in Phimpme as is shown below.

double dist(int ax, int ay,int bx, int by){
   return sqrt(pow((double) (ax - bx), 2) + pow((double) (ay - by), 2));
}

void tuneVignette(Bitmap* bitmap, int val) {
  register unsigned int i,x,y;
  unsigned int width = (*bitmap).width, height = (*bitmap).height;
  unsigned int length = width * height;
  unsigned char* red = (*bitmap).red;
  unsigned char* green = (*bitmap).green;
  unsigned char* blue = (*bitmap).blue;
  double radius = 1.5-((double)val/100), power = 0.8;
  double cx = (double)width/2, cy = (double)height/2;
  double maxDis = radius * dist(0,0,cx,cy);
  double temp,temp_s;
   for (y = 0; y < height; y++){
       for (x = 0; x < width; x++ ) {
           temp = dist(cx, cy, x, y) / maxDis;
           temp = temp * power;
           temp_s = pow(cos(temp), 4);
           red[x+y*width] = truncate((int)(red[x+y*width]*temp_s));
           green[x+y*width] = truncate((int)(green[x+y*width]*temp_s));
           blue[x+y*width] = truncate((int)(blue[x+y*width]*temp_s));
       }
   }
}

All these above mentioned functions are called from main.c file by creating JNI functions corresponding to each. These JNI functions are further defined with proper name in Java and arguments are passed to it. If you are not clear with JNI, refer my previous posts.

Resources

Continue ReadingEnhancing Images using Native functions in Phimpme Android

Iteration through the Android File System in the phimpme project

Android uses the single file system structure which has a single root. The task involved creating a custom folder chooser to whitelist folders while displaying images in the gallery in the Phimpme Photo App. The challenge arose in iterating over the files in the most efficient way. The best possible way to represent the file structure is in the form of tree data structure as given below.

Current Alternative

Currently, the MediaStore class contains metadata for all available media on both internal and external storage devices. Since it only returns a list of a particular media file format, it refrains the developer from customizing the structure in his way.

Implementation

Create a public class which represents the file tree. Since each subtree of the tree could itself be represented as file tree itself, therefore the parent of a node will be a FileTree object itself. Therefore declare a list of FileTree objects as children of the node, a FileTree object as the parent of the particular node, node’s own File object along with string values filepath and display name associated with it.

public class FileTree {
 public final String filepath;
 public final String displayName;
 public final List<FileTree> childFileTreeList = new ArrayList<>();
 public final FileTree parent;
 public boolean hasMedia = false;

 public FileTree(String filepath, String displayName, FileTree parent) {
    this.filepath = filepath;
    this.displayName = displayName;
    this.parent = parent;
 }
}

For iterating through the file system, we create a recursive function which is called on the root of the Android file system. If the particular file is a directory, with the help of Depth First traversal algorithm, the directory is traversed. Else, the file is added to the list of the file. The below code snippet is the recursive function.

public static void walkDir(FileTree fileTree, List<File> files) {
  File listFile[] = fileTree.file.listFiles();

  if (listFile != null) {
      for (File file : listFile) {
          if (file.isDirectory()) {
              FileTree childFileTree = new FileTree(file, file.getName(), fileTree);
              fileTree.childFileTreeList.add(childFileTree);
              walkDir(childFileTree, files);
          }
          else {
                  files.add(file);

          }
      }
  }
}

Conclusion

The android file system was used to whitelist folders so that the images of the folders could neither be uploaded nor edited.

For the complete guide to whitelisting folders, navigate here

Continue ReadingIteration through the Android File System in the phimpme project

Native Functions for Performing Image Processing in Phimpme Android

In Android, image processing can be performed using Java or RenderScript or Native(C/C++). The performance of native code(C/C++) for image processing is much better than Java and RenderScript. So we used native code for image processing in the photo editor of the Phimpme image application. In this blog, I explain how image processing is performed in Phimpme Android.

Setting up build script for native code.

NDK helps us to develop Android applications using native languages like C and C++ so that heavy tasks can be performed in relatively less time. We can also use libraries built using C/C++ in Android application using this NDK. NDK can be downloaded using the SDK manager of Android Studio and can be set up following instructions in Android developers’ site.

After setting up the NDK, we will create a simple application involving native code and understand the flow of functions from Java to native.

The java files are present in app/src/main/java directory. Similarly, all the native files are present in app/src/main/jni directory.

So now let’s create necessary files in jni directory.

  • main.c – Native functions are added here
  • Android.mk and Application.mk – make files for building native code using ndkbuild.

Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := main.c       // include the files that should be 
                                // built(.c, .cpp, .h)
LOCAL_LDLIBS += -llog
LOCAL_MODULE := modulename      //name of the module (custom)
include $(BUILD_SHARED_LIBRARY)

Application.mk

APP_OPTIM := release
APP_ABI := all                    //architectures for which native lib
                               //has to be built(can be one or many. 
                               //should be separated by comma)
APP_PLATFORM := android-25 

Add these lines to app’s build.gradle for building native code along with the Gradle build.

externalNativeBuild {
   ndkBuild {
       path 'src/main/jni/Android.mk'
   }
}

The above lines in build.gradle will run Android.mk during the Gradle build.

When the Android.mk runs, it compiles all native code and generates modulename.so files in .externalNativeBuild/ndkBuild directory for all the mentioned architectures. This .so file for a particular architecture is a library containing all the native code compatible with that architecture.

So when this library(.so file) is statically imported into Java code, native code gets linked to Java and enables calling native functions directly from Java.

Importing native Library into Java

Static import of this library can be done by writing the below lines in your Java class.

static {
    System.loadLibrary("modulename");
}

Creating a native function and calling it from Java?

Unlike normal java code, where you call a function by its name, here in native Android development, the name of the native function is different from what you call it in java. To understand this clearly, let’s see an example of simple hello world application. Define the native function in Java and call it normally like any other function.

package org.fossasia.phimpme;

import 

public class MainActivity extends Activity {
   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);       
       textView = (TextView)findViewById(R.id.textview);
       textView.setText(helloworld());
   }

    static {
        System.loadLibrary(“modulename”);
    }

     Public native String helloworld();
}

Here the definition of the native function is present in org.fossasia.phimpme package and MainActivity class. So the name of the function in the native file should be “Java_org_fossasia_phimpme_MainActivity_helloworld”.

It follows a general structure of concatenating these strings Java, package name, Class name, function name defined in Java and replacing all full stops(.) with underscores(_).

The first two arguments in native function are JNIEnv* and jobject. These are present always. For a zero argument definition of a function in Java, there will be these two arguments in the native function. If there are two arguments defined in Java, there will be two arguments additional to these two arguments in the native function. These help in passing data in and out of the native part.

Here’s an example of a native function to output a string to Java

main.c :

#include <jni.h>
jstring Java_org_fossasia_phimpme_MainActivity_helloworld  (JNIEnv * env, jobject obj){
    return (*env)->NewStringUTF(env, "Hello World from Native");
}

Now when you run this application, you see “Hello World from Native” displayed on the screen. I hope this post clears about the flow of the native functions and how to link them with Java.

References

https://developer.android.com/ndk/guides/index.html

Continue ReadingNative Functions for Performing Image Processing in Phimpme Android

Implementing UNDO and REDO in Image Editor of Phimpme Android

The main feature in any image editor application like Phimpme Android other than editing the image is the ability to revert the changes (UNDO) that are done and the ability to revert the reversion i.e reperform the changes (REDO).

In Phimpme Application, we implemented this by using an ArrayList of Bitmaps. We stored the copy of the image bitmap whenever the modification is done on it. This helped us to get back to the previous image when required. But there is a problem in this method. They method may produce OutOfMemory Error when storing the bitmap in ArrayList when memory gets full. So for dealing solving this in the Phimpme application, we added a try – catch block and when the out of memory exception is caught, we recycled and removed the initial modified image from list i.e the image of index 1 in ArrayList. Index 0 is the original image on which we are working on. When we recycled that image, it gives space for adding another image, So added the recent image at the end of the list.

addToUndoList() function is shown below.

private void addToUndoList() {
   try{
       recycleBitmapList(++currentShowingIndex);
       bitmapsForUndo.add(mainBitmap.copy(mainBitmap.getConfig(),true));
   }catch (OutOfMemoryError error){
       bitmapsForUndo.get(1).recycle();
       bitmapsForUndo.remove(1);
       bitmapsForUndo.add(mainBitmap.copy(mainBitmap.getConfig(),true));
   }
}

In the above function, bitmapsForUndo is ArrayList in which we added modified bitmaps in the editor of Phimpme application. mainBitmap is the image bitmap on which all modifications are being done in the editor. The sense of integer variable currentShowingIndex is clear from its name that it points to the index of the image that is currently getting shown.

Eg. Consider a case when you perform 5 edits on an image using Phimpme Editor, then 6 image bitmaps get stored in the ArrayList including the original image and currentShowingIndex will be 5. Now if you undo the steps twice the currentShowingIndex becomes 3. The bitmaps of the index 4 and 5 have not been removed from the ArrayList yet. So they will be useful if you want to redo the changes.

  

When you make an another edit, an image bitmap gets added at index 4 and that should be the last element of the ArrayList. But you see that there is a bitmap of index 5 making that the last element of the ArrayList, not the newly added one. So in order to achieve that, the elements present in the ArrayList whose index is greater than currentShowingIndex have to recycled and removed before adding a newly modified image bitmap to the ArrayList. The first line in the try block of the above functions is referring to the function that is going to implement this. That function’s implementation is given below

private void recycleBitmapList(int fromIndex){
   while (fromIndex < bitmapsForUndo.size()){
       bitmapsForUndo.get(fromIndex).recycle();
       bitmapsForUndo.remove(fromIndex);
   }
}

Removing the bitmap from the ArrayList doesn’t clear the memory. That bitmap has to be recycled before getting removed from the ArrayList which is performed in the above function of the Phimpme application’s image editor.  The above recycleBitmapList function recycles and removes the bitmaps which have an index greater than or equal to the index that is passed as an argument to that function.

This function should also be called in onDestroy function of android activity as

recycleBitmapList(0);

This recycles and removes the whole ArrayList.

As now the implementation of the creation and recycling of the ArrayList is done, we can use this ArrayList to create getter functions for the undo and redo bitmaps. When the getUndoBitmap() is called the currentShowingIndex should decrement by one if greater than zero. When getRedoBitmap() is called the currentShowingIndex has to be incremented by one until it gets equal to the index of the last element present in the array list.

These methods are shown below.

private Bitmap getUndoBitmap(){
   if (currentShowingIndex - 1 >= 0)
       currentShowingIndex -= 1;
   else currentShowingIndex = 0;

   return bitmapsForUndo
           .get(currentShowingIndex)
           .copy(bitmapsForUndo.get(currentShowingIndex).getConfig(), true);
}

private Bitmap getRedoBitmap(){
   if (currentShowingIndex + 1 <= bitmapsForUndo.size())
       currentShowingIndex += 1;
   else currentShowingIndex = bitmapsForUndo.size() - 1;

   return bitmapsForUndo
           .get(currentShowingIndex)
           .copy(bitmapsForUndo.get(currentShowingIndex).getConfig(), true);
}

Logic part is done by here. We integrated these getter functions to button click functions of Phimpme Image Editor. setButtonVisibility() is called whenever undo or redo is button is pressed. This function sets the enable state and visibility of the button i.e the undo button is visible and enabled only if undo is possible. So does for the redo button.

setButtonVisibility() is shown below.

private void setButtonsVisibility() {
   if (currentShowingIndex > 0) {
       undo.setColorFilter(Color.BLACK);
       undo.setEnabled(true);
   }else {
       undo.setColorFilter(Color.GRAY);
       undo.setEnabled(false);
   }

   if (currentShowingIndex + 1 < bitmapsForUndo.size()) {
       redo.setColorFilter(Color.BLACK);
       redo.setEnabled(true);
   }else {
       redo.setColorFilter(Color.GRAY);
       redo.setEnabled(false);
   }
}

The above function grays the button if it is in the disabled state and will be black when the enabled state conditions are satisfied.

Finally, the OnClick() function of the editor of Phimpme is shown below.

@Override
public void onClick(View v) {
   switch (v.getId()){
       case R.id.edit_undo:
           onUndoPressed();
           break;
       case R.id.edit_redo:
           onRedoPressed();
           break;
   }
}

private void onUndoPressed() {
   if (mainBitmap != null) {
       if (!mainBitmap.isRecycled()) {
           mainBitmap.recycle();
       }
   }
   mainBitmap = getUndoBitmap();
   mainImage.setImageBitmap(mainBitmap);
   setButtonsVisibility();
}

private void onRedoPressed() {
   if (mainBitmap != null) {
       if (!mainBitmap.isRecycled()) {
           mainBitmap.recycle();
       }
   }
   mainBitmap = getRedoBitmap();
   mainImage.setImageBitmap(mainBitmap);
   setButtonsVisibility();
}

This shows how we implemented undo and redo in Image Editor of Phimpme Image Application.

Continue ReadingImplementing UNDO and REDO in Image Editor of Phimpme Android

Implementing a zoomable ImageView by Extending the Default ViewPager in Phimpme Android

When I was trying to give default gallery-like experience to the gallery of Phimpme Image Application, where you can zoom an image with pan and pinch controls along with the ability to navigate to another photo by swipe gestures, I faced a problem in which when the zoomed image is swiped expecting it to get panned, instead of that, the viewpager switched to another page.

This implementation of Viewpager with zoomable image in it might seem straightforward in the beginning but once you start implementing this in most common way i.e using default ViewPager for navigation between images and zooming libraries like TouchImageView, subsampling-scale-image-view or PhotoView for zooming the image with pinch and pan controls, you will notice that when you swipe left or right on the zoomed image, the pager navigates to other images instead of the zoomed image getting panned. The viewpager responds to the swipe event and causes page change and it doesn’t let zoomable view to respond to that event.

In the above screenshot, front image is zoomed and when we swipe left, instead of the image getting panned, it is switching to next page.

How to solve this?

As the problem is caused by the default ViewPager utilizing the swipe event without transferring it to child views, a custom viewpager can be created by extending default viewpager and having a touch intercept method which transfers the event to its child views. A sample implementation of this custom view pager which I used in the app is shown below.


public class CustomViewPager extends ViewPager {
    public CustomViewPager(Context context) {
        super(context);
    }

    public CustomViewPager(Context context, AttributeSet attrs)
    {
        super(context,attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        try {
            return super.onInterceptTouchEvent(ev);
        } catch (IllegalArgumentException e) {
            return false;
        }
    }
}

Now we can replace normal viewpager with this custom view pager in image viewing activity and layout resource for that activity. The normal pager adapter which is used with default view pager can be used with this custom viewpager also. You can get a clear understanding of what I described here by having a look at the below implementation.

activity_imageview.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent">

   <package.name.path.to.CustomViewPager
       android:id="@+id/cviewpager"
       android:layout_width="match_parent"
       android:layout_height="match_parent"/>
</RelativeLayout>

ImageViewActivity.java

public class ImageViewActivity extends Activity {
    CustomViewPager cViewPager;
    ArrayList<String> imageList;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_imageview);
        imageList = createList(); //some method for creating a list
        cViewPager = (CustomViewPager)findViewById(R.id.cviewpager);  
        mViewPager.setAdapter(new ImagePagerAdapter(imageList));
    }

    class ImagePagerAdapter extends PagerAdapter {
        ArrayList<String> imageList; 
        
        ImagePagerAdapter(ArrayList<String> imageList){
            this.imageList = imageList;
        }

        @Override
        public int getCount() {
            return (null != imageList) ? imageList.size() : 0;
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public View instantiateItem(ViewGroup container, int position) {
            PhotoView photoView = new PhotoView(container.getContext());
            Glide.with(getContext())
                .load(UriFromFile(new File(imageList.get(position))))
                .asBitmap()
                .thumbnail(0.2f)
                .into(photoView);
            photoView.setMaximumScale(5.0F);
            photoView.setMediumScale(3.0F);
            container.addView(photoView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            return photoView;
        }

        @Override
         public void destroyItem(ViewGroup container, int position, Object object) {
             container.removeView((View) object);
         }
     }
}

In the above pager adapter, the PhotoView library is used for a zoomable view. The image is loaded into photoview using image caching library Glide. The pager adapter here takes a List of paths to images on the device as the input argument. A simple method for creating such list had been discussed in my first post.

Here you can see that the on swiping left the zoomed image got panned.

This method of implementing zoomable view in ViewPager is used many gallery applications. One among those applications is LeafPic. We are now integrating that into Phimpme Image Application.

References:

https://developer.android.com/reference/android/support/v4/view/ViewPager.html

https://developer.android.com/training/gestures/viewgroup.html

https://github.com/MikeOrtiz/TouchImageView

https://github.com/chrisbanes/PhotoView

 

Continue ReadingImplementing a zoomable ImageView by Extending the Default ViewPager in Phimpme Android

Adding Multiple Themes in Phimpme Android

In Phimpme-Android we decided to add a new feature that is providing multiple themes to the users. We have 3 types of themes in Phimpme Dark Theme, Light Theme and Amoled Theme. In this post, I am explaining how I added multiple themes support in phimpme android.

 Choose Theme in Phimpme Dialog

You need a Helper class that will store the data about the theme and all the colors related to a theme.

Before you begin you need to create a Helper class. In Phimpme I created a Helper class as ThemeHelper

public class ThemeHelper {

 public static final int DARK_THEME = 2;
 public static final int LIGHT_THEME = 1;
 public static final int AMOLED_THEME = 3;

 private PreferenceUtil SP;
 private Context context;

 private int baseTheme;
 private int primaryColor;
 private int accentColor;

 public ThemeHelper(Context context) {
  this.SP = PreferenceUtil.getInstance(context);
  this.context = context;
  updateTheme();
 }
}

Which contains all the basic method to get colors for textview, icon, toolbar, switch, imageview, background and app primary color.

Now you to provide user to select theme and it can be done using dialog box. Once the user selected any of the theme we have to update that theme and it can be done by following code :

 public void updateTheme(){
  this.primaryColor = SP.getInt(context.getString(R.string.preference_primary_color),
        getColor(R.color.md_light_blue_300));
  this.accentColor = SP.getInt(context.getString(R.string.preference_accent_color),
        getColor(R.color.md_light_blue_500));
  baseTheme = SP.getInt(context.getString(R.string.preference_base_theme), LIGHT_THEME);
 }

Now we have updated the our theme in Phimpme now we have to set the color according to a theme.

To get colors of all components we need to add some function in our helper class which will provide us the colors according to the theme.

As I said we are having 3 themes in Phimpme so I used 3 case to compare which theme user has selected.

I have added the functions to get colors for background, text and subtext as follows in phimpme.

public int getBackgroundColor(){
  int color;
  switch (baseTheme){
    case DARK_THEME:color = getColor(R.color.md_dark_background);break;
    case AMOLED_THEME:color = getColor(R.color.md_black_1000);break;
    case LIGHT_THEME:
    default:color = getColor(R.color.md_light_background);
  }
  return color;
 }



 public int getTextColor(){
  int color;
  switch (baseTheme){
    case DARK_THEME:color = getColor(R.color.md_grey_200);break;
    case AMOLED_THEME:color = getColor(R.color.md_grey_200);break;
    case LIGHT_THEME:
    default:color = getColor(R.color.md_grey_800);
  }
  return color;
 }

 public int getSubTextColor(){
  int color;
  switch (baseTheme){
    case DARK_THEME:color = getColor(R.color.md_grey_400);break;
    case AMOLED_THEME:color = getColor(R.color.md_grey_400);break;
    case LIGHT_THEME:
    default:color = getColor(R.color.md_grey_600);
  }
  return color;
 }

In the above functions, I am comparing which theme user has selected and returned the color according to the theme.

Now set the color to text by using above function you don’t need care which theme user has selected because those function will check and return the color according to the theme.

So it can be done simply,

textview.setTextColor(getTextColor());
editText.setTextColor(getTextColor());
editText.setHintTextColor(getSubTextColor());

                                     Light Theme &  Dark Theme (Phimpme)

Resources:

https://github.com/fossasia/phimpme-android/blob/development/app/src/main/java/org/fossasia/phimpme/leafpic/util/ThemeHelper.java

http://www.hidroh.com/2015/02/16/support-multiple-themes-android-app/

Continue ReadingAdding Multiple Themes in Phimpme Android