Zooming Feature in the Phimpme Android’s Camera

The Phimpme Android application comes with a complete package of camera, Edit images, sharing and gallery functionalities. It has a well featured and fully functional camera with all the capabilities that a user expects from a camera application. One such feature in the Phimpme Android application is the zooming functionality. It provides the user the option to zoom in using the pinch gesture of the fingers or the user can select the settings to zoom in from the volume buttons. In this tutorial, I will be explaining how I achieved the zooming functionality in the Phimpme Android app.

Step 1

The first thing we need to do is to check whether the device will support the zoom in functionality or not to avoid random crashes while runtime of the application and while performing the zoom action in case the camera of the device doesn’t support this feature. This can be done by the following lines of code:

Camera.Parameters params = mCamera.getParameters();
Boolean supports = params.isZoomSupported();

Step 2

Now after getting the camera parameters and checking whether the camera supports the zoom in functionality, we need to add the touch listener to the surface view of the camera so that we can get the touch locations and the finger spacing of the user to get the pinch to zoom in functionality. This can be done using the following line of code.

surfaceView.setOnTouchListener(this);

Whenever the user touches the screen this touch listener gives a callback to the overridden onTouchEvent method and passes the MotionEvent to the function. The motion event object in Android handles the movement reports. Now in the onTouchEvent method, we calculate the finger spacing between the two fingers and calculate the approximate amount by which the user wants to zoom in. The finger spacing can be calculated using the following lines of code.

float x = event.getX(0) - event.getX(1);
   float y = event.getY(0) - event.getY(1);
   return FloatMath.sqrt(x * x + y * y);

After getting the finger spacing we need to cancel the auto focus of the camera before performing the zoom action so that the application does not crash. This can be achieved by a single line of code below.

mCamera.cancelAutoFocus();

Step 3

The final step is to set the zoom level in the camera application by calculating the zoom level by using the finger spacing. For this, first we need to get the max zoom level supported by the device so that we do not apply the zoom level that is not supported by the device. The calculation of max zoom level and setting of the desired zoom level by the user can be performed by using the following lines of code.

int maxZoom = params.getMaxZoom();
   int zoom = params.getZoom();
   float newDist = getFingerSpacing(event);
   if (newDist > mDist) {
       //zoom in
       if (zoom < maxZoom)
           zoom++;
   } else if (newDist < mDist) {
       //zoom out
       if (zoom > 0)
           zoom--;
   }
   mDist = newDist;
   params.setZoom(zoom);

This is how we have achieved the functionality of zooming in and clicking pictures in the Phimpme Android application. To get the full source code and to know how to use the volume control buttons to zoom in/out, please refer to the Phimpme Android repository.

Resources

  1. GitHub – Open camera source code : https://github.com/almalence/OpenCamera
  2. Android developer’s guide – MotionEvents in Android : https://developer.android.com/reference/android/view/MotionEvent.html
  3. StackOverflow – Pinch to zoom functionality : https://stackoverflow.com/questions/8120753/android-camera-preview-zoom-using-double-finger-touch
  4. GitHub – Phimpme Android repository : https://github.com/fossasia/phimpme-android

Maintaining Aspect Ratio of Images while Uploading in Open Event Frontend

In Open Event Frontend, we are using the image-upload at many places such as the cover photo of the event on the create event page, also at the system images (in the admin routes) where the user gets to change the image uploaded by him earlier, and also at the ‘profile’ route where the user can change his photo. But at different places, different dimensions of photos are needed since we are keeping standard in Open Event Frontend.

Therefore the image needs to be in a size with the correct ratio at the time of uploading. We are using the cropper component  for achieving this. The cropper is a modal which pops up after the image upload modal so that a user can crop the image according to the aspect ratio kept specific for that purpose. It looks as follows:

While dealing with an issue in Open Event Frontend, we had to have change in aspect ratio for ‘avatar’ images, unlike the other images. So, we had to modify our cropper modal so as to have different aspect ratios for different images when needed.

We solved the above problem as follows:

In our image-modal, we pass the aspect ratio as a parameter. So in our case, we wanted to set the aspect ratio 1:1 for the ‘avatar’ images only. Following is the code snippet of what we wanted:

{{widgets/forms/image-upload
needsCropper=true
label=(t 'Update Image')
id='user_image'
aspectRatio=(if (eq subTopic.name 'avatar') (array 1 1))
icon='photo'
hint=(t 'Select Image')
maxSizeInKb=10000
helpText=(t 'For Cover Photos : 300x150px (2:1 ratio) image.
For Avatar Photos : 150x150px (1:1 ratio) image.')}}

Thus, we passed the ‘aspectRatio’ as a parameter to the ‘image-upload’ modal. The image-upload further calls the cropper modal and passes ‘aspectRatio’.

{{#if needsCropper}}
{{modals/cropper-modal isOpen=cropperModalIsShown imgData=imgData onImageCrop=(action 'imageCropped') aspectRatio=aspectRatio}}
{{/if}}

Thus, we can use the passed param i.e ‘aspectRatio’ in our cropper to modify the logic for the aspect ratio. Following is what we did so as to obtain the aspect Ratio of 1:1 for ‘avatar’ images only. The default aspect ratio for all other images is 2:1.

onVisible() {
let viewPort = {};
let factor = 150;
const aspectRatio = this.getWithDefault('aspectRatio', [2, 1]);
viewPort.width = aspectRatio[0] * factor;
viewPort.height = aspectRatio[1] * factor;
viewPort.type = 'square';
this.$('.content').css('height', '300px');
this.$('img').croppie({
customClass : 'croppie',
viewport : viewPort,
boundary : {
height: 250
}
});
},

As shown above, we have kept a multiplying factor which is fixed. According to the aspect ratio specified, we calculate and set the width and height in the viewport object.

Thus, following is the thing (Aspect Ratio 1:1 ) we achieved for the ‘avatar’ images:


Resources
Official Ember JS guide: https://guides.emberjs.com/v1.10.0/templates/actions/

Blog on making our own modals: http://ember.guru/2014/master-your-modals-in-ember-js

Source code: https://github.com/sumedh123/open-event-frontend/tree/system-images

Uploading Images via APIs in the Open Event Server

APIs help us to send and receive data in some particular data format that can then be used individually or integrated with a frontend UI. In our case, the entire API server is used to manage all the requests from the frontend and send back the necessary response. Usually, the application is to send simple form data which is then stored into the backend database and a valid jsonapi response is shown. However other than normal text, url, datetime data one very important data is media files, in our case event images, user images, slides, etc. In this blog, we will particularly deal with how we can upload images in the server using API.

Sending Data

Firstly, we need to decide how do we send the data in the post request of the API. So we are sending a base64 encoded string representing the image along with the image extension appended to it, for example, data:image/png;base64,iVBORw0KGgoAAAANS. This is a widely used format for showing images over the web. So when we send a POST request we send a json encoded body like:

{
    "data": "data:image/png;base64,iVBORw0KGgoAAAANS"
}

Converting Base64 Data to Image

There are 2 parts of the data in the string that we receive. The first part basically tells us the format of the image(.gif in this case) and string encoding(base64 in this case), the second part gives us the encoded string. So, from the first part, we extract the file extension for the image file to be created. We use uuid.uuid4() for a random filename.

filename = '{}.{}'.format(str(uuid.uuid4()).data.split(";")[0].split("/")[1])

Now to write the base64 encoded string as an image file, we first need to get only the encoded string part from the data and then decode it. We use string decode function of python for the decoding purpose. Then we write the data to the image file and save it.

file.write(data.split(",")[1].decode("base64")

API Response

Finally using whatever logic you are using for serving your static files, you generate the static file path for the image saved. And then create another json encoded response which returns you the url for the saved image in the server.

{
    "url": "https://xyz.storage.com/asd/fgh/hjk/1233456.png"
}

And there you have your image uploaded and served.

PIL to convert type and quality of image

Image upload is an important part of the server. The images can be in different formats and after applying certain javascript modifications, they can be changed to different formats. For example, when an image is uploaded after cropping in open event organizer server, it is saved in PNG format. But PNG is more than 5 times larger than JPEG image. So when we upload a 150KB image, the image finally reaching the server is around 1MB which is huge. So we need to decide in the server which image format to select in different cases and how to convert them.

Continue reading “PIL to convert type and quality of image”

How can you get an access to Instagram API?

unnamed.png

First of all you need to know that Instagram API uses OAuth 2.0 protocol. OAuth 2.0 provides a specific authorization flow for web apps, desktop apps and mobile apps. Instagram requires authentication before getting information from their API, but don’t be afraid it’s very simple.

Pre Requirements:

  • Created account in Instagram
  • Registered Client(You can create your own client here)

Instagram Developer Documentation.png

Requirements:

CLIENT_ID -79e1a142dbeabd57a3308c52ad43e31d
CLIENT_SECRET -34a6834081c44c20bd11e0a112a6adg1
REDIRECT_URI - http://127.0.0.1:8001/iCallback

You can get above information from https://www.instagram.com/developer/clients/manage/

CODE - You need to open page https://api.instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=REDIRECT-URI&response_type=code
https://api.instagram.com/oauth/authorize/?client_id=79e1a142dbeabd57a3308c52ad43e31d&redirect_uri=http://127.0.0.1:8001/iCallback&response_type=code

You will be redirected to

http://your-redirect-uri?code=CODE

In my case it looks like this:

http://127.0.0.1:8001/iCallback?code=2e122f3d76e8125b8b4982f16ed623c2

Now we have all information to get access token!

 curl -F 'client_id=CLIENT_ID'  
      -F 'client_secret=CLIENT_SECRET' 
      -F 'grant_type=authorization_code'  
      -F 'redirect_uri=REDIRECT_URI' 
      -F 'code=CODE'  https://api.instagram.com/oauth/access_token

if everything is ok you should receive

 { "access_token": "fb2e77d.47a0479900504cb3ab4a1f626d174d2d",
   "user": { "id": "1574083",
   "username": "rafal_kowalski",
   "full_name": "Rafal Kowalski",
   "profile_picture": "..." } } 

In Open Event we used it to get all media from instagram – to use it  as for example a background in an event details’ page

curl 'https://api.instagram.com/v1/users/self/media/recent/?access_token=ACCESS_TOKEN'