Displaying Image location Address In Phimpme Android Application

In Phimpme Android application one of the features available is to view the details of any image. The details consists of attributes including Date and time at which the image was captured, size of the image, title, path, EXIF data, description added to the image, location etc. However in the location attribute the location coordinates of the image as well as the location address can be displayed depending on the user’s preference. The process of obtaining the coordinates from address is called as Geocoding and obtaining string address from coordinates is called reverse Geocoding. So in this post I will be explaining how to implement set of strings denoting the address from the available coordinates.

Step 1

First we need to create an instance of the class Geocoder passing context and function Locale.getDefault() as the parameters.  The function of the attribute Locale.getdefault is provided below.

Locale.getDefault() – It returns the current value of the default locale for the current instance of the Java Virtual Machine. The Java Virtual Machine sets the default locale during startup based on the host environment.The code snippet to perform the above mentioned operation is given below.

Geocoder geocoder = new Geocoder(context, Locale.getDefault());

Step 2

Now a function call of getFromLocation() of the Geocoder class is done where we need to pass the Lattitude and Longitude values of the Location object as parameters. The lattitude and longitudes values can be obtained by the use of the Location object functions getLatitude() and getLongitude() respectively. The function getFromLocation() will return a list of Address objects which will contain the extracted addresses from the passed latitude and longitude values. We also need to pass a third parameter an integer value which will determine the maximum no of addresses to be returned. Here we have requested for a maximum of one address. The following code snippet is used to perform the desired function call.

try {
 List<Address> addressList = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1);
} catch (IOException e) {
  e.printStackTrace();
}

Step 3

After obtaining the list of Address objects returned from the function call of getFromLocation() we will extract the first address from the list since we want a maximum of 1 address. The Address object will contain information like the address name, country, state, postal code etc. Now the set of strings describing the location can be retrieved with the help of the function getMaxAddressLineIndex() of Address class. The code snippets to perform the above mentioned operations is provided below.

ArrayList<String> addresslines = new ArrayList<String>();
Address address = addressList.get(0);
for(int i = 0; i <= address.getMaxAddressLineIndex(); i++) {
  addresslines.add(address.getAddressLine(i));
}
details.put(context.getString(R.string.location), TextUtils.join(System.getProperty(“line.separator”),
      addresslines));

The screenshot displaying the location address is provided below.

This is how we have achieved the functionality of displaying location address in a set of strings from available coordinates in the Phimpme Android application. To get the full source code, please refer to the Phimpme Android GitHub repository listed in the resources section below.

Resources

1.Android Developer Guide – https://developer.android.com/training/location/display-address.html

2.Github-Phimpme Android Repository – https://github.com/fossasia/phimpme-android/

3.Address Class Guide- https://developer.android.com/reference/android/location/Address.html

 

Leak Canary in Phimpme Android

Leak Canary is a memory detection library for Android and Java. A memory leak occurs when you hold an object for too long after its purpose has been served. If some object is holding another object then the Garbage collector will not be able to collect and this is known as Memory Leak. These memory leaks can be minor (in KB’s) or can lead to an app in ANR state with OutOfMemoryError.Hence to recover and capture this memory leak, Leak Canary is used for Android and Java.

For every functioning done in android, the system needs resource such as memory. Hence in Java the Garbage Collector(GC) plays a major role in deallocating the memory. The GC is mainly used to reclaim memory. Now the question arises, why do we need a memory leak detection library when GC is already present. The answer is sometimes the developers makes programming mistakes and that leads to inhibit the GC to collect the objects that are of no use and mark them as useful objects.

The GC starts from one point(root) and marks active to all the objects that holds references from GC root and the objects which are not marked are wiped out of memory.Hence when some unuseful objects is marked active, memory leak occurs.Hence to eliminate these problems  of memory leaks, we have employed the use of Leak Canary in our project.

The Phimpme project and every related project has possible memory leaks, like for instance we have used fragments in settings activity and to catch that memory leak we have added the refwatcher instance. Hence if any memory leaks occur we get the error such as ‘org.fossasia.phimpme’  leaked 40kb.This can also be checked by Leaks App in the android phone, which has features of showing and sharing the heap dump and info.

To add the Leak Canary in your android app, follow these steps:

  • Add the dependencies in build.gradle(app level)

    dependencies {
       debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.4'
       releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
     }
  • Add the following code to your Application class

    public class MyApplication extends Application{
    @Override
    public void onCreate() {
       if (LeakCanary.isInAnalyzerProcess(this)) {
           // This process is dedicated to LeakCanary for heap analysis.
           // You should not init your app in this process.
           return;
       }
       LeakCanary.install(this);
    }
    
  • Leak canary now will automatically detect the memory leaks  from all activities.

For fragment a refwatcher is needed, hence

public class MYFragment extends Fragment {
    ...

    @Override
    public void onDestroy() {
        super.onDestroy();
        MainApplication.getRefWatcher(getActivity()).watch(this);
    }
}

Hence LeakCanary is setup finally, and now the memory leaks will be detected.

Resources

 

Option to Print Photos in the Phimpme Android Application

In the Phimpme Android application, users can perform various operations on the photos available such as copy, move, add the image to favourites collection, share the images with others, use it as covers, wallpapers and much more. However one another important functionality that has been added in the Phimpe Android application is printing of images. In this post we will be discussing about the implementation of the above mentioned functionality.

Step 1

First we need to create an instance of the class PrintHelper passing context as the constructor parameter which can be done with the following line of code.

PrintHelper photoPrinter = new PrintHelper(this);

Step 2

Now a  function call of setScalemode() is done where we require passing a parameter out of the two options SCALE_MODE_FIT and SCALE_MODE_FILL. The difference between the two options is explained below.

SCALE_MODE_FIT – This option sizes the image so that the whole image is displayed within the printable area of the page.

SCALE_MODE_FILLThis option scales the image so that it fills the entire printable area of the page. Choosing this setting means that some portion of the top and bottom, or left and right edges of the image is left out. This option is the default value if no scale mode is set.

Though neither of the scaling options alter the existing aspect ratio of the image, we are going with the latter of the two as the requirement here is to display the whole image in the printable area. The following code snippet is used to perform the desired function call.

photoPrinter.setScaleMode(PrintHelper.SCALE_MODE_FIT);

Step 3

After obtaining an instance of the class PrintHelper and calling the function setScalemode with the proper scale parameter, the path of the image to be printed is extracted and is passed in as a parameter to the decodefile function of the class BitmapFactory which has another parameter.

A Bitmap object is the return result of the operation performed by the function decodefile. The Bitmap object is thereafter passed in as a parameter to the printBitmap() function of the PrintHelper class along with a string attribute which will denote the file name of the printed photo. The code snippet to the above mentioned operations are given below.

Bitmap bitmap = BitmapFactory.decodeFile(getAlbum().getCurrentMedia().getPath(), new BitmapFactory.Options());
photoPrinter.printBitmap(getString(R.string.print), bitmap);

After the printbitmap() is called no further action is required from the side of the application. The Android system print interface appears where the users can select the printing options. The user can proceed to print the image or cancel the operation. If the user decides to proceed with the operation a print job is created and printing operation notification appears in the system navigation bar. The system print interface appearing is displayed below.

This is how we have achieved the functionality of printing images in the Phimpme Android application. To get the full source code, please refer to the Phimpme Android GitHub repository listed in the resources section below.

Resources

1.Android Developer Guide – https://developer.android.com/training/printing/index.html

2.Github-Phimpme Android Repository – https://github.com/fossasia/phimpme-android/

3.PrintHelper Class Guide – https://developer.android.com/reference/android/support/v4/print/PrintHelper.html

 

Camera Controls Using Volume Buttons In The Phimpme Application

The Phimpme Android application has a camera, Gallery section, edit image section and also the inbuilt sharing option. In spite of having all of the above features, the Phimpme application doesn’t compromise on the quality and functions of each of the sections. For instance, we can control the camera fully with the help of just the volume buttons. For this, we have provided an option in the settings of the application to change and select the behaviour of the volume buttons according to the users choice. In this post, I will be discussing how we have achieved this functionality.

Step 1

First, we have to display an ArrayList of options using the ListPreference in the settings. The user can perform the following functions using the volume keys.

  1. Take Photo
  2. Focus
  3. Zoom in/out
  4. Change Exposure Level
  5. Switch Auto Level on/off

There are also two other option to change device volume and to do nothing in case the user wants the default behaviour.

The above options in the settings can be provided using the following lines of code.

<ListPreference
   android:defaultValue="volume_take_photo"
   android:entries="@array/preference_volume_keys_entries"
   android:entryValues="@array/preference_volume_keys_values"
   android:key="preference_volume_keys"
   android:summary="@string/preference_volume_keys_summary"
   android:title="@string/preference_volume_keys" />

Step 2

Now as the user selects a particular option from the ListPreference, the value in the SharedPreference associated with a particular key value gets updated. After this, we have to perform the particular activity as soon as the volume button is pressed. For this, we have to Override the onKeyDown() function of the KeyEvent.Callback class in Android. This function takes in the Integer keycode and the KeyEvent as the parameters.

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
   if (MyDebug.LOG)
       Log.d(TAG, "onKeyDown: " + keyCode);
   boolean handled = mainUI.onKeyDown(keyCode, event);
   if (handled)
       return true;
   return super.onKeyDown(keyCode, event);
}

Step 3

We have defined another onKeyDown() method in the MainUI class to keep the code modularized. In this, we have made use of the Switch cases to perform the different actions. This can be done by using the following line of code snippet.

Switch (volume_keys) {
  case "volume_take_photo":
     main_activity.takePicture();
     return true;

  case "volume_zoom":
     if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
        main_activity.getPreview().zoomTo(main_activity.getPreview().getCameraController().getZoom() + 1);
     }
     else {
        main_activity.getPreview().zoomTo(main_activity.getPreview().getCameraController().getZoom() - 1);
     }
     return true;

In the above code snippet, we have defined the function to perform the zoom operation and to click picture using the volume keys. Similarly, we can add the functions to perform all the above mentioned activities. To get the full source code, please refer to the Phimpme Android GitHub repository mentioned in the resources section below.

Resources

  1. Android Developer Guide – KeyEvent.Callback class – https://developer.android.com/reference/android/view/KeyEvent.Callback.html
  2. GitHub – Phimpme Android Repository – https://github.com/fossasia/phimpme-android/
  3. StackOverflow – Handling key events in Android – https://stackoverflow.com/questions/5631977/keyevent-handling-in-android
  4. Blog post – Handleling Key Events – https://android-developers.googleblog.com/2009/12/back-and-other-hard-keys-three-stories.html

 

Slideshow option in the Phimpme Android Application

The Phimpme Android application along with all the basic features for viewing and sharing images also has some interesting functions, for example, the ability to view all the images at once using the slideshow option. The users can also manage the time duration for which a particular photo will be displayed before switching on to the next image. In this post, we will be discussing how we have achieved this functionality in the Phimpme Android application.

Step 1

First, we have to provide the user with an option to enter the time duration for which they want to view a particular photo. For this, we have made use of the themed dialog box which will take the input from the user and then we will be converting the time entered by them into milliseconds. This can be done by using the following code snippet.

dialog.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.ok).toUpperCase(), new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface dialog, int which) {
       String value= editTextTimeInterval.getText().toString();
       if(!"".equals(value))
       {
           slideshow=true;
           int intValue = Integer.parseInt(value);
           SLIDE_SHOW_INTERVAL = intValue * 1000;

Step 2

For changing the photo at a particular duration of time, we need to make use of the Runnable interface in Java, which uses a method run() to execute the tasks. To use it, we have to create a Handler object and initialize it. This can be done using the following lines of the code.

private Handler handler;
handler = new Handler();

After this, we have to define and initialize our Runnable object which will be used later to change the picture. The code snippet for initializing the runnable object is given below.

Runnable slideShowRunnable = new Runnable() {
   @Override
   public void run() {
       try{
           mViewPager.scrollToPosition((getAlbum().getCurrentMediaIndex() + 1) % getAlbum().getMedia().size());
       }
       catch (Exception e) {
           e.printStackTrace();
       }
       finally{
           handler.postDelayed(this, SLIDE_SHOW_INTERVAL);
       }

As the run() method gets executed, the scrollToPosition function of the ViewPager class gets called which changes the position of the image displayed to the next image available.

Step 3

To call the runnable object we created in the second step, we have to use the postDelayed function of the Handler class which takes in a Runnable object and the time in milliseconds as the parameter. The code snippet for this is provided below.

handler.postDelayed(this, SLIDE_SHOW_INTERVAL);

Please note that we have also included the above line of code in the finally block after each successful run of the method because as the slideshow starts, we will have to call this function to scroll to the new position after every finite provided duration.

This is how we have achieved the Slideshow functionality in the Phimpme Android application. To get the full source code for this implementation, please check out the Phimpme Android GitHub repository listed in the resources section below.

Resources

  1. GitHub – Phimpme Android Repository – https://github.com/fossasia/phimpme-android/
  2. Android Developer Guide – Handler class – https://developer.android.com/reference/android/os/Handler.html
  3. StackOverflow – Runnable in Java – https://stackoverflow.com/questions/13327571/in-a-simple-to-understand-explanation-what-is-runnable-in-java
  4. StackOverflow – Image Slideshow in Android – https://stackoverflow.com/questions/2995145/image-slideshow-example-in-android

 

Compressing Albums in the Phimpme Android Application

The Phimpme Android application comes in with all the functionalities ranging from viewing images to taking photos, editing pictures  and sharing them with the world from within a single application without having to switch to or install other social media apps on your mobile phone. Apart from these basic functionalities, the Phimpme Android app also comes with additional features to enhance user experience like the ability to compress the whole album with a large number of photos so that it becomes easier to share them. In this post, I will be explaining how we achieved this functionality to compress the Albums.

Step 1

The first thing we need to do before compressing an album is to get all the paths of the images in that album and store it in an ArrayList<String> so that it can be used later for the compression process. This can be done using the code snippet provided below, it stores all the paths of the file in a particular folder whose name ends with .jpg

path = new ArrayList<>();
File folder = new File(getAlbums().getSelectedAlbum(0).getPath() + "/");
File[] fpath = folder.listFiles();
for(int i = 0; i < fpath.length; i++){
   if(fpath[i].getPath().endsWith(".jpg") ){
       path.add(fpath[i].getPath());
   }
}

Step 2

Since the compression is a heavy task, we can make use of an AsyncTask to run the task on the background thread so that the user experience is not at all hampered. In the onPreExecute method of the AsyncTask, we need to display the Notification that the compression of the particular album has started, for this we have made use of the Notification handler class that we have created in the Phimpme Android application to ease the process of displaying the notification and to avoid repetition of codes. The onPreExecute method of the AsyncTask is given below.

@Override
protected void onPreExecute() {
   super.onPreExecute();
   NotificationHandler.make(R.string.folder, R.string.zip_fol, R.drawable.ic_archive_black_24dp );
}

Step 3

On the doInBackground method of the AsyncTask, we run the process to compress the files one by one. For this we will make use of the ZipEntry class which is used to represent a zip file entry in Android/Java. First we will create a File with the .zip extension.  After this, we will make use of an object of the class ZipOutputStream as depicted in the code snippet provided below.

BufferedInputStream origin = null; 
FileOutputStream dest = new FileOutputStream(_zipFile); 
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest)); 
byte data[] = new byte[BUFFER];

After initializing the ZipOutPutStream object, we will put the zip entries in it by using the putNextEntry function of the class. To create a Zip entry of a file, we need to make use of for loop to generate the object of type ZipEntry and after that by using the putNextEntry function of the class, we will put the entries one by one as depicted in the code snippet given below.

for (int i = 0; i < path.size(); i++) {
FileInputStream fi = new FileInputStream(path.get(i));
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(path.get(i).substring(path.get(i).lastIndexOf("/") + 1));
out.putNextEntry(entry);

While preparing the Zip file, we will update the progress of the compression operation by making use of the Notification handler class.

This is how we have implemented the feature to compress the Albums in the Phimpme Android Application. To get the full source code for the same, please check the Phimpme Android GitHub repository listed on the resources below.

Resources

  1. StackOverflow – Compressing Files in Android – https://stackoverflow.com/questions/25562262/how-to-compress-files-into-zip-folder-in-android
  2. Blog – Compressing Files in Android programmatically – http://stacktips.com/tutorials/android/how-to-programmatically-zip-and-unzip-file-in-android
  3. GitHub – Phimpme Android Repository – https://github.com/fossasia/phimpme-android/

Enhancing Rotation in Phimp.me using Horizontal Wheel View

Installation

To implement rotation of an image in Phimp.me,  we have implemented Horizontal Wheel View feature. It is a custom view for user input that models horizontal wheel controller. How did we include this feature using jitpack.io?

Step 1: 

The jitpack.io repository has to be added to the root build.gradle:

allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
}


Then, add the dependency to your module build.gradle:

compile 'com.github.shchurov:horizontalwheelview:0.9.5'


Sync the Gradle files to complete the installation.

Step 2: Setting Up the Layout

Horizontal Wheel View has to be added to the XML layout file as shown below:

<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2">

<com.github.shchurov.horizontalwheelview.HorizontalWheelView
android:id="@+id/horizontalWheelView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toStartOf="@+id/rotate_apply"
android:padding="5dp"
app:activeColor="@color/accent_green"
app:normalColor="@color/black" />

</FrameLayout>


It has to be wrapped inside a Frame Layout to give weight to the view.
To display the angle by which the image has been rotated, a simple text view has to be added just above it.

<TextView
android:id="@+id/tvAngle"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="center"
android:layout_weight="1"
android:gravity="center"
android:textColor="@color/black"
android:textSize="14sp" />

Step 3: Updating the UI

First, declare and initialise objects of HorizontalWheelView and TextView.

HorizontalWheelView horizontalWheelView = (HorizontalWheelView) findViewById(R.id.horizontalWheelView);
TextView tvAngle= (TextView) findViewById(R.id.tvAngle);

 

Second, set up listener on the HorizontalWheelView and update the UI accordingly.

horizontalWheelView.setListener(new HorizontalWheelView.Listener() {
@Override
public void onRotationChanged(double radians) {
updateText();
updateImage();
}
});


updateText()
updates the angle and updateImage() updates the image to be rotated. The following functions have been defined below:

private void updateText() {
String text = String.format(Locale.US, "%.0f°", horizontalWheelView.getDegreesAngle());
tvAngle.setText(text);
}

private void updateImage() {
int angle = (int) horizontalWheelView.getDegreesAngle();
//Code to rotate the image using the variable 'angle'
rotatePanel.rotateImage(angle);
}


rotateImage()
is a method of ‘rotatePanel’ which is an object of RotateImageView, a custom view to rotate the image.

Let us have a look at some part of the code inside RotateImageView.

private int rotateAngle;


‘rotateAngle’ is a global variable to hold the angle by which image has to be rotated.

public void rotateImage(int angle) {
rotateAngle = angle;
this.invalidate();
}


The method invalidate() is used to trigger UI refresh and every time UI is refreshed, the draw() method is called.
We have to override the draw() method and write the main code to rotate the image in it.

The draw() method is defined below:

@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (bitmap == null)
return;
maxRect.set(0, 0, getWidth(), getHeight());// The maximum bounding rectangle

calculateWrapBox();
scale = 1;
if (wrapRect.width() > getWidth()) {
scale = getWidth() / wrapRect.width();
}

canvas.save();
canvas.scale(scale, scale, canvas.getWidth() >> 1,
canvas.getHeight() >> 1);
canvas.drawRect(wrapRect, bottomPaint);
canvas.rotate(rotateAngle, canvas.getWidth() >> 1,
canvas.getHeight() >> 1);
canvas.drawBitmap(bitmap, srcRect, dstRect, null);
canvas.restore();
}

private void calculateWrapBox() {
wrapRect.set(dstRect);
matrix.reset(); // Reset matrix is ​​a unit matrix
int centerX = getWidth() >> 1;
int centerY = getHeight() >> 1;
matrix.postRotate(rotateAngle, centerX, centerY); // After the rotation angle
matrix.mapRect(wrapRect);
}

 

And here you go:

Resources

Refer to Github- Horizontal Wheel View for more functions and for a sample application.

Link Preview Holder on SUSI.AI Android Chat

SUSI Android contains several view holders which binds a view based on its type, and one of them is LinkPreviewHolder. As the name suggests it is used for previewing links in the chat window. As soon as it receives an input as of link it inflates a link preview layout. The problem which exists was that whenever a user inputs a link as an input to app, it crashed. It crashed because it tries to inflate component that doesn’t exists in the view that is given to ViewHolder. So it gave a Null pointer Exception, due to which the app crashed. The work around for fixing this bug was that based on the type of user it will inflate the layout and its components. Let’s see how all functionalities were implemented in the LinkPreviewHolder class.

Components of LinkPreviewHolder

@BindView(R.id.text)
public TextView text;
@BindView(R.id.background_layout)
public LinearLayout backgroundLayout;
@BindView(R.id.link_preview_image)
public ImageView previewImageView;
@BindView(R.id.link_preview_title)
public TextView titleTextView;
@BindView(R.id.link_preview_description)
public TextView descriptionTextView;
@BindView(R.id.timestamp)
public TextView timestampTextView;
@BindView(R.id.preview_layout)
public LinearLayout previewLayout;
@Nullable @BindView(R.id.received_tick)
public ImageView receivedTick;
@Nullable
@BindView(R.id.thumbs_up)
protected ImageView thumbsUp;
@Nullable
@BindView(R.id.thumbs_down)
protected ImageView thumbsDown;

Currently in this it binds the view components with the associated id using declarator @BindView(id)

Instantiates the class with a constructor

public LinkPreviewViewHolder(View itemView , ClickListener listener) {
   super(itemView, listener);
   realm = Realm.getDefaultInstance();
   ButterKnife.bind(this,itemView);
}

Here it binds the current class with the view passed in the constructor using ButterKnife and initiates the ClickListener.

Now it is to set the components described above in the setView function:

Spanned answerText;
text.setLinksClickable(true);
text.setMovementMethod(LinkMovementMethod.getInstance());
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
answerText = Html.fromHtml(model.getContent(), Html.FROM_HTML_MODE_COMPACT);
} else {
answerText = Html.fromHtml(model.getContent());
}

Sets the textView inside the view with a clickable link. Version checking also has been put for checking the version of Android (Above Nougat or not) and implement the function accordingly.

This ViewHolder will inflate different components based on the thing that who has requested the output. If the query wants to inflate the LinkPreviewHolder them some extra set of components will get inflated which need not be inflated for the response apart from the basic layout.

if (viewType == USER_WITHLINK) {
   if (model.getIsDelivered())
       receivedTick.setImageResource(R.drawable.ic_check);
   else
       receivedTick.setImageResource(R.drawable.ic_clock);
}

In the above code  received tick image resource is set according to the attribute of message is delivered or not for the Query sent by the user. These components will only get initialised when the user has sent some links.

Now comes the configuration for the result obtained from the query.  Every skill has some rating associated to it. To mark the ratings there needs to be a counter set for rating the skills, positive or negative. This code should only execute for the response and not for the query part. This is the reason for crashing of the app because the logic tries to inflate the contents of the part of response but the view that is passed belongs to query. So it gives NullPointerException there, so there is a need to separate the logic of Response from the Query.

if (viewType != USER_WITHLINK) {
   if(model.getSkillLocation().isEmpty()){
       thumbsUp.setVisibility(View.GONE);
       thumbsDown.setVisibility(View.GONE);
   } else {
       thumbsUp.setVisibility(View.VISIBLE);
       thumbsDown.setVisibility(View.VISIBLE);
   }

   if(model.isPositiveRated()){
       thumbsUp.setImageResource(R.drawable.thumbs_up_solid);
   } else {
       thumbsUp.setImageResource(R.drawable.thumbs_up_outline);
   }

   if(model.isNegativeRated()){
       thumbsDown.setImageResource(R.drawable.thumbs_down_solid);
   } else {
       thumbsDown.setImageResource(R.drawable.thumbs_down_outline);
   }

   thumbsUp.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View view) { . . . }
   });



   thumbsDown.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View view) { . . . }
   });

}

As you can see in the above code  it inflates the rating components (thumbsUp and thumbsDown) for the view of the SUSI.AI response and set on the clickListeners for the rating buttons. Them in the below code it previews the link and commit the data using Realm in the database through WebLink class.

LinkPreviewCallback linkPreviewCallback = new LinkPreviewCallback() {
   @Override
   public void onPre() { . . . }

   @Override
   public void onPos(final SourceContent sourceContent, boolean b) { . . . }
}

This method calls the api and set the rating of that skill on the server. On successful result it made the thumb Icon change and alter the rating method and commit those changes in the databases using Realm.

private void rateSusiSkill(final String polarity, String locationUrl, final Context context) {..}

References

Creating a Notification in Open Event Android App

It is a good practice to show user a notification for alerts and have their attention for important events they want to remember. Open Event Android app shows notifications for the actions like bookmarks, upcoming events etc. In this blog we learn how to create similar kind of alert notification.

 

Displaying notification after bookmarking a track

NotificationCompat is available as part of the Android Support Library, so the first step is opening your project’s module-level build.gradle file and adding the support library to the dependencies section. First we initialize the notification manager with the context of application so a user can see notification irrespective of where it is in app.

NotificationManager mManager = (NotificationManager) this.getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
int id = intent.getIntExtra(ConstantStrings.SESSION, 0);
String session_date;
Session session = realmRepo.getSessionSync(id);

We then get the info we want to display in the notification from the intent. While adding an action to your notification is optional, the reality is that the vast majority of applications add actions to their notifications. We define a notification action using a PendingIntent. In this instance, we update our basic notification with a PendingIntent.

Intent intent1 = new Intent(this.getApplicationContext(), SessionDetailActivity.class);
intent1.putExtra(ConstantStrings.SESSION, session.getTitle());
intent1.putExtra(ConstantStrings.ID, session.getId());
intent1.putExtra(ConstantStrings.TRACK,session.getTrack().getName());
PendingIntent pendingNotificationIntent = PendingIntent.getActivity(this.getApplicationContext(), 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
Bitmap largeIcon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);

We also test the condition for the OS version to display the marker image, see image 1 for reference. The minimum requirement for a notification are:

  • An icon: Create the image you want to use and then add it to you project’s ‘drawable’ folder. Here notification shows bookmark option
  • Title text. You can set a notification’s title either by referencing a string resource, or by adding the text to your notification directly.
  • Detail text. This is the most important part of your notification, so this text must include everything the user needs to understand exactly what they’re being notified about.
int smallIcon = R.drawable.ic_bookmark_white_24dp;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) smallIcon = R.drawable.ic_noti_bookmark;

String session_timings = String.format("%s - %s",
       DateConverter.formatDateWithDefault(DateConverter.FORMAT_12H, session.getStartsAt()),
       DateConverter.formatDateWithDefault(DateConverter.FORMAT_12H, session.getEndsAt()));
session_date = DateConverter.formatDateWithDefault(DateConverter.FORMAT_DATE_COMPLETE, session.getStartsAt());

Finally we build notification using notification builder having various options to set text style, small icons, big icon etc., see the complete class here,

NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
       .setSmallIcon(smallIcon)
       .setLargeIcon(largeIcon)
       .setContentTitle(session.getTitle())
       .setContentText(session_date + "\n" + session_timings)
       .setAutoCancel(true)
       .setStyle(new NotificationCompat.BigTextStyle().bigText(session_date + "\n" + session_timings))
       .setContentIntent(pendingNotificationIntent);
intent1.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);

mBuilder.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
mManager.notify(session.getId(), mBuilder.build());

References

Getting Started Developing on Phimpme Android

Phimpme is an Android app for editing photos and sharing them on social media. To participate in project start by learning how people contribute in open source, learning about the version control system Git and other tools like Codacy and Travis.

Firstly, sign up for GitHub. Secondly, find the open source projects that interest you. Now as for me I started with Phimpme. Then follow these steps:

  1. Go through the project ReadMe.md and read all the technologies and tools they are using.
  2. Now fork that repo in your account.
  3. Open the Android Studio/Other applications that are required for that project and import the project through Git.
  4. For Android Studio sync all the Gradle files and other changes and you are all done and ready for the development process.

Install the app and run it on a phone. Now explore each and every bit use this app as a tester, think about the end cases and boundary condition that will make the app ‘ANR’ (App not responding) dialog appear. Congratulations you are ready to create an issue if that is a verified and original and actually is a bug.

Next,

  • Navigate to the main repo link, you will see an issues section as follows:
  • Create a new issue and report every detail about the issue (logcat, screenshots) For eg. Refer to Issue-1120
  • Now the next step is to work on that issue
  • On your machine, you don’t have to change the code in the development branch as it’s considered to be as a bad practice. Hence checkout as a new branch.
    For eg., I checked out for the above issue as ‘crashfixed’
git checkout -b "Any branch name you want to keep"
  • Make the necessary changes to that branch and test that the code is compiling and the issue is fixed followed by
git add.
git commit -m "Fix #Issue No -Description "
git push origin branch-name
  • Now navigate to the repo and you will an option to create a Pull Request.
    Mention the Issue number and description and changes you done, include screenshots of the fixed app.For eg. Pull Request 1131.

Hence you have done your first contribution in open source while learning with git. The pull request will initiate some checks like Codacy and Travis build and then if everything works it is reviewed and merged by co-developers.

The usual way how this works is, that it should be reviewed by other co-developers. These co-developers do not need merge or write access to the repository. Any developer can review pull requests. This will also help contributors to learn about the project and make the job of core developers easier.

Resources