Adding option to Unfavourite image in Phimp.Me

This blog is in accordance with the P.R #1900. Here I have implemented the option to unfavourite image in the Phimp.Me android application. Implementation In the Phimp.Me app there are the following modes present: 1. all_photos: All the photos are displayed in this mode irrespective of where they are saved. 2. fav_photos: The photos which are added to favourites are displayed in the fav_photos. 3. Albums_mode: All the albums which are present in the app are displayed in the albums_mode. The main idea here is to find whether the selected image is already FAVOURITE or not. If it is already FAVORITED then it can be removed from that mode by removing its path form the Realm Database. If it isn’t already FAVORITED then the image is ignored and the next image is taken into consideration. The process of removing the images from favourites can be an expensive one as the user can select myriad images which would ultimately block the Main UI. So it is better handled asynchronously and is implemented using the AsyncTask. Whenever the user adds an image to the FAVOURITES, it gets added to the Realm Database where the model class being used is the FavouriteImagesModel.The selected media in the all_photos and fav_photos mode can be accessed via by selectedMedia.size()  and the number of selected media in the albums_mode can be accessed by getAlbum().getSelectedCount(). So in the execute method of doInBackground() a condition check is initially made and then 2 separate loops are run depending upon the mode in which the selected images exist. Initially it is checked whether the selected image is already a FAVOURITE one or not by the following code. If it belongs to the favourite mode then the size of the favouriteImageModels would become 1. RealmResults<FavouriteImagesModel> favouriteImagesModels = realm.where                                                (FavouriteImagesModel.class).equalTo("path", selectedMedias.get(i).getPath( )).findAll( ); If ( favouriteImagesModels.size( ) == 1) {             favouriteImagePresent = true;              imagesUnfavourited++;    } Now as the image belongs to the favourite mode we ultimately use the following code to remove the image from FAVOURITES.  favouriteImagesModels.deleteAllFromRealm(); The full code which handle the option to unfavourite an image is shown below. @Override                    protected Boolean doInBackground(String... arg0) {                              if ( all_photos || fav_photos )   {                            realm = Realm.getDefaultInstance();                            realm.executeTransaction ( new Realm.Transaction( ) {                                                                @Override                                public void execute (Realm realm)  {                                    for (int i = 0 ;  i < selectedMedias.size( ) ;  i++) {                                        RealmResults<FavouriteImagesModel> favouriteImagesModels = realm.where                                                (FavouriteImagesModel.class).equalTo("path", selectedMedias.get(i).getPath( )).findAll( );                                        If ( favouriteImagesModels.size( ) == 1) {                                            favouriteImagePresent = true;                                            imagesUnfavourited++;                                        }                                        favouriteImagesModels.deleteAllFromRealm();                                    }                                }                            });                        }          else if ( !fav_photos && !albumsMode ) {                            realm = Realm.getDefaultInstance();                            realm.executeTransaction(new Realm.Transaction() {                                                           @Override                                public void execute(Realm realm) {                                    for (int i = 0;  i < getAlbum().getSelectedCount();  i++) {                                        RealmResults<FavouriteImagesModel> favouriteImagesModels = realm.where                                                (FavouriteImagesModel.class).equalTo("path", getAlbum( ).getSelectedMedia(i).getPath( ) ).findAll( );                                        If ( favouriteImagesModels.size() == 1) {                                            favouriteImagePresent = true;                                            imagesUnfavourited++;                                        }                                        favouriteImagesModels.deleteAllFromRealm();                                    }                                }                    } After the doInBackground( ) method has been executed the onPostExecute( ) comes into play and some other UI related changes are done such as a SnackBar message…

Continue ReadingAdding option to Unfavourite image in Phimp.Me

Implementing Filters in Phimp.me Android App

Image filters in phimp.me android app are implemented using the OpenCV library that enables the android developers to write code in C++/C which is compatible with java. I have implemented around eight filters in the filters.cpp file, mainly dealing with the colour variations that took place between the images. Suppose that the color model used in consideration is the RGB (Red,Green,Blue) model and we define that every pixel is defined using these values. If we simply want to boost or focus on the red part of the image it can be done by enhancing the particular color in the source image i.e. modifying each pixel of the app by enhancing the blue color of each pixel. The code for the same can be written as : void applyBlueBoostEffect(cv::Mat &src, cv::Mat &dst, int val) { register int x, y; float opacity = val * 0.01f; cvtColor(src, src, CV_BGRA2BGR); dst = Mat::zeros(src.size(), src.type()); uchar r, g, b,val1; for (y = 0; y < src.rows; y++) { for (x = 0; x < src.cols; x++) { r = src.at<Vec3b>(y, x)[0]; g = src.at<Vec3b>(y, x)[1]; b = src.at<Vec3b>(y, x)[2]; val1 = saturate_cast<uchar>(b*(1+opacity)); if(val1 > 255) { val1 = 255; } dst.at<Vec3b>(y, x)[0] = saturate_cast<uchar>(r); dst.at<Vec3b>(y, x)[1] = saturate_cast<uchar>(g); dst.at<Vec3b>(y, x)[2] = saturate_cast<uchar>(val1); } } } In the above function what we are doing is taking the source image and converting it to a matrix of known number of rows and columns. Now, we loop over this matrix created and at each position in the matrix calculate the red, green and blue values present there. There is a parameterized value “val” that determines the amount to which the color chosen is enhanced. A simple variable val1 contains the enhanced value of the color chosen, here which is color blue. We modify and boost the color by the equation  : B = B*(1 + (0.01f* val) ) Finally at this particular position in the matrix all the three values of the color are updated i.e. the original red and green color but the modified blue color. The output of this filter converts images as below shown : Before                                                                                   After    In the above method defined we instead of enhancing one color we can even modify two colors or all the three at the same time to get different effects as per the required needs. An example where I implemented a filter to enhance two colors at the same time is given below, the purpose of this filter is to add a violet color tone in the image. The code for the filter is : void applyRedBlueEffect(cv::Mat &src, cv::Mat &dst, int val) { register int x, y; float opacity = val * 0.01f; cvtColor(src, src, CV_BGRA2BGR); dst = Mat::zeros(src.size(), src.type()); uchar r, g, b; uchar val1,val3; for…

Continue ReadingImplementing Filters in Phimp.me Android App

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_FILL- This 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…

Continue ReadingOption to Print Photos in the Phimpme Android Application

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…

Continue ReadingCompressing Albums in the Phimpme Android Application

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.

Continue ReadingEnhancing Rotation in Phimp.me using Horizontal Wheel View

Automatic Signing and Publishing of Android Apps from Travis

As I discussed about preparing the apps in Play Store for automatic deployment and Google App Signing in previous blogs, in this blog, I’ll talk about how to use Travis Ci to automatically sign and publish the apps using fastlane, as well as how to upload sensitive information like signing keys and publishing JSON to the Open Source repository. This method will be used to publish the following Android Apps: Phimp.me Loklak wok SUSI AI Open Event Orga App PSLab Current Project Structure The example project I have used to set up the process has the following structure: It’s a normal Android Project with some .travis.yml and some additional bash scripts in scripts folder. The update-apk.sh file is standard app build and repo push file found in FOSSASIA projects. The process used to develop it is documented in previous blogs. First, we’ll see how to upload our keys to the repo after encrypting them. Encrypting keys using Travis Travis provides a very nice documentation on encrypting files containing sensitive information, but a crucial information is buried below the page. As you’d normally want to upload two things to the repo - the app signing key, and API JSON file for release manager API of Google Play for Fastlane, you can’t do it separately by using standard file encryption command for travis as it will override the previous encrypted file’s secret. In order to do so, you need to create a tarball of all the files that need to be encrypted and encrypt that tar instead. Along with this, before you need to use the file, you’ll have to decrypt in in the travis build and also uncompress it for use. So, first install Travis CLI tool and login using travis login (You should have right access to the repo and Travis CI in order to encrypt the files for it) Then add the signing key and fastlane json in the scripts folder. Let’s assume the names of the files are key.jks and fastlane.json Then, go to scripts folder and run this command to create a tar of these files: tar cvf secrets.tar fastlane.json key.jks   secrets.tar will be created in the folder. Now, run this command to encrypt the file travis encrypt-file secrets.tar   A new file secrets.tar.enc will be created in the folder. Now delete the original files and secrets tar so they do not get added to the repo by mistake. The output log will show the the command for decryption of the file to be added to the .travis.yml file. Decrypting keys using Travis But if we add it there, the keys will be decrypted for each commit on each branch. We want it to happen only for master branch as we only require publishing from that branch. So, we’ll create a bash script prep-key.sh for the task with following content #!/bin/sh set -e export DEPLOY_BRANCH=${DEPLOY_BRANCH:-master} if [ "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_REPO_SLUG" != "iamareebjamal/android-test-fastlane" -o "$TRAVIS_BRANCH" != "$DEPLOY_BRANCH" ]; then echo "We decrypt key only for…

Continue ReadingAutomatic Signing and Publishing of Android Apps from Travis

Enabling Google App Signing for Android Project

Signing key management of Android Apps is a hectic procedure and can grow out of hand rather quickly for large organizations with several independent projects. We, at FOSSASIA also had to face similar difficulties in management of individual keys by project maintainers and wanted to gather all these Android Projects under singular key management platform: Phimp.me Pocket Science Lab loklak wok Open Event Android and sample apps eventyay Organizer App Ask SUSI.AI To handle the complexities and security aspect of the process, this year Google announced App Signing optional program where Google takes your existing key’s encrypted file and stores it on their servers and asks you to create a new upload key which will be used to sign further updates of the app. It takes the certificates of your new upload key and maps it to the managed private key. Now, whenever there is a new upload of the app, it’s signing certificate is matched with the upload key certificate and after verification, the app is signed by the original private key on the server itself and delivered to the user. The advantage comes where you lose your key, its password or it is compromised. Before App Signing program, if your key got lost, you had to launch your app under a new package name, losing your existing user base. With Google managing your key, if you lose your upload key, then the account owner can request Google to reassign a new upload key as the private key is secure on their servers. There is no difference in the delivered app from the previous one as it is still finally signed by the original private key as it was before, except that Google also optimizes the app by splitting it into multiple APKs according to hardware, demographic and other factors, resulting in a much smaller app! This blog will take you through the steps in how to enable the program for existing and new apps. A bit of a warning though, for security reasons, opting in the program is permanent and once you do it, it is not possible to back out, so think it through before committing. For existing apps: First you need to go to the particular app’s detail section and then into Release Management > App Releases. There you would see the Get Started button for App Signing. The account owner must first agree to its terms and conditions and once it's done, a page like this will be presented with information about app signing infrastructure at top. So, as per the instructions, download the PEPK jar file to encrypt your private key. For this process, you need to have your existing private key and its alias and password. It is fine if you don’t know the key password but store password is needed to generate the encrypted file. Then execute this command in the terminal as written in Step 2 of your Play console: java -jar pepk.jar --keystore={{keystore_path}} --alias={{alias}} --output={{encrypted_file_output_path}} --encryptionkey=eb10fe8f7c7c9df715022017b00c6471f8ba8170b13049a11e6c09ffe3056a104a3bbe4ac5a955f4ba4fe93fc8cef27558a3eb9d2a529a2092761fb833b656cd48b9de6a You will have to…

Continue ReadingEnabling Google App Signing for Android Project