All apps are developed in the Kotlin programming language. Since 2019, Kotlin has been the preferred language for developing Android applications. Google promotes using Kotlin, which was created by the JetBrains team. In the latest versions of Android Studio, starting a new project with the Java programming language is not even an option, although Java classes can still be added alongside Kotlin classes within the project structure. The Kotlin programming language is largely based on Java, but is more streamlined and modern. It first emerged in 2011 and is relatively new. Although Java still has more documentation available online, Kotlin is gaining popularity. Google has introduced an excellent code converter in Android Studio that converts Java to Kotlin, making app development enjoyable.
The software development cycle is iterative development, and the goal was to not use complex frameworks, which can slow down the apps, so standard linear coding was performed.
Next, the mathematical background of the apps is presented. The discrete versions of the formulas is implemented, and standard functions are used in the apps.
The objective improvements can be seen in the user interfaces, which are simple and clear, compared to the other overcomplicated apps. Also being simpler, they have better response times. The gains compared to other existing apps are that the presented apps have a simpler and more intuitive interface, which will be seen from the screenshots of the apps. They only use the necessary libraries and no cloud connection, and their source code is short, so they have less bugs and a very good response time.
3.1. Simple Gallery App
The basic idea of this app is to create a really simple gallery app, with no infinite number of functions that are not even used, making the app responsive and faster to load.
The gallery app is also one of the apps that appears to be simple, but is actually not. Initially, it needs to load all the images and videos in GridView or RecycleView. RecycleView is the best option due to its flexibility in loading different types of items (
Figure 1). After this, an image-loading API is needed. There are four options—load it natively or use one of the following three image loading libraries: Coil, Glide, or Picasso. It is better to use an image-loading API since it is faster and better suited to load different types of images and videos. Picasso is the oldest and may be the slowest. The best solution is Coil or Glide. Coil was chosen, because it is more optimized for the Kotlin language and leverages Kotlin’s language features. The initial task is to be able to load any type of image or video, with any image type encompassing the following: *.jpg, *.jpeg, *.gif, *.png, *.bmp, *.webp, *.tiff, *.avif, *.apng, *.svg, *.ico, and others. For videos, the goal is to be able to preview a screenshot from any common video types.
In the main Activity, all images should be loaded in a grid format, of course, taking care of portrait and landscape orientations. Here, the hardest part is resizing the thumbnails in order to fit a certain number of thumbnails in the width of the mobile device (the width being different in landscape and portrait views). From a design point of view, it is not always the best solution to adjust the distance between thumbnails.
In this Activity, we have another issue: we need to select images with a longer tap and maximize them with a simple tap. We need to select them with check marks or by changing the background color in order to share or delete multiple images. These buttons can appear in the bottom of the Activity, only when the selection process is initiated as in
Figure 2.
At the top, a counter can be made to show the number of selected images. To enable multiple touch events, a selection tracker should be used in RecycleView; this way, it will not pose any problems in distinguishing between a long tap and a short tap. The idea is to implement the selection process: first, we perform a long tap, but after short taps, we select multiple images. We would not like to confuse between long tap and short tap events; in other words, when we make short taps to select multiple images, we would not like to maximize them.
The initial Activity is actually made up of two activities with two corresponding Layout files. The RecycleView Activity is actually the Main Activity *.kt file with the main Layout *.xml file; also, a RecycleViewAdapter Activity *.kt file is needed which loads a card_layout Layout *.xml file.
The second page is the ImageDetail Activity *.kt file with the corresponding image_detail Layout *.xml file (
Figure 3). Here, we can have four buttons: share, info, delete, and back. For videos, there could be put a play button for video preview. The hardest part in this Activity is, again, the touch events; we need to zoom the image with a pinching motion and switch between images with left or right swipes. For this to work, we need to use another library. We need to extend our class to GestureDetector to adhere to TouchEvents by computing the x coordinates of the gesture to know if the swipe gesture was to the left or to the right. We also need to use ScaleGestureDetector to zoom in on the images.
As for the buttons, the share button is an Intent, but we need to share image or video files via e-mail, SMS, or a messaging app; the info button opens another infoview Layout *.xml file (
Figure 4) with image detail contents, the delete button deletes the image, and the back button goes back to the main RecycleView Activity. Unexpectedly, one of the hardest operations to code was the delete option, due to the fact that different functions were needed for different Android API versions, because the permissions in the Manifest file change with every new Android version (it was necessary to make another function for API 29 and another for API 30). The other bit that was complex to code, and maybe not useful, was the info button. It was too complex to code for the result it gave. This is an image gallery app, so image info can be a secondary task; not many people are interested in the size and date of images. The first part was to obtain the real path of the image, and then the creation date, and to put it in a specific format.
Table 1 shows performance and usability metrics fo the gallery app.
After that, for this file info, three separate functions were used: one for computing the megapixels, the other for the resolution, and the last for the image size on disk. All these data become quite complex to code, with respect to the output it gave. Of course, other extra image information could also be added, such as focal length or a map with a pin, where the image was taken.
As seen when creating a simple image gallery, this idea is not easy, but this image gallery was created to only make the basic function fast, not implement a large number of functions which are not even used.
The following techniques form the foundation of many image-processing workflows and are often combined to achieve complex tasks such as object detection, image compression, and enhancement.
Fourier Transform is applied for analyzing the frequencies in images:
: Fourier transformation of the image.
: Initial image in the spatial domain.
: Exponential function with complex arguments representing the frequency elements.
Fourier Transform (
Figure 5) breaks down an image’s frequency details by shifting it from the spatial to the frequency domain. The frequency components are typically illustrated using a graph to show their magnitudes. The 2D Fourier Transform dissects the image into various sinusoidal elements. The central area predominantly captures low-frequency components representing smooth regions, whereas high-frequency components signify edges and sharp details reside further from the center. Applications of the Fourier Transform include image filtering (such as low-pass and high-pass), compression, and noise reduction.
Gaussian blur is applied for image-smoothing purposes:
: Gaussian distribution function.
: The standard deviation of the Gaussian distribution.
: Locations within the image.
Gaussian blur (
Figure 6) acts as a low-pass filter to enhance image smoothness by reducing noise and detail. The kernel used resembles the shape of a bell curve. Applying a Gaussian blur involves convolving the image with a Gaussian kernel, where the kernel’s values stem from the Gaussian function. The degree of blurring is regulated by the kernel size and the standard deviation (
). Gaussian blur finds application in edge detection as a preprocessing step and in minimizing noise for image segmentation.
Convolution (
Figure 7) is utilized to apply filters to images:
: Post-convolution resulting image.
: Original image.
: Convolutional filter or kernel.
∗: Convolution process.
Figure 7.
Convolution of two functions, where the blue line is one of the functions and the red line is the other function.
Figure 7.
Convolution of two functions, where the blue line is one of the functions and the red line is the other function.
In image processing, filters are used on images through convolution to capture particular features, such as edges and textures. A diagram might help depict the process of convolving an image with a kernel. During convolution, a kernel (a small matrix) moves across the image, and at each position, the sum of the element-wise products of the kernel and the image segment yields the output pixel. Typical convolution filters include edge detection kernels (such as Sobel and Prewitt), blurring kernels (such as Gaussian), and sharpening kernels.
Principal Component Analysis (PCA) is utilized to reduce dimensions in image processing:
: Matrix of raw data (pictures).
: Matrix containing the principal component eigenvectors.
: Matrix of the projected data.
PCA (
Figure 8) helps reduce dimensionality while maintaining the majority of the variance present in the data. Typically, a graph is used to display the data points alongside the principal components. PCA is performed by calculating the eigenvectors and eigenvalues of the data’s covariance matrix and then projecting the data onto the principal components, which are the eigenvectors corresponding to the largest eigenvalues. PCA has applications in compression, achieved by diminishing color channels or pixel dimensions, noise reduction through the retention of only the most critical components, and feature extraction.
Color transformation (
Figure 9) within image processing can be expressed through matrix operations:
: Adjusted color values.
: Initial color components.
Matrix: Matrix used to adjust color values.
Figure 9.
Transforming colors through matrix operations.
Figure 9.
Transforming colors through matrix operations.
Matrix multiplication is often utilized in color transformations, which involve changing images between various color spaces for purposes such as analysis, enhancement, or compression. Some typical transformations include RGB to grayscale, which simplifies a color image to one channel; RGB to HSV/HSI, which divides intensity from color details, aiding segmentation and enhancement; and YCbCr, used in video compression. Color transformations aid in emphasizing particular color components, image segmentation, and thresholding. The following chart displays both the initial and transformed color space.
JPEG compression (
Figure 10) employs discrete cosine transform:
: DCT coefficient.
: Pixel value at coordinates .
and : Scaling factors.
cos: Cosine function utilized for the transformation.
Figure 10.
JPEG compression with discrete cosine transform (DCT).
Figure 10.
JPEG compression with discrete cosine transform (DCT).
JPEG utilizes discrete cosine transform (DCT) to compress images. DCT breaks down an image into segments of varying significance, known as spatial frequency components. Although comparable to the Fourier Transform, DCT relies exclusively on cosine functions. The majority of image data are encapsulated within a limited number of DCT coefficients. DCT is implemented in JPEG compression, which diminishes the image size by retaining only the most crucial coefficients and eliminates redundancies present in the image information. A typical graph can illustrate the DCT coefficients.
Image contrast can be improved through histogram equalization:
s: Pixel value following transformation.
: Mapping function.
L: Count of intensity levels.
N: The overall count of pixels.
: Distribution of the intensity values of the pixels.
Histogram equalization (
Figure 11) improves image contrast by redistributing the most common intensity values across the range. The image histogram’s cumulative distribution function (CDF) is calculated, and the initial pixel values are remapped based on the CDF. This technique is applied to enhance the visibility of low-contrast images and is used in preprocessing for object recognition or segmentation. An accompanying graph shows the original and equalized histograms.
Sobel operators serve the purpose of detecting edges (
Figure 12):
Figure 12.
Detecting edges (gradient magnitude).
Figure 12.
Detecting edges (gradient magnitude).
Algorithms such as Sobel are used to identify edges within an image by accentuating areas with significant changes in spatial gradient. This method involves convolving the image with two 3 × 3 kernels, one designed to detect horizontal edges and the other for vertical edges. The results of these convolutions are combined to determine the magnitude of the gradient. Sobel algorithms are utilized for tasks such as recognizing object boundaries and as a preprocessing step in computer vision tasks. A graph can represent the magnitude of the gradient.
Bilinear interpolation (
Figure 13) is employed to change the size of images:
: Pixel value obtained by interpolation.
a and b: Decimal parts of the coordinates.
: Known values of the pixels in adjacent positions.
Figure 13.
Bilinear interpolation.
Figure 13.
Bilinear interpolation.
Bilinear interpolation adjusts image size by predicting pixel values using the four closest neighboring pixels. During scaling, the value of each new pixel is determined by calculating the weighted average of these four surrounding pixels. This interpolation method is commonly applied in image resizing processes, such as upsampling and downsampling, as well as in geometric transformations like rotation and translation. A graph can illustrate the interpolation process among four points.
3.2. Simple File Manager App
The basic idea of this app is to make a simple file management application that loads the file system and can perform all basic operations. Again not having a lot of useless functions, it will be loaded faster and be more responsive. The app should load the entire file system with some fast access icons in the upper part of the app (
Figure 14). The app should present a preview of image thumbnails; here, Glide or Coil API can be used, Coil being better for the Kotlin programming language (
Figure 15).
With the app, there should be a possibility to select multiple files or folders to perform operations with multiple files or folders (
Figure 16). The basic operations are as follows: Details, New Folder, Cut, Copy, Paste, Rename, Share, and Delete (
Figure 17).
The app can show the location of files with breadcrumbs in the top. The fast access icons can be the main storage, images, videos, audios, documents, downloads, or photo camera pictures (DCIM).
The apps looks simple, having only one complex Activity *.kt file which can make all the operations enlisted before, but the app actually uses four Layout *.xml files—one for the main activity; here, the portrait and landscape orientations need to be taken care of. A custom_list Layout *.xml file for loading files and folders in a ListView, with an icon (file, folder, or image thumbnail) placed inline with the file or folder name. An infoview popup Layout *.xml file is needed, which is used to display the file information like the path, creation date, and file size on disk. Another newfolder Layout *.xml file is also needed for the popup, which is used to create a new folder or to rename a file or folder.
A simple file manager app was created to be as simple as possible, not have too complex code, load faster, and be more responsive.
Table 2 shows performance and usability metrics fo the file manager app.
The organization of a file system can be depicted as a tree diagram (
Figure 18).
The following techniques play vital roles in data storage optimization, data security, and ensuring data integrity in modern computing systems.
File size calculation (
Figure 19) is important for determining how much disk space a file takes. Several factors influence a file’s size: the type of data it contains (such as text, images, or videos, which differ in size depending on content and format), the encoding or format (text files in ASCII or UTF-8 require varying space for characters, while compressed formats like JPEG and PNG decrease file size by eliminating redundancy), metadata (such as the file’s creation date and permissions, which also consume space), and the file system block size (since files are stored in blocks, even tiny files using a minimum block size will contribute to take up disk space). To assess file sizes, directories and their subdirectories must be examined.
A bar graph that illustrates various file types along with their sizes can serve as an excellent visual representation.
S: Overall size.
N and b: Number of files.
: The size of the file i.
M: The total count of directories.
: Count of files within the j-th directory.
: The file size of the k-th item in the directory j.
Huffman encoding effectively reduces file size by compressing data. The process involves the following: frequency analysis, which identifies the occurrence frequency of each character in the input data; constructing a binary tree by creating a Huffman Tree, where characters that appear more frequently are assigned with shorter binary codes; and encoding, where characters are assigned with variable-length binary codes, with more common characters receiving shorter codes. This method is utilized for lossless compression in text files, images, and multimedia formats such as *.zip and *.png. In a Huffman encoding example, for the text ABRACADABRA, A might be represented by 0, B by 10, etc. Huffman encoding is used for compression without data loss.
: The Huffman encoding for the symbol x.
: The probability that the symbol i will appear.
: The number of bits in the Huffman code for symbol i.
A line graph can be used to visualize the relationship between the average length and frequency distribution of symbols in Huffman encoding (
Figure 20).
RSA’s objective is to facilitate secure data encryption through a public and private key pair. The RSA encryption process involves several steps: key generation, starting with the selection of two large prime numbers, p and q, followed by calculating and . A public key e is chosen such that and e are co-primed with . The private key d is determined by the relation . Once the keys are generated, the data can be encrypted and decrypted with the formulas provided below. RSA is applied in secure communications such as HTTPS, digital signatures, and file encryption. Its robustness comes from the challenge of factoring in large numbers. RSA encryption employs a key pair: the public key and the private key.
In the context of RSA encryption (
Figure 21), a straightforward graph could illustrate the correlation between the key size (measured in bits) and the encryption time.
Figure 21.
RSA encryption time.
Figure 21.
RSA encryption time.
Permissions in Unix-like systems are represented using a bitmask.
Merge these values for the owner, group, and other users.
r: Permission to read (4).
w: Permission to write (2).
x: Permission to execute (1).
The SHA-256 hash function is designed to generate a unique fixed-size hash value (or digest) from any input data, thus ensuring its integrity. It works as follows: input (any length of data, such as a file or message), processing (involves padding the input to make its length a multiple of 512 bits, initializing hash values with eight established 32-bit constants, and compressing by iteratively working through 512-bit data blocks), and output (a 256-bit (32-byte) hash value is produced). Notable characteristics of SHA-256 include the following: determinism (identical inputs always result in identical hashes), fixed length (always produces a 256-bit output, independent of input size), collision resistance (finding distinct inputs that produce the same hash is challenging), and pre-image resistance (deriving the original input from the hash is difficult). SHA-256 is used in various applications such as checking file integrity (e.g., download verification), digital signatures, and hashing passwords. The SHA-256 hash function is represented by the following formula:
A graph illustrating SHA-256 (
Figure 22) can display the avalanche effect by showing the Hamming distance between the hash outputs of minimally different inputs.
To identify duplicate files, you can utilize hash-based deduplication:
For disk I/O performance modeling,
Data Size: The amount of data to be moved.
Transfer Rate: Rate at which data are transferred.
Seek Time: Time required to position the read/write head on the disk.
Latency: The time taken to wait for the disk to spin to the correct position.
Using extents to allocate file storage:
RAID 5 storage computation for parity and user data: