Creating Instruction Guide using Bottomsheet

The PSLab android app consists of different instruments like oscilloscope, multimeter, wave generator etc and each instrument has different functionality and usage so it is necessary that there should be an instruction guide for every instrument so that the user can easily read the instruction to understand the functionality of the instrument. In this we will create an instruction guide for the Wave Generator which will contain information about the instrument, it’s functionalities, steps for how to use the instrument. The main component that I used to create instruction guide is Bottom Sheet. Bottom Sheet is introduced in Android Support v23.2 . It is a special UI widget which slide up from the bottom of the screen and it can be used to reveal some extra information that we cannot show on the main layout like bottom menus,  instructions etc. They are of two types : Modal Bottom Sheet:-  This Bottom Sheet has properties very similar to normal dialogs present in Android like elevation only difference is that they pop up from the bottom of screen with proper animation and they are implemented using BottomSheetDialogFragment Class. Persistent Bottom Sheet:- This Bottom Sheet is included as a part of the layout and they can be slid up and down to reveal extra information. They are implemented using BottomSheetBehaviour Class. For my project, I used persistent Bottom Sheet as modal Bottom Sheet can’t be slid up and down by the swipe of the finger whereas persistent Bottom Sheet can be slid up and down and can be hidden by swipe features. Implementing the Bottom Sheet Step 1: Adding the Dependency To start using Bottom Sheet we have to add the dependency (We have also include Jake Wharton-Butterknife library for view binding but it is optional.) dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation "com.android.support:appcompat-v7:26.0.1" implementation "com.android.support:design:26.0.1" implementation "com.jakewharton:butterknife:8.8.1" annotationProcessor "com.jakewharton:butterknife-compiler:8.8.1" Step 2: Creating Bottom Sheet layout file In this step, we will create the layout of the Bottom Sheet, as our purpose of making Bottom Sheet is to show extra information regarding the instrument so we will include ImageView and TextView inside the layout that will be used to show the content later. Some attributes in the layout worth noting are: app:layout_behavior: This attribute makes the layout act as Bottom Sheet. app:behavior_peekHeight: This is the height of the Bottom Sheet when it is minimized. app:behavior_hideable: Defines if the Bottom Sheet can be hidden by swiping it down. Here, we will also create one extra LinearLayout having height equal to the peek_height. This  LinearLayout will be at the top of the BottomSheet as shown in Figure 1 and it will be visible when the BottomSheet is in a minimized state(not hidden). Here we will put text view with like “Show guide” and an arrow pointing upwards so that it is easier for the user to understand that sheet can be viewed by sliding up. Here is the gist[2] that contains code for the layout of the Bottom Sheet guide After this step, we can see a layout…

Continue ReadingCreating Instruction Guide using Bottomsheet

Creating Control Panel For Wave Generator using Constraint Layout

  In the blog Creating the onScreen Monitor Using CardView I had created the monitors to view the wave properties in this blog we will create the UI of controlling panel that will be used for that monitors with multiple buttons for both analog and digital waveforms. Which layout to choose? In today’s world, there are millions of Android devices present with different screen sizes and densities and the major concern of an Android developer is to make the layout that fits all the devices and this task is really difficult to handle with a linear or relative layout with fixed dimensions. To create a complex layout with lots of views inside the parent using linear layout we have to make use of the attribute layout_weight for their proper stretching and positioning, but such a complex layout require a lot of nesting of weights and  android tries to avoid it by giving a warning : Nested Weights are bad for performance This is because layout_weight attribute requires a widget to be measured twice[1]. When a LinearLayout with non-zero weights is nested inside another LinearLayout with non-zero weights, then the number of measurements increases exponentially. So, to overcome this issue we will make use of special type of layout “Constraint Layout” which was introduced in Google I/O 2016. Features of Constraint Layout:- It is similar to Relative Layout as all views are laid out according to their relationship with the sibling, but it is more flexible than Relative Layout. It helps to flatten the view hierarchy for complex layouts. This layout is created with the help of powerful tool provided by Android which has a palette on the left-hand side from where we can drag and drop the different widgets like TextView, ImageView, Buttons etc. and on the right-hand side it provides options for positioning, setting margins and other styling option like setting color, change text style etc. It automatically adjusts the layout according to the screen size and hence doesn’t require the use of layout_weight attribute. In following steps, I will create the controlling panel for Wave generator which is a complex layout with lots of buttons with the help of constraint layout. Step 1: Add the dependency of the Constraint Layout in the Project To use Constraint layout add the following to your build.gradle file and sync the project dependencies { implementation "com.android.support.constraint:constraint-layout:1.1.0" } Step 2: Applying Guidelines to the layout Guidelines[3] are anchors that won’t be displayed in your app, they are like one line of a grid above your layout and can be used to attach or constraint your widgets to it. They are only visible on your blueprint or preview editor. These will help to position and constraint the UI components on the screen easily. For adding guidelines : As shown in Figure 1 Right-click anywhere on the layout -> Select helpers -> Select horizontal or vertical guideline according to your need. And for positioning the guideline we have to set the value of attribute layout_constraintGuide_percent…

Continue ReadingCreating Control Panel For Wave Generator using Constraint Layout

Implementing Leak Canary in PSLab Android App

This post discusses the issue of memory leaks and how to handle them efficiently and what are the tools available which help developers in managing the memory leaks. After working on PR #824 opened under PSLab - Android repository I have got a greater idea about how to manage the memory efficiently and what are the tools that should be used to ease the work. What are memory leaks and how it affects the quality of app? In simple words, memory leaks happen when you hold on to an item for long after its purpose have been served. It is as simple as that. But let us dive in further to understand more about this topic. Programming languages like C and C++ need memory management done by user whereas in higher level languages like Java, Python, etc. low-level memory management and garbage collection is done automatically by the system. But it is due to programming faults that memory leaks happen and so care needs to be taken with higher level languages too in handling memory efficiently. In Android or say in any OS (operating system), every item has to be destroyed or say deleted or freed from the memory after its purpose is served. But if the reference of this object is passed on to any other object which has a greater time of importance than this, then it will try to hold this object for long and so the garbage collector won’t be able to collect it and so there will be memory leaks in the code. This is how memory leaks occurs inside an app. Now, the issue of memory leaks is utmost important among developers as due to it, the app becomes slow, laggy, eats up a lot of memory and the app crashes after some time of use which creates a very bad user experience. As the user keeps on using the app, the heap memory also keeps on increasing and due to memory leaks, this heap can’t be freed by the garbage collector. Thus, all these issues contribute to making the threads or processes running inside the app slower and it can result in a lag of time from microseconds to milliseconds too!! How can you detect these memory leaks? This blog is mostly for Android developers and so I will use the environment of Android Studio for reference. For controlling memory leaks, Android Studio has a very powerful tool named Monitors. There are individual monitors not only for memory usage but for CPU, GPU, and network usage as well. An example of it is shown in figure 1 below. Figure 1. Monitor in Android Studio Now how to observe the graphs that are produced by Monitors to see if there are any memory leaks? The first alarming case is when the memory usage graph constantly increases and doesn’t come down as time passes and even not decreases when you put the app in the background. The tools which are used to undo memory leaks…

Continue ReadingImplementing Leak Canary in PSLab Android App

Implementing search bar in PSLab app

PSLab offers a large range of functions with a large field of applications. This results in the long access path to certain information and functionality in the app, which affects the user experience (UX) negatively. In this blog post, I will show how the UX in Android applications with many functions can be improved by implementing a ‘Search Bar’. Further, I have created a screencast that follows the step-by-step description shown in this blog post [1]. Search Bar is the functionality which can be seen nowadays in almost all the apps. It is a handy widget for users to navigate the app. PSLab's Android app was lacking this functionality and so I added it using an external library from Mirrajabi called ‘search-dialog’ [3]. I decided to use this library as it provides a search bar with built-in functionality of highlighting the text that’s selected or written down in the ‘Edit Text’ field by the user and also it overlays on the screen which provides a good user experience rather than other search bars which overlap on the text provided on the screen with a ListView. Further working with this dependency was easier than working with others as it seems well designed. The Search Bar was added in the Saved Experiments section which contains a lot of default experiments along with their respective manuals. The search bar was intended to provide the user with a greater UI so that they don’t need to navigate through sections every time to find an experiment. This blog is a step by step guide on how to implement it in any app. First, add dependencies in the gradle at both project level and app level. By adding these dependencies, we don’t need to worry about writing code for how the Search Bar will filter searches and how it will be placed on the screen. App Level : 'com.github.mirrajabi:search-dialog:1.1' Project Level : maven { url "https://jitpack.io" }  Create a SearchModel class which will help to carry out the search operations. public class SearchModel implements Searchable { private String mTitle; public SearchModel(String mTitle) { this.mTitle = mTitle; } public void setTitle(String mTitle) { this.mTitle = mTitle; } @Override public String getTitle() { return mTitle; } } The SearchModel class helps to communicate with the filtered search results by returning the string which is selected: SearchModel(String string) - It is a default constructor setTitle(String string) - It is a method used to set the string provided in the search results getTitle() - It is also a method to get the string selected by the user from the filtered search result provided  Now provide some data which needs to be searched using an ArrayList or by using StringArrays and then converting it to Search Model Object. private ArrayList<SampleSearchModel> createSampleData(){ ArrayList<SampleSearchModel> items = new ArrayList<>(); items.add(new SampleSearchModel("First item")); items.add(new SampleSearchModel("Second item")); items.add(new SampleSearchModel("Third item")); items.add(new SampleSearchModel("Fourth item")); return items; }  At last add the constructor in MainActivity to make the search bar. SimpleSearchDialogCompat(Context context, String title, String searchHint, @Nullable Filter…

Continue ReadingImplementing search bar in PSLab app

Creating the onScreen Monitor Using CardView

The PSLab works as a Wave generator and the Oscilloscope when connected with the Android device. The mockup of the Wave Generator has been created in the blog Creating the Mockup for Wave Generator using Moqups. In this blog, we will create the monitor screen in the PSLab Android app using Android studio. Deciding the layout to use for monitors As monitors with rounded border look appealing on android device screen so we will select CardView to be the base layout of our monitors. The CardView element allows us to add certain properties like rounded corners, elevation etc. To use the CardView in your app, add the following dependency to build.gradle file and sync the project. dependencies { // CardView implementation 'com.android.support:cardview-v7:27.1.1' } Add the <android.support.v7.widget.CardView> widget to your layout and all the other views to be shown on monitor screen will be placed inside this card layout as its child layout. Creating the monitor cards We need to decide how much spacing is to be provided to the cards to properly view them on different screens. Here we make use of the special attribute in the View class. android:layout_weight=1 This attribute assigns an "importance" value to a View in terms of how much space it should occupy on the screen. That is if we give equal weight to two views which inside the same parent then they will both occupy equal space. To implement this create two CardViews inside <LinearLayout> with the same layout_weight. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.CardView android:layout_width="0dp" android:layout_height="250dp" android:layout_weight="1" card_view:cardCornerRadius="10dp" /> <android.support.v7.widget.CardView android:layout_width="0dp" android:layout_height="250dp" android:layout_weight="1" card_view:cardCornerRadius="10dp" /> </LinearLayout> So this will give us a screen like this with equally weighted monitor card views as shown in Figure 1. Both the cards have the width equal to half the screen width which is set automatically by the Android. Creating partitions in the monitors We have to make partitions in the monitor screen so that every characteristic of Wave Generator like wave frequency, phase, selected pin etc. can be viewed simultaneously on the screen with proper spacing. For making partitions we need to make a separator line which we can create by using <View> and setting the attributes as below: <View android:layout_width="@dimen/length_0dp" android:layout_height="@dimen/divider_width" android:background="@color/dark_grey" /> This will create a thin line having the grey color. We have to make four partitions of the card view for both monitors as shown in Figure 2. Populating the screen with text and image icons We have to include the <TextView> and <ImageView> to show the property data on the monitors. Different partitions as shown in Figure 3 can be used for showing different data such as: Top Partition:- To view the selected wave.  Left Partition:- To view the selected waveform eg:- sine, triangular  Right Partition:- To view the selected wave characteristics like frequency, phase etc.  Bottom Partition:-  To view the property selected which has been selected by user After inserting all the desired Views and providing the Views with dummy data and icons and customizing the Views by giving proper color…

Continue ReadingCreating the onScreen Monitor Using CardView

Saving Sensor Data in CSV format

PSLab Android app by FOSSASIA provides a variety of features to its users. One of them is accessing various types of sensors both built into mobile phone and external sensors connected with PSLab device. In earlier versions users were only able to view the captured data. Moving forward, adding improvements to the app, now there is a feature to save those data displayed in graphs in csv format. This feature is important in many ways. One is educational. In a classroom, teachers can ask students to perform an experiment and prepare a report using the data collected. By just visualizing they cannot do this. Actual data points must be made available. Another use is sharing data sets related to say environmental data over different demographics. CSV, or comma-separated values file is a text file where stored data are separated by commas. The file stores these tabular data (numbers and text) in plain text format. Each line of the file represents a data record. Each data record consists of one or more fields, separated by commas. CSV files are commonly used to store sensor data because of its easy use. This post is about how PSLab device uses CSV file to write sensor data in it. In PSLab android source code, there is a dedicated class to handle read sensor data from different instruments called “CSVLogger”. Developers can easily instantiate this class wherever they want a data logging as follows; CSVLogger logger = new CSVLogger(<SUBFOLDER>); logger .writeCSVFile("Heading1,Heading2,Heading3\n");   This will create a blank folder in “PSLab” folder in device storage.  The CSV file is generated with the following convention according to the date and time where data is saved in the file. yyyymmdd-hhmmss.csv A sample file would have a name like 20180710-07:30:28.csv inside the SUBFOLDER which is specific to each instrument. Folder name will be the one used when initiating the CSVLogger. With this method, logging data is pretty easy. Simply create a string which is a comma seperated and ended with a new line character. Then simply call the writeCSVFile(data) method with the string as a parameter added to it. It will keep appending string data until a new file is created. File creation can be handled by developers at their own interests and preferences. String data = String.valueOf(System.currentTimeMillis()) + "," + item.getX() + "," + item.getY() + "\n"; logger.writeCSVFile(data);   To bring out an example let’s view how it’s implemented in Lux Meter instrument. This is a good source one can refer to when adding this feature in fragments inside a main activity. In Lux Meter, there is the parent activity named Lux Meter and inside that there are two fragments, one is fragmentdata and the other one is fragmentsettings. Data capturing and saving occurs inside fragmentdata. Menu icon controlling happens in the parent activity and we have bound a variable across the main activity and child fragment as follows; LuxMeterActivity parent = (LuxMeterActivity) getActivity(); if (parent.saveData) {/* Save Data */}   This makes it easier listening menu icon…

Continue ReadingSaving Sensor Data in CSV format

Creating the Mockup for Wave Generator using Moqups

PSLab is a versatile device that can be used as a waveform signal generator, as it is capable of producing different types of waves. It provides a cheap and portable alternative as compared to the physical wave generators available in markets. The current UI of the Wave Generator present in the PSLab app as shown in Figure 1 is very basic and makes use of some basic UI components which are not able to properly expose all the functionalities of the wave generator. This affects the user experience negatively. In this blog, we will discuss creating a mockup for the PSLab wave generator using modern design to improve the UI/UX of the app. I will try to keep the layout simple enough so that users can easily understand it.  Understanding the Functionality of Waveform Generator We can observe from the image that PSLab provide: Two pins named W1 and W2 which are capable of producing two different sine or saw-tooth waves having different characteristics simultaneously. Four Pins named SQ1, SQ2, SQ3, SQ4 which are capable of producing Square Wave and Pulse Width modulation signals. It also allows us to change the properties of these waves such as frequency, phase, duty cycle. Before starting with the mockup a physical commodity Waveform Signal Generator as shown in Figure 3 was inspected. Most waveform generators in the market have a control panel with buttons and a monitor screen so that the user can view the changes while controlling the characteristics of the wave. I will try to create a layout similar to this in following steps by dividing it into two halves: Upper half containing monitor screens Lower half containing controlling panel having buttons Steps for creating the mockup Step 1: Choosing the Tool The tool that we will be using here to create a mockup is moqups[1]. I have chosen this software because:- It is an online tool and doesn’t require it to be set up first It is very efficient and powerful tool for creating mobile or web wireframes. It doesn't require any prior knowledge of programming language. It has a palette to drag & drop UI elements like Buttons, Textboxes, Checkboxes etc. on the left-hand side, allows modifying the features of each element (here on the right) and other options at the top related to prototyping, previewing etc as shown in figure 4. Step 2: Creating the base of the layout The mockup tool moqups[1] have a built-in template of Android screen which we can drag and drop from the palette and use it as our base. Step 3: Creating the Lower Half Now as discussed above I will create controlling panel containing buttons in the lower half of the screen, for this I will drag and drop buttons from the palette on the right-hand side and design them according to the theme of the app that is making their border round shape, changing their color to match the theme color and editing text in them. As the PSLab has…

Continue ReadingCreating the Mockup for Wave Generator using Moqups

Building PSLab Android app with Fdroid

Fdroid is a place for open source enthusiasts and developers to host their Free and Open Source Software (FOSS) for free and get more people onboard into their community. Hosting an app in Fdroid is not a fairly easy process just like hosting one in Google Play. We need to perform a set of build checks prior to making a merge request (which is similar to pull request in GitHub) in the fdroid-data GitLab repository. PSLab Android app by FOSSASIA has undergone through all these checks and tests and now ready to be published. Setting up the fdroid-server and fdroid-data repositories is one thing. Building our app using the tools provided by fdroid is another thing. It will involve quite a few steps to get started. Fdroid requires all the apps need to be built using: $ fdroid build -v -l org.fossasia.pslab   This will output a set of logs which tell us what went wrong in the builds. The usual one in a first time app is obviously the build is not taking place at all. The reason is our metadata file needs to be changed to initiate a build. Build:<versioncode>,<versionname> commit=<commit which has the build mentioned in versioncode> subdir=app gradle=yes   When a metadata file is initially created, this build is disabled by default and commit is set to “?”. We need to fill in those blanks. Once completed, it will look like the snippet above. There can be many blocks of “Build” can be added to the end of metadata file as we are advancing and upgrading through the app. As an example, the latest PSLab Android app has the following metadata “Build” block: Build:1.1.5,7 commit=0a50834ccf9264615d275a26feaf555db42eb4eb subdir=app gradle=yes   In case of an update, add another “Build” block and mention the version you want to appear on the Fdroid repository as follows: Auto Update Mode:Version v%v Update Check Mode:Tags Current Version:1.1.5 Current Version Code:7   Once it is all filled, run the build command once again. If you have properly set the environment in your local PC, build will end successfully assuming there were no Java or any other language syntax errors. It is worth to mention few other facts which are common to Android software projects. Usually the source code is packed in a folder named “app” inside the repository and this is the common scenario if Android Studio builds up the project from scratch. If this “app” folder is one level below the root, that is “android/app”, the build instructions shown above will throw an error as it cannot find the project files. The reason behind this is we have mentioned “subdir=app” in the metadata file. Change this to “subdir=android/app” and run the build again. The idea is to direct the build to find where the project files are. Apart from that, the commit can be represented by a tag instead of a long commit hash. As an example, if we had merge commits in PSLab labeled as “v.<versioncode>”, we can simply use “commit=v.1.1.5” instead…

Continue ReadingBuilding PSLab Android app with Fdroid

Creating Device Screen to show connection status of PSLab Device

For using the PSLab Device the user needs to connect the PSLab device to an Android Phone having a PSLab android app. So there should be a screen that should be able to show the proper status of the PSLab device connection to the android app dynamically and it should also contain instructions on “How to connect the device to the app”. So, in this blog we will create a device screen which shows the proper status of the connection of the PSLab device with the phone. First step will be designing the layout for the device screen, for this we will create a fragment named HomeFragment and for its layout we will make use of the Relative Layout as view group and then create a Linearlayout inside it and position it at the center so that it always appears at the center in different screen sizes. Then inside the LinearLayout, we will create(as shown in respective order) : ImageView and TextView for showing the status of device connection. Linear Layout with multiple TextView showing instructions on “How to connect the device to the screen”. A TextView that will direct the user to a webview showing PSLab website. After creating all the above views we have created the layout will look like this: - Now for showing the PSLab connection status dynamically, we have to implement following logic: When the device is connected it should show the connected icon and text and hide the instructions. When the device is disconnected it should show the disconnected icon and text and also the instructions. For this, we will create a method inside the HomeFragment Java file which make use of Arguments deviceConnected and deviceFound to store device connected status. public static HomeFragment newInstance(boolean deviceConnected, boolean deviceFound) { HomeFragment homeFragment = new HomeFragment(); homeFragment.deviceConnected = deviceConnected; homeFragment.deviceFound = deviceFound; return homeFragment; } When both arguments are true we will show the connected text and icon and hide the instructions. And when both arguments are false we will show the disconnected text and icon and display the instructions. if (deviceFound && deviceConnected) { tvConnectMsg.setVisibility(View.GONE); tvVersion.setText(scienceLab.getVersion()); tvVersion.setVisibility(View.VISIBLE); imgViewDeviceStatus.setImageResource(R.drawable.icons8_usb_connected_100); tvDeviceStatus.setText(getString(R.string.device_connected_successfully)); } else { imgViewDeviceStatus.setImageResource(R.drawable.icons_usb_disconnected_100); tvDeviceStatus.setText(getString(R.string.device_not_found)); } How do we know that the device is connected? For this, we have to handle the USB Attach event [1] that is whenever the USB is connected the Android will give a broadcast of USB connected and on receiving that broadcast in the app we will replace the HomeFragment giving setting both arguments to true.  We will create a Broadcast Receiver[2] in the main activity which executes it’s onReceive() method on receiving USB attach event. private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) { UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if(device!=null){ getSupportFragmentManager().beginTransaction().replace(R.id.frame, HomeFragment.newInstance(true, true)).commitAllowingStateLoss(); } } } }; Here In the OnReceive Method, we will replace our device screen fragment by passing parameters deviceConnected = true and deviceFound = true to HomeFragment newInstance() method. Every time we create a…

Continue ReadingCreating Device Screen to show connection status of PSLab Device

Publish an Open Source app on Fdroid

Fdroid is a famous software repository hosted with numerous free and open source Android apps. They have a main repository where they allow developers hosting free and ad free software after a thorough check up on the app. This blog will tell you how to get your project hosted in their repository using steps I followed to publish the PSLab Android app. Before you get started, make sure you have the consent from your developer community to publish their app on Fdroid. Fdroid requires your app to use all kind of open resources to implement features. If there is any closed source libraries in your app and you still want to publish it on Fdroid, you may have to reimplement that feature by any other mean without using closed source resources. They will also not allow to have Google’s proprietary “play-services” in your app along with proprietary ad services. You can find the complete inclusion policy document from their official page. When your app is fully ready, you can get started with the inclusion procedure. Unlike how we are publishing apps on Google Play, publishing an app on Fdroid is as simple as sending a pull request to their main repository. That’s exactly what we have to do. In simple terms all we have to do is: Fork the Fdroid main data repository Make changes to their files to include our app Do a pull request First of all you need a GitLab account as the Fdroid repository is hosted in GitLab. Once you are ready with a GitLab account, fork and clone the f-droid-data repository. The next step is to install the fdroid-server. This can be simply done using apt: $ sudo apt install fdroidserver Once that is done, go into the directory where you cloned the repository and run the following command to read current meta data where it saves all the information related to existing apps on Fdroid; $ fdroid readmeta This will list out various details about the current meta files. Next step is to add our app details into this meta file. This can be done easily using following command or you can manually create folders and files. But the following is safer; $ fdroid import --url https://github.com/fossasia/pslab-android --subdir app Replace the link to repository from the --url tag in the above command. For instance the following will be the link for fossasia-phimpme android; $ fdroid import --url https://github.com/fossasia/phimpme-android --subdir app This will create a file named as “org.fossasia.pslab” in the metadata directory. Open up this text file and we have to fill in our details. Categories License Web Site Summary Description Description needs to be terminated with a newline and a dot to avoid build failures. Once the file is filled up, run the following command to make sure that the metadata file is complete. $ fdroid readmeta Then run the following command to clean up the file $ fdroid rewritemeta org.fossasia.pslab We can automatically add version details using the following command: $ fdroid…

Continue ReadingPublish an Open Source app on Fdroid